如何获取`mov rdx,symbol`来移动符号值而不是clang intel语法中符号地址的值? 丑陋的解决方法:切换到.att_syntax并返回:无效且无效的解决方法:lea

我有以下代码与macOS上的clang一起使用:

.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本身的值。

如果我使用AT&T语法,则该行为movq $hello_len,%rdx,可以正常工作。 clang的GAS intel语法版本等效于什么?

cmswwy 回答:如何获取`mov rdx,symbol`来移动符号值而不是clang intel语法中符号地址的值? 丑陋的解决方法:切换到.att_syntax并返回:无效且无效的解决方法:lea

使用真实的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

大家都在问