G ++生成逻辑上错误的汇编代码

时间:2017-11-24 14:49:48

标签: c++ g++ real-mode

今天我使用g ++编译c ++文件,但是g ++似乎存在一个问题,即当char类型的变量与任意数量的char类型比较,其中bit 7设置为1时,g ++将始终假设为false使得这些东西错了。更具体地说,c ++代码看起来像这样:

  // test.cpp
    __asm__(".code16gcc \n\t");
    int equals0(char i)
    {
        return i==0x80;
    }
    int equals1(char i)
    {
        return i==0x10;
    }
    int equals2(int i)
    {
        return i==0x80;
    }

请注意,有一个前导句和#34; .code16gcc",我用它生成实模式代码。

更具体地说,我的g ++版本是" g ++(GCC)6.4.0"在cygwin上。

现在,使用以下命令将此文件编译为汇编代码: g++ -S -o test.s test.cpp -m32

结果文件是:

     // some trival information is omitted
    .code16gcc 

    .text
    ...
__Z7equals0c://func equals0
    pushl   %ebp
    movl    %esp, %ebp
    subl    $4, %esp
    movl    8(%ebp), %eax
    movb    %al, -4(%ebp)  //no comparison with 0x80 
    movl    $0, %eax   //always returns 0
    leave
    ret
    ...
__Z7equals1c://func equals1
    pushl   %ebp
    movl    %esp, %ebp
    subl    $4, %esp
    movl    8(%ebp), %eax
    movb    %al, -4(%ebp)
    cmpb    $16, -4(%ebp)  // a comparison with 0x10
    sete    %al
    movzbl  %al, %eax    // returns value depend on the result of comparison
    leave
    ret
    ...
__Z7equals2i://func equals2
    pushl   %ebp
    movl    %esp, %ebp
    cmpl    $128, 8(%ebp) //also a comparison
    sete    %al
    movzbl  %al, %eax
    popl    %ebp
    ret

请注意,函数equals0将始终返回0(保存在eax中),但函数equals1将根据生成的汇编文件返回正确的结果。

并且还要注意,函数equals0和equals1之间的唯一区别是用于比较的常量,对于equals0为0x80,对于equals1为0x10。基于此,我们可以说g ++生成了逻辑上错误的代码。

有人知道为什么并会解释这个吗?

2 个答案:

答案 0 :(得分:8)

0x80int 128)超出了签名字符范围([-128;127])。

所以签名的字符不能等于0x80

答案 1 :(得分:2)

  

请注意,有一个前导句和#34; .code16gcc",我用它生成实模式代码。

错误。您的__asm__(".code16gcc \n\t");只是 emit 某些汇编程序指令(这将使您的汇编程序切换模式)。但是您想要更改GCC编译器的行为(例如,您希望GCC发出实模式16位汇编代码)。

AFAIK,你不能轻易做到这一点。也许您可以找到GCC的变体(可能是一些交叉编译器)来发出实模式代码,然后您需要在C ++转换单元上使用该变体。你正梦想GCC的一些-m16realmode旗帜不存在。 GCC -m16 mode(非常混乱地命名!)仍然发出32位代码:

  

-m16选项与-m32相同,除了它在汇编输出的开头输出.code16gcc汇编指令,以便二进制可以在16位运行模式。

(实际上,二进制不能以16位模式运行,除非你提供并调用一些额外的代码切换到32位;你可能需要在汇编程序中编写那个开关模式代码)

2017年,没有理由使用传统实模式16位代码。可能有用的唯一情况是编写引导加载程序时。那么你不应该使用GCC,更重要的是你应该使用一些现有的引导装载程序(如GRUB)并将精力集中在其余部分上。

如果您正在为玩具操作系统编码(并且您应该至少以32位模式对其进行编码),请查看OSDEV wiki以获取提示。

  

现在,使用以下代码将此文件编译为汇编代码:g++ -S -o test.s test.cpp -m32

对于整个源仍然使用32位模式(不是16位传统)(并在其中错误地插入错误的 .code16gcc汇编程序指令)。顺便说一下,你最好要求一些优化。所以我建议使用g++ -m32 -Wall -O -fverbose-asm -S -o test.s test.cpp进行编译。

顺便说一句,您对char x86架构中-m32 已修复的重要性感到困惑(因此charsigned char在x86上;在PowerPC上它可能是unsigned char)。我不建议使用-funsigned-char重新编译,但是如果你这样做,那么你正在更改ABI,你需要重新编译所有,包括你的C标准库和你的C ++标准库。