NASM 汇编程序:如何使用 EQU 对齐值,以获取标签后的下一个对齐边界而无需实际填充

NASM assembler : How to align values using EQU, to get the next alignment boundary after a label without actually padding

我想计算对齐地址。 'label' 应对齐 4 字节边界。

        bits 32
        mov eax, label
end:    ; here is the end of program
        align 4
label:

此 nasm 代码输出 8 个字节。

$ nasm -f bin test.nasm -o test.o
$ ndisasm -o 0x0 -b 32 test.o
00000000  B808000000        mov eax,0x8
00000005  90                nop
00000006  90                nop
00000007  90                nop

但实际上程序大小是5个字节。 是否可以在不扩展程序大小的情况下定义标签? 我希望将 'label' 定义为值 8,test.o 的文件大小将为 5.

我还尝试使用“$”变量计算地址,如下所示。

        bits 32
        mov eax, label
end:    ; here is the end of program
label   equ ( ($+3) & ~3 )

但是nasm不接受这个表达式。

$ nasm -f bin test.nasm -o test.o
test.nasm:4: error: `&' operator may only be applied to scalar values

这是意料之中的事情。 end 的绝对地址通常在链接之后才知道,因此地址的任何转换都必须由链接器计算。大多数系统上的链接器可以执行一些有限的运算(主要是加法和减法),汇编器通过在重定位 table 中插入适当的条目来请求这些运算。这就是为什么你可以做 mov eax, label+17mov eax, label2-label1 之类的事情。但它不会像 & 那样进行完全任意的运算,并且没有重定位来对这样的表达式进行编码。

(我想既然我们在这里输出二进制,汇编程序对绝对地址有更多的了解;但它可能不是为了利用这种特殊情况而设计的。)

所以我认为你在这里不走运。您可以在 label 之前忍受额外的填充字节,或者您可以在运行时进行对齐,例如

mov eax, label+3
and eax, ~3

你应该只使用 align 4.

equ 在汇编时计算。所以在链接后它无法知道 $ 在运行时的对齐方式。此外,它给你的错误,

error: `&' operator may only be applied to scalar values

那是因为 ($+3) 不是标量值。连组装的时候都无法评价。

另一方面,align 4 会将对齐要求传递到目标文件中,链接器将正确对齐 label。如果你想验证这是正确的,你可以检查最终 executable.

上的名单(符号 table)

正如 Nate 所解释的那样,NASM 不会对地址进行任意数学运算,即使在制作平面二进制文件而不是仅仅将重定位写入 ELF 或 COFF 时也是如此 .o / .obj .

NASM 在制作平面二进制文件时连接部分,但 不会为 .bss.
发出任何字节 这个程序汇编成你想要的5字节机器码:

        bits 32
        mov eax, label
end:    ; here is the end of program

section .bss
        align 4       ; in the BSS where they don't end up in the file
label:
$ nasm -fbin foo.asm
$ ll foo
-rwxr-xr-x 1 peter peter 5 Dec 30 15:16 foo
$ ndisasm -b32 foo
00000000  B808000000        mov eax,0x8

我有点惊讶它起作用了; IDK,如果它是官方支持的,或者如果我很幸运。
它也适用于 YASM。

在 FASM 中,即使没有 section .bss 指令,它似乎也只是跳过了在文件末尾写入对齐填充。 (所以我得到了一个 6 字节的文件,因为我没有为 32 位平面二进制输出查找正确的 FASM 指令,所以它使用 66 操作数大小前缀来编码 mov eax, 8 16 位模式。)