2015-11-02 5 views
0

Я только начинаю изучать сборку x86, и я немного смущен, почему этот маленький пример не работает. Все, что я хочу сделать, - это распечатать содержимое регистра eax как десятичное значение. Это мой код в AT & T Синтаксис:x86 asm printf вызывает segfault при использовании синтаксиса intel (gcc)

.data 
intout: 
    .string "%d\n" 
.text 
.globl main 

main: 

movl $666, %eax 
pushl %eax 
pushl $intout 
call printf 

movl $1, %eax 
int $0x80 

который я скомпилировать и запустить следующим образом:

gcc -m32 -o hello helloworld.S 
./hello 

Это работает как освобожденный (Печать 666 на консоль). На небольшом примечании я хотел бы отметить, что я не понимаю, что именно «movl $ 1,% eax» и «int $ 0x80» должны быть выполнены здесь. Я также не уверен, что «pushl $ intout» делает. Почему мой вывод состоит из двух отдельных записей стека? И что именно делает макрос .string?

Это только побочные вопросы, так как моя проблема real заключается в том, что я не могу найти способ сделать этот прогон, используя гораздо проще читать/писать/понимать синтаксис Intel.

Вот код:

.intel_syntax noprefix 

.data 
    intout: 
     .string "%d\n" 
.text 
.globl main 

main: 

mov eax, 666 
push eax 
push intout 
call printf 

mov eax, 1 
int 0x80 

Запуск этого же, как и выше, он просто печатает «ошибку сегментации».

Что я делаю неправильно?

+0

На самом деле не рекомендуется использовать 'movl $ 1,% eax; int $ 0x80'. Вы должны просто «вызывать exit», если используете библиотеку C так, как вы. Или вы могли бы просто «ret», предположив, что вы правильно очищаете стек, чего не делаете. У вас есть 2 записи, потому что вы передаете 2 аргумента (строка формата и номер). Что касается того, что делает '.string', я думаю, вы можете догадаться об этом, или, вы знаете, прочитать [manual] (https://sourceware.org/binutils/docs-2.20/as/String.html). – Jester

+0

В чем разница между int 0x80 и вызовом exit? – CaffeineAddict

+0

Первый - это прямой системный вызов, который не оставляет возможности для корректной работы библиотеки C. Кроме того, он менее портативен. – Jester

ответ

2

Вы должны использовать push OFFSET intout, иначе 32-разрядное значение, хранящееся в intout, будет помещено в стек, а не его адрес.

intout - это всего лишь ярлык, который по сути является именем, присвоенным адресу в вашей программе. Последующая директива .string "%d\n" определяет последовательность байтов в вашей программе, как выделение памяти, так и инициализацию этой памяти. В частности, он выделяет 4 байта в разделе .data и инициализирует их символами '%', 'd', '\n' и '\0'. Поскольку метка intout определена непосредственно перед строкой .string, она имеет адрес первого байта в строке.

В строке push intout приведена инструкция, которая считывает 4 байта, начиная с адреса, на который указывает intout, и выталкивает их в стек (в частности, он вычитает 4 из ESP и затем копирует их в 4 байта, которые теперь указываются на по ESP.) Строка push $intout (или push OFFSET intout) подталкивает 4 байта, которые составляют 32-разрядный адрес intout в стеке.

Это означает, что линия push intout толкает бессмысленное значение в стек. Функция printf заканчивает ее интерпретацию как указатель, адрес, в котором должна храниться строка формата, но поскольку она не указывает на правильное местоположение в памяти, сбой вашей программы.

+0

Это относится только к синтаксису Intel? – CaffeineAddict

+1

Синтаксис '' 'in at & t сделал то же самое. – Jester

+0

А, понял, спасибо. И как это вообще работает? Где хранится intout, почему он знает, что число в формате находится в стеке. Я весьма озадачен. Есть ли путеводитель в любом месте, чтобы лучше понять, как это работает. До сих пор мои лекции охватывали только регистры и управление потоком. – CaffeineAddict

Смежные вопросы