NASM子程序调用分段错误

时间:2016-12-18 02:03:39

标签: assembly x86 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之外,我不会以任何方式操作堆栈。

1 个答案:

答案 0 :(得分:0)

嗯,你需要调试器,因为你的代码中存在一些问题,并且它有点太大而无法准确地运行它(比如100%防护堆栈/等),所以只有很少的东西我明白了:

CHAR_CHECK:循环测试循环期间的长度,所以当有人给你太长的字符串时你不要覆盖.bss内存。您可以在CHAR_LOOP:下方移动长度检查,当edi超出范围时,停止循环播放。

在存储N之前添加空字符(交换这两个mov行),因为N存储在内存中X之后,所以31(? )长输入字符串,您将覆盖N0(这特别是不可利用,但长字符串的副本可能是)。

jl/jg用于长度检查,但长度是无符号的,因此jb/ja对我来说更有意义(不是错误,签名测试>=1 && <= 30会在无符号的同时失败一,如果你有编程OCD,那感觉就不对。)

好/坏char测试 - 只需进行两次测试('0' <= char && char <= '2')就可以缩短测试时间,因为['0', '1', '2']是值[48, 49, 50]

现在更严重的事情发生了。

在I / J循环中你不能重置J,所以内循环的逻辑会有缺陷。

push dword [X]我认为这不符合你的想法。字符串的地址是X[X]是内存的内容(字符串的字符)。 (这将使sufcmp代码提前进行段错误,此时会尝试访问&#34;地址&#34; '0010',这是不合法的。

在交换中,例如mov edx, dword [y + edi] ...您将edi增加1,但Y被定义为dwords数组,因此索引应该是{{1} }。

edi*4嗯,不,它会将cmp esi, dword [N-1] ; if i = N-1与地址esi的值进行比较,因此如果N-1包含16且前面是单个零字节,那么{ {1}}会将[N]与值cmp进行比较(N-1的记忆为esi,因此409600 10 00 00 00)。

[N] == 0x00000010 - 不,[N-1] == 0x00001000会做评论所说的。 mov eax, dword [X] ; move address of X to eax将获取地址lea的内容。

mov - 再次使用带有X数组的+ -1索引。

你忘记调用print_string,只调用新行。

您可以将该部分重写为:

add eax, [y + esi]

而且,由于我的残忍和疲惫,我保留了最后一个笔记的最大担心。

我认为这根本不会起作用。您的冒泡排序正在迭代原始的dword字符串(好吧,它不是,但是一旦您使用正确的地址解决了参数问题,它就会)。

每一次。因此,您可以在每次迭代中根据原始字符串继续对mov eax,[y + esi*4] ; eax = Y[i] lea eax,[X + eax] ; eax = address X + Y[i] 数组的内容进行混洗。

我的答案中最重要的部分是第一句话。你绝对需要调试器。如果你觉得这种语言到目前为止对你有所帮助,你的来源并没有证明这一点。实际上我可以看到一些理解,所以你基本上是正确的,但你必须是一个完全神童的孩子能够在没有调试器的情况下在合理的时间内完成。我会把你评为高于平均水平,也许是好的,但远离惊人的前提。

如果你仍然想要没有调试器,请改变技术。不要编写+运行代码,不要写这么多代码。通过更小的步骤来做,并继续显示所有类型的东西,以确保您的新3行代码做他们应该做的。例如,如果你为X创建空存根只是从指针打印字符串,它会在尝试访问字符串后立即进行段错误。

这可能会给你更好的提示,比几乎最终的应用程序代码是segfaulting,所以在最近的10行中你没有狩猎问题,你有50多个推理。

编辑:算法建议:

除非你真的必须使用冒泡排序,否则我会避免这种情况,并且做蛮力的愚蠢&#34;计算&#34;排序的变种。

Y

我希望你能够破译它...这意味着我会计算每个后缀有多少后缀&#34;更小&#34;按字典顺序,即。完整的O(N 2 )循环循环(实际上是N ^ 3,因为比较字符串是另一个O(N)...但是谁关心N = 30,甚至N 5 是可以忍受的。)

然后以正确的顺序打印后缀,您只需反复搜索sufcmp数组,第一次搜索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] 较小的数字(最小的数字),然后是{{ 1}},...等等你打印所有这些。

实际上你可以循环遍历所有后缀,计算多少个后缀,并将该后缀的索引放入count,因此对于打印,你只需要将0数组从0循环到N-1 ,没有涉及搜索。