优化禁止插入地址大小覆盖前缀
Optimization Disables Insertion of Address-Size Override Prefix
编译时:
#include <inttypes.h>
void foo(void)
{
*(uint16_t *) (0xb8000) = 0xf61;
}
和
gcc test.c -c -m16 -O1
我收到以下警告:
/tmp/ccyziKm4.s: Assembler messages:
/tmp/ccyziKm4.s:9: Warning: 753664 shortened to 32768
当我放下 -O1
开关时,我得到 none 并且 gcc 使用 0x67
前缀按预期切换地址大小(-m16
基本上发出前缀 32位码):
00000000 <foo>:
0: 66 55 push %ebp
2: 66 89 e5 mov %esp,%ebp
5: 66 b8 00 80 0b 00 mov [=13=]xb8000,%eax
b: 67 c7 00 61 0f movw [=13=]xf61,(%eax)
10: 90 nop
11: 66 5d pop %ebp
13: 66 c3 retl
所以,显然这与优化开关-O1
有关。 gcc 手册页描述了它设置的所有选项,我写了一个脚本来挑出每一个并将它们传递给 gcc,但它并没有真正起作用。现在,gcc 根本不显示警告,即使有一大堆警告。
我很感激任何关于如何解决这个问题的建议。
我会说这是 gcc 中的错误,但我看到了此行为背后的一些逻辑:
没有优化的 GCC 使用 2 条指令生成非常简单的代码(我更喜欢 intel 语法):
mov eax, 0xb8000 # move value 0xb8000 to eax
movw [eax], 0xf61 # move value 0xf61 to address stored in eax
二进制视图:
66 b8 00 80 0b 00
^ operation: move 16 bit value to 16 register ax
^ size override prefix to indicate that 32 bit data is used instead of 16 bit, so eax should be used instead of ax
66 c7 00 61 0f
^ operation: move 16 bit value to 16 address in ax
^ size override prefix
GCC with optimization 尝试优化,因此生成以下代码:
movw [0xb8000], 0xf61 # mov value 0xf61 directly to 32 bit address 0xb8000 without any intermediate registers
二进制视图:
66 c7 05 00 80 0b 00 61 0f
^ operation: move 16 bit value to 16 bit address
^ size override prefix
因此,32 位操作码实际上与 16 位操作码相同,但 66/67 prefix。
问题来了:
- 操作
movw [REGISTER], 0xf61
在两种16/32模式下都是合法的和官方支持的
- 操作
movw [0xb8000], 0xf61
是合法的,但值 > 16 位 (0xffff) 在 16 实模式下不受官方支持,在 32 保护模式下 - 它们得到官方支持
这就是编译器发出警告并将值 0xb8000 截断为 0x8000 以生成合法且官方支持的指令的原因。
注意:我认为 gcc 在第一种情况下也应该发出警告,因为它在 16 位中不像您预期的那样工作:
- 在实模式下允许这样的指令,但是
eax
不能超过0xffff
(实际上它不使用eax
而只使用ax
部分)。
- 在 protected/unreal 模式下允许这样的指令并且将使用完整的
eax
。
我不知道为什么 gcc 允许您使用 m16
标志,但不正确支持 16 位代码生成和实模式内存模型。我建议你改用别的东西。 20年前watcom很酷
如果您处于虚幻模式,它自动意味着您可以而且应该使用 m32 指令。
编译时:
#include <inttypes.h>
void foo(void)
{
*(uint16_t *) (0xb8000) = 0xf61;
}
和
gcc test.c -c -m16 -O1
我收到以下警告:
/tmp/ccyziKm4.s: Assembler messages:
/tmp/ccyziKm4.s:9: Warning: 753664 shortened to 32768
当我放下 -O1
开关时,我得到 none 并且 gcc 使用 0x67
前缀按预期切换地址大小(-m16
基本上发出前缀 32位码):
00000000 <foo>:
0: 66 55 push %ebp
2: 66 89 e5 mov %esp,%ebp
5: 66 b8 00 80 0b 00 mov [=13=]xb8000,%eax
b: 67 c7 00 61 0f movw [=13=]xf61,(%eax)
10: 90 nop
11: 66 5d pop %ebp
13: 66 c3 retl
所以,显然这与优化开关-O1
有关。 gcc 手册页描述了它设置的所有选项,我写了一个脚本来挑出每一个并将它们传递给 gcc,但它并没有真正起作用。现在,gcc 根本不显示警告,即使有一大堆警告。
我很感激任何关于如何解决这个问题的建议。
我会说这是 gcc 中的错误,但我看到了此行为背后的一些逻辑:
没有优化的 GCC 使用 2 条指令生成非常简单的代码(我更喜欢 intel 语法):
mov eax, 0xb8000 # move value 0xb8000 to eax
movw [eax], 0xf61 # move value 0xf61 to address stored in eax
二进制视图:
66 b8 00 80 0b 00
^ operation: move 16 bit value to 16 register ax
^ size override prefix to indicate that 32 bit data is used instead of 16 bit, so eax should be used instead of ax
66 c7 00 61 0f
^ operation: move 16 bit value to 16 address in ax
^ size override prefix
GCC with optimization 尝试优化,因此生成以下代码:
movw [0xb8000], 0xf61 # mov value 0xf61 directly to 32 bit address 0xb8000 without any intermediate registers
二进制视图:
66 c7 05 00 80 0b 00 61 0f
^ operation: move 16 bit value to 16 bit address
^ size override prefix
因此,32 位操作码实际上与 16 位操作码相同,但 66/67 prefix。
问题来了:
- 操作
movw [REGISTER], 0xf61
在两种16/32模式下都是合法的和官方支持的 - 操作
movw [0xb8000], 0xf61
是合法的,但值 > 16 位 (0xffff) 在 16 实模式下不受官方支持,在 32 保护模式下 - 它们得到官方支持
这就是编译器发出警告并将值 0xb8000 截断为 0x8000 以生成合法且官方支持的指令的原因。
注意:我认为 gcc 在第一种情况下也应该发出警告,因为它在 16 位中不像您预期的那样工作:
- 在实模式下允许这样的指令,但是
eax
不能超过0xffff
(实际上它不使用eax
而只使用ax
部分)。 - 在 protected/unreal 模式下允许这样的指令并且将使用完整的
eax
。
我不知道为什么 gcc 允许您使用 m16
标志,但不正确支持 16 位代码生成和实模式内存模型。我建议你改用别的东西。 20年前watcom很酷
如果您处于虚幻模式,它自动意味着您可以而且应该使用 m32 指令。