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 4AT&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. Использование регистров

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