I'm new to assembly and I'm trying to work on an implementation of the C function puts in assembly with nasm x86-64 on my Ubuntu.
Everything was fine until I write the tests for the function: there is a behavior that I can not reproduce: When I send char *str = strdup(""); to my puts function (alias ft_puts), my function return an error instead of printing a newline like the original puts function.
Here is my puts implementation (ft_puts.s):
section .data
new_line db 10 ; new line
section .text
global ft_puts
extern strlen
ft_puts: ; Alias int puts(const char *s)
call strlen
push rax ; Number of printed chars have to be returned by ft_puts
cmp rax, 0
jg print_string ; if length > 0, print string
cmp rax, 0
jz print_newline ; else if length == 0 (jle or <= 0, give the same result to my problem), print new line char
jmp error ; else go to error
print_string:
mov rsi, rdi ; string arg for write
mov rdi, 1 ; file_descriptor arg for write
mov rdx, rax ; length arg returned by ft_strlen for write
mov rax, 1 ; write
syscall
test rax, rax
jle error ; if write failed, go to error
jmp print_newline ; else print new line char
print_newline:
mov rsi, new_line ; new line as string arg for write
mov rdx, 1 ; new line string length
mov rax, 1 ; write
syscall
test rax, rax
jle error ; if write failed, go to error
jmp success ; else go to success
success:
pop rax ; Get number of chars printed by print_string
inc rax ; Add new line printed by print_newline to this number
jmp end
error:
pop rax
mov rax, -1 ; Return EOF (alias -1) on error
jmp end
end:
ret
Sorry if my code looks terrible, I started assembly a week ago. I'm new to Stack Overflow and the help center told me to give a code for testing my code. So, here is a main.c:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int ft_puts(const char *s);
int main(void)
{
char *str;
int ret[2];
str = strdup(".");
write(1, "[", 1);
ret[0] = puts(str);
write(1, "]", 1);
write(1, "\n", 1);
write(1, "[", 1);
ret[1] = ft_puts(str);
write(1, "]\n", 2);
printf("puts return value : %d | ft_puts return value : %d\n\n", ret[0], ret[1]);
free(str);
str = strdup("");
write(1, "[", 1);
ret[0] = puts(str);
write(1, "]", 1);
write(1, "\n", 1);
write(1, "[", 1);
ret[1] = ft_puts(str);
write(1, "]\n", 2);
printf("puts return value : %d | ft_puts return value : %d\n", ret[0], ret[1]);
free(str);
return (0);
}
And to compile and run this code :)
nasm -f elf64 ft_puts.s -o ft_puts.o
gcc -c main.c
gcc main.o ft_puts.o -o test
./test
The problem seems to be in my print_newline label but I can't find it. If someone can help me ? (It's hard to get some help on assembly language around me irl) (I don't know if should include C tag to my question too haha, so much questions)
Two problems. One, the
call strlen
is allowed to clobber some registers which includerdi
and you need that later. So, surround thecall strlen
withpush rdi
andpop rdi
to save and restore it.Second, you do not initialize
rdi
in theprint_newline
block. You need to set it to1
forstdout
just like you did in theprint_string
block.PS: You should learn to use a debugger.