我有一个用NASM编写的简单dprintf
程序,它可以打印包含6个以上参数的长格式。我正在按照调用约定的要求传递参数( RDI , RSI , RDX , RCX , R8 , R9 )。只要我只使用那些程序,我的程序就可以正常工作。
我不知道为什么每次尝试将某些东西作为附加参数推入堆栈时都会出现段错误。来源:
;a comment
%macro DATA 0
section .data
string: db "%6$ca comment%1$c%4$cmacro DATA 0%1$csection .data%1$c%2$cstring: db %3$c%5$s%3$c, 0%1$c%2$cpath: db %3$cGrace_kid.s%3$c, 0%1$c%4$cendmacro%1$c%4$cdefine SC_OPEN 0x2000005%1$c%4$cmacro MAIN 0%1$c%1$cDATA%1$c%1$csection .text%1$c%2$cglobal start%1$c%2$cglobal _main%1$c%2$cextern _dprintf%1$c%1$cstart:%1$c%2$ccall _main%1$c%2$cret%1$c%1$c_main:%1$c%2$cpush rbp%1$c%2$cmov rbp, rsp%1$c%2$cmov rax, SC_OPEN%1$c%2$clea rdi, [rel path]%1$c%2$cmov rsi, 0x0200%1$c%2$cxor rsi, 0x0002%1$c%2$cmov rdx, 0640o%1$c%2$cclc%1$c%2$csyscall%1$c%2$cjc ret%1$c%2$ccmp rax, 0%1$c%2$cjle ret%1$c%2$cmov rdi,rax%1$c%2$clea rsi, [rel string]%1$c%2$cmov rdx, 10%1$c%2$cmov rcx, 9%1$c%2$ccall _dprintf%1$c%2$cxor rax, rax%1$cret:%1$c%2$cleave%1$c%2$cret%1$c%4$cendmacro%1$c%1$cMAIN%1$c", 0
path: db "Grace_kid.s", 0
%endmacro
%define SC_OPEN 0x2000005
%macro MAIN 0
DATA
section .text
global start
global _main
extern _dprintf
start:
call _main
ret
_main:
push rbp
mov rbp, rsp
;sub rsp, 16
mov rax, SC_OPEN
lea rdi, [rel path]
mov rsi, 0x0200
xor rsi, 0x0002
mov rdx, 0640o
clc
syscall
jc ret
cmp rax, 0
jle ret
mov rdi, rax
lea rsi, [rel string]
mov rdx, 10
mov rcx, 9
mov r8, 34
mov r9, 37
mov rbx, 59
push rbx
xor rax, rax
call _dprintf
xor rax, rax
ret:
leave
ret
%endmacro
MAIN
我汇编并链接以下命令:
nasm -fmacho64 file.s
ld file.o -macosx_version_min 10.14 -lSystem
这很好用,但是我想添加额外的参数。我尝试使用以下方法将其推入堆栈:
mov rbx, 59
push rbx
是否对 RSP 分配一些字节是否存在错误。
我在MacOS Mojave下,并且正在使用最新版本的NASM。
答案 0 :(得分:2)
您的格式字符串包含%5$s
,这意味着它将尝试将第5个vararg(总共第7个arg)打印为字符串。由于第5个参数是常量59(大概是字符常量';'
)而不是字符串,因此最终会在printf内部崩溃(通常是从printf调用的strlen内部)
答案 1 :(得分:2)
由于该问题最终具有一定价值,因此可以使用答案。有两个重大问题:
您使用具有 C 等效项的7个参数调用_dprintf
:
dprintf (fd, format_str, 10, 9, 34, 47, 59)
问题在于,在格式字符串中,您有%5$s
。第五个 variadic参数是值59,而不是指向字符串的指针。 dprintf
正在尝试访问没有权限的内存,并且您收到错误EXC_BAD_ACCESS和段错误。您在格式字符串中也有%6$c
,但是没有第六个 variadic参数。从您的注释中可以很明显地看出,您希望format_str
本身是第5个参数,而值59是第6个参数。推后两个参数的代码应如下所示:
push 59 lea rbx, [rel string] push rbx xor rax, rax call _dprintf
相应的 C 调用应为:
dprintf (fd, format_str, 10, 9, 34, 47, format_str, 59)
注意:当在堆栈中压入不适合寄存器的参数时,必须以相反的顺序压入
x86-64 System V ABI calling convention至少需要对堆栈进行16字节对齐,然后才能调用一致性函数(包括 System 和 C 库)。在MacOS上,系统库对堆栈对齐问题非常敏感,因为即使出于性能考虑,系统库也会使用对齐的SIMD指令,即使仅使用整数类参数也是如此。
_main
也符合此标准。 ABI要求在通话前16点对齐。如果传递的参数需要256位SIMD向量,则需要32字节对齐-但这不是这种情况。输入_main
(或符合x86-64调用约定规则的任何函数)后,由于返回地址现在位于堆栈中,因此堆栈未对齐8。 push RBP
从RSP中减去8,堆栈现在再次在16字节边界上对齐。如果在堆栈上压入偶数个参数来满足dprintf
之类的调用,对齐将保持不变。如果您传递一个奇数,您将再次错位。在这些情况下,必须在推入参数之前从RSP中减去8。
如果您确实打算这样做:
dprintf (fd, format_str, 10, 9, 34, 47, 59)
在将额外的1参数压入堆栈之前,您必须从RSP中减去8。代码看起来像这样:
push rax ; Push any register on stack or use `add rsp, -8` to align parameters push 59 xor rax, rax call _dprintf
如果您将2个额外的参数传递给dprintf
,则不需要进行此类堆栈调整,因为被压入的偶数个参数不会破坏16字节对齐方式