assembly 在英特尔语法中消除标签与寄存器名称的歧义

wdebmtf2  于 2023-10-19  发布在  其他
关注(0)|答案(2)|浏览(85)

我想知道如何区分英特尔语法中某些指令中的标签名称和寄存器名称。例如,call rdx通常意味着间接跳转,但如果我们在同一个汇编文件中有一个标签rdx呢?我相信它可以被解释为直接跳转到rdx。有没有什么符号可以告诉汇编程序哪个是哪个?

pbgvytdp

pbgvytdp1#

这取决于汇编程序。
我认为对于大多数情况,寄存器名称优先,所以call rdx将始终是RIP=RDX,而不检查是否存在同名的标签(或外部符号)。

AT&T语法call *%rdx,而不是AT&T call rdx

一些汇编器有语法来消除歧义,但是在不同的汇编器之间没有标准的语法
例如对于NASM,这是Symbol name conflicts with new register names in new NASM versions?的副本,您可以使用$eax来引用名称为eax的符号/标签,而不是寄存器。

对于Euroassembler,您可以将:后缀到名称上,以强制将其解释为符号,而不是寄存器,如call rdx:。(https://euroassembler.eu/eadoc/#SymbolName),
对于GAS .intel_syntax noprefix,您可以将符号名称放在引号中。这似乎很模糊;我不建议将GAS Intel语法用于生产环境。如果你更喜欢Intel而不是AT&T的语法,这对于阅读编译器输出是很好的,但是它有一些怪癖,包括这个和 * Distinguishing memory from constant in GNU as .intel_syntax *,这使得它比NASM更糟糕。

.intel_syntax noprefix
rdx:
call rdx            # call reg
call "rdx"          # call rel32

mov eax, [rip + "rdx"]    # rip-relative load of that machine code
mov eax, ["rdx" + rdx]    # use the register as an offset from the symbol
# mov eax, [rip + rdx]    # error: RIP-relative doesn't work with other regs

然后我们可以在AT&T语法中进行反汇编,以便对我们得到的内容更加清晰/明确。

$ gcc -c gas-symbol.s && objdump -drwC -Matt gas-symbol.o

...
0000000000000000 <rdx>:
   0:   ff d2                   call   *%rdx
   2:   e8 f9 ff ff ff          call   0 <rdx>
   7:   8b 05 f3 ff ff ff       mov    -0xd(%rip),%eax        # 0 <rdx>
   d:   8b 82 00 00 00 00       mov    0x0(%rdx),%eax   f: R_X86_64_32S .text

由于符号是由同一文件中的标签定义的,因此RIP相关版本在组装时解析。disp32(%rdx)正在等待链接器填写绝对地址,即will only work in a non-PIE
顺便说一句,重定位信息(由-r打印)只显示.text,而不是符号名称rdx,这与相对分支和RIP相对负载不同。相对寻址在汇编时完全解析,链接器没有实际的重定位来填充,所以打印<rdx>只是信息性的。但是[symbol + reg]的绝对地址有一个重定位条目,objdump可能正在打印它使用的符号名。如果我使用.globl rdx使该符号在符号表中完全可见,它将打印rdx-0x4而不是.text
Intel语法是GNU工具链中AT&T语法背后的二等公民。GCC不使用引号,所以编译使用gcc -masm=intel全局变量的C将使它发出一个.s,GAS会阻塞它!(假设目标ABI(如i386或x86-64 System V)不使用前导下划线装饰C符号名称; _eax避免了问题,例如在macOS或32位Windows上。
理论上(也许只是几行)你可以使用.intel_syntax prefix,这样%rdx仍然需要在寄存器名称上,而裸rdx被解释为一个符号,但这是一个邪恶的突变混合体,当人们看到sub %ecx, %eax并假设它是AT&T语法时,会因为%装饰器而打破人们的大脑。而且,clang的汇编程序也不支持它:只有.att_syntax prefix.intel_syntax noprefix。有很好的理由,因为没有人应该使用这种令人憎恶的语法。

ajsxfq5m

ajsxfq5m2#

有没有什么符号可以告诉汇编程序哪个是哪个?
这取决于你使用的汇编程序。没有通用的语法可以这样做。
然而,有些汇编器有区分标签和保留字的特性。例如,section "3.1 Layout of a NASM Source Line" in the nasm documentation声明,您可以将标签前缀为$,以将其与寄存器区分开来:
一个标识符也可以前缀为$,以表明它是作为一个标识符而不是一个保留字来读的;因此,如果你链接的其他模块定义了一个名为eax的符号,你可以在NASM代码中引用$eax来区分这个符号和寄存器

相关问题