Author Topic: Trying to convert 32-bit code into 64-bit: Line feed ignored  (Read 423 times)

Offline cid

  • New Member
  • Posts: 1
Hello, recently I found this nasm tutorial https://asmtutor.com/#lesson7. Unfortunately, there is no 32-bit version of the code. I want to convert some of the code in 64-bit (elf64 on linux) but I stuck on the sprintlf 'function'. This section should print the string in the eax register and a line feed by calling the sprint function twice. My problem is that this function acts as the normal sprint function: \n or 0Ah byte is ignored while executing. Maybe it has something to do with the stack register or the compare instruction.

To compile the program in elf64 I had to convert all 32-bit registers like eax, esp... to their 64-bit equivalents: rax,rsp...

This is my modified version:
Code: [Select]
;------------------------------------------
; int slen(String message)
; String length calculation function
slen:
    push    rbx
    mov     rbx, rax
 
nextchar:
    cmp     byte [rax], 0
    jz      finished
    inc     rax
    jmp     nextchar
 
finished:
    sub     rax, rbx
    pop     rbx
    ret
 
 
;------------------------------------------
; void sprint(String message)
; String printing function
sprint:
    push    rdx
    push    rcx
    push    rbx
    push    rax
    call    slen
 
    mov     rdx, rax
    pop     rax
 
    mov     rcx, rax
    mov     rbx, 1
    mov     rax, 4
    int     80h
 
    pop     rbx
    pop     rcx
    pop     rdx
    ret
 
 
;------------------------------------------
; void sprintLF(String message)
; String printing with line feed function
sprintLF:
    call    sprint
 
    push    rax         ; push rax onto the stack to preserve it while we use the rax register in this function
    mov     rax, 0Ah    ; move 0Ah into rax - 0Ah is the ascii character for a linefeed
    push    rax         ; push the linefeed onto the stack so we can get the address
    mov     rax, rsp    ; move the address of the current stack pointer into rax for sprint
    call    sprint      ; call our sprint function
    pop     rax         ; remove our linefeed character from the stack
    pop     rax         ; restore the original value of rax before our function was called
    ret                 ; return to our program
Output when moving "Hello World" to rax, calling sprintlf and repeat the steps again: Hello WorldHello World
The line feed is not printed.

Offline debs3759

  • Global Moderator
  • Jr. Member
  • *****
  • Posts: 66
  • Country: gb
    • GPUZoo
Re: Trying to convert 32-bit code into 64-bit: Line feed ignored
« Reply #1 on: July 28, 2019, 12:31:17 PM »
Don't the print routines usually expect the trailing line feed to be part of the string input? I have never seen one which add a line feed to the string you send it. That shouldn't cause your error though. I have never coded for Linux or 64-bit though, so I may be wrong. I only know 8 bit to 32 bit code, but am surprised if protocols are all that different. Your code looks rather complex for just outputting a simple string.
My graphics card database: www.gpuzoo.com

Offline fredericopissarra

  • Jr. Member
  • *
  • Posts: 55
Re: Trying to convert 32-bit code into 64-bit: Line feed ignored
« Reply #2 on: July 28, 2019, 02:34:04 PM »
If you want to use your routines called from C functions, you need to obey the calling SysV ABI convention (since you are dealing with ELF). In amd64 code the first 6 "integer" arguments (including pointers) are passed through registers: RDI, RSI, RDX, RCX, R8 and R9. If you use floating point arguments, the 7 first are passed through XMM0-XMM7.

So, your slen funcion should be:
Code: [Select]
; size_t slen( char *s );
slen:
  xor eax,eax   ; EAX is used as counter.
.loop:
  cmp byte [rdi],0
  jz  .exit
  add rax,1
  add rdi,1
  jmp .loop
.exit:
  ret

Yet, syscalls called though 'int 0x80' lacks support for 64 bits pointers. You should use 'syscall' instruction (with different function id for write()):
Code: [Select]
sprint:
  mov rsi, rdi  ; save RDI on RSI (RSI used by write syscall).
  call  slen
  mov rdx, rax  ; RDX is the string length.
  mov eax, 1    ; write syscall
  mov edi, eax  ; stdout
  syscall
  ret

sprintF:
  call sprint

  mov eax,1
  mov edx,eax
  mov edi,eax
  lea  rsi,[lf]
  syscall
  ret
lf: db `\n`

OBS: When you change an E?? register, the upper 32 bits of R?? register is zeroed, autmatically.
« Last Edit: July 28, 2019, 02:40:15 PM by fredericopissarra »

Offline debs3759

  • Global Moderator
  • Jr. Member
  • *****
  • Posts: 66
  • Country: gb
    • GPUZoo
Re: Trying to convert 32-bit code into 64-bit: Line feed ignored
« Reply #3 on: July 28, 2019, 07:29:05 PM »
OBS: When you change an E?? register, the upper 32 bits of R?? register is zeroed, autmatically.

Is that documented behaviour? I know in 32-bit code, the upper 16 bits are not changed if you write to a 16-bit general purpose register.
My graphics card database: www.gpuzoo.com

Offline fredericopissarra

  • Jr. Member
  • *
  • Posts: 55
Re: Trying to convert 32-bit code into 64-bit: Line feed ignored
« Reply #4 on: July 28, 2019, 10:42:07 PM »
OBS: When you change an E?? register, the upper 32 bits of R?? register is zeroed, autmatically.

Is that documented behaviour? I know in 32-bit code, the upper 16 bits are not changed if you write to a 16-bit general purpose register.
Yes, it is.... You can get this from Intel and AMD documentation. This happens only when you change the lower 32 bits of Rxx registers... Like:
Code: [Select]
xor eax,eax ; Will zero the entire RAX
mov r8d,1 ; Will initialize R8 with 1 (upper 32 bits zeroed as well
The reason is simple: You don't need to use REX prefix for 'xor eax,eax', but it is needed for 'xor rax,rax'.

Notice if you do:
Code: [Select]
xor ax,axOnly the first 16 bits are zeroed, leaving the rest untouched.

Another advantage of x86-64.... There is a DIL, SIL, BPL and SPL aliases for the lower 8 bits of RDI, RSI, RBP and RSP (not available on i386 mode).
« Last Edit: July 28, 2019, 10:45:25 PM by fredericopissarra »