NASM x86_64 printf 7 аргумент - PullRequest
2 голосов
/ 11 марта 2019

У меня есть простая dprintf программа, написанная на NASM, которая печатает длинный формат с более чем 6 аргументами.Я передаю аргументы, как требует соглашение о вызовах ( RDI , RSI , RDX , RCX , R8 , R9 ).Пока я использую только те, моя программа работает нормально.

Я не могу понять, почему я получаю segfault каждый раз, когда пытаюсь что-то добавить в стек в качестве дополнительных аргументов.Вот источник:

;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 ]

2 голосов
/ 12 марта 2019

Поскольку этот вопрос имел какое-то значение, он, вероятно, мог бы использовать ответ. Есть две существенные проблемы:

  • Вы звоните _dprintf с 7 параметрами, которые имеют C эквивалент:

    dprintf (fd, format_str, 10, 9, 34, 47, 59)
    

    Проблема в том, что в строке формата у вас есть %5$s. Пятый переменный параметр - это значение 59, а не указатель на строку. dprintf пытается получить доступ к памяти, к которой у него нет разрешения, и вы получаете ошибку EXC_BAD_ACCESS и ошибку сегмента. У вас также есть %6$c в строке формата, но у вас нет 6-го переменного параметра . Из ваших комментариев стало ясно, что вы хотите, чтобы сам format_str был 5-м параметром, а значение 59 - 6-м. Код для отправки последних 2 параметров должен был выглядеть так:

    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 Соглашение о вызовах ABI System V требует как минимум 16-байтового выравнивания стека перед вызовом соответствующей функции (которая включает System и C библиотека). В MacOS системная библиотека очень чувствительна к проблемам выравнивания стека, поскольку она использует выровненные SIMD-инструкции везде, где это возможно, из соображений производительности, даже при использовании только целочисленных параметров класса.

    _main также соответствует этому стандарту. ABI требует 16-байтового выравнивания в точке непосредственно перед вызовом. 32-байтовое выравнивание требуется, если вы передаете параметры, требующие 256-битных векторов SIMD, но здесь это не так. После ввода _main (или любой функции, которая соответствует правилам соглашения о вызовах x86-64), стек смещается на 8, поскольку адрес возврата теперь находится в стеке. push RBP вычитает 8 из RSP, и стек теперь снова выровнен по 16-байтовой границе. Если вы поместите четное число параметров в стек, чтобы удовлетворить вызов, такой как dprintf, выравнивание все равно останется без изменений. Если вы пропустите нечетное число, вы снова сместитесь. В этих случаях вы должны вычесть 8 из RSP, прежде чем нажимать параметры.

    Если бы вы действительно хотели сделать:

    dprintf (fd, format_str, 10, 9, 34, 47, 59)
    

    Вам пришлось бы вычесть 8 из RSP, прежде чем добавить дополнительный 1 параметр в стек. Код выглядел бы так:

    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-байтовое выравнивание

2 голосов
/ 12 марта 2019

Ваша строка формата содержит %5$s, что означает, что она попытается вывести 5-й vararg (7-й аргумент в целом) в виде строки.Поскольку этот 5-й аргумент является константой 59 (предположительно символьная константа ';'), а не строкой, это приводит к сбою внутри printf (обычно внутри strlen, вызываемого из printf)

...