elouann.dev
FR — version française

ProjectsLow-level & embedded

Libasm

libc functions in assemblyongoing

Period
Jun 2025 – present
Context
42 curriculum
Team
solo
Grade
not graded yet
  • C
  • Assembly
  • NASM x86-64 Intel
  • Low-level
  • Imperative programming
  • Tester

View source

Rewriting libc functions directly in x86-64 assembly, following the calling conventions so that a C program can use them without noticing the difference. All of it driven by tests.

C testASSERT_SAME(ft_strlen,strlen,"héllo")ft_strlen.sxor rax, rax.loop:cmp byte [rdi+rax], 0je .done ; inc rax.done: retrdi = srax = lengthABI System Vthe TDD cyclewrite the testit failswrite the asmit passesnext function
DiagramA call from C: arguments arrive in registers, the result goes back in rax. The tester compares each function with the libc one.

The contract

For a function written in NASM to be callable from C, it must follow the System V ABI: the first argument arrives in rdi, the second in rsi, the third in rdx, and the return value goes back in rax. Some registers must be handed back untouched to the caller.

On the menu: strlen, strcpy, strcmp, strdup, read, write, and as bonuses linked lists and an atoi_base. For read and write, errno must also be set the way libc does.

Test first

I did this project with TDD: I write the tests first, then the function. The tester is a small C framework. An ASSERT_SAME macro calls my function and the libc one with the same inputs, and compares the results.

Cases are picked to trap the assembly: empty string, bytes above 127, UTF-8, a null byte in the middle, a 10,000-character string, or an unaligned pointer. The tester compiles and links the library, runs the tests in a child process, then runs everything again under Valgrind.

Where I am

The project is still in progress: the tester is in place and the functions are coming one after another.

What I took away

  • The ABI is the contract that lets two languages talk to each other.
  • In assembly nothing is implicit: every register used is a decision.
  • Writing tests first stops you from believing a function works because it “looks” like it works.