Странная ошибка сегментации в сборке

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

LoadBuff:
    push ebx                 
    push edx
    push eax

    mov eax,3       ;sys_read call      
    mov ebx,0               ;read from standard input
    mov ecx,Buff            ;pass the buffer adress
    mov edx,BuffLen         ;pass the number of bytes to be read at a time
    int 80h                 ;call the linux kernel
    mov ebp,eax
    ;cmp eax,0              ;number of characters read is returned in eax
    ;jz exit                ;if zero character is returned i.e end of iinput file      
                            ;jump to exit

    xor ecx,ecx 
    pop eax
    pop edx
    pop ebx
    ret

Если линии

;cmp eax,0                   
;jz exit                      

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

обратная трассировка gdb дает

#0  0x00000000 in ?? ()

Любая идея, почему это происходит именно так?


person bornfree    schedule 06.05.2011    source источник
comment
я не опубликовал полную программу, так как она длинная ... но в вызывающей программе есть метка выхода. Это просто процедура программы, которая приводит к ошибке seg. если хотите, могу выложить всю программу.   -  person bornfree    schedule 06.05.2011
comment
Нет потребности. Выпрыгнуть из такой функции, как правило, невозможно (если вы не уверены, что знаете, что делаете). Ответ ниже описывает это более подробно.   -  person Greg Hewgill    schedule 06.05.2011


Ответы (1)


Вы используете NASM, но не указываете, используете ли вы синтаксис в стиле Intel или синтаксис в стиле AT&T. Однако, глядя на ваш пример кода, я предполагаю, что это стиль Intel.

В синтаксисе в стиле Intel такие операции, как mov, работают следующим образом:

mov <destination>, <source>

Другими словами, они пытаются имитировать стиль мышления «пункт назначения = источник». В синтаксисе AT&T все наоборот:

mov <source>, <destination>

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

Теперь посмотрите на эту строку кода:

mov ebp, eax

Если вы используете синтаксис в стиле Intel (а я думаю, что это так, потому что синтаксис в стиле AT&T будет mov %ebp, %eax), вы перемещаете содержимое регистра eax в ebp. ebp традиционно используется как "базовый указатель"... обратите внимание на слово "указатель"... и часто используется именно так. Когда вы получаете 0 в eax, вы перезаписываете существующий базовый указатель нулевым указателем. Начинаются дурацкие шутки.


Однако это не единственная проблема. Еще проблема вот в чем:

jz exit

Я не вижу метки выхода нигде в коде, который вы разместили, поэтому вы прыгаете ГДЕ-ТО (иначе ассемблер будет скулить) за пределы вашей процедуры. В процессе вы передаете код очистки стека, оставляя стек в неизвестном состоянии. По сути, вы поместили содержимое трех регистров в стек и оставили его там, где его не ожидают другие подпрограммы.

Проблема в том, что вы пропускаете код очистки. В начале вашей процедуры вы нажимаете ebx, edx и eax. В конце вашей процедуры вы правильно выталкиваете их в обратном порядке (eax, edx и ebx). Это оставляет стек в том же состоянии при выходе, что и при входе, и код, который полагается на это, настроен на выполнение, как ожидалось.

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

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

person JUST MY correct OPINION    schedule 06.05.2011
comment
Спасибо за быстрый ответ. Я не разместил здесь полный код. В программаторе вызывающего абонента есть метка выхода. - person bornfree; 06.05.2011
comment
Мне выложить всю программу?? - person bornfree; 06.05.2011
comment
Я добавил расширенное разъяснение фактической проблемы. - person JUST MY correct OPINION; 06.05.2011
comment
Я понял вашу точку зрения и согласен с вами. Но как ни странно, если я опускаю строки выхода cmp eax, 0 и jz, программа выдает ошибку seg, когда встречает конец файла (т.е. eax = 0). Она не может вернуться к вызывающий. С этими строками (cmp eax, 0 jz exit) процедура при обнаружении конца входного файла может перейти к метке выхода без ошибки сегмента. Хотя я думаю, что последнее неверно, поскольку я не очистил стек. - person bornfree; 06.05.2011
comment
А, тогда я неправильно понял, в чем проблема. Не могли бы вы перефразировать его, и я взгляну на него еще раз. - person JUST MY correct OPINION; 06.05.2011
comment
О, подождите, прежде чем вы это сделаете, скажите мне, какой синтаксис ассемблера вы используете: стиль Intel или стиль AT&T? - person JUST MY correct OPINION; 06.05.2011
comment
Хорошо, неважно, я изменил свой ответ, чтобы он соответствовал тому, что кажется реальной проблемой. - person JUST MY correct OPINION; 06.05.2011
comment
Спасибо за вашу помощь до сих пор. Я загружу полный исходный код, который сделает картину более ясной. - person bornfree; 06.05.2011
comment
Наконец, я отладил его. Спасибо за ваше ценное предложение. Я сделал несколько глупых ошибок в дизайне, которые привели к вышеуказанной ошибке сегмента. Теперь все готово. - person bornfree; 07.05.2011
comment
Если вы задаетесь вопросом о отрицательном голосовании: я был удивлен, увидев его на моем экране репутации / активности. Должно быть, это произошло случайно. Вернул бы, если бы мог. Мне жаль! - person Tim Bodeit; 22.06.2014