Странная ошибка сегментации в сборке - PullRequest
5 голосов
/ 06 мая 2011

Я написал утилиту hexdump на ассемблере, используя компилятор nasm и компоновщик ld. Программа должна сбрасывать шестнадцатеричные значения для любого входного файла. Однако при этом происходит ошибка в одной конкретной процедуре «LoadBuff». чтение ввода в буфер 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 Backtrace дает

#0  0x00000000 in ?? ()

Есть идеи, почему так происходит?

1 Ответ

1 голос
/ 06 мая 2011

Вы используете 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 перепрыгивает через эту точку, поэтому вездеВ стеке есть три значения, которых там быть не должно.Вы должны перепрыгнуть на свой код очистки, а не мимо it.

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

...