NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: cid on July 28, 2019, 09:22:09 AM
-
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:
;------------------------------------------
; 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.
-
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.
-
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:
; 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()):
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.
-
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.
-
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:
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:
xor ax,ax
Only 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).