【问题标题】:NASM subroutines and segmentation faultNASM 子程序和分段错误
【发布时间】:2014-11-12 00:42:54
【问题描述】:

我一直在试验 NASM 和汇编语言,因此,我对子例程如何在这种语言中工作有一个基本的想法。然而,我的问题是,如何在同一过程中捆绑不同的标签。例如,在我在 S.O. 中找到的以下代码中:

;-----------------------------------------------
;SECTION .DATA
;Instantiated variables/Constants
;-----------------------------------------------
section .data

result:     db "The smallest number is: " , 0x0a
result_len:     equ $-result

nl:     db "   ", 0x0a
nl_len  equ $-nl

    matrix: dw  25, 24, 23, 22, 21
            dw  20, 19, 18, 17, 16 
            dw  15, 14, 13, 12, 11 
            dw  10,  9,  8,  7,  6
            dw   5,  4,  3,  2,  1


;-----------------------------------------------
;SECTION .BSS
;Non initialized variables
;-----------------------------------------------
section .bss


;-----------------------------------------------
;SECTION .TEXT
;Code
;-----------------------------------------------
section .text
global _start 

_start: 

mov edi, 0
mov esi, 0
mov ecx, 12         

outerLoop:
cmp edi, 50                  ;each element is 2 bytes (2 ascii characters)
ja  endloop                  ;we need 50 because it's 5 elements per row
mov esi, 0                   ;and 5 rows
innerLoop:
cmp esi, 5                   ;Compare esi(inner loop index) to 5
jae innerEnd                 ;jump if it reached the end of the row
mov ax, [matrix + edi + esi*2]
cmp ax, cx
jg  biggerThan
mov cx, ax
biggerThan:
inc esi
jmp innerLoop
innerEnd:
add edi, 10                  ;row has been complete, go to next
jmp outerLoop

endloop:
push    ecx

mov eax, 4
mov ebx, 1
mov ecx, result
mov edx, result_len
int 0x80

mov eax, 4
mov ebx, 1
mov ecx, esp
add [ecx], DWORD 30h
mov edx, 2
int 0x80

; display new line
mov eax, 4
mov ebx, 1
mov ecx, nl
mov edx, nl_len
int 0x80

exit:
mov eax, 1          ;eax contains 1 so quit
mov ebx, 0
int 0x80

是否可以将 innerLoop、outerLoop、bigThan、innerEnd 等捆绑在一个过程中并像这样调用该过程:

;-----------------------------------------------
;SECTION .DATA
;Instantiated variables/Constants
;-----------------------------------------------
section .data

result:     db "The smallest number is: " , 0x0a
result_len:     equ $-result

nl:     db "   ", 0x0a
nl_len  equ $-nl

    matrix: dw  25, 24, 23, 22, 21
            dw  20, 19, 18, 17, 16 
            dw  15, 14, 13, 12, 11 
            dw  10,  9,  8,  7,  6
            dw   5,  4,  3,  2,  1


;-----------------------------------------------
;SECTION .BSS
;Non initialized variables
;-----------------------------------------------
section .bss


;-----------------------------------------------
;SECTION .TEXT
;Code
;-----------------------------------------------
section .text
global _start 

_start: 

mov edi, 0
mov esi, 0
mov ecx, 12         

call findSmallestNumber


findSmallestNumber:
outerLoop:
cmp edi, 50                  ;each element is 2 bytes (2 ascii characters)
ja  endloop                  ;we need 50 because it's 5 elements per row
mov esi, 0                   ;and 5 rows
innerLoop:
cmp esi, 5                   ;Compare esi(inner loop index) to 5
jae innerEnd                 ;jump if it reached the end of the row
mov ax, [matrix + edi + esi*2]
cmp ax, cx
jg  biggerThan
mov cx, ax
biggerThan:
inc esi
jmp innerLoop
innerEnd:
add edi, 10                  ;row has been complete, go to next
jmp outerLoop

endloop:
push    ecx


ret

mov eax, 4
mov ebx, 1
mov ecx, result
mov edx, result_len
int 0x80

mov eax, 4
mov ebx, 1
mov ecx, esp
add [ecx], DWORD 30h
mov edx, 2
int 0x80

; display new line
mov eax, 4
mov ebx, 1
mov ecx, nl
mov edx, nl_len
int 0x80



exit:
mov eax, 1          ;eax contains 1 so quit
mov ebx, 0
int 0x80

这样做会始终导致 Linux 中出现分段错误错误,因此我尝试这样做的方式肯定有问题。 任何帮助将不胜感激!

【问题讨论】:

    标签: assembly x86 segmentation-fault nasm subroutine


    【解决方案1】:

    至少有 2 个问题:

    1. push ecx 不应该是子程序的一部分,因为它已经是主程序中的代码。它通过write 系统调用设置用于打印的缓冲区。它的位置由mov ecx, esp 行使用。请注意,ret 从堆栈中弹出一个地址,然后返回那里。因此,在这种情况下,它会弹出您的 ecx 值并尝试将其用作导致错误的返回地址。
    2. 当子程序返回时,cpu 继续执行call 指令之后的代码。在您的情况下,这意味着它将再次进入findSmallestNumber 子例程(因为它直接在call 之后),但这次堆栈上将没有返回地址。因此,即使第 1 点已修复,ret 仍然会出错。对此的解决方案是将子程序移出直接执行路径。这是在退出调用之后将整个子例程放在代码的末尾。

    PS:如果您打算使用汇编代码,您应该学习使用调试器,这样您就可以单步执行代码并查看问题所在。您可能还想了解通常使用的calling conventions。如果您想与其他代码交互,您将需要它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-11
      • 1970-01-01
      • 2016-09-10
      • 2014-01-31
      相关资源
      最近更新 更多