Ответ 1
puts
неявно добавляет новую строку, а stdout - буферизируется по строке (по умолчанию на терминалах). Поэтому текст из printf
может просто сидеть там в буфере. Ваш вызов _exit(2)
не очищает буферы, потому что это exit_group(2)
, а не exit(3)
функция библиотеки. (См. Мою версию вашего кода ниже).
Ваш вызов printf(3)
также не совсем прав, потому что вы не сделали нуль %al
перед вызовом функции var-args без аргументов FP. (Хорошо поймать @RossRidge, я пропустил это). xor %eax,%eax
- лучший способ сделать это. %al
будет отличным от нуля (из puts()
возвращаемого значения), что, по-видимому, связано с тем, что printf segfaults. Я тестировал в своей системе, и printf, кажется, не возражает, когда стек смещается (что есть, поскольку вы дважды нажали, прежде чем называть его, в отличие от puts).
Кроме того, вам не нужны инструкции push
в этом коде. Первый arg находится в %rdi
. Первые 6 целых аргументов идут в регистры, 7-е и более поздние - в стек. Вы также пренебрегаете появлением стека после возвращения функций, что работает только потому, что ваша функция никогда не пытается вернуться после испорчения стека.
ABI требует выравнивания стека на 16В, а push
- это один из способов сделать это, который на самом деле может быть более эффективным, чем sub $8, %rsp
на последних процессорах Intel с движком стека, и он занимает меньше байтов. (См. x86-64 SysV ABI и другие ссылки в x86 tag wiki).
Улучшенный код:
.text
.global main
main:
lea message, %rdi # or mov $message, %edi if you don't need the code to be position-independent: default code model has all labels in the low 2G, so you can use shorter 32bit instructions
push %rbx # align the stack for another call
mov %rdi, %rbx # save for later
call puts
xor %eax,%eax # %al = 0 = number of FP args for var-args functions
mov %rbx, %rdi # or mov %ebx, %edi will normally be safe, since the pointer is known to be pointing to static storage, which will be in the low 2G
call printf
# optionally putchar a '\n', or include it in the string you pass to printf
#xor %edi,%edi # exit with 0 status
#call exit # exit(3) does an fflush and other cleanup
pop %rbx # restore caller rbx, and restore the stack
xor %eax,%eax # return 0
ret
.section .rodata # constants should go in .rodata
message: .asciz "Hello, World!"
lea message, %rdi
дешево, и для этого дважды требуется меньшее количество инструкций, чем две инструкции mov
, чтобы использовать %rbx
. Но поскольку нам нужно было отрегулировать стек на 8B, чтобы строго следовать гарантии ABI 16B, мы могли бы также сделать это, сохранив регистр с сохранением вызова. mov reg,reg
является очень дешевым и маленьким, поэтому использование сохраненного вызова является естественным.
Использование mov %edi, %ebx
и прочее подобное сохраняет префикс REX в кодировке машинного кода. Если вы не уверены/не понимаете, почему безопасно копировать только низкие 32 бит, обнуляя верхние 32b, тогда используйте 64-битные регистры. Как только вы поймете, что происходит, вы узнаете, когда вы можете сохранить байты машинного кода, используя 32-разрядный размер операнда.