x86_64汇编语言中标签和宏之间的区别



我对C和Python没有什么经验。在x86_64中,汇编语言标签就像函数,宏就像对我有更多控制权的函数。标签和宏之间的真正区别是什么?

标签只是偏移量和地址的可读名称(区别取决于使用的二进制格式(
它们之所以有用,不仅是因为它们使源代码更具可读性(所有地址看起来都一样,请尝试反向工程来查看(,还因为它们的值是由汇编程序自动计算的。

考虑以下示例片段:

BITS 64
pow2:
mov eax, 1 
test edi, edi
jz .end

.loop:
add rax, rax

dec edi
jnz .loop

.end:
ret

并将其组装成一个二进制文件:

> nasm pow2.asm -o pow2

我们可以选择而不是使用任何标签。为了做到这一点,我们需要跟踪每条指令的偏移量/地址,这样当我们跳转时,我们就可以告诉汇编程序正确的目的地。

BITS 64
mov eax, 1      ;5 bytes, offset 0
test edi, edi   ;2 bytes, offset 5
jz SHORT 16     ;2 bytes, offset 7

add rax, rax    ;3 bytes, offset 9
dec edi         ;2 bytes, offset 12
jnz SHORT 9     ;2 bytes, offset 14

ret             ;1 bytes, offset 16

任务一点也不好!我们需要知道表单中每条指令的长度,并且我们必须非常小心,不要出错,否则我们将引入非常奇怪的错误的乏味来源。

尽管如此,这两个二进制文件是相同的,表明标签只是数字的名称。

> nasm pow2_nolabels -o pow2_nolabels
> diff pow2 pow2_nolabels
>

如果我们想用xor eax, eax / inc eax更改mov eax, 1以保存1个字节,该怎么办
如果我们使用标签,我们所要做的就是更改指令mov eax, 1,但如果不是,我们需要更改后续跳转的每个目标
这个简单的函数已经有两个了,更不用说更复杂的函数了。


宏是完全不同的东西。它们是一种文本处理机制,其中文本是源
它们允许非常快速地生成结构化的源代码和模式,并且可以使代码更加可读和DRY。

例如,NASM有非常强大的宏,可以使汇编程序看起来像脚本:

BITS 64
;------ LIBRARY ---------
%define STDOUT 1
%define SYS_WRITE 1
%define SYS_EXIT 60
%macro print 1
SECTION .data
%%string db %1, 10
%%string_end:

SECTION .text
mov eax, SYS_WRITE
mov edi, STDOUT
lea rsi, [REL %%string]
mov edx, %%string_end - %%string  ;Assume max 4GiB string
syscall

%endm
%macro exit 1
mov eax, SYS_EXIT
mov edi, %1
syscall
%endm


;------ PROGRAM --------
GLOBAL _start
_start:
print "Hello, world!"
print "Bye"
exit 0

请注意,该程序类似于高级语言,特别是宏会自动生成分配字符串所需的样板代码,在汇编时找到其长度,并将其打印到stdout
还请注意,像%define这样的简单宏对于简单的文本替换(STDOUT->1(是多么有用,以避免神奇的常量并使代码可读(STDOUTSYSWRITE都有值1,但它们的含义完全不同;如果没有宏,我们在源中只能找到两个1(。

请注意,宏只是关于源转换。它们在汇编时进行处理:当调用宏时,它的主体在调用站点中进行处理和替换。这是几乎所有宏(包括内置宏(都被扩展后的来源:

[global _start]
_start:
[section .data]
..@2.string db "Hello, world!", 10
..@2.string_end:
[section .text]
mov eax, 1
mov edi, 1
lea rsi, [REL ..@2.string]
mov edx, ..@2.string_end - ..@2.string
syscall
[section .data]
..@5.string db "Bye", 10
..@5.string_end:
[section .text]
mov eax, 1
mov edi, 1
lea rsi, [REL ..@5.string]
mov edx, ..@5.string_end - ..@5.string
syscall
mov eax, 60
mov edi, 0
syscall

看看每个宏是如何扩展成一组指令和指令的?这与普通的call完全不同,显然也与标签完全不同。

相关内容

  • 没有找到相关文章

最新更新