NASM子例程调用分段错误 [英] NASM Subroutine Call Segmentation Fault

查看:67
本文介绍了NASM子例程调用分段错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在从事一个涉及NASM的学校项目,尽管这种语言对我来说是某种意义,但我总是最终遇到一个毫无意义的问题.

I'm working on a school project involving NASM, and while the language makes some kind of sense to me I always end up having a problem that makes no sense.

我正在编写的程序包含1个命令行参数,该参数必须为0、1、2和/或2s的字符串.如果不是,则会显示错误消息,程序结束.

The program I'm writing involves 1 command line argument, which must be a string of 0s, 1s, and/or 2s. If it is not, an error message is displayed and the program ends.

如果没有错误,则按顺序显示字符串的后缀".

If there are no errors, the "suffixes" of the string are displayed in order.

示例:

"./sufsort 00100102" should produce

sorted suffixes:
00100102
00102
0100102
0102
02
100102
102
2

现在,当我尝试调用子例程sufcmp

Right now, I'm getting a segmentation fault when I try to call my subroutine 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

我得到了

我找不到任何会导致分段错误的东西,因为除了在子例程返回之后推送函数参数和恢复esp之外,我没有以其他任何方式操作堆栈.

I can't find anything that would cause a segmentation fault, since I don't manipulate the stack in any way besides pushing the function arguments and restoring esp after the subroutine returns.

推荐答案

好吧,您将需要调试器,因为您的代码中存在多个问题,而且对于我而言,要准确地运行它有点太大(例如100 %保护堆栈/等),所以我只看到几件事:

Well, you will need debugger, as there are several problems in your code, and it's a bit too large for me to run it in head accurately (like 100% guarding stack/etc), so only few things I see:

CHAR_CHECK:循环中测试循环期间的长度,因此当有人给您太长的字符串时,您不会覆盖.bss内存.您可以将长度检查右移到CHAR_LOOP:下,当edi超出范围时,停止循环.

In CHAR_CHECK: loop test the length during loop, so you don't overwrite .bss memory when somebody gives you too long string. You can move the length check right under CHAR_LOOP:, when edi is out of bounds, stop looping.

还应在存储N之前添加空字符(交换这两个mov行),因为N紧接在X之后存储在内存中,因此使用31(?)长的输入字符串,您将覆盖N0(这尤其不可利用,但可能是长字符串的副本).

Also add the null character before storing N (swap those two mov lines), as N is stored right after X in memory, so with 31 (?) long input string you will overwrite N to 0 (this particularly is not exploitable, but the copy of long string may be).

jl/jg用于长度检查,但是长度是无符号的,所以jb/ja对我来说更有意义(不是错误,有符号的测试>=1 && <= 30将与无符号的测试同时失败,只是没有如果您已经编程过OCD,感觉不错.

jl/jg used in length check, but length is unsigned, so jb/ja would made more sense to me (not a bug, signed test >=1 && <= 30 will fail at the same time as unsigned one, just doesn't feel right if you have programming OCD).

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

good/bad char test - you can make it a bit shorter by doing only two tests ('0' <= char && char <= '2'), as ['0', '1', '2'] are values [48, 49, 50].

现在出现了更严重的问题.

And now more serious stuff follows.

在I/J循环中,您不会重置J,因此内部循环的逻辑将有缺陷.

In I/J loop you don't reset J, so logic of your inner loop will be flawed.

push dword [X]我认为这没有达到您的预期.字符串的地址是X[X]是内存的内容(字符串的字符). (这会导致sufcmp代码在尝试访问地址" '0010'(这是不合法的)时提早进行段错误.

push dword [X] I don't think this does what you think it does. The address of string is X, [X] is content of memory (chars of string). (this will make the sufcmp code to segfault early, when it will try to access "address" '0010', which is not legal.

在交换中,例如mov edx, dword [y + edi] ...,您将edi递增1,但是Y被定义为dword数组,因此在任何地方索引都应为edi*4.

In the swap, for example mov edx, dword [y + edi] ... you increment edi by 1, but Y is defined as array of dwords, so everywhere the indexing should be 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).

cmp esi, dword [N-1] ; if i = N-1 uhm, nope, it will compare esi with value at address N-1, so if [N] contains 16 and ahead of it is single zero byte, the cmp will compare esi with value 4096 (memory at N-1 is: 00 10 00 00 00, so [N] == 0x00000010 and [N-1] == 0x00001000).

mov eax, dword [X] ; move address of X to eax-不,lea会按照注释中的说明进行操作. mov将在地址X处获取内容.

mov eax, dword [X] ; move address of X to eax - no, lea would do what the comment says. mov will fetch content of at address X.

add eax, [y + esi]-再次对dword数组使用+ -1索引.

add eax, [y + esi] - again using +-1 indexing with dword array.

您忘了调用print_string,只调用了新行.

And you forget to call print_string, only new line is called.

您可以将该部分改写为:

You can rewrite that part as:

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

而且,由于我残酷又疲倦,所以我最担心的是最后一个音符.

And, as I'm cruel and tired, I kept the my biggest worry for last note.

我认为这根本行不通.您的冒泡排序是在原始的X字符串上进行迭代(嗯,不是,但是一旦用正确的地址解决了参数问题,它就会).

I don't think this will work at all. Your bubble sort is iterating over original X string (well, it's not, but once you fix the argument issues with correct addresses, it will).

每次.因此,每次迭代时,您都应根据原始字符串重新整理Y数组的内容.

Every time. So you keep shuffling content of Y array according to original string in every iteration.

我的回答中最重要的部分是第一句话.您绝对需要调试器.如果您觉得到目前为止该语言对您仍然有意义,那么您的消息来源还不能证明这一点.实际上,我可以看到一些了解,所以您基本上是正确的,但是您必须是天才的小孩子,才能在合理的时间内不使用调试器就可以将其删除.我只会将您的评分评为高于平均水平,也许还不错,但离出色的场所很远.

The most important part of my answer is the first sentence. You absolutely need debugger. If you felt like the language made some sense to you up till now, your source doesn't prove that. Actually I can see a glimpses of understanding, so you are basically right, but you would have to be total prodigy whizz kid to be able to pull this without debugger within reasonable time. I would grade you only as above-average, maybe good, but far away from prodigious premises.

如果您仍然希望不使用调试器,请更改技术.不要编写太多代码而不进行编译和运行.通过更小得多的步骤来执行此操作,并不断显示各种内容,以确保新的3行代码能够实现应有的作用.例如,如果您要为sufcmp创建一个空的存根,仅从指针打印该字符串,则在尝试访问该字符串后将立即进行段错误.

If you still want to go without debugger, change technique. Don't write so much of code without compiling + running it. Do it by much much much smaller steps, and keep displaying all sort of things, to be sure your new 3 lines of code do what they should. For example if you would create empty stub for sufcmp just printing the string from pointer, it would segfault right after trying to access the string.

与几乎最终的应用程序代码出现段错误相比,这可能会给您带来更好的提示,因此,您不必花50多来推理最近的10行代码,就可以找到问题.

That would maybe give you better hint, than when almost final app code is segfaulting, so instead of hunting problem on recent 10 lines you have 50+ to reason about.

算法建议:

除非您确实必须使用冒泡排序,否则我会避免这种情况,并进行暴力哑巴计数"排序.

Unless you really must use bubble sort, I would avoid that, and do the brute-force dumb "count" variant of sort.

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 2 )循环(实际上是N ^ 3,因为比较字符串是另一个O(N)...但谁关心N = 30,甚至N 5 是可以忍受的.)

I hope you will be able to decipher it... it means that I would calculate for every suffix how many suffixes are "smaller" lexicographically, ie. full O(N2) loopy loop (which is in reality N^3, because comparing strings is another O(N) ... but who cares with N=30, even N5 would be bearable).

然后以正确的顺序打印后缀,您只需一次又一次地搜索count数组,第一次是0较小计数(这是最小的),然后是1,...等.直到您将它们全部打印出来.

Then to print suffixes in correct order you simply search the count array again and again, first time for 0 smaller-count (that's the smallest one), then for 1, ... etc. Till you print all of them.

实际上,您可以循环浏览所有后缀,计算出多少个较小的后缀,然后将该后缀的索引放入sorted[smaller_count],因此对于打印,您将只循环遍历sorted数组从0到N-1,无需进行搜索

Actually you may loop through all suffixes, calculate how many are smaller, and put index of that suffix into sorted[smaller_count], so for printing you will just loop through sorted array from 0 to N-1, no searching involved.

这篇关于NASM子例程调用分段错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆