优化禁止插入地址大小覆盖前缀

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 位中不像您预期​​的那样工作:

  1. 在实模式下允许这样的指令,但是eax不能超过0xffff(实际上它不使用eax而只使用ax部分)。
  2. 在 protected/unreal 模式下允许这样的指令并且将使用完整的 eax

我不知道为什么 gcc 允许您使用 m16 标志,但不正确支持 16 位代码生成和实模式内存模型。我建议你改用别的东西。 20年前watcom很酷

如果您处于虚幻模式,它自动意味着您可以而且应该使用 m32 指令。