LINUX.ORG.RU

Assembler Gas непонятное для меня поведение программы

 


0

1

здрасьте здрасте люди добрые

объясните кто знает, почему программа не вылетает с ошибкой а уходит в цикл


.section .bss
out:
.space 8

.section .data

frt: "%d"

.section .text
.global main
main:
  lea out, %rsi
  lea frt(%rip), %rdi
  mov $0, %rax
  call scanf
  mov $0, %rax

  mov out, %rsi
  lea ft(%rip), %rdi  // только тут заметил значение RIP, как надпись покойся с миром.
   mov $0, %rax
  call printf
   mov $0, %rax

  

если добавить в конец ret, то что он снимет со стека? адрес следующей команды за scanf или за printf?

что тут произойдет? scanf положит на стек адрес следующей команды, а это будет mov $0, %rax. дальше все попорядку и когда дойдет до printf то на стек ляжет адрес команды следующей за printf.

а что потом? можете объяснить, как это все будет разматываться?

что тут произойдет? scanf положит на стек адрес следующей команды, а это будет mov $0, %rax. дальше все попорядку и когда дойдет до printf то на стек ляжет адрес команды следующей за printf.

вы чета недопонимаете.

lea out, %rsi lea frt(%rip), %rdi mov $0, %rax call scanf mov $0, %rax

эта пурга - это вызов фукнции scanf, а последняя команда забирает результат вызова из регистра rax, $0… тут адрес равен нулю чтоли?? подзабыл я интеловский асм. стек после вызова сканф будет такой же что и до вызова. ничто не изменится.

дальше идет вызов printf с передачей параметров. после вызова функции на стеке может быть что-то только если функция возвращает результат через стек. а тут возврат через регистры. или его нет вообще

alysnix ()

вообще у нормального интеловского ассемблера приемник идет первым, а в этом коде первый явно источник, иначе чепуха получается. никто конечно не мешает написать ассемблер с положением операндов наоборот…но.. вопрос имеется. что это за ассемблер такой?

alysnix ()
Ответ на: комментарий от anonymous

можете помочь тут? почему если эпилог не написан в коде, то будет циклом ждать ввод с клавиатуры(scanf) и будет вывод на экран(printf). нужен ли тут ret? ret снимает с виршины стека адрес команды и передает по нему управление. не вызывает вопросов до scanf. как только мы доходим до него,

call scanf

call положит на вершину стека адрес следующей команды.

идем , идем и доходим до

call printf

сall положит на вершину снова адрес следующей команды.

что имеем? имеем на вершине два адреса.

я так понимаю, если нет ret то все равно со стека будет снят адрес и ему передастся управление. но как?

Assembler ()
Ответ на: комментарий от Assembler

call кладет адрес возврата, то есть адрес следующей команды за call. кладет его, чтобы ret его оттуда взял и произошел возврат на эту команду. этот ret есть внутри вызываемой функции. как только выполнится ret внутри функции, этот адрес будет со стека снят и установлен как текущий ip. то есть после выхода из функции вызываемой через call,и ее возврата через ret. никакого адреса на стеке уже нет, раз из функции возвратились.

адрес возврата на стеке лежит пока вызываемая функция еще не сказала ret.

alysnix ()
Ответ на: комментарий от Assembler
  1. scanf - блокирующая функция, то есть если она вызвалась, она там ждет данных.
  2. чтобы ее вызвать, надо знать соглашение по распределению регистров при передаче параметров для компилятора.

lea out, %rsi
lea frt(%rip), %rdi
mov $0, %rax

вот тут параметры для нее разложили по регистрам, но насколько правильно - сказать не могу. смотри как другие функции вызывает компилятор. аналогично по printf цикла тут вообще никакого нет поскольку нет никакого jump и код линейный. и вообще это какой - кусок. в конце должен быть ret по идее, если это main. main такая же функция как и прочие. просто ее вызывают первой при старте задачи.

что такое эпилог у тебя? эпилог функции? так это просто возврат из нее. у тебя его нет. если нет возврата, код пойдет дальше выполнятся. а раз там ничего нет, рухнет по прерыванию о неверном коде инструкции.

это рукописное что-то? первые опыты с асмом?

alysnix ()
Ответ на: комментарий от alysnix

да блин, я пытаюсь разобраться. дело в том что программа не крашится. это и идет в разрез с тем что вы написали.


pop %rbp
mov %rbp, %rsp;  это эпилог.

если не шарите в Ассемблер, как я , то вы не сможете помочь. мы как два слепца будем плутать . Пожалуйста посетите другую тему, в которой вы сильны. Здесь вы не помощник .

может знающие люди зайдут и объяснят. хотя надежды мало.

Assembler ()
Ответ на: комментарий от Assembler

в данном коде нет никакого цикла вообще. и быть не может. никаких адресов возврата на стеке быть не может, если функция уже вернулась. и «эпилог» тут не причем. а теперь ищите «знающих»

alysnix ()
Ответ на: комментарий от alysnix

ты понимаешь что такое цикл, и когда программа зациклина?

зациклина, это значит программа просит каждый раз ввести значение с клавиатуры и выводит это значение на экран. она должна была проделать это ровно один раз. но по каким то причинам он делает это бесконечно. я и хочу выяснить почему. я так ни писал. программа работает так сама, по каким то не известным мне причинам.

что значит в данном коде нет цикла? что хотели этим сказать? в коде его нет, потому что он цикл там мне был не нужен. но программа работает циклически. я цикл не создавал. короче не знаю как тебе объяснить. я понял только одно, что вы такой же нуб, как и я. прочитали в гугле статью и решили что знаете стек и Ассемблер.

Assembler ()
Ответ на: комментарий от Assembler

ты хоть ret в конце напиши. main это функция, из нее вернуться надо.

mov $0, %rax

ты после того как загрузил 0 в rax в последней строке, куда пошел? что там дальше-то? если ничего у тебя не написано, значит мусор.

alysnix ()
Ответ на: комментарий от alysnix

блин, я не зная как тебя зовут, ну явно алеша))))))))))) ты нравишься мне))))))

недописанно специально, чтобы понять как же на самом деле работает программа. почему она вместо того чтобы вылетить с ошибкой превращается в вечный цикл. суть не в том чтобы она заработала, а втом чтобы понять почему так происходит. до

call printf
mov $0, %rax

и что происходит дальше?

Assembler ()
Ответ на: комментарий от Assembler

так и работает. грузит ноль в сегмент rax, увеличивает счетчик команд. но поскольку у тебя ничего дальше не написано, там лежит нечто. что там лежит сказать нельзя, пока нет карты памяти, что надо просить у линкера. запросто там лежит вход в рантайм например. то есть провалившись в своем коде дальше, ты заходишь и инициализацию рантайма, который вызывает снова main. короче провалишись дальше ты набрел на вызов мейна. а это делается в конце инициализации рантаймового кода. другого вразумительного обьяснения нет. ничего другого, скачущего на main там быть не может.

поставь ret в конце и не морочь людям голову и себе тоже. он там ДОЛЖЕН БЫТЬ.

alysnix ()
Ответ на: комментарий от alysnix

стопэ. нам видимо надо вместе в школу. ret не поможет.

ret-это команда, которая снимает со стека , с его верха адрес следующей команды за call.

последник call в моем коде это call printf. за ним ничего не идет. если я посалю ret то по вашей логике я снял бы со стека следующей команды за printf а за ней как раз идет ret, который идет на стек и снимает адрес с вершины стека.

чтобы все правильно отработало значит нужна такая последовательность


leave
ret
тогда call printf положит на стек адрес команды
leave
[/cod]
 а ret его снимет со стека и передаст управление.



но программа работает и без leave только с ret. значит не вы ни я неправы.  

блин, хоть бы один шарящий зашел. 

Assembler ()
Ответ на: комментарий от Assembler

почитайте уже, кто кладет на стек адрес возврата, и кто берет это адрес. я вам уже стописят раз сказал.

кладет на стек адрес возврата команда call!!! читайте описание команды а забирает его со стека и возвращается команда ret!!! тоже читайте описание.

ставьте ret в конце кода, на стеке в данный момент находится адрес возврата в функцию, которая вызвала ваш main. ваш main ВЫЗВАЛИ командой call! вот к вызывавшему и надо выйти, чтобы завершить вашу программу.

формирование пролога и эпилога функции у интела это команды enter, leave. пролог и эпилог вам в данном не нужны. они нужны если делается фрейм на стеке для переменных и/или параметров вашей функции. раз у вас нет enter, то и leave вам не нужен.

у вас в голове нереальная каша, вы путаете пролог эпилог с адресом возврата

alysnix ()
Ответ на: комментарий от alysnix

алексей, вы не правы.

/////

////
////

call scanf; на стеке адрес возврата команды, что идет за call scanf

////
///
call printf на вершине стека адрес возрвата что ниже call printf, а это ret
///
/////
ret; cнимет со стека свой адрес.


leave не нуждается в enter. вы просто читаете гугл и не имеете собственных знаний. очень жаль. еще одним невежей на форуме стало больше. а до меня раньше доходили слухи, что данный ресурс славится высококвалифицированными специалистами. мне говорили что тут почти все боги в программировании. вобщем ждем богов. может снизойдут.

Assembler ()
Ответ на: комментарий от Assembler

блин. это ж надо так морочиться. call положил на стек адрес возврата. а забрала его оттуда команда ret, что находится в конце ВЫЗВАННОЙ функции. ОНА В КОНЦЕ ВЫЗВАННОЙ ФУНКЦИИ, той которую вы вызвали. в данном случае scanf, там внутри scanf, есть ret. если б его там не было, вы бы не вернулись в свой main. но поскольку вы вернулись, адреса возврата на стеке уже нет, потому что исполнилась команда ret, вытолкнула адрес со стека, перешла по нему - то есть возврат произошел.

А этот ret, который вы все не можете написать тут, и который нужен, он для возврата в функцию что вызвала main. ваш main тоже вызвали, и ему нужен свой ret, чтобы main завершился.

alysnix ()

Чтобы посмотреть, что выполняется после mov $0, %rax можно дизассемблировать конечную программу, например через objdump -D -R a.out. У меня выводит следующее:

...
0000000000401132 <main>:
  401132:	48 8d 34 25 44 40 40 	lea    0x404044,%rsi
  401139:	00
  40113a:	48 8d 3d ff 2e 00 00 	lea    0x2eff(%rip),%rdi        # 404040 <frt>
  401141:	48 c7 c0 00 00 00 00 	mov    $0x0,%rax
  401148:	e8 03 ff ff ff       	callq  401050 <scanf@plt>
  40114d:	48 c7 c0 00 00 00 00 	mov    $0x0,%rax
  401154:	48 8b 34 25 44 40 40 	mov    0x404044,%rsi
  40115b:	00
  40115c:	48 8d 3d dd 2e 00 00 	lea    0x2edd(%rip),%rdi        # 404040 <frt>
  401163:	48 c7 c0 00 00 00 00 	mov    $0x0,%rax
  40116a:	e8 d1 fe ff ff       	callq  401040 <printf@plt>
  40116f:	48 c7 c0 00 00 00 00 	mov    $0x0,%rax
  401176:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
  40117d:	00 00 00

0000000000401180 <__libc_csu_init>:
  401180:	41 57                	push   %r15
  401182:	4c 8d 3d 87 2c 00 00 	lea    0x2c87(%rip),%r15        # 403e10 <__init_array_start>
  401189:	41 56                	push   %r14
  40118b:	49 89 d6             	mov    %rdx,%r14
  40118e:	41 55                	push   %r13
  401190:	49 89 f5             	mov    %rsi,%r13
  401193:	41 54                	push   %r12
...

Обратите внимание: сразу после mov $0x0,%rax идет nop и __libc_csu_init.

Оригинальная программа у меня крэшится еще на вызове первого scanf(), а не зависает. Думаю, из-за нарушения выравнивания стека перед входом в scanf() (можно проверить дизассемблировав scanf() в libc.so.6).

sf ★★ ()
Ответ на: комментарий от Assembler

с какими флагами компилируете этот код?

gcc a.S -o a -fno-PIE -no-pie

надеюсь вы не на виндовс?

__libc_csu_init - это часть glibc: https://sourceware.org/git/?p=glibc.git;a=blob;f=csu/elf-init.c;h=98b3f11ff5487caf3823591c80fbadd047efb663;hb=HEAD#l68

glibc не работает на виндовс.

sf ★★ ()
Ответ на: комментарий от sf

Обратите внимание: сразу после mov $0x0,%rax идет nop и __libc_csu_init.

я автору это и сказал. он улетел в инит рантайма куда-то и рекурсивно вызвал main снова. тут даже дизасм не нужен.

вообще что там и как лежит, зависит от многого(конкретно от сборщика, скриптов сборки). автору свезло что он ушел в рекурсию, а мог и порушить что-нить. или получать чудесные и неожиданные эффекты, чтобы потом отвечающие на его вопросы мозги себе сломали.

alysnix ()
Ответ на: комментарий от Assembler

Ну так если у тебя слинковано без –no-stdlib, то и main вызывается и возврат имеет. Вообще управление на старте передаётся _start, которая делает подготовку и потом зовёт main. А после main, её возврат используется в sys_exit.

anonymous ()
Ответ на: комментарий от alysnix

я автору это и сказал. он улетел в инит рантайма куда-то и рекурсивно вызвал main снова. тут даже дизасм не нужен.

Очевидно его интересует точный механизм работы неправильной программы чтобы снять все вопросы. Для этого на конкретную программу лучше посмотреть.

sf ★★ ()
Ответ на: комментарий от Assembler

мне вдруг захотелось чтобы где-то там, за непоставленным ret, встретилась команда форматирования диска. чтобы исследование - «как работает неправильная программа» превратилось в увлекательное приключение.

alysnix ()
Ответ на: комментарий от alysnix

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

Assembler ()