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

时间:2017-05-18 18:02:13

标签: c gcc assembly x86 warnings

编译时:

#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开关时,我得到no,gcc使用0x67前缀按预期切换地址大小(-m16基本上发出前缀32位代码):< / p>

00000000 <foo>:
   0:   66 55                   push   %ebp
   2:   66 89 e5                mov    %esp,%ebp
   5:   66 b8 00 80 0b 00       mov    $0xb8000,%eax
   b:   67 c7 00 61 0f          movw   $0xf61,(%eax)
  10:   90                      nop
  11:   66 5d                   pop    %ebp
  13:   66 c3                   retl

所以,显然这与优化开关-O1有关。 gcc手册页描述了它设置的所有选项,我编写了一个脚本来单独列出每个选项并将它们传递给gcc,但它并没有真正起作用。现在,gcc根本没有显示警告,即使是整个警告。

我很感激有关如何解决此问题的任何建议。

1 个答案:

答案 0 :(得分:1)

我会说这是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是合法的,但值&gt; 16位(0xffff)在16实模式下不受官方支持,32位受保护 - 它们是官方支持的

这就是编译器发出警告并将值0xb8000截断为0x8000以生成合法和官方支持的指令的原因。

注意:我相信gcc也应该在第一种情况下发出警告,因为它不能像你预期的那样工作16位:

  1. 在实模式下允许此类指示,但eax不能超过0xffff(实际上它不会使用eax,而只会使用ax部分。
  2. 在受保护/虚幻模式下,允许使用此类指令并使用完整eax
  3. 我不知道为什么gcc允许你使用m16标志,而不能正确支持16位代码生成和实模式内存模型。我建议你改用别的东西。 20年前,watcom非常酷。

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