NASM x86_64 printf第7个参数

时间:2019-03-11 17:51:22

标签: assembly stack printf x86-64 nasm

我有一个用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。

2 个答案:

答案 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字节对齐方式