2016-12-18 3 views
0

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

Программа, которую я пишу, включает в себя 1 аргумент командной строки, который должен быть строкой 0s, 1s и/или 2s. Если это не так, отображается сообщение об ошибке, и программа заканчивается.

Если ошибок нет, «суффиксы» строки отображаются в порядке.

Пример:

"./sufsort 00100102" should produce 

sorted suffixes: 
00100102 
00102 
0100102 
0102 
02 
100102 
102 
2 

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

%include "asm_io.inc" 

section .data 
    arg_error_msg: db "Error, incorrect number of arguments. Only 2 arguments are allowed.", 0 
    bad_char_msg: db "Error, invalid character used. Only 0, 1, or 2 are allowed.", 0 
    bad_length_msg: db "Error, invalid input length. Length must be between 1 and 30.", 0 
    sorted_msg: db "sorted suffixes:", 0 
    ; y is an array of suffix indeces, which are sorted later by the main method 
    y: dd 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 

section .bss 
    argc: resd 1  ; number of command line arguments 
    X: resb 31   ; array copy of the input string 
    N: resd 1   ; length of the input string 

section .text 
    global asm_main 

sufcmp:       ; sufcmp(String Z, int i, int j) 
    enter 0, 0 
    pusha 

    mov edx, dword [ebp+16]  ; edx = String Z 
    mov esi, dword [ebp+12]  ; esi = int i 
    mov edi, dword [ebp+8]  ; edi = int j 

    CMP_LOOP: 
     cmp byte [edx+esi], byte 0   ; if Z[i] = null, ret -1 
     je CMP_NEGATIVE 
     cmp byte [edx+edi], byte 0   ; if Z[j] = null, ret 1 
     je CMP_POSITIVE 
     mov al, byte [edx+edi] 
     cmp byte [edx+esi], al ; if Z[i] < Z[j], ret -1 
     jl CMP_NEGATIVE 
     cmp byte [edx+esi], al ; if Z[i] > Z[j], ret 1 
     jg CMP_POSITIVE 
     inc esi        ; increment i and j 
     inc edi 
     jmp CMP_LOOP      ; repeat 

    CMP_NEGATIVE: 
     popa 
     mov eax, dword -1 
     jmp CMP_DONE 

    CMP_POSITIVE: 
     popa 
     mov eax, dword 1 
     jmp CMP_DONE 

    CMP_DONE: 
     leave 
     ret 

asm_main:       ; sufsort(String inputString) 
    enter 0, 0 
    pusha 

    ARG_CHECK:      ; Check number of arguments 
     mov eax, dword [ebp+8]  ; eax = # of line arguments 
     cmp eax, dword 2   ; if there are just 2 line argument, skip the error 
     je CHAR_CHECK 
     mov eax, arg_error_msg  ; display an error message 
     call print_string 
     call print_nl 
     jmp DONE     ; terminate the program 

    CHAR_CHECK:      ; Check characters & get length of string 
     mov ebx, dword [ebp+12] 
     mov ecx, dword [ebx+4]  ; eax = input string 
     mov edi, dword 0   ; edi will be the counter 
     CHAR_LOOP: 
      cmp byte [ecx], byte 0 ; if Z[edi] = null, end the loop 
      je CHAR_LOOP_DONE 
      mov al, byte [ecx] 
      cmp al, '0'  ; if byte [ecx} != '0', '1', '2', complain 
      je GOOD_CHAR 
      cmp al, '1' 
      je GOOD_CHAR 
      cmp al, '2' 
      je GOOD_CHAR 
      BAD_CHAR: 
       mov eax, bad_char_msg ; display an error message 
       call print_string 
       call print_nl 
       jmp DONE    ; terminate the program 
      GOOD_CHAR: 
       mov [X + edi], al  ; copy the character into X[edi] 
      inc ecx 
      inc edi 
      jmp CHAR_LOOP 
     CHAR_LOOP_DONE: 
      mov [N], edi    ; N = length of Z 
      mov [X + edi], byte 0  ; add a null character to the end of X 

    LENGTH_CHECK:    ; Check the length of the input string 
     cmp dword [N], 1  ; if N < 1 or N > 30, stop the program 
     jl BAD_LENGTH 
     cmp dword [N], 30 
     jg BAD_LENGTH 
     jmp SHOW_COPY   ; else, continue 
     BAD_LENGTH: 
      mov eax, bad_length_msg  ; display an error message 
      call print_string 
      call print_nl 
      jmp DONE     ; terminate the program 

    SHOW_COPY:    ; output X to check if it copied properly 
     mov eax, X 
     call print_string 
     call print_nl 

    BUBBLE_SORT:    ; Bubble sort, which sorts substrings using array y 
     mov esi, [N]   ; esi = i (counts from N to 0) 
     mov edi, dword 1  ; edi = j (counts from 1 to i) 
     BUBBLE_SORT_I: 
      cmp esi, dword 0   ; if i = 0, end the outer loop 
      je SORTED_SUFFIXES 
      BUBBLE_SORT_J: 
       cmp esi, edi   ; if i = j, end the inner loop 
       je BUBBLE_SORT_J_DONE 
       push dword [X]  ; call sufcmp, which takes 3 args (String Z, int i, int j) 
       push dword [edi] 
       push dword [esi] 
       call sufcmp 
       add esp, 12    ; move esp back 12 bytes, to undo the 3 pushes 
       cmp eax, dword -1 
       je NO_SWAP 
        mov ebx, dword [y + edi-1]   ; temp = y[j-1] 
        mov edx, dword [y + edi-1]   ; comparison temp 
        mov edx, dword [y + edi]   ; y[j-1] = y[j] 
        mov [y + edi], ebx 
       NO_SWAP: 
       inc edi     ; j += 1 
       jmp BUBBLE_SORT_J 
      BUBBLE_SORT_J_DONE: 
      dec esi    ; i -= 1 
      jmp BUBBLE_SORT_I 

    SORTED_SUFFIXES_T:   ; print "sorted suffixes" 
     mov eax, sorted_msg 
     call print_string 
     call print_nl 

    SORTED_SUFFIXES: 
     mov esi, dword 0  ; esi = i 
     PRINT_STR_LOOP: 
      cmp esi, dword [N-1]   ; if i = N-1, end the outer loop 
      je DONE 
      mov eax, dword [X]    ; move address of X to eax 
      add eax, [y + esi]    ; move eax to address of X[y[i]] 
      call print_nl     ; put each suffix on a separate line 
      inc esi       ; i += 1 
      jmp PRINT_STR_LOOP 

    DONE: 
     popa 
     leave 
     ret 

И я получаю эту

enter image description here

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

+2

Ошибка сегментации не означает, что с стеком возникает проблема. Это означает, что какой-то указатель где-то недействителен. Я заметил, что вы используете 'pusha' в 32-битном коде, который не делает то, что вы думаете. (Вероятно, вы хотите «pushad».) Пройдите код в отладчике, чтобы найти плохой указатель. –

+0

@RaymondChen: 'pushad' является псевдонимом' pusha', код операции идентичен. CPU будет делать это в зависимости от того, в каком режиме он запускается. Но он определенно нуждается в отладчике в любом случае, также отображается недопустимая заметка указателя. – Ped7g

+0

Используйте отладчик (например, gdb), чтобы узнать, какие инструкции неисправны (и каковы значения регистра в этой точке). См. Http://stackoverflow.com/tags/x86/info, особенно внизу, где есть подсказки gdb. –

ответ

0

Ну, вам понадобится отладчик, так как в вашем коде есть несколько проблем, и для меня он слишком велик, чтобы он точно управлял голосом (например, 100% защитный стек/и т. Д.), Так что всего лишь несколько вещей, которые я вижу :

В CHAR_CHECK: цикл проверяет длину во время цикла, поэтому вы не перезаписываете память .bss, когда кто-то дает вам слишком длинную строку. Вы можете переместить проверку длины прямо под CHAR_LOOP:, когда edi находится вне границ, остановите цикл.

добавить также нулевой символ перед сохранением N (SWAP эти две mov линии), а N хранится сразу после X в памяти, так и с 31 (?) Длинной входной строки вы перезаписать N к 0 (это, в частности, не но копия длинной строки может быть).

jl/jg используется в проверке длины, но длина без знака, поэтому jb/ja бы больше смысла для меня (не ошибка, подписанная тест >=1 && <= 30 подведет в то же время, как беззнаковое один, просто не чувствует себя хорошо, если у вас есть программирование OCD).

good/bad char test - вы можете сделать это немного короче, выполнив только два теста ('0' <= char && char <= '2'), так как ['0', '1', '2'] - значения [48, 49, 50].

И теперь более серьезный материал следует.

В цикле I/J вы не сбрасываете J, поэтому логика вашего внутреннего цикла будет ошибочной.

push dword [X] Я не думаю, что это делает то, что вы думаете. Адрес строки: X, [X] - это содержимое памяти (символы строки). (Это сделает sufcmp код сегментацию рано, когда он будет пытаться получить доступ к «адресу» '0010', которая не является законным.

В свопе, например mov edx, dword [y + edi] ...вы увеличиваете edi на 1, но Y определяется как массив слов, поэтому везде индексирование должно быть edi*4.

cmp esi, dword [N-1] ; if i = N-1 ммм, Нету, он сравнивает esi со значением по адресу N-1, так что если [N] содержит 16 и впереди него одиночный нулевой байт, то cmp будет сравнивать esi со значением 4096 (память на N-1: 00 10 00 00 00 , поэтому [N] == 0x00000010 и [N-1] == 0x00001000).

mov eax, dword [X] ; move address of X to eax - no, lea будет делать то, что говорится в комментарии. mov будет получать содержимое по адресу X.

add eax, [y + esi] - снова с использованием + -1 индексации с dword массив.

И вы забыли вызвать print_string, вызывается только новая строка.

Вы можете переписать эту часть как:

mov eax,[y + esi*4] ; eax = Y[i] 
lea eax,[X + eax]  ; eax = address X + Y[i] 

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

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

Каждый раз. Таким образом, вы сохраняете перетасовку содержимого массива Y в соответствии с исходной строкой на каждой итерации.


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

Если вы все еще хотите обойтись без отладчика, измените технику. Не пишите столько кода, не компилируя + запустив его. Сделайте это гораздо намного меньшими шагами и продолжайте показывать всевозможные вещи, чтобы убедиться, что ваши новые 3 строки кода делают то, что им нужно. Например, если вы создадите пустую заглушку для sufcmp, просто распечатав строку из указателя, она будет segfault сразу после попытки доступа к строке.

Это могло бы дать вам лучший намек, чем когда почти окончательный код приложения является segfault, поэтому вместо проблемы с охотой на последние 10 строк у вас есть 50+ причин.


EDIT: Алгоритм предложение:

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

i:[0,N): count[i] = countif(j:[0,N): X[j] < X[i]) 
i:[0,N): j:[0,N): if (i == count[j]) print X[j] 

Я надеюсь, что вы будете в состоянии расшифровать его ... это значит, что я бы вычислить для каждого суффикса, сколько суффиксы «меньше» лексически, то есть. полный O (N) loopy loop (что на самом деле N^3, потому что сравнение строк - это еще один O (N) ...но кто заботится о N = 30, даже N был бы терпимым).

Затем напечатать суффиксы в правильном порядке, вы просто искать count массив снова и снова, в первый раз для 0 меньшего подсчета (это самый маленький), то для 1, ... и т.д. До печати все из них ,

На самом деле вы можете пройти через все суффиксы, рассчитать, сколько их меньше, и поместить индекс этого суффикса в sorted[smaller_count], поэтому для печати вы будете просто прокручивать массив sorted от 0 до N-1, без поиска.

+0

** Конечно, я не отлаживал ваш код (это не [MCVE]), поэтому я могу ошибаться в некоторых предположениях, что такое ошибка и что не сработает, может быть, ваш алгоритм правильный, и я просто как-то не получаю его ** – Ped7g

+0

Большое вам спасибо! Извините, я настолько некомпетентен с этим материалом, но, как я уже сказал, сборка имеет для меня какой-то смысл, но не так много. Причина, по которой я не использую отладчик, состоит в том, что я не знаю, как; Я никогда не узнал, как использовать его в своем курсе. Благодаря вашим советам и немного более возиться, это работает! – Matt

+0

@Matt: Нах, ты делаешь на самом деле вполне нормально. Несколько пропустил этот общий алгоритм, он был ошибочным, не так ли? Но, как правило, вы демонстрируете понимание принципа того, что вы делаете. Мой список «проблем» несколько педантичен, плюс вам нужны годы опыта, чтобы «видеть» их, особенно в собственном источнике. Обычно вы просто делаете проблему и исправляете ее во время отладки + рефакторинг, когда важно переключить ум на «чтение чужого источника, а не моего». Об отладчике: либо бросьте курс, либо изучите его самостоятельно. 'gdb' работает для любых двоичных, а не только asm (инвестиций). – Ped7g

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