NASM子程序调用分段错误
NASM Subroutine Call Segmentation Fault
我正在做一个涉及 NASM 的学校项目,虽然语言对我来说有某种意义,但我总是遇到一个毫无意义的问题。
我写的程序涉及1个命令行参数,必须是0s、1s、and/or2s的字符串。如果不是,则显示错误消息并结束程序。
如果没有错误,字符串的"suffixes"按顺序显示。
示例:
"./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
我得到这个
我找不到任何会导致分段错误的东西,因为除了压入函数参数和在子例程之后恢复 esp 之外,我没有以任何方式操作堆栈 returns。
好吧,你需要调试器,因为你的代码中有几个问题,而且它有点太大了,我无法准确地 运行 它在头上(比如 100% 保护 stack/etc) ,所以我只看到了一些东西:
在 CHAR_CHECK:
循环中测试循环中的长度,这样当有人给你太长的字符串时你不会覆盖 .bss
内存。你可以把长度检查移到CHAR_LOOP:
下面,当edi
超出范围时,停止循环。
在存储 N
之前还要添加空字符(交换那两个 mov
行),因为 N
在内存中存储在 X
之后,所以 31 (?) 长输入字符串,您会将 N
覆盖为 0
(这尤其不可利用,但长字符串的副本可能是)。
jl/jg
用于长度检查,但长度是无符号的,所以 jb/ja
对我来说更有意义(不是错误,签名测试 >=1 && <= 30
将同时失败作为未签名的,如果你有编程强迫症,那就感觉不对。
good/bad 字符测试 - 你可以通过只做两个测试 ('0' <= char && char <= '2')
来缩短它,因为 ['0', '1', '2']
是值 [48, 49, 50]
.
现在更严肃的事情接踵而至。
在I/J循环中你没有重置J,所以你的内循环逻辑会有缺陷。
push dword [X]
我认为这并不像您认为的那样。字符串的地址是X
,[X]
是内存的内容(字符串的字符)。 (这将使 sufcmp
代码在尝试访问 "address" '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
- 不,lea
会按照评论所说的去做。 mov
将获取地址 X
.
的内容
add eax, [y + esi]
- 再次对 dword
数组使用 +-1 索引。
而且你忘了调用 print_string,只调用了新的线路。
您可以将该部分重写为:
mov eax,[y + esi*4] ; eax = Y[i]
lea eax,[X + eax] ; eax = address X + Y[i]
而且,因为我很残忍和疲倦,所以我把最大的担忧留到最后一个音符。
我认为这根本行不通。您的冒泡排序正在迭代原始 X
字符串(好吧,它不是,但是一旦您用正确的地址解决了参数问题,它就会)。
每一次。因此,您在每次迭代中都根据原始字符串不断改组 Y
数组的内容。
我的回答中最重要的部分是第一句话。你绝对需要调试器。如果到目前为止您觉得该语言对您有意义,那么您的消息来源并不能证明这一点。实际上我可以看出一丝理解,所以你基本上是对的,但你必须是完全的神童才能在合理的时间内在没有调试器的情况下拉出这个。我只会给你打中等以上的分数,也许还不错,但离惊人的地方还很远。
如果您仍然不想使用调试器,请更改技术。不要在没有编译 + 运行ning 的情况下写这么多代码。通过更小的步骤来完成它,并继续显示各种东西,以确保您的新 3 行代码执行它们应该执行的操作。例如,如果您为 sufcmp
创建空存根,只是从指针打印字符串,它会在尝试访问该字符串后立即出现段错误。
这可能会给你更好的提示,而不是当几乎最终的应用程序代码出现段错误时,所以你有 50 多行可以推理,而不是在最近的 10 行上寻找问题。
编辑:算法建议:
除非你真的必须使用冒泡排序,否则我会避免这种情况,并使用蛮力愚蠢的 "count" 排序变体。
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]
我希望你能破译它...这意味着我会计算每个后缀有多少个后缀 "smaller" 字典顺序,即。完整的 O(N2) 循环(实际上是 N^3,因为比较字符串是另一个 O(N) ... 但是谁在乎 N=30,甚至 N5 可以忍受)。
然后要以正确的顺序打印后缀,您只需一次又一次地搜索 count
数组,第一次搜索 0
smaller-count(这是最小的),然后搜索 1
, ...等等。直到你打印所有这些。
实际上你可以遍历所有的后缀,计算有多少个更小,然后将那个后缀的索引放入 sorted[smaller_count]
,所以为了打印你只需从 0 到 N 遍历 sorted
数组-1,不涉及搜索。
我正在做一个涉及 NASM 的学校项目,虽然语言对我来说有某种意义,但我总是遇到一个毫无意义的问题。
我写的程序涉及1个命令行参数,必须是0s、1s、and/or2s的字符串。如果不是,则显示错误消息并结束程序。
如果没有错误,字符串的"suffixes"按顺序显示。
示例:
"./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
我得到这个
我找不到任何会导致分段错误的东西,因为除了压入函数参数和在子例程之后恢复 esp 之外,我没有以任何方式操作堆栈 returns。
好吧,你需要调试器,因为你的代码中有几个问题,而且它有点太大了,我无法准确地 运行 它在头上(比如 100% 保护 stack/etc) ,所以我只看到了一些东西:
在 CHAR_CHECK:
循环中测试循环中的长度,这样当有人给你太长的字符串时你不会覆盖 .bss
内存。你可以把长度检查移到CHAR_LOOP:
下面,当edi
超出范围时,停止循环。
在存储 N
之前还要添加空字符(交换那两个 mov
行),因为 N
在内存中存储在 X
之后,所以 31 (?) 长输入字符串,您会将 N
覆盖为 0
(这尤其不可利用,但长字符串的副本可能是)。
jl/jg
用于长度检查,但长度是无符号的,所以 jb/ja
对我来说更有意义(不是错误,签名测试 >=1 && <= 30
将同时失败作为未签名的,如果你有编程强迫症,那就感觉不对。
good/bad 字符测试 - 你可以通过只做两个测试 ('0' <= char && char <= '2')
来缩短它,因为 ['0', '1', '2']
是值 [48, 49, 50]
.
现在更严肃的事情接踵而至。
在I/J循环中你没有重置J,所以你的内循环逻辑会有缺陷。
push dword [X]
我认为这并不像您认为的那样。字符串的地址是X
,[X]
是内存的内容(字符串的字符)。 (这将使 sufcmp
代码在尝试访问 "address" '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
- 不,lea
会按照评论所说的去做。 mov
将获取地址 X
.
add eax, [y + esi]
- 再次对 dword
数组使用 +-1 索引。
而且你忘了调用 print_string,只调用了新的线路。
您可以将该部分重写为:
mov eax,[y + esi*4] ; eax = Y[i]
lea eax,[X + eax] ; eax = address X + Y[i]
而且,因为我很残忍和疲倦,所以我把最大的担忧留到最后一个音符。
我认为这根本行不通。您的冒泡排序正在迭代原始 X
字符串(好吧,它不是,但是一旦您用正确的地址解决了参数问题,它就会)。
每一次。因此,您在每次迭代中都根据原始字符串不断改组 Y
数组的内容。
我的回答中最重要的部分是第一句话。你绝对需要调试器。如果到目前为止您觉得该语言对您有意义,那么您的消息来源并不能证明这一点。实际上我可以看出一丝理解,所以你基本上是对的,但你必须是完全的神童才能在合理的时间内在没有调试器的情况下拉出这个。我只会给你打中等以上的分数,也许还不错,但离惊人的地方还很远。
如果您仍然不想使用调试器,请更改技术。不要在没有编译 + 运行ning 的情况下写这么多代码。通过更小的步骤来完成它,并继续显示各种东西,以确保您的新 3 行代码执行它们应该执行的操作。例如,如果您为 sufcmp
创建空存根,只是从指针打印字符串,它会在尝试访问该字符串后立即出现段错误。
这可能会给你更好的提示,而不是当几乎最终的应用程序代码出现段错误时,所以你有 50 多行可以推理,而不是在最近的 10 行上寻找问题。
编辑:算法建议:
除非你真的必须使用冒泡排序,否则我会避免这种情况,并使用蛮力愚蠢的 "count" 排序变体。
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]
我希望你能破译它...这意味着我会计算每个后缀有多少个后缀 "smaller" 字典顺序,即。完整的 O(N2) 循环(实际上是 N^3,因为比较字符串是另一个 O(N) ... 但是谁在乎 N=30,甚至 N5 可以忍受)。
然后要以正确的顺序打印后缀,您只需一次又一次地搜索 count
数组,第一次搜索 0
smaller-count(这是最小的),然后搜索 1
, ...等等。直到你打印所有这些。
实际上你可以遍历所有的后缀,计算有多少个更小,然后将那个后缀的索引放入 sorted[smaller_count]
,所以为了打印你只需从 0 到 N 遍历 sorted
数组-1,不涉及搜索。