简体   繁体   中英

Assembly JMP and RET

I am new to Assembly.
And I have this code

section .data                   ; we define (global) initialized variables in .data section
    an: dd 0                    ; an is a local variable of size double-word, we use it to count the string characters

section .text                   ; we write code in .text section
    global do_Str               ; 'global' directive causes the function do_Str(...) to appear in global scope

section .text                   ; we write code in .text section
    global do_Str               ; 'global' directive causes the function do_Str(...) to appear in global scope

do_Str:                         ; do_Str function definition - functions are defined as labels
    push ebp                    ; save Base Pointer (bp) original value
    mov ebp, esp                ; use Base Pointer to access stack contents (do_Str(...) activation frame)
    pushad                      ; push all signficant registers onto stack (backup registers values)
    mov ecx, dword [ebp+8]      ; get function argument on stack
                                ; now ecx register points to the input string
    yourCode:                   ; use label to build a loop for treating the input string characters
        cmp byte [ecx], ' '
        JE  updateAndCount
        inc ecx                 ; increment ecx value; now ecx points to the next character of the string
        cmp byte [ecx], 0       ; check if the next character (character = byte) is zero (i.e. null string termination)
        jnz yourCode            ; if not, keep looping until meet null termination character

    updateAndCount:
        mov byte [ecx], '_'
        inc dword[an]
        ret

    popad                       ; restore all previously used registers
    mov eax,[an]                ; return an (returned values are in eax)
    mov esp, ebp                ; free function activation frame
    pop ebp                     ; restore Base Pointer previous value (to returnt to the activation frame of main(...))
    ret                         ; returns from do_Str(...) function

but when running it(I have c code calling it), I get this error:

Segmentation fault (core dumped)

I know it has something to do with the return from updateAndCount, but I'm not sure how to fix it.

Expanding from my comments with an example.

The snippet starting with the updateAndCount label mustn't end with ret , it should jump (or fall through) to where you want your loop to continue. That is not the only control flow error you did however. After the jnz yourCode you probably want an unconditional jump to the function epilogue (which starts with popad ). Or move the epilogue around to follow directly behind the jnz yourCode and then let it fall through if the jnz does not jump.

Moreover you did not initialise the an variable in the function so it will act like a static variable in C, that is if your function is called repeatedly an will keep incrementing and not be reset when your function is called subsequently. This may be intentional but you did not specify which behaviour is intended. Besides, yourCode is an exceptionally undescriptive label. I'd recommend replacing it by .loop -- the leading dot makes it alocal label for NASM . The name is to describe the intention of that jump target.

Here's an example correcting the control flow, initialising the an variable, and changing the labels to descriptive and local labels. I will not optimise the program beyond that, nor make it threadsafe (which it isn't due to the global variable).

section .data                   ; we define (global) initialized variables in .data section
    an: dd 0                    ; an is a local variable of size double-word, we use it to count the string characters

section .text                   ; we write code in .text section
    global do_Str               ; 'global' directive causes the function do_Str(...) to appear in global scope

do_Str:                         ; do_Str function definition - functions are defined as labels
    push ebp                    ; save Base Pointer (bp) original value
    mov ebp, esp                ; use Base Pointer to access stack contents (do_Str(...) activation frame)
    pushad                      ; push all signficant registers onto stack (backup registers values)
    mov dword [an], 0

;;; continues in the next code block
;;; all the code blocks in this answer combine to one function with one loop

This zero-initialises the variable to zero. (I'd use and with zero personally, which is slightly shorter than mov with a zero immediate number. But for clarity we'll use mov .)

    mov ecx, dword [ebp+8]      ; get function argument on stack
                                ; now ecx register points to the input string
    jmp .first

This unconditional jump fixes another logic error. If the very first byte in the string is a NUL byte we should most likely honour it, not continue to process whatever follows after it.

.loop:                          ; use label to build a loop for treating the input string characters
    cmp byte [ecx], ' '
    jne .do_not_updateAndCount

I inverted the condition code of the jump. Now it jumps if we don't want to run the .updateAndCount part.

.updateAndCount:
    mov byte [ecx], '_'
    inc dword [an]

I kept your label (now local with the leading dot) even though it is not referenced anywhere. It can serve as a comment for what this block is supposed to do, however.

Crucially, the control flow falls through after the end of the .updateAndCount block here. Equivalently, you could add an unconditional jmp .next at the end to jump (back) into the loop. If you moved around the .updateAndCount block (eg to behind the epilogue's ret ) then this jmp .next would be required.

.do_not_updateAndCount:

.next:
    inc ecx                     ; increment ecx value; now ecx points to the next character of the string
.first:
    cmp byte [ecx], 0           ; check if the next character (character = byte) is zero (i.e. null string termination)
    jnz .loop                   ; if not, keep looping until meet null termination character

    popad                       ; restore all previously used registers
    mov eax, [an]               ; return an (returned values are in eax)
    mov esp, ebp                ; free function activation frame
    pop ebp                     ; restore Base Pointer previous value (to returnt to the activation frame of main(...))
    ret                         ; returns from do_Str(...) function

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM