2012-11-30 2 views
2

Я хочу написать следующий код C в Ассамблее:Сборочные (AT & T 32 бит) Scanf вопросы

int main(void) 
{ 
    int x,y; 
    scanf("%d%d",&x,&y); 
    printf("%d%d",x,y); 
    return 0; 
} 

Сначала я попробовал это только один целое число для сканирования/печати:

.section .rodata #read only data section 
fmt: .string "%d%d" 
    .text 
.globl main  
    .type main, @function 
main: 
pushl %ebp #save the old frame pointer 
movl %esp, %ebp #create the new frame pointer 

pushl %esp #location of x 
pushl $fmt 
call scanf 

    #stack should now have exactly the scanned number x and then the format, as needed for printf. 
call printf 

movl $0, %eax 
movl %ebp, %esp #restore the old stack pointer - release all used memory. 
popl %ebp #restore old frame pointer (the caller function frame) 
ret 

Но это не работает. По какой-то причине, следующий трюк сделал его работу (добавлено до Printf):

addl $4,%esp #pop format 
pushl 4(%esp) 
pushl $fmt 

Я не понимаю, почему бы PUSHL 4 (% ЭСП) заставить его работать, поэтому мой первый вопрос я прошу разъяснение по этому вопросу. Затем я попытался сделать то же самое с двумя переменными:

fmt: .string "%d%d" 
[...] 
    pushl %esp #location of x 
    pushl %esp #location of y 
    pushl $fmt 
    call scanf 

Но это вызвало ошибку сегментации. Он даже не достиг PRINTF часть, где я бы попробовал что-то вроде этого:.

addl $4,%esp #pop format  
pushl 8(%esp) 
pushl 8(%esp) 
pushl $fmt 

call printf 

(Следуя той же логике, с PushL 4 (% ЭСП), прежде чем Так что мой второй вопрос, как может Я заставить его работать с двумя переменными Спасибо

редактировать:.!? Почему следующий код не будет работать для сканирования двух переменных

subl $8,%esp #leave place for two vars 
pushl -4(%ebp) #location of x 
pushl -8(%ebp) #location of y 
pushl $fmt 
call scanf 

ответ

1

«Он должен вычитать% особ на 4, а затем сохранить% особ на месте особ указывает на»

Это было бы на процессоре 8086. От 80286 он хранит ESP внутри, затем вычитает, а затем записывает значение, хранящееся внутри. Вы должны сначала назначить переменную (одно нажатие или один esp-4), а затем сохранить адрес этой переменной (второе нажатие). Для первого вопроса вы должны сделать 3 нажатия или одну суб и 2 нажатия. В вашем случае esp указывает на местоположение стека, где был сохранен старый EBP. Вы можете использовать

push eax 
push esp 
push fmt 

это будет работать.

Кроме того, о второй проблеме, вы сослались на линиях, которые не даже дело,

Ах да, я скопировал неправильные строки кода, извините. Я обращался к этой статье:

pushl %esp #location of x 
pushl %esp #location of y 
pushl $fmt 
call scanf 

Я указал, почему ваш код неверен. Вы должны нажать 2 адреса переменных. Вместо этого вы нажимаете адрес старого EBP, затем адрес ячейки в стеке с параметром prev (который указывает на старый EBP); В результате при чтении один параметр перепутался, получив значение, введенное в scanf. Когда он хочет записать другое значение, вместо адреса ячейки, он имеет предыдущий int.

И наконец, не могли бы вы объяснить предлагаемый вами код? Почему я двигаюсь EDX и EAX в особ, я даже не знаю, что в них

К сожалению, это синтаксис Intel, следовательно, MOV EAX, особ означает «писать особ в EAX». Это действительно не очень хороший код. Только для примера.

Я выделяю одну переменную в стеке, получаю ее адрес в eax. Затем выделите другой var, сохраните его адрес в edx. Затем нажмите оба адреса, затем нажмите смещение fmt.

Вы должны сначала выделить пространство. И вам не нужен кадр, если вы не собираетесь обращаться к локальным варам, используя относительные адреса EBP. Вы можете нажать ebp - 4 и т. Д. Просто скомпилируйте свой код и посмотрите, как он работает в любом отладчике (я использовал ollydbg для проверки вашего кода); Наконец, вы можете попросить компилятор C сгенерировать список asm и посмотреть, как это делает компилятор.

+0

Теперь я все понял, за исключением того, что нужно беспокоиться о том, чтобы нажать% eax или еще много чего. Почему бы просто не добавить -4 перед esps (т. Е. Иметь код pushl -4 (% esp) #location x (и тот же для y, перед scanf) сделать трюк? – nodwj

+0

Потому что он не выполняет trick '.Если вы нажимаете esp-4, вы по-прежнему не выделяете память для переменных. –

+0

Итак, если теперь я понимаю проблему, scanf не может перезаписать адрес, если адрес, указанный для scanf, указывает на себя? – nodwj

0

с этим:

pushl %esp #location of x 
pushl $fmt 
call scanf 

вы перезаписываете EBP.

Во-первых, CPU запоминает значение регистра (старое значение esp), затем вычитает 4, а затем сохраняет значение старого ESP. В этом случае это старый EBP. Когда вы сначала вычитаете 4, вы выделяете переменную в стеке (лучше PUSH EAX - она ​​короче, 1 байт);

Аналогичная проблема втором случае:

addl $4,%esp #pop format  
pushl 8(%esp) 
pushl 8(%esp) 
pushl $fmt 

Здесь первый параметр указывает не X, а второй параметр. Второй момент указывает на EBP. Вы должны выделить переменные в стеке первым:

push ebp 
mov ebp, esp 
push eax 
mov edx, esp 
push eax 
mov eax, esp 
push eax 
push edx 
push offset fmt 
call xyz 

И еще: если вы не работаете с местными переменными, вам не нужно нажать EBP, нет необходимости в создании указателя кадра. Кроме того, после выделения ВАР в стеке, вы могли бы использовать это:

LEA eax, [EBP - 4] 
LEA edx, [EBP - 8] 
+0

Я не понимаю, почему бы pushl% esp переписать% ebp? Он должен вычесть% esp на 4, а затем сохранить% esp в местоположении, на которое указывает esp. Кроме того, о второй проблеме вы ссылались на строки, которые даже не имели значения, поскольку ошибка сегментации была инициирована еще раньше, так что это не проблема. Наконец, не могли бы вы объяснить свой предложенный код? Почему я должен переместить edx и eax в esp, я даже не знаю, что в них ... Спасибо! – nodwj

+0

Добавлено как другой ответ, так как он больше, чем разрешено. –

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

  • Нет связанных вопросов^_^