Почему появляется ошибка 24: тоже может быть открыт дескриптор файла в asm - PullRequest
0 голосов
/ 12 февраля 2020

вопрос число1:

Имея этот нос:

section .data
dat db "write out this:%x", 0xa, 0x0

section .text
global main
extern printf
main:
    push rbp
    mov rbp, rsp
    mov rdi, dat
    mov esi, 0xdeedbeef
    call printf
    leave 
    ret

дает errno 24 - слишком много файловых дескрипторов открыто.

НО ЕСЛИ ИЗМЕНЕНО int 80h вместо

    leave 
    ret

Завершится без ошибки, как это?

Кроме того, вопрос номер 2: Если я не сделаю соглашение о вызовах следующим образом:

push rbp
mov rbp, rsp

И только mov rbp, rsp, без push ing rbp раньше, тогда command terminated, хотя до этого не было вызова ни одной функции, следовательно, нет нужно pu sh указатель базы. Так зачем это нужно (в глазах компилятора) и закончится?

1 Ответ

2 голосов
/ 13 февраля 2020

Вопрос 1

Вы ошибаетесь из-за того, что это связано с дескрипторами файлов. Это не то, о чем сообщается.

Как вы пояснили в комментариях, 24 - это число, которое отображается, когда вы echo $? после запуска программы. Это код выхода из программы; обычно значение возвращается из функции main или передается в exit(). Это может быть все, что вы хотите, и обычно не соответствует значению errno.

Так почему ваша программа выдает код выхода 24? Если возвращается main, то кодом выхода является его возвращаемое значение. Ожидается, что возвращаемое значение функции останется в регистре rax, когда оно вернется (при условии, что оно имеет целочисленный тип или тип указателя). Но вы никогда не трогаете регистр rax, поэтому он по-прежнему содержит значение, оставленное там, когда возвращается printf. Теперь printf возвращает количество успешно напечатанных символов, которое для выбранной вами строки равно ... 24 (считайте их).

Это просто совпадение, что 24 также происходит с кодом errno для "слишком большого количества дескрипторов открытых файлов" - это совершенно не связано.

Если вы хотите выйти с кодом выхода 0 чтобы сигнализировать об успехе, вы должны xor rax, rax как раз перед вашим ret.

Вы не показали точный код, который вы использовали при изменении его на int 0x80, чтобы самостоятельно вызвать системный вызов _exit. Но в этом случае код выхода будет таким, какой есть в регистре ebx при выполнении системного вызова. Возможно, ваш код ставит ноль в ebx, или, может быть, вам повезло, и он уже содержит ноль.

(Примечание: int 0x80 - это интерфейс для 32-битных системных вызовов, и он не подходит в 64-битной программе, о которой вы, похоже, пишете, хотя она может работать в некоторых случаях. Интерфейс 64-битного системного вызова использует инструкцию syscall и объясняется в A.2.1 ABI.)

Вопрос 2

Вы должны выровнять стек.

При вызове функции C из сборки (как printf в данном случае) требуется, чтобы x86-64 ABI Section 3.2.2 выровнял стек по 16-байтовой границе. Стек выравнивается соответствующим образом перед вызовом main, а адрес возврата, выдвинутый call, вычитает 8 байтов.

Так что, если вы вообще не касаетесь стека в вашем коде, он не будет правильно выровнен при вызове printf, и это может привести к тому, что он обработает sh. (Ассемблер не поможет вам в этом; это не его работа.) Но когда вы извлекаете pu sh rbp, это вычитает еще 8 байтов и выравнивает стек должным образом. Так что либо оставьте этот код внутри, либо выровняйте стек самостоятельно.

И в любом случае, имейте в виду, что если вы измените свой код на pu sh больше содержимого в стеке, вам придется соответствующим образом изменить его, прежде чем выполнять какие-либо вызовы функций.

...