MIPS和x86_64之间对象对齐的差异

Ili*_*eit 4 c++ x86-64 mips abi

我有一个使用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(){}
Run Code Online (Sandbox Code Playgroud)

读取二进制文件后,交换字节(由于字节顺序),我发现它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
Run Code Online (Sandbox Code Playgroud)

添加virtual方法后,在SGI MIPS上输出为

size of class A: 8
size of class B: 16
Run Code Online (Sandbox Code Playgroud)

在x86-64 Linux上:

size of class A: 16
size of class B: 24
Run Code Online (Sandbox Code Playgroud)

看起来在这些机器上处理虚拟方法(或者只是一般方法?)的方式是不同的.任何想法为什么或如何解决这个问题?

Mat*_*lia 7

希望能够让这两个结构匹配的二进制布局与继承 有虚方法 跨不同字节在我看来就像一个失败的事业(我甚至不知道你是怎么设法做fwrite/ fread即使在相同的架构序列化的工作-覆盖vtable地址是灾难的一个方法 - 即使在"普通"架构上也没有任何保证,即使在完全相同的二进制文件的多次运行中它们也将位于同一地址中.

现在,如果这个序列化格式已经写好,你必须处理它,我完全避免"匹配二进制布局"的方式; 你会生气,得到一个非常脆弱的结果.

相反,首先要一次找出源数据的确切二进制布局; 您可以轻松地使用offsetofMIPS计算机上的所有成员,甚至只需打印每个成员的地址并计算相关差异.

现在您已经拥有了二进制布局,请编写一些与架构无关的反序列化代码.假设你发现你发现它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);
}
Run Code Online (Sandbox Code Playgroud)

请注意,这不是浪费精力,因为如果您碰巧更改编译器,编译设置或可能影响类的二进制布局的任何其他内容,即使是MIPS版本,您也很快就会需要此代码.

顺便说一句,这段代码的生成可能是自动化的,因为它是调试信息中可用的所有数据 ; 所以,如果你有这种犯罪序列化格式的许多结构,你可以半自动生成反序列化代码(并将它们转移到更适合未来的东西).