今天我使用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 ++生成了逻辑上错误的代码。
有人知道为什么并会解释这个吗?
答案 0 :(得分:8)
0x80
(int
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
已修复的重要性感到困惑(因此char
是signed char
在x86上;在PowerPC上它可能是unsigned char
)。我不建议使用-funsigned-char
重新编译,但是如果你这样做,那么你正在更改ABI,你需要重新编译所有,包括你的C标准库和你的C ++标准库。