NASM Forum > Programming with NASM

Segmentation fault question

(1/4) > >>

azagaros:
This is on linux 64.  I am getting a segmentation fault at trying to read the command line arguments and print them out. I am not sure where my logic error is or which boundary I am crossing.  As far as I know my management of the stack is working.

--- Code: ---section .data


strNewLine   db 10   
   
   
section .text
    global _start
_start:
    pop rcx         ; pop the count of arguments
.argloop:
    pop rax         ; pop the ptr of the arg.
    call _printstr  ; print the string
    call _printnl   ; print a new line character
    dec rcx
    cmp rcx, 0
    jne .argloop
   
    call _ExitProg
    ret
   
;rax ptr to string to count
;rbx count of string

_printstr:
    push rcx
    push rax   
    mov rbx,0
.lenloop:
    inc rax
    inc rbx
    mov cl, [rax]
    cmp cl, 0
    jne .lenloop
   
    mov rax, 1
    mov rdi, 1
    pop rsi         ;pop rax to rsi
    mov rdx, rbx
    syscall
    pop rcx
    ret

_printnl:
    mov rax, 1
    mov rdi, 1
    mov rsi, strNewLine
    mov rdx, 1
    syscall
    ret
       
   
_ExitProg:
   xor rdi, rdi
   mov rax, 60
   syscall
   

--- End code ---

Frank Kotler:
I am not good on 64-bit code. It looks like rcx is being trashed in one of your routines. Either save rcx across the two routines, or test rax for zero instead of counting on argc.

I think that'll work...

Best,
Frank


fredericopissarra:
Maybe this could help:

--- Code: ---; test.asm
;
; compile with:
;   $ nasm -felf64 -o test.o test.asm
;   $ ld -o test test.o
;
    bits 64
    default rel   ; x86-64 default addressing is RIP relative!

    ; Moved to .rodata.
    section .rodata

strNewLine:  db `\n`
   
    section .text

    global _start
_start:
    ; argc is in [rsp]
    ; argv[n] are in [rsp+8*n+8]
    ;mov   ecx,[rsp]  ; argc (not used here!)

    xor   ebx,ebx     ; RBX is saved by syscalls (SysV ABI).
                      ; Used as index...
.loop:
    mov   rdi,[rsp+rbx*8+8]
    test  rdi,rdi     ; argv[rbx] == NULL?
    jz    .no_more_args
    call  printstr    ; print the string
    call  printnl     ; print a new line character
    add   ebx,1       ; avoid using INC/DEC (they are slow!).
    jmp   .loop
   
.no_more_args:
    mov   eax,60    ; syscall_exit
    xor   edi,edi   ; exit(0).
    syscall
   
; rdi = string ptr.
strlen:
    xor   eax,eax
.loop:
    cmp   byte [rdi+rax],0
    jz    .strlen_end
    add   eax,1
    jmp   .loop
.strlen_end:
    ret

;rdi = string ptr
printstr:
    call  strlen
    mov   edx,eax    ; string length.

    mov   eax,1      ; syscall_write
    mov   rsi,rdi    ; string ptr.
    mov   edi,eax    ; stdout
    syscall
    ret

printnl:
    mov   eax,1       ; syscall_write
    mov   edi,eax     ; stdout
    mov   edx,eax     ; 1 char do print
    lea   rsi,[strNewLine]  ; points to '\n'.
    syscall
    ret
--- End code ---

azagaros:
Thank you Fredericopissarra.  You got me by one stumbling block.  Just trying to understand how the registers and stack on linux are handled is being challenging.  A small expansion of code has created a new segmentation fault.

--- Code: ---    bits 64
    default rel  ; x86-64 default addressing is RIP Relative


; section .data

section .rodata
strNewLine   db 10
strSpace     db 32
strFileName  db 'is a file name.',0
strOption    db 'is an option flag.',0
   
   
section .text
    global _start
_start:
    xor rbx, rbx    ;zero the rbx- will not get trashed by sys v abi (the system calls)
   
    ; rsp is the **argv
.argloop:
    mov rdi, [rsp+rbx*8+8]
    test rdi, rdi   ; testing for null
    jz .noMoreArg   ; jump on zero
    cmp rbx, 0      ; consuming the first arg.
    je .skipProcess
    call printstr   ; print the string
    call printSpc   ; print a space
    ; procecc the arg
    call processArg
    call printnl    ; print a new line character
.skipProcess:
    add rbx, 1      ; increment the index.
    jmp .argloop

.noMoreArg:   
    call ExitProg
    ret
   
; rdi = string ptr   
strlen:
    xor rax, rax    ; zero rax register
.loop:
    cmp byte[rdi+rax],0  ;check for null termination of string
    jz .strlenend
    add rax,1
    jmp .loop
.strlenend:
    ret

; rdi = str ptr
printstr:
    call strlen
    mov rdx, rax  ; store the length rdx
   
    mov rax, 1    ; syscall write
    mov rsi, rdi  ; str ptr
    mov rdi, rax  ; stdout
    syscall
    ret
   
; rdi = str ptr
printnl:
    mov rax, 1
    mov rdi, rax
    mov rdx, rax
    lea rsi, [strNewLine]
    syscall
    ret

;rdi = str ptr
printSpc:
    mov rax, 1
    mov rdi, rax
    mov rdx, rax
    lea rsi, [strSpace]
    syscall
    ret

; rdi = str ptr to process       
processArg:
    xor rax, rax  ;string index position
    cmp byte[rdi+rax],'-'
    je .argOption
    ; is the stack getting trashed? stack alignment? (system calls use the local stack?)
    push rdi   ; save the current rdi (should not change)
    push rax   ; save the working rax (It is getting trashed?) (it gets trashed in the string length function)
    mov rdi, strFileName  ; set up a differnet rdi...
    call printstr
    pop rax
    pop rdi
    ;Assumed file concept (can have more than one)
    ;allocate a file infromation block
    ;copy the file name to the file name of the block
.argOption:
    add rax, 1  ;consume the option flag
    ; the test for the list of options.
    push rax
    push rdi
    mov rdi, strOption
    call printstr
    pop rdi
    pop rax
   
    ret
     
ExitProg:

   xor rdi, rdi
   mov rax, 60
   syscall

--- End code ---

As for you Kolter as a NASM developer some food for thought:
https://eli.thegreenplace.net/2012/01/03/understanding-the-x64-code-models

every thing I am fighting seems to be in the code generation of Nasm or Fasm for that matter

fredericopissarra:
SysV ABI amd64 calling convention:

The integer arguments (or pointers) are passed to a function in order: RDI, RSI, RDX, RCX, R8 and R9, from 7th argument to the last, they are stored on stack cdecl style. For syscalls, instead of RCX, R10 is used.

For floating point, XMM0 to XMM7 will be used for arguments.

Example: int f( int a, float b, int c ); // EDI=a, XMM0=b, ESI=c

Registers RSP, RBP, RBX and R12 to R15 must be preserved between calls. Any other registers can be changed inside a function.

RAX is the returned value...

Navigation

[0] Message Index

[#] Next page

Go to full version