assembly - NASM - 宏本地标签作为另一个宏的参数
问题描述
我正在尝试使用宏(如本教程所示)来打印字符串。该宏PRINT
创建本地标签来定义字符串内容 ( str
) 和长度 ( strlen
),然后将这些作为参数传递给_syscall_write
进行系统调用的第二个宏。
但是运行代码失败,我收到一条Segmentation fault (core dumped)
消息。
我怀疑问题出在这个特定的行上,但我不明白为什么。
mov rsi, %1 ; str
mov rdx, %2 ; strln
这是完整的代码:
%macro PRINT 1
; Save state
push rax
push rdi
push rsi
push rdx
%%str db %1, 0 ; arg0 + null terminator
%%strln equ $ - %%str ; current position - string start
; Write
_syscall_write %%str, %%strln
; Restore state
pop rdx
pop rsi
pop rdi
pop rax
%endmacro
%macro _syscall_write 2
mov rax, 1
mov rdi, 1
mov rsi, %1 ; str
mov rdx, %2 ; strln
syscall
%endmacro
global _start
section .data
SYS_EXIT equ 60
EXIT_CODE equ 0
section .text
_start:
PRINT "Hello World!"
exit:
mov rax, SYS_EXIT
mov rdi, EXIT_CODE
syscall
这是目标文件的反汇编(来自已注释掉 push/pop 的版本)。
查看扩展的代码,我仍然看不出有什么问题。字节 0x0..0xC 看起来像乱码,但对应于Hello World!
. 在 syscall 到 sys_write 之前,rax
似乎rdi
收到了 的预期值0x1
,rsi
其值0x0
指向字符串开始,rdx
其值0xd
是字符串长度 (12 + 1)...
Disassembly of section .text:
0000000000000000 <_start>:
0: 48 rex.W
1: 65 gs
2: 6c ins BYTE PTR es:[rdi],dx
3: 6c ins BYTE PTR es:[rdi],dx
4: 6f outs dx,DWORD PTR ds:[rsi]
5: 20 57 6f and BYTE PTR [rdi+0x6f],dl
8: 72 6c jb 76 <SYS_EXIT+0x3a>
a: 64 21 00 and DWORD PTR fs:[rax],eax
d: b8 01 00 00 00 mov eax,0x1
12: bf 01 00 00 00 mov edi,0x1
17: 48 be 00 00 00 00 00 movabs rsi,0x0
1e: 00 00 00
21: ba 0d 00 00 00 mov edx,0xd
26: 0f 05 syscall
0000000000000028 <exit>:
28: b8 3c 00 00 00 mov eax,0x3c
2d: bf 00 00 00 00 mov edi,0x0
32: 0f 05 syscall
解决方案
rex.W gs ins
是特权指令,并且在用户空间中存在错误。这是您的程序的第一条指令,从%%str db %1, 0
宏中的扩展开始,而不更改部分。
不要将数据放在将作为指令执行的地方;用于section .rodata
只读数据,(或.rdata
在 Windows 上)然后切换回原始部分。
%macro PRINT 1
...
section .rodata
%%str db %1, 0 ; arg0 + null terminator
%%strln equ $ - %%str ; current position - string start
section .text
... rest of the macro
这无条件地切换到.text
section,无论您在使用此宏时位于哪个部分(例如.text.cold
或其他一些自定义部分)。
GAS 会让你.pushsection .rodata
/.popsection
在任何部分中正确扩展宏。NASM 有一个不同的机制来允许这个,它不会嵌套。 有关如何定义as的详细信息,请参阅NASM 手册,并且“原始指令”将在不这样做的情况下切换。section foo
__?SECT?__
[section foo]
[section bar]
切换回原始部分的更安全的版本
%macro PRINT 1
...
[section .rodata] ; switch to .rodata without updating __?SECT?__
%%str db %1, 0 ; arg0 + null terminator
%%strln equ $ - %%str ; current position - string start
__?SECT?__ ; switch back to original section, likely [section .text]
... rest of the macro
%push [optional context-name] / %pop
通过使用在上下文堆栈上保存/恢复,实际上可以嵌套切换部分的宏__?SECT?__
,因此您可以使用用户级section
not [section]
。但这可能与宏的使用不兼容,这些宏也是%define
您希望稍后可见的东西。由于这在实践中不太可能需要,所以我没有尝试。(例如,您切换到 .rodata 并使用另一个宏,该宏本身会切换到其他位置。)我提到这一点主要是因为 GAS .pushsection
/.popsection
确实很容易嵌套。
如果您的任何静态数据每次都相同,您可以将其拉到宏之外,或者使用%ifndef
/%define
保护,以便第一次使用宏(在一个文件中)发出数据(带有不使用的普通标签%%
, like debugprint_str
) 供以后扩展参考。(请参阅在宏中定义变量会导致同一变量的多个定义)。这样做的好处是,如果宏使用零次,则数据根本不会成为您程序的一部分。但是对于宏参数字符串,除非您将字符串烘焙到符号名称中,否则您将无法在汇编时进行重复消除/折叠。
另请注意,equ
指令不关心它们所在的部分(除非它们$
在定义中使用)。所以strln
需要与 相同的部分str
,但SYS_EXIT
与 无关section .data
。它是一个汇编时常量,当您以这种方式使用它时,它会变成立即数。
mov r64, imm64
将绝对地址放入寄存器是一种低效的方法。它需要在 PIE 可执行文件中进行加载时修复,并且比 position-independent 更长lea rsi, [rel %%str]
。NASM 组装mov rsi, str
成 10-byte mov r64, imm64
,而 YASM 使用mov r/m64, sign_extended_imm32
(它甚至在 PIE 可执行文件中不起作用)。 https://nasm.us/doc/nasmdo11.html#section-11.2
您也许可以编写一个使用%ifidn
字符串相同条件的宏来检查rsi
字符串 arg,在这种情况下什么都不做(指针已经在 RSI 中),否则使用lea rsi, [rel %%str]
. 但是,这不适用于内存中的指针mov rsi, [rbx]
,但在哪里可以工作。取决于你希望你的宏有多花哨。您可能会在 arg 字符串%if
中查找一个条件并使用而不是.[
mov
lea
如果您想保存/恢复您破坏的所有寄存器,请记住它syscall
本身会破坏 RCX(保存的 RIP)和 R11(保存的 RFLAGS)。
通常,您只需记录哪些注册了宏破坏器;这些都是 x86-64 System V 中的 call-clobbered 寄存器。但是如果您想要一个调试打印宏,您可能希望它保存/恢复所有内容?除了push
/pop
摧毁 RSP 以下的红色区域。
我不认为我曾经在 asm 中使用过调试打印,只是用调试器设置断点并点击“继续”以查看下一个断点。或者只是单步并观察寄存器值的变化,例如使用 GDB 的layout reg
.
推荐阅读
- java - 如何以编程方式禁用android中其他应用程序的网络使用?
- php - 如何保护对django网站的恶意攻击
- javascript - React Native upload file using fetch, server $_FILES return object, how to access tmp_name
- php - 类名中的空格是否会导致简单 HTML DOM 出现问题?
- java - 关于java类和构造函数
- python - 输出缺少尾随换行符
- javascript - 比较 2 个对象并将键删除/添加到对象 B
- python - 如何在 asyncio 中复制线程代码?
- reactjs - JSX 元素“h1”没有相应的结束标记。ts(17008)
- javascript - 如何使用脚本 Photoshop 保存文件?