I'm currently developing my NASM x64 version of GLIBC, and I'm having a problem testing it. The ASM code is first compiled in .so, next my test code (in C language) is compiled via GCC, then I load my shared library via LD_PRELOAD
and execute my binary.
Here's my ASM implementation of putchar()
:
bits 64
STDOUT equ 1
section .text
global putchar
putchar:
push rdi
mov rax, 1
mov rdi, STDOUT
mov rsi, rsp
mov rdx, 1
syscall
pop rdi
ret
Here's the test main:
#include <stddef.h>
extern size_t putchar(const char str);
int main(void)
{
putchar('z');
return (0);
}
And here's the Makefile :
CC = nasm
CFLAGS = -f elf64
SRC = $(wildcard src/*.asm)
OBJ = $(SRC:.asm=.o)
NAME = minilibc.so
all: $(NAME)
$(NAME): $(OBJ)
ld -fPIC -shared -o $(NAME) $(OBJ) -nostdlib
%.o: %.asm
$(CC) $(CFLAGS) $< -o $@
clean:
rm -f $(OBJ)
fclean: clean
rm -f $(NAME)
re: fclean all
test: re
gcc -o test/test test/main.c -g3
LD_PRELOAD=./minilibc.so ./test/test
.PHONY: all clean fclean re
The problem is that GDB continues to use the glibc function when I try to set a breakpoint:
(gdb) b putchar
Breakpoint 1 at 0x1030
...
Breakpoint 1, 0x00007ffff7c7bb20 in putchar () from /usr/lib/libc.so.6
I've also tried compiling with -nostlib
(in make test) but it doesn't work:
ld: warning: cannot find entry symbol _start; defaulting to 0000000000001020
...
/home/mimahk/Documents/Workspace C/minilibc/test/main.c:7:(.text+0xa): undefined reference to `putchar'
I know that in reality the binary needs a _start
label to run but despite several searches I can't code it.
I tested this but it doesn't work:
asm(".global _start; _start: call main; movq %rax, %rdi; movq $60, %rax;");
I get : main.c:9:(.text+0x19): undefined reference to putchar
Can you tell me how I can test my shared library?
Thanks to comments from @PeterCordes and @Jester I was able to correct the problem.
First, I had to modify the code to precede main()
with an ASM label named _start
.
Then I modified a few options in my Makefile to link the .so directly at runtime (and added the basic debug options):
-ggdb3
: Enables in-depth debugging with GDB.
-fno-builtin
: Removes GCC's automatic code replacements (e.g. printf("%s\n", str)
by puts(str)
.
-nostdlib
: Do not include GLIBC.
-fno-plt
: Use GOT directly without PLT.
-L./ -lminilibc
: Instructs the linker to link my executable to the minilibc
library (it should therefore be called libminilibc.so
).
-Wl,-rpath=./
: Tells the linker where my library is at runtime (because it's not in /usr/lib/
) and saves me the trouble of using LD_PRELOAD
.
Here's the complete code:
main.c
extern int putchar(const char str);
asm(".global _start; _start: call main; movq %rax, %rdi; movq $60, %rax; syscall"); // Call the main() function and exit with the code corresponding to the return of main().
int main(void)
{
putchar('H');
return (0);
}
Makefile
CC = nasm
CFLAGS = -f elf64
SRC = $(wildcard src/*.asm)
OBJ = $(SRC:.asm=.o)
NAME = libminilibc.so
all: $(NAME)
$(NAME): $(OBJ)
ld -shared -o $(NAME) $(OBJ)
%.o: %.asm
$(CC) $(CFLAGS) $< -o $@
clean:
rm -f $(OBJ)
fclean: clean
rm -f $(NAME)
re: fclean all
test: re
gcc -o test/test test/main.c -ggdb3 -fno-builtin -nostdlib -fno-plt -L./ -lminilibc -Wl,-rpath=./
.PHONY: all clean fclean re