С реальным GAS (в Linux) ваш код собирается в mov rdx, sign_extended_imm32
так, как вы хотите.
Но, к сожалению, clang собирает его в mov rdx, [0xc]
. Это может быть или не быть ошибкой, но это определенно несовместимость. (Команда MacOS gcc
вообще не является коллекцией компиляторов GNU, это Apple Clang: бэкэнд LLVM, внешний интерфейс clang, абсолютно никакого отношения к проекту GNU.)
OFFSET hello_len
непохоже на работу . (Я неправильно предположил, что это будет на первый взгляд, но clang не поддерживает оператор OFFSET; он .intel_syntax
не полностью пригоден для использования.)
Clang не может даже собрать егособственный .intel_syntax noprefix
вывод.
Не может быть способа заставить лягушку синтаксиса Intel использовать значение (адрес) символа в качестве немедленного.
// hello.c
char hello[] = "abcdef";
char *foo() { return hello; }
clang -S
print mov edi, offset hello
который не будет собираться с помощью встроенного ассемблера clang! https://godbolt.org/z/x7vmm4.
$ clang -fno-pie -O1 -S -masm=intel hello.c
$ clang -c hello.s
hello.s:10:18: error: cannot use more than one symbol in memory operand
mov eax, offset hello
^
$ clang --version
clang version 8.0.1 (tags/RELEASE_801/final)
Target: x86_64-pc-linux-gnu
...
IMO, это ошибка, вы должны сообщить об этом на https://bugs.llvm.org
от clang (исполняемые файлы Linux без PIE могут использовать статические преимуществаадреса находятся в младших 32 битах виртуального адресного пространства, используя mov r32, imm32
вместо REA-относительного LEA. И, конечно, не mov r64, imm64
.)
Обходные пути: вы не можете просто использоватьС препроцессором. . - hello
является контекстно-зависимым;он имеет другое значение, когда .
- это другая позиция. Таким образом, подстановка текста не будет работать.
Ugly Обходной путь: переключитесь на .att_syntax
и обратно:
Переключитесь на .att_syntax
и обратно на mov $hello_len, %edx
Uglyи неэффективный обходной путь: lea
Это не сработает для 64-битных констант, но вы можете использовать lea
для помещения адреса символа в регистр.
К сожалению, clang / LLVMвсегда использует режим адресации disp32
, даже для регистра + маленькая константа, когда маленькая константа является именованным символом. Я предполагаю, что это действительно обрабатывает его как адрес, который может иметь перемещение.
С учетом этого источника:
## your .rodata and = or .equ symbol definitions
_main:
mov eax, 0x2000004 # optimized from RAX
mov edi, 1
lea rsi, [rip + hello]
mov edx, hello_len # load
lea edx, [hello_len] # absolute disp32
lea edx, [rdi-1 + hello_len] # reg + disp8 hopefully
# mov esi, offset hello # clang chokes.
# mov rdx, OFFSET FLAT hello_len # clang still chokes
.att_syntax
lea -1+hello_len(%rdi), %edx
lea -1+12(%rdi), %edx
mov $hello_len, %edx
.intel_syntax noprefix
syscall
mov rax, 0x2000001
syscall
clang собирает его в этот машинный код, разобранный objdump -drwC -Mintel
. Обратите внимание, что LEA необходим ModRM + SIB для кодирования 32-битного режима абсолютной адресации в 64-битном коде.
0: b8 04 00 00 02 mov eax,0x2000004 # efficient 5-byte mov r32, imm32
5: bf 01 00 00 00 mov edi,0x1
# RIP-relative LEA
a: 48 8d 35 00 00 00 00 lea rsi,[rip+0x0] # 11 <_main+0x11> d: R_X86_64_PC32 .data-0x4
11: 8b 14 25 0c 00 00 00 mov edx,DWORD PTR ds:0xc # the load we didn't want
18: 8d 14 25 0c 00 00 00 lea edx,ds:0xc # LEA from the same [disp32] addressing mode.
1f: 8d 97 0b 00 00 00 lea edx,[rdi+0xb] # [rdi+disp32] addressing mode, missed optimization to disp8
25: 8d 97 0b 00 00 00 lea edx,[rdi+0xb] # AT&T lea -1+hello_len(%rdi), %edx same problem
2b: 8d 57 0b lea edx,[rdi+0xb] # AT&T with lea hard-coded -1+12(%rdi)
2e: ba 0c 00 00 00 mov edx,0xc # AT&T mov $hello_len, %edx
33: 0f 05 syscall
35: 48 c7 c0 01 00 00 02 mov rax,0x2000001 # inefficient mov r64, sign_extended_imm32 from your source
3c: 0f 05 syscall
GAS, собирающий один и тот же источник, делает 8d 57 0b lea edx,[rdi+0xb]
для lea edx, [rdi-1 + hello_len]
версии.
См. https://codegolf.stackexchange.com/questions/132981/tips-for-golfing-in-x86-x64-machine-code/132985#132985 - LEA из регистра с известной константой является выигрышем для размера кода с близкими / маленькими константами и на самом деле хорош для производительности . (Пока известная константа получилась таким образом без зависимости от длинной цепочки вычислений).
Но, как вы можете видеть, clang не может оптимизировать это и все еще использует режим адресации reg + disp32, даже когдасмещение будет соответствовать дисплею8. Это все еще немного лучший размер кода, чем [abs disp32]
, для которого требуется байт SIB;без байта SIB эта кодировка означает [RIP + rel32]
.