MIPS 和 x86_64 之间的对象对齐差异
Difference in object alignment between MIPS and x86_64
我有一个二进制对象,它是使用 MIPSpro 编译器在 SGI 64 位机器上生成的。 我正在尝试在运行 RHEL 6.7 的 64 位x86_64机器上读取此二进制对象。对象的结构类似于
class A {
public:
A(){
a_ = 1;
}
A(int a){
a_ = a;
}
virtual ~A();
protected:
int a_;
};
class B : public A {
public:
// Constructors, methods, etc
B(double b, int a){
b_ = b;
a_ = a;
}
virtual ~B();
private:
double b_;
};
A::~A(){}
B::~B(){}
读取二进制文件后,交换字节(由于字节序),我发现b
是正确的,但a
未对齐,表明数据与我当前的构建未对齐。
我对此有两个问题。 首先,MIPS Pro编译器如何对齐其字段,这与gcc
的工作方式有何不同。 我对继承类的情况感兴趣。 其次,gcc
或C++中是否有一个选项可以强制对齐方式与MIPS相同?
更新 1: 为了进一步澄清,代码是在MIPS ABI n64上编译的。 我可以访问原始C++源代码,但我无法在MIPS机器上更改它。 我不得不在x86_64上阅读二进制文件。
更新 2: 我在向两台机器上的两个类添加virtual
析构函数之前和之后都运行了 sizeof 命令。
在 MIPS 和 x86_64 上,虚拟指令之前的输出为
size of class A: 4
size of class B: 16
添加virtual
方法后,在 SGI MIPS 上,输出为
size of class A: 8
size of class B: 16
在 x86-64 Linux 上:
size of class A: 16
size of class B: 24
看起来虚拟方法(或者只是一般的方法?)在这些机器上的处理方式是不同的。 任何想法为什么或如何解决这个问题?
希望使两个结构的二进制布局与继承相匹配,并拥有虚拟方法和跨不同的字节序,在我看来就像一个失败的原因(我什至不知道你是如何设法使fwrite
/fread
序列化即使在相同的架构上工作的 - 覆盖 vtable 地址是灾难的秘诀 - 即使在"正常"架构上,也没有什么能保证你它们将位于同一地址中,即使跨完全相同二进制文件的多次运行也是如此)。
现在,如果这种序列化格式已经写成一成不变,并且你必须处理它,我会完全避免"匹配二进制布局"的方式;你会生气并得到一个非常脆弱的结果。
相反,首先一劳永逸地找出源数据的确切二进制布局;您可以使用MIPS机器上所有成员的offsetof
轻松完成,甚至只需打印每个成员的地址并计算相关差异。
现在您已经有了二进制布局,请编写一些独立于体系结构的反序列化代码。假设您发现您发现A
是由以下部分组成的:
- 0x00:VPTR(8字节);
- 0x08:
a_
(4 字节); - 0x0c: (填充) (4 字节)
B
由以下部分组成:
- 0x00:VPTR(8字节);
- 0x08:
A::a_
(4 字节); - 0x0c:(填充)(4 个字节);
- 0x10:
b_
(8 字节)。
然后,您将编写代码,手动反序列化给定结构中的每个字段。例如:
typedef unsigned char byte;
uint32_t read_u32_be(const byte *buf) {
return uint32_t(buf[0])<<24 |
uint32_t(buf[1])<<16 |
uint32_t(buf[2])<<8 |
uint32_t(buf[3]);
}
int32_t read_i32_be(const byte *buf) {
// assume 2's complement in unsigned -> signed conversion
return read_u32_be(buf);
}
double read_f64_be(const byte *buf) {
static_assert(sizeof(double)==8);
double ret;
std::reverse_copy(buf, buf+8, (byte*)&ret);
return ret;
}
void read_A(const byte *buf, A& t) {
t.a_ = read_i32_be(buf+8);
}
void read_B(const uint8_t *buf, B& t) {
read_A(buf, t);
t.b_ = read_f64_be(buf+0x10);
}
请注意,这不是浪费精力,因为如果您碰巧更改了编译器、编译设置或任何其他可能影响类二进制布局的内容,即使对于 MIPS 版本,您也很快就会需要此代码。
顺便说一句,此代码的生成可能是自动化的,因为它是调试信息中可用的所有数据;因此,如果您有许多这种犯罪序列化格式的结构,则可以半自动生成反序列化代码(并将它们移动到更合理的位置以备将来使用)。
逆向工程:
将一些不同的对象实例写入多个文件中。
使用十六进制编辑器查找差异。
至少您可以获得二进制文件中每个值的位置。
最后,处理字节序。
- 哪些值存储在对齐的结构/类对象的填充字节中
- 如何分配适合容纳 T 类型对象的缓冲区(可能过度对齐、可能有运算符 new 等)
- 对象地址是否保证是其类型对齐的倍数
- MIPS 和 x86_64 之间的对象对齐差异
- Sizeof 舍入到对齐方式,但编译器仍将对象放在剩余的字节中
- 自动热键:重新映射 MS Visio 的快捷方式,以将手动选择的对象对齐到顶部(好像按:"AlignTop")
- 在以下情况下,是否可以分配未对齐的对象?
- 如何分配内存对齐C++对象数组?
- STD :: Aligned_Storage如何正确对齐任何对象的存储
- std::可选和提升::可选是否尊重托管对象的对齐限制?
- 如何强制对齐堆分配的对象
- 将对象位置与网格对齐
- 对象数组对齐与 __attribute__aligned() 或 alignas()
- 对象数组作为链表中的字符数组 - 我对对齐的假设是否有效
- 如何使用指针合法地访问对齐错误的对象
- 我不明白在下面的代码中将 char buffer[] 与 X 类型的对象对齐的原因
- 标准是否要求自动存储中的对象对任何类型都具有正确的对齐方式(例如malloc)
- xcode中的对象字节对齐
- 子对象的对齐小于基对象的对齐
- 类与虚拟函数的成员对象对齐和动态分配的问题