如何获取`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?

查看:152
本文介绍了如何获取`mov rdx,symbol`来移动符号值而不是clang intel语法中符号地址的值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码与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寻址模式.它的代码大小仍然比稍高,后者需要一个SIB字节;没有SIB字节,则编码表示[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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆