x64: Почему этот фрагмент кода дает мне "Ошибка границы адреса"
Почему следующая сборка x64 дает мне "Ошибка границы адреса"? Это происходит только когда я добавляю код после call _print_string
, Я предполагаю, что некоторые регистры были изменены, но не должны ли они быть восстановлены после _print_string
функция возвращает?
Я использую Mac OS X
obj_size = 8
.data
hello_world: .asciz "hello world!"
.text
.globl _main
_main:
pushq %rbp
movq %rsp, %rbp
leaq hello_world(%rip), %rdi
callq _print_string
subq obj_size, %rsp
movq 1, %rax
movq %rax, obj_size(%rsp)
addq obj_size, %rsp
leave
ret
И программа на С:
void
print_string(char *str)
{
printf("%s\n", str);
}
1 ответ
Проблема с этим кодом довольно проста. В GNU Assembler, использующем синтаксис AT&T - буквальные константы, которые используются в качестве непосредственного операнда, должны начинаться с префикса $
(знак доллара), в противном случае константа рассматривается как операнд памяти.
Эти строки имеют эту проблему:
subq obj_size, %rsp
movq 1, %rax
[snip]
addq obj_size, %rsp
В этих случаях, так как вы хотите использовать константы obj_size
а также 1
как значение (непосредственный операнд), а не ссылка на память. Инструкции выше должны были:
subq $obj_size, %rsp
movq $1, %rax
[snip]
addq $obj_size, %rsp
subq obj_size, %rsp
попытался вычесть 64-битное значение по адресу памяти 0x8 из значения в RSP. movq 1, %rax
попытался переместить 64-битное значение по адресу памяти 0x1 в RAX. В вашей программе произошел сбой, поскольку эти места памяти в OS/X не могут быть прочитаны.
Хорошую статью о разнице между синтаксисом AT&T и Intel можно найти на веб-сайте IBM. В частности, они имеют эту разницу в списке:
В синтаксисе AT&T непосредственным операндам предшествует $; в синтаксисе Intel непосредственные операнды не являются. Например: Intel:
push 4
AT&T:pushl $4
Чтобы сузить проблемы, подобные этим, часто полезно использовать отладчик. В OS/X, если вы не используете XCode, вы можете использовать отладчик LLDB из командной строки. Учебник по использованию LLDB может быть полезным. В этом случае вы могли бы запустить LLDB как lldb ./nameofprogram
а затем использовал run
команда, чтобы позволить этому продолжаться, пока он не потерпел неудачу. Затем отладчик показал бы вам, в какой инструкции сборки произошел сбой.
Если вы хотите знать соглашение о вызовах, используемое 64-битным кодом OS/X, Apple определяет его следующим образом:
Соглашения о вызовах функций OS X x86-64 аналогичны соглашениям о вызовах функций, описанным в Дополнении к процессору архитектуры AMD64 двоичного интерфейса приложения System V.
Здесь вы можете найти Приложение Процессора Архитектуры AMD64 для Бинарного Интерфейса Приложения V здесь. Список сохраненных регистров вызывающего и вызываемого абонентов представлен на рисунке 3.4. Использование регистров