如何获取`mov rdx,symbol`来移动符号值而不是clang intel语法中符号地址的值? [英] How to get `mov rdx, symbol` to move symbol value and not value at symbol's address in clang intel-syntax?
问题描述
我有以下代码与macOS上的clang一起使用:
I have the following code which I'm using with clang on macOS:
.intel_syntax noprefix
.data
hello: .ascii "Hello world\n"
hello_len = . - hello
.text
.globl _main
_main:
mov rax, 0x2000004
mov rdi, 1
lea rsi, [rip + hello]
mov rdx, hello_len # <-------
syscall
mov rax, 0x2000001
syscall
虽然看起来它应该打印"Hello World"并退出,但实际上是段错误.事实证明,这是因为mov rdx, hello_len
实际上试图移动地址hello_len
上的值,而不是hello_len
本身的值.
While it looks like it should print "Hello World" and exit, it actually segfaults. It turns out it's because mov rdx, hello_len
actually tries to move the value that is at address hello_len
, not the value of hello_len
itself.
如果我使用AT& T语法,则该行将为movq $hello_len, %rdx
,该行可以正常工作. clang的GAS intel语法版本等效于什么?
If I used AT&T syntax, the line would be movq $hello_len, %rdx
which works properly. What's the equivalent in clang's version of GAS intel syntax?
推荐答案
借助真正的GAS(在Linux上),您的代码可以随意组装为mov rdx, sign_extended_imm32
.
With real GAS (on Linux), your code assembles to a mov rdx, sign_extended_imm32
like you want.
但是,是的,很不幸,clang将其组装到了mov rdx, [0xc]
中.那可能是bug,也可能不是bug,但这绝对是不兼容的. (MacOS的gcc
命令根本不是GNU编译器集合,它是Apple Clang:LLVM后端,clang前端,与GNU项目完全无关.)
But yes, clang assembles it to mov rdx, [0xc]
unfortunately. That may or may not be a bug, but it's definitely an incompatibility. (MacOS's gcc
command is not the GNU Compiler Collection at all, it's Apple Clang: LLVM backend, clang frontend, absolutely nothing to do with the GNU project.)
OFFSET hello_len
似乎无效. (我一开始就错误地认为它会,但是clang不支持OFFSET运算符;它的.intel_syntax
不能完全使用.)
OFFSET hello_len
doesn't seem to work. (I had incorrectly assumed it would on first guess, but clang doesn't support the OFFSET operator; it's .intel_syntax
is not fully usable.)
这是 c报告错误.另请参见为什么这个简单的汇编程序可以使用AT& T语法,但不能使用Intel语法?
Clang甚至无法汇编自己的.intel_syntax noprefix
输出.
可能没有办法让clang Intel语法使用符号的值(地址)作为立即数.
Clang can't even assemble its own .intel_syntax noprefix
output.
There may not be a way to get clang Intel syntax to use a symbol's value (address) as an immediate.
// hello.c
char hello[] = "abcdef";
char *foo() { return hello; }
clang -S
打印的mov edi, offset hello
不会与clang的内置汇编器一起汇编! https://godbolt.org/z/x7vmm4 .
clang -S
prints mov edi, offset hello
which won't assemble with clang's built-in assembler! 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,这是一个错误,您应该在clang的 https://bugs.llvm.org
IMO this is a bug, you should report it on clang's https://bugs.llvm.org
(Linux非PIE可执行文件可以通过使用mov r32, imm32
而不是相对于RIP的LEA来利用静态地址位于虚拟地址空间的低32位中.当然不能使用mov r64, imm64
.)
(Linux non-PIE executables can take advantage of static addresses being in the low 32 bits of virtual address space by using mov r32, imm32
instead of RIP-relative LEA. And of course not mov r64, imm64
.)
解决方法:您不能只使用C预处理器. . - hello
是上下文相关的;当.
位于不同位置时,它具有不同的值.因此,文本替换将无效.
Workarounds: you can't just use the C preprocessor. . - hello
is context-sensitive; it has a different value when .
is a different position. So a text substitution wouldn't work.
切换到.att_syntax
并返回mov $hello_len, %edx
这不适用于64位常量,但是您可以使用lea
将符号地址放入寄存器中.
This won't work for 64-bit constants, but you can use lea
to put a symbol address into a register.
不幸的是,当小常量是命名符号时,即使对于寄存器+小常量,clang/LLVM始终使用disp32
寻址模式.我想它真的把它当作可能有重定位的地址来对待.
Unfortunately clang/LLVM always uses a disp32
addressing mode, even for register + small constant, when the small constant is a named symbol. I guess it really is treating it like an address that might have a relocation.
提供此来源:
## 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位绝对寻址模式进行编码.
clang assembles it to this machine code, as disassembled by objdump -drwC -Mintel
. Note that the LEA needs a ModRM + SIB to encode a 32-bit absolute addressing mode in 64-bit code.
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]
版本.
GAS assembling the same source makes 8d 57 0b lea edx,[rdi+0xb]
for the lea edx, [rdi-1 + hello_len]
version.
请参见 https://codegolf. stackexchange.com/questions/132981/tips-for-golfing-in-x86-x64-machine-code/132985#132985 -已知常数寄存器中的 LEA可以赢得代码大小附近/小常数,实际上对于性能而言还不错. (只要已知常数以这种方式实现,而不必依赖一长串计算).
See https://codegolf.stackexchange.com/questions/132981/tips-for-golfing-in-x86-x64-machine-code/132985#132985 - LEA from a known-constant register is a win for code-size with nearby / small constants, and is actually fine for performance. (As long as the known-constant got that way without a dependency on a long chain of calculations).
但是,正如您所看到的那样,clang无法优化它,并且即使位移可以容纳在disp8中,它仍然使用reg + disp32寻址模式.它的代码大小仍然比[RIP + rel32]
.
But as you can see, clang fails to optimize that and still uses a reg+disp32 addressing mode even when the displacement would fit in a disp8. It's still slightly better code-size than [abs disp32]
which requires a SIB byte; without a SIB byte that encoding means [RIP + rel32]
.
这篇关于如何获取`mov rdx,symbol`来移动符号值而不是clang intel语法中符号地址的值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!