Optimization disables address forwarding prefix insertion
When compiling:
#include <inttypes.h>
void foo(void)
{
*(uint16_t *) (0xb8000) = 0xf61;
}
from
gcc test.c -c -m16 -O1
I am getting the following warning:
/tmp/ccyziKm4.s: Assembler messages:
/tmp/ccyziKm4.s:9: Warning: 753664 shortened to 32768
And when I drop the switch -O1
, I don't get it, and gcc uses the prefix 0x67
to switch the address size as expected (it -m16
basically emits prefixed 32-bit code):
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
So obviously it has to do with the optimization switch -O1
. The gcc man page describes all the options it sets, and I wrote a script to highlight each one and pass them to gcc, but it doesn't really work. Now gcc does not display any warning at all, even with all linkage.
I appreciate any suggestions on how to resolve this.
source to share
I would say this is a bug in gcc, but I can see some logic behind this behavior:
GCC without optimization produces pretty simple code with two instructions (I prefer the syntax of intelligence):
mov eax, 0xb8000 # move value 0xb8000 to eax
movw [eax], 0xf61 # move value 0xf61 to address stored in eax
Binary view:
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
Optimized GCC tries to optimize, so it generates the following code:
movw [0xb8000], 0xf61 # mov value 0xf61 directly to 32 bit address 0xb8000 without any intermediate registers
Binary view:
66 c7 05 00 80 0b 00 61 0f
^ operation: move 16 bit value to 16 bit address
^ size override prefix
So, 32-bit opcodes are actually the same opcodes as 16 bits, but prefixed with 66/67 .
And here's the problem:
- The operation
movw [REGISTER], 0xf61
is official and officially supported in 16/32 modes. - Operation
movw [0xb8000], 0xf61
is legal, but values> 16 bits (0xffff) are not officially supported in 16 real mode, in 32 protected ones - they are officially supported
This is why the compiler issues a warning and truncates the value from 0xb8000 to 0x8000 to create a legal and officially supported statement.
Note. I believe gcc should also issue a warning in the first case, as it doesn't work the way you would expect in 16 bits:
- In real mode, such an instruction is allowed, but
eax
cannot exceed0xffff
(effectively it does not useeax
, but only a partax
). - in protected / unreal mode such instruction is allowed and full will be used
eax
.
I don't know why gcc allows the flag to be used m16
without properly supporting the generation of 16-bit code and real mode memory models. I suggest you switch to something else. 20 years ago watcom was really cool.
If you are in unreal mode, this automatically means that you can and should use m32 instructions.
source to share