使用真实的GAS(在Linux上),您的代码可以随意组合为mov rdx,sign_extended_imm32
。
但是,很遗憾,clang将其组装到mov rdx,[0xc]
中。那可能是bug,也可能不是bug,但这绝对是不兼容的。 (MacOS的gcc
命令根本不是GNU编译器集合,它是Apple Clang:LLVM后端,clang前端,与GNU项目完全无关。)
OFFSET hello_len
似乎无效。 (我误认为它会是第一个猜测,但是clang不支持OFFSET运算符;它的.intel_syntax
不能完全使用。)
这是clang bug has already been reported。另请参见Why does this simple assembly program work in AT&T syntax but not Intel syntax?
Clang甚至无法汇编自己的.intel_syntax noprefix
输出。
可能没有办法让clang Intel语法使用符号的值(地址)作为立即数。
// hello.c
char hello[] = "abcdef";
char *foo() { return hello; }
clang -S
打印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,这是一个错误,您应该在on的https://bugs.llvm.org
上报告它
(Linux非PIE可执行文件可以通过使用mov r32,imm32
而不是相对于RIP的LEA来利用静态地址位于虚拟地址空间的低32位中。当然不能使用mov r64,imm64
。)
解决方法:您不能只使用C预处理器。 . - hello
是上下文相关的;当.
位于不同位置时,它具有不同的值。因此,文本替换将无效。
丑陋的解决方法:切换到.att_syntax
并返回:
切换到.att_syntax
,然后再返回mov $hello_len,%edx
无效且无效的解决方法: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才能以64位代码对32位绝对寻址模式进行编码。
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无法优化它,并且即使位移可以容纳在disp8中,它仍然使用reg + disp32寻址模式。它的代码大小仍然比[abs disp32]
稍小,后者需要一个SIB字节;没有SIB字节,则编码意味着[RIP + rel32]
。
,
如果您将操作码更改为:
lea rax,hello_len
有效。在 old unix 中,as或=更冗长的.set对左值进行运算。在现实中,hello_len是一个地址;特别是地址12。
我无法用masm语法回忆起 = 。我记得 equ 具有类似的用途,但都没有明确说明。
我们主要使用 cpp (有时是awk)为我们完成任务,并避免了asm功能。
本文链接:https://www.f2er.com/3126851.html