我正在尝试在汇编中编写一些特殊例程,对于x86-64(即使是x86示例也没问题)。问题是:我的临时工作只在链接时解决。
例如,
addq $Label2-Label1, %rax
会将两个标签/符号之间的差异添加到rax中。不幸的是,因为GNU汇编程序只进行了一次传递(我不知道为什么,即使像FASM这样的开源汇编程序也会执行多次传递),它无法解析这些传递,因此它会使链接器执行此操作。
不幸的是,它会为链接器保留4个字节(32位立即数),这不是我想要的,因为标签的差异总是在-128到+127范围内。
我的问题是,如何强制或指定指令应该是8位立即?比如,这里的语法是什么?在AT& T语法或英特尔中,两者都可以。例如,在NASM中,您可以:
add rax, byte Label2-Label1
指定8位立即。但是如何在GAS中做到这一点?什么是强制它使用8位立即即使它不知道立即本身的语法...我理想地在GAS中想要这个,具体原因,所以请不要告诉我使用NASM作为解答!
编辑:对不起,我忘了提,是的,这是一个“前瞻性参考”。两个标签都是在指令之后定义的,这就是为什么GAS无法解决它们,我认为它是一个重定位,但是使用'.byte label2-label1'肯定是因为我测试了它,所以我知道它应该是可能的如果它有一些语法...答案 0 :(得分:3)
如果Label1
和Label2
位于同一源文件中,则问题似乎与链接器无关(在这种情况下,GAS不会生成任何重定位)也不是因为GAS是单程汇编程序。它足够聪明,可以生成正确大小的分支,这是一个类似的问题。
问题归结为GAS不够智能,无法在跳转和分支以外的情况下选择正确大小的指令,并且无法明确指定操作数的大小。 " .d8"置换后缀几乎是您想要的语法,但您在技术上不使用置换。但它无论如何都不会起作用,因为leal.d8 Label2-Label1(%eax),%eax
无法正常工作,尽管实际上正在使用位移。
因此,只留下一个替代方案,手工组装操作码。例如,在32位汇编中:
.byte 0x83, 0xC0, (label2 - label1)
至于为什么GAS只是一个单程汇编程序,并且通常不做其他汇编程序的许多事情,答案很简单。它主要用于组装GCC的输出。
答案 1 :(得分:1)
不幸的是,这是不可能的;因为GAS没有弄清楚差异应该是什么,所以除了留下4字节的重定位条目以供链接器解析之外别无选择。这是因为目标文件格式(很可能是ELF)不支持8位重定位 - 它只支持32位重定位。因此,它将始终使用32位立即。