Печать целого числа в сборке x86

Я пытаюсь напечатать целое число в 32-битной сборке x86 на MacOS High Sierra, используя этот код:

.cstring
STR_D:
.asciz "%d"
.globl _main

_main:
    movl $53110, %edi     #moves constant int into register
    sub $8, %esp          #make space on stack
    movl %edi, 4(%esp)   #move int to stack
    movl $STR_D, 0(%esp) #move "%d" to stack
    call _printf
    add $8, %esp          #restore stack pointer
    ret

Я компилирую с помощью команды

 gcc -m32 -Wl,-no_pie -o foo foo.s

а затем выполнить исполняемый файл foo. Затем я получаю сообщение об ошибке "Недопустимая инструкция: 4". Я уверен, что ошибка заключается в инструкции, перемещающей строковую константу в стек. Если я удалю эту строку и вызову следующей функции, все будет работать. Что я делаю неправильно?

Дополнительная информация / вопрос: я делаю это как часть проекта по написанию компилятора. Когда я выполняю эти инструкции в Linux (при этом, конечно, меняя специфичные для платформы вещи, такие как main вместо _main,.data вместо.cstring и т. Д.), Это работает. Почему macOS не работает? Связано ли это с выравниванием стека?

Моя версия компилятора (полученная gcc -version) - Apple LLVM 9.0.0 (clang-900.0.39.2).

1 ответ

Решение

Хорошо, я выяснил, в чем проблема. Это в два раза:

Сначала я обнаружил, что мне не хватает сегмента.text. Это, очевидно, необходимо, даже если оно пустое. Это и стало причиной ошибки "Недопустимая инструкция: 4". Однако, если вы исправите это, вы получите SEGFAULT.

Как я и подозревал в этом вопросе, это связано с выравниванием стека. Требования к macOS отличаются от требований к Linux: стек должен быть выровнен в 16-разрядной системе macOS. Это означает, что перед выполнением вызова функции (который помещает адрес возврата в стек), стек должен быть выровнен по 12 (то есть указатель стека должен выглядеть следующим образом: 0xnnnnnnnC). Чтобы убедиться в этом, нужно убедиться, что

1) нажмите n раз, где n кратно 3 (нажмите 3 раза, 6 раз, 9 раз и т. Д.)

2) если вы изменяете указатель стека самостоятельно (как я сделал), убедитесь, что он соответствует 12-битному требованию

В общем, код, который работает для меня, выглядит так:

.cstring
STR_D:
.asciz "%d"
.text                   #need this, even if it's empty
.globl _main

_main:
  movl $53110, %edi     #moves constant int into register
  sub $12, %esp         #make space on stack, respecting alignment
  movl %edi, 4(%esp)    #move int to stack
  movl $STR_D, 0(%esp)  #move "%d" to stack
  call _printf
  add $12, %esp         #restore stack pointer
  ret

Изменить: если кто-то заинтересован в выравнивании стека, я нашел объяснение на этом сайте очень полезным!

Другие вопросы по тегам