TPM可信计算:PCR扩展、信任链与vTPM回滚机制解析
2026/11/2 5:03:18
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
person.cpp:
#include"Person.h"classTest{public:intm_a=1;};intperson(){Test t;inta=t.m_a;returna;}person2.cpp:
#include"person2.h"classTest{public:intm_a=10;};intperson2(){Test t;inta=t.m_a;returna;}main.cpp:
#include<iostream>#include"Person.h"#include"person2.h"intmain(){std::cout<<person()<<std::endl;std::cout<<person2()<<std::endl;}打印的结果不是1、10,而是1、1,分析下原因
C++程序的构建分为两个阶段:
.cpp文件是独立的「编译单元」,编译器单独处理每个编译单元,不感知其他编译单元的代码;.o/.obj)合并为最终可执行文件,此时会处理全局范围内的符号(类、函数、全局变量等)。你的代码中,person.cpp和person2.cpp都在全局命名空间中定义了同名类Test:
person.cpp中Test::m_a = 1;person2.cpp中Test::m_a = 10。这直接违反了C++的ODR规则:
ODR规则要求:在整个程序中,一个类(或函数、全局变量)的定义必须唯一。如果出现多个不同的定义,属于「未定义行为(UB)」,链接器会任意选择其中一个定义作为最终生效的版本。
编译阶段:
person.cpp编译时,编译器看到Test的定义是m_a=1,因此person()函数中t.m_a取值为1,生成的目标文件(person.o)中记录Test的符号关联到m_a=1;person2.cpp编译时,编译器看到Test的定义是m_a=10,因此person2()函数中t.m_a取值为10,生成的目标文件(person2.o)中记录Test的符号关联到m_a=10;链接阶段:
链接器合并person.o和person2.o时,发现两个目标文件中都有全局命名空间的Test类符号。由于ODR违规,链接器不会报错(C++标准未要求链接器检测类的重复定义),而是「择优选择」其中一个定义(通常是先遇到的那个,比如person.o中的Test)。
最终,person2()函数实际使用的是person.cpp中Test的定义(m_a=1),因此输出结果是1、1而非1、10。
核心思路:隔离两个Test类的作用域,让它们成为不同的符号,避免链接冲突。常见方案有两种:
匿名命名空间的特性:编译单元内的匿名命名空间中的实体,仅在当前编译单元可见(内部链接),不会暴露到全局符号表,自然避免冲突。
修改person.cpp:
#include"Person.h"// 匿名命名空间:仅当前cpp可见namespace{classTest{public:intm_a=1;};}intperson(){Test t;inta=t.m_a;returna;}修改person2.cpp:
#include"person2.h"// 匿名命名空间:仅当前cpp可见namespace{classTest{public:intm_a=10;};}intperson2(){Test t;inta=t.m_a;returna;}给每个Test类指定不同的命名空间,显式隔离作用域:
person.cpp:
#include"Person.h"namespacePersonNS{classTest{public:intm_a=1;};}intperson(){PersonNS::Test t;// 显式指定命名空间inta=t.m_a;returna;}person2.cpp:
#include"person2.h"namespacePerson2NS{classTest{public:intm_a=10;};}intperson2(){Person2NS::Test t;// 显式指定命名空间inta=t.m_a;returna;}person2.o),输出可能变成10、10,这也是UB的典型特征。修复后,两个Test类成为独立的实体,链接器不会混淆,最终输出会是预期的1、10。