From 8308f4198be93a0447a3db416f50d8041530949c Mon Sep 17 00:00:00 2001 From: airone01 <21955960+airone01@users.noreply.github.com> Date: Tue, 24 Feb 2026 13:34:43 +0100 Subject: [PATCH 01/20] feat: libasm startup --- pcc/libasm/.gitignore | 2 ++ pcc/libasm/Makefile | 34 ++++++++++++++++++++++++++++++++++ pcc/libasm/README.txt | 24 ++++++++++++++++++++++++ pcc/libasm/src/ft_strlen.s | 21 +++++++++++++++++++++ pcc/libasm/test.c | 12 ++++++++++++ 5 files changed, 93 insertions(+) create mode 100644 pcc/libasm/.gitignore create mode 100644 pcc/libasm/Makefile create mode 100644 pcc/libasm/README.txt create mode 100644 pcc/libasm/src/ft_strlen.s create mode 100644 pcc/libasm/test.c diff --git a/pcc/libasm/.gitignore b/pcc/libasm/.gitignore new file mode 100644 index 0000000..76e579a --- /dev/null +++ b/pcc/libasm/.gitignore @@ -0,0 +1,2 @@ +test + diff --git a/pcc/libasm/Makefile b/pcc/libasm/Makefile new file mode 100644 index 0000000..ba0b6fa --- /dev/null +++ b/pcc/libasm/Makefile @@ -0,0 +1,34 @@ +NAME := libasm.a +AR := ar +ARFLAGS := rcs +AS := nasm +ASFLAGS := -f elf64 -Wall -Werror +SRC := src/ft_strlen.s +OBJ := $(SRC:.s=.o) + +all: $(NAME) + +$(NAME): $(OBJ) + $(AR) $(ARFLAGS) $(NAME) $(OBJ) + +%.o: %.s Makefile FORCE + $(AS) $(ASFLAGS) $< -o $@ + +clean: + $(RM) $(OBJ) + +fclean: clean + $(RM) $(NAME) ./test + +re: fclean + $(MAKE) all + +test: $(NAME) + $(CC) test.c $(NAME) -o test + ./test + +FORCE: + +.PHONY: all clean fclean re test FORCE + + diff --git a/pcc/libasm/README.txt b/pcc/libasm/README.txt new file mode 100644 index 0000000..1a0f4e0 --- /dev/null +++ b/pcc/libasm/README.txt @@ -0,0 +1,24 @@ +libasm +====== + +A library of basic functions, written in assembly. + +Assembly specifications +----------------------- + +This project is programmed for baseline x86_64 with the System V AMD64 convention, for the GNU/Linux ABI. +The Intel syntax is the one used, as required by the subject. +This is not programmed for any AVX extension. + +Provided functions +------------------ + +The functions provided by this project are the following: + +- ft_strlen (man 3 strlen) +- ft_strcpy (man 3 strcpy) +- ft_strcmp (man 3 strcmp) +- ft_write (man 2 write) +- ft_read (man 2 read) +- ft_strdup (man 3 strdup, call to malloc allowed) + diff --git a/pcc/libasm/src/ft_strlen.s b/pcc/libasm/src/ft_strlen.s new file mode 100644 index 0000000..7f36025 --- /dev/null +++ b/pcc/libasm/src/ft_strlen.s @@ -0,0 +1,21 @@ +; input: rdi -> pointer to NUL-terminated string +; output: rax -> length (size_t) + +section .text + global ft_strlen + +ft_strlen: ; callee clean-up + mov rax, rdi ; save str ptr in rax + +.find_loop: + cmp byte [rdi], 0 + je .done + inc rdi + jmp .find_loop + +.done: + ; rdi now points to NUL terminator + sub rdi, rax ; calc ptrs diff + mov rax, rdi ; ret len in rax + ret + diff --git a/pcc/libasm/test.c b/pcc/libasm/test.c new file mode 100644 index 0000000..43a60c4 --- /dev/null +++ b/pcc/libasm/test.c @@ -0,0 +1,12 @@ +#include + +extern unsigned long ft_strlen(const char *s); + +int main(void) { + { + printf("len = %lu\n", ft_strlen("hello, world")); // 12 + printf("len = %lu\n", ft_strlen("")); // 0 + } + + return 0; +} From 6cdad37ee23ced6b0cab5a27187db57894429838 Mon Sep 17 00:00:00 2001 From: airone01 <21955960+airone01@users.noreply.github.com> Date: Tue, 24 Feb 2026 14:03:37 +0100 Subject: [PATCH 02/20] feat(libasm): ft_strcpy --- pcc/libasm/Makefile | 3 ++- pcc/libasm/src/ft_strcpy.s | 27 +++++++++++++++++++++++++++ pcc/libasm/src/ft_strlen.s | 10 +++++----- pcc/libasm/test.c | 10 ++++++++++ 4 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 pcc/libasm/src/ft_strcpy.s diff --git a/pcc/libasm/Makefile b/pcc/libasm/Makefile index ba0b6fa..9273e9d 100644 --- a/pcc/libasm/Makefile +++ b/pcc/libasm/Makefile @@ -3,7 +3,8 @@ AR := ar ARFLAGS := rcs AS := nasm ASFLAGS := -f elf64 -Wall -Werror -SRC := src/ft_strlen.s +SRC := ft_strlen ft_strcpy +SRC := $(addsuffix .s,$(addprefix src/,$(SRC))) OBJ := $(SRC:.s=.o) all: $(NAME) diff --git a/pcc/libasm/src/ft_strcpy.s b/pcc/libasm/src/ft_strcpy.s new file mode 100644 index 0000000..e910314 --- /dev/null +++ b/pcc/libasm/src/ft_strcpy.s @@ -0,0 +1,27 @@ +; input: rdi -> destination pointer +; input: rdi -> source pointer to NUL-terminated string +; output: rax -> original dest pointer + +section .text + global ft_strcpy + +ft_strcpy: + mov rax, rdi ; dest ptr in rax + +.find_loop: + ; copy data byte + mov cl, byte [rsi] + mov byte [rdi], cl + + ; check if str end + cmp cl, 0 + je .done + + ; move to next dest and src bytes + inc rdi + inc rsi + jmp .find_loop + +.done: + ret ; rax still has original ptr + diff --git a/pcc/libasm/src/ft_strlen.s b/pcc/libasm/src/ft_strlen.s index 7f36025..df54dc0 100644 --- a/pcc/libasm/src/ft_strlen.s +++ b/pcc/libasm/src/ft_strlen.s @@ -4,18 +4,18 @@ section .text global ft_strlen -ft_strlen: ; callee clean-up - mov rax, rdi ; save str ptr in rax +ft_strlen: + mov rax, rdi ; save str ptr in rax .find_loop: - cmp byte [rdi], 0 + cmp byte [rdi], 0 je .done inc rdi jmp .find_loop .done: ; rdi now points to NUL terminator - sub rdi, rax ; calc ptrs diff - mov rax, rdi ; ret len in rax + sub rdi, rax ; calc ptrs diff + mov rax, rdi ; ret len in rax ret diff --git a/pcc/libasm/test.c b/pcc/libasm/test.c index 43a60c4..32b6c12 100644 --- a/pcc/libasm/test.c +++ b/pcc/libasm/test.c @@ -1,6 +1,9 @@ #include +#include +#include extern unsigned long ft_strlen(const char *s); +extern const char *ft_strcpy(const char *dst, const char *src); int main(void) { { @@ -8,5 +11,12 @@ int main(void) { printf("len = %lu\n", ft_strlen("")); // 0 } + { + const char *s = calloc(128, sizeof(const char)); + ft_strcpy(s, + "Violence is the last refuge of the incompetent. - Isaac Asimov"); + printf("copied string: \"%s\"\n", s); + } + return 0; } From 5bac0b7532e0f4d0014e046977fa496d696698e4 Mon Sep 17 00:00:00 2001 From: airone01 <21955960+airone01@users.noreply.github.com> Date: Tue, 24 Feb 2026 14:09:06 +0100 Subject: [PATCH 03/20] fix(libasm): patch test --- pcc/libasm/test.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pcc/libasm/test.c b/pcc/libasm/test.c index 32b6c12..1ad4301 100644 --- a/pcc/libasm/test.c +++ b/pcc/libasm/test.c @@ -12,10 +12,13 @@ int main(void) { } { - const char *s = calloc(128, sizeof(const char)); - ft_strcpy(s, - "Violence is the last refuge of the incompetent. - Isaac Asimov"); + char *s = calloc(128, sizeof(const char)); + const char *sn = ft_strcpy( + s, "Violence is the last refuge of the incompetent. - Isaac Asimov"); printf("copied string: \"%s\"\n", s); + printf("is the return value the original dest ptr (should be): %d\n", + sn == s); + free(s); } return 0; From 3238ca48dc10598db36a9fed8d2e7209e9c49358 Mon Sep 17 00:00:00 2001 From: airone01 <21955960+airone01@users.noreply.github.com> Date: Tue, 24 Feb 2026 14:24:47 +0100 Subject: [PATCH 04/20] feat(libasm): ft_strcmp --- pcc/libasm/Makefile | 2 +- pcc/libasm/src/ft_strcmp.s | 30 ++++++++++++++++++++++++++++++ pcc/libasm/src/ft_strcpy.s | 2 +- pcc/libasm/test.c | 16 ++++++++++++++++ 4 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 pcc/libasm/src/ft_strcmp.s diff --git a/pcc/libasm/Makefile b/pcc/libasm/Makefile index 9273e9d..341f011 100644 --- a/pcc/libasm/Makefile +++ b/pcc/libasm/Makefile @@ -3,7 +3,7 @@ AR := ar ARFLAGS := rcs AS := nasm ASFLAGS := -f elf64 -Wall -Werror -SRC := ft_strlen ft_strcpy +SRC := ft_strlen ft_strcpy ft_strcmp SRC := $(addsuffix .s,$(addprefix src/,$(SRC))) OBJ := $(SRC:.s=.o) diff --git a/pcc/libasm/src/ft_strcmp.s b/pcc/libasm/src/ft_strcmp.s new file mode 100644 index 0000000..90ea69e --- /dev/null +++ b/pcc/libasm/src/ft_strcmp.s @@ -0,0 +1,30 @@ +; input: rdi -> s1 pointer of NUL-terminated string +; input: rsi -> s2 pointer of NUL-terminated string +; output: rax -> int difference between data in pointers +; note: NULL pointer segfaults + +section .text + global ft_strcmp + +ft_strcmp: + mov rax, 0 ; init rax + +.find_loop: + ; calculate diff + add al, byte [rdi] + sub al, byte [rsi] + + ; check if either str ends + cmp byte[rdi], 0 + je .done + cmp byte[rsi], 0 + je .done + + ; move to next dest and src bytes + inc rdi + inc rsi + jmp .find_loop + +.done: + ret ; rax has output result + diff --git a/pcc/libasm/src/ft_strcpy.s b/pcc/libasm/src/ft_strcpy.s index e910314..b1d4f27 100644 --- a/pcc/libasm/src/ft_strcpy.s +++ b/pcc/libasm/src/ft_strcpy.s @@ -1,5 +1,5 @@ ; input: rdi -> destination pointer -; input: rdi -> source pointer to NUL-terminated string +; input: rsi -> source pointer to NUL-terminated string ; output: rax -> original dest pointer section .text diff --git a/pcc/libasm/test.c b/pcc/libasm/test.c index 1ad4301..de57d13 100644 --- a/pcc/libasm/test.c +++ b/pcc/libasm/test.c @@ -1,9 +1,16 @@ #include #include +#include #include extern unsigned long ft_strlen(const char *s); extern const char *ft_strcpy(const char *dst, const char *src); +extern unsigned long ft_strcmp(const char *s1, const char *s2); + +void comp_ft_strcmp(const char *s1, const char *s2) { + printf("'%s' vs '%s' (mine): %zu\n", s1, s2, ft_strcmp(s1, s2)); + printf("'%s' vs '%s' (libc): %d\n", s1, s2, strcmp(s1, s2)); +} int main(void) { { @@ -21,5 +28,14 @@ int main(void) { free(s); } + { + const char *s1 = "hello"; + const char *s2 = "hella"; + comp_ft_strcmp(s1, s2); + s1 = ""; + s2 = ""; + comp_ft_strcmp(s1, s2); + } + return 0; } From 3bbf684842fb5ca59d26f3c361fd728985f1838f Mon Sep 17 00:00:00 2001 From: airone01 <21955960+airone01@users.noreply.github.com> Date: Tue, 24 Feb 2026 14:41:39 +0100 Subject: [PATCH 05/20] fix(libasm): patch strcmp bug --- pcc/libasm/src/ft_strcmp.s | 26 ++++++++++++++++---------- pcc/libasm/test.c | 7 +++++-- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/pcc/libasm/src/ft_strcmp.s b/pcc/libasm/src/ft_strcmp.s index 90ea69e..9e18075 100644 --- a/pcc/libasm/src/ft_strcmp.s +++ b/pcc/libasm/src/ft_strcmp.s @@ -7,24 +7,30 @@ section .text global ft_strcmp ft_strcmp: - mov rax, 0 ; init rax + ; xor is a fast way to bzero a reg + xor rax, rax + xor rcx, rcx .find_loop: - ; calculate diff - add al, byte [rdi] - sub al, byte [rsi] + mov al, byte [rdi] + mov cl, byte [rsi] - ; check if either str ends - cmp byte[rdi], 0 - je .done - cmp byte[rsi], 0 + ; early stop when any char diff + cmp al, cl + jne .done + + ; just check rsi's because the previous check made sure they are the same + cmp al, 0 je .done - ; move to next dest and src bytes inc rdi inc rsi jmp .find_loop .done: - ret ; rax has output result + ; because rax and rcx were bzero'd, they hold only the exact positive + ; unsigned value of the characters, meaning subbing them correctly here + ; handles neg results as intended + sub rax, rcx + ret diff --git a/pcc/libasm/test.c b/pcc/libasm/test.c index de57d13..7e88ea6 100644 --- a/pcc/libasm/test.c +++ b/pcc/libasm/test.c @@ -5,10 +5,10 @@ extern unsigned long ft_strlen(const char *s); extern const char *ft_strcpy(const char *dst, const char *src); -extern unsigned long ft_strcmp(const char *s1, const char *s2); +extern int ft_strcmp(const char *s1, const char *s2); void comp_ft_strcmp(const char *s1, const char *s2) { - printf("'%s' vs '%s' (mine): %zu\n", s1, s2, ft_strcmp(s1, s2)); + printf("'%s' vs '%s' (mine): %d\n", s1, s2, ft_strcmp(s1, s2)); printf("'%s' vs '%s' (libc): %d\n", s1, s2, strcmp(s1, s2)); } @@ -32,6 +32,9 @@ int main(void) { const char *s1 = "hello"; const char *s2 = "hella"; comp_ft_strcmp(s1, s2); + s1 = "hella"; + s2 = "hello"; + comp_ft_strcmp(s1, s2); s1 = ""; s2 = ""; comp_ft_strcmp(s1, s2); From 58aea3b70c984aaf507cc08478ea877ce3f42fbf Mon Sep 17 00:00:00 2001 From: airone01 <21955960+airone01@users.noreply.github.com> Date: Thu, 26 Feb 2026 18:34:54 +0100 Subject: [PATCH 06/20] fix: remove useless tars --- external-libs/libXpm/doc/xpm.PS.gz | Bin 49010 -> 0 bytes .../invalid/CVE-2016-10164-poc.xpm.gz.gz.gz | Bin 1058 -> 0 bytes piscine-c/shell00/ex01/testShell00.tar | Bin 10240 -> 0 bytes piscine-c/shell00/ex02/exo2.tar | Bin 10240 -> 0 bytes 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 external-libs/libXpm/doc/xpm.PS.gz delete mode 100644 external-libs/libXpm/test/pixmaps/invalid/CVE-2016-10164-poc.xpm.gz.gz.gz delete mode 100644 piscine-c/shell00/ex01/testShell00.tar delete mode 100644 piscine-c/shell00/ex02/exo2.tar diff --git a/external-libs/libXpm/doc/xpm.PS.gz b/external-libs/libXpm/doc/xpm.PS.gz deleted file mode 100644 index 40e25de9d484deacc48785e266707b5c1e33a37d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49010 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 diff --git a/external-libs/libXpm/test/pixmaps/invalid/CVE-2016-10164-poc.xpm.gz.gz.gz b/external-libs/libXpm/test/pixmaps/invalid/CVE-2016-10164-poc.xpm.gz.gz.gz deleted file mode 100644 index 71d451535ea34db35b15137888c6129ef0fbb8b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1058 zcmV+-1l{`|iwFpA>N;2e19D|^b!>DlcyMhlXL^1vXL^1B?UL%$ry>0)4y*cOQH~A*t&+mLVU8`4176c#D7|r!9 zvc^_k`>7^g!)FTGqTR{-D6BGvPZkXHyn1JMmC-g`&D*Q6vxk4Y+FQZreOYz!aG@;q z;zLU*|HQuQy(K3$tTzmmo4OBPI{xSGvliC-UCej1#n;~TdP}`aWgWb2GahMVz9o`k z)ZGQK4Lf2Q7}DV5?zQ+0VPX*aIj~$GH?INif*=S_TViLh_#Q=Imk%!QA`)eU7(V0U zk2p}P5(6uixaCZ)1yA+2;Sxv(7kmoa3>Wx)AgWH7 zA+9H`s^ZFc5Ib(`gdWO)83K~a-E@3+zQbLZ$} z_U)pxXTLq~@Teisr&~paRc&P^w=Vywb-v>vn?Z(epRSO7)k)vh!uMv|2aH#`MM~@F z7xv9$w0#6mO3#XvrnbKv%~mp`{Sm0I?a>4%xN!dy)9*i z9`~KE%&Zv&!fGk=l&wf73cNvvZ`tl{>uTPxt(aj=cZQm;?CXMZxa|no3*Jl5G!prY5-)V$qI3 zab^vHZ@tHzsIT-(wBih3bY$bWNU0cjWX%C=*x%V+>?)TZ%eHG}rH{w*+ne^>&t7|0 z&lw!cA8YJ%aOE#$`0D!Z$(d8?>G*{#;7a4d%WD`NUde-O`{ucwB^sK#>4ltYC!?O% zG_?N3Uvy7?&hR-*BQXg*AroLZfS32N-A9#Xdv{LTBK_3Ea^2ys%U-(?DlWNMt`T;d z#=a%Pce`DGki!!5)jEqwvmkKHAuz` zACi#-L^6_XBqO;7$w=`b8L7`gGN#)|Msf|3k>W!#Qs+T3rrStHat)G^;zKf0fd8M2 za3wa6h0kyjS2%X)QO6DXDR>zWC5k7herPCz-(U#_fju(_!$~j)EW*ekMN*YwfWLik c(##>IAdU++wf0sWzaXUj3(2mL=Ito}0GgHwfdBvi diff --git a/piscine-c/shell00/ex01/testShell00.tar b/piscine-c/shell00/ex01/testShell00.tar deleted file mode 100644 index 85356ef0fa8d3fdc563fc94a9cfe957a8d0b85fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10240 zcmeIuJr2S!42EIPoFYeXoF5Lt0Z@e^Iz&{W>A>v?OzpxZt^fDwf0I;v%$CpIaR<5 diff --git a/piscine-c/shell00/ex02/exo2.tar b/piscine-c/shell00/ex02/exo2.tar deleted file mode 100644 index 8d0adc7a47fc5893b9a16ece275e581956c24e5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10240 zcmeI0L2iUF3`MigxgS%h;UViKZu{fWd{rM3{&L@dmfh7#JLz`^womLBOr00ck)1V8`;KmY_l00ck) R1V8`;KmY_l00jP!z&GijlP>@O From b7b7c94f0fe8407602d368049a813afcf0680666 Mon Sep 17 00:00:00 2001 From: airone01 <21955960+airone01@users.noreply.github.com> Date: Thu, 26 Feb 2026 19:21:49 +0100 Subject: [PATCH 07/20] feat(libasm): ft_write --- pcc/libasm/Makefile | 2 +- pcc/libasm/src/ft_write.s | 35 +++++++++++++++++++++++++ pcc/libasm/test.c | 55 +++++++++++++++++++++++++-------------- 3 files changed, 72 insertions(+), 20 deletions(-) create mode 100644 pcc/libasm/src/ft_write.s diff --git a/pcc/libasm/Makefile b/pcc/libasm/Makefile index 341f011..6f2d7e2 100644 --- a/pcc/libasm/Makefile +++ b/pcc/libasm/Makefile @@ -3,7 +3,7 @@ AR := ar ARFLAGS := rcs AS := nasm ASFLAGS := -f elf64 -Wall -Werror -SRC := ft_strlen ft_strcpy ft_strcmp +SRC := ft_strlen ft_strcpy ft_strcmp ft_write SRC := $(addsuffix .s,$(addprefix src/,$(SRC))) OBJ := $(SRC:.s=.o) diff --git a/pcc/libasm/src/ft_write.s b/pcc/libasm/src/ft_write.s new file mode 100644 index 0000000..1d6dcc1 --- /dev/null +++ b/pcc/libasm/src/ft_write.s @@ -0,0 +1,35 @@ +; input: rdi -> file descriptor (int) +; input: rsi -> pointer to data buffer (const void*) +; input: rdx -> count of bytes of data to write (size_t) +; output: rax -> bytes written, -1 on error (size_t) + +section .text + global ft_write + extern __errno_location + +ft_write: + mov rax, 1 ; 1 is the syscall number for sys_write on Linux + ; all data already in place, we just call + syscall + + cmp rax, 0 + jl .error + ret ; after call rax will be bytes written count + +.error: + neg rax ; invert code + + ; Because we called, stack shift is now 8, but Linux tells us it should be + ; divisible by 16 before calling or undefined behaviors will occur. + ; This push both saves the code to rax and alligns the stack. + push rax + call __errno_location + pop rcx ; pop back the saved code into rcx + + ; Technically errno is a 32-bit int. We can move the 32-bit portion of rcx + ; into the mem address pointed by rax. This also means we don't need to + ; zero out the memory pointed by rax. + mov [rax], ecx + + mov rax, -1 + ret diff --git a/pcc/libasm/test.c b/pcc/libasm/test.c index 7e88ea6..e036536 100644 --- a/pcc/libasm/test.c +++ b/pcc/libasm/test.c @@ -1,43 +1,60 @@ +#include #include #include #include #include +#define ASSERT_EQ(actual, expected, label) \ + do { \ + if ((actual) == (expected)) { \ + printf("[OK] %s\n", label); \ + } else { \ + printf("[KO] %s (line %d): Expected %ld, got %ld\n", label, __LINE__, \ + (long)(expected), (long)(actual)); \ + } \ + } while (0) + extern unsigned long ft_strlen(const char *s); extern const char *ft_strcpy(const char *dst, const char *src); extern int ft_strcmp(const char *s1, const char *s2); +extern ssize_t ft_write(int fd, const void *buf, size_t count); -void comp_ft_strcmp(const char *s1, const char *s2) { - printf("'%s' vs '%s' (mine): %d\n", s1, s2, ft_strcmp(s1, s2)); - printf("'%s' vs '%s' (libc): %d\n", s1, s2, strcmp(s1, s2)); +void comp_ft_strcmp(const char *s1, const char *s2, const char *label) { + ASSERT_EQ(ft_strcmp(s1, s2), strcmp(s1, s2), label); } int main(void) { { - printf("len = %lu\n", ft_strlen("hello, world")); // 12 - printf("len = %lu\n", ft_strlen("")); // 0 + ASSERT_EQ(ft_strlen("hello, world"), 12, "ft_strlen should work"); + ASSERT_EQ(ft_strlen(""), 0, "ft_strlen should work with empty strings"); } { char *s = calloc(128, sizeof(const char)); - const char *sn = ft_strcpy( - s, "Violence is the last refuge of the incompetent. - Isaac Asimov"); - printf("copied string: \"%s\"\n", s); - printf("is the return value the original dest ptr (should be): %d\n", - sn == s); + const char *o = + "Violence is the last refuge of the incompetent - Isaac Asimov"; + const char *sn = ft_strcpy(s, o); + ASSERT_EQ(*sn, *o, "ft_strcpy should work"); + ASSERT_EQ(sn, s, "ft_strcpy should return dst"); free(s); } { - const char *s1 = "hello"; - const char *s2 = "hella"; - comp_ft_strcmp(s1, s2); - s1 = "hella"; - s2 = "hello"; - comp_ft_strcmp(s1, s2); - s1 = ""; - s2 = ""; - comp_ft_strcmp(s1, s2); + comp_ft_strcmp("hello", "hella", "ft_strcmp should handle positive diff"); + comp_ft_strcmp("hella", "hello", "ft_strcmp should handle negative diff"); + comp_ft_strcmp("", "", "ft_strcmp should handle equality"); + } + + { + ssize_t ret; + + ret = ft_write(2, "Testing ft_write...\n", 20); + ASSERT_EQ(ret, 20, "ft_write should print exact number of characters"); + + errno = 0; + ret = ft_write(-1, "fail", 4); + ASSERT_EQ(ret, -1, "ft_write should return -1"); + ASSERT_EQ(errno, 9, "ft_write should set errno on error"); } return 0; From 85e03023471739317ed5b67896234fb4051e30a7 Mon Sep 17 00:00:00 2001 From: airone01 <21955960+airone01@users.noreply.github.com> Date: Thu, 26 Feb 2026 19:55:18 +0100 Subject: [PATCH 08/20] fix(libasm): patch tester --- pcc/libasm/Makefile | 4 +-- pcc/libasm/src/ft_read.s | 35 ++++++++++++++++++ pcc/libasm/src/ft_write.s | 2 +- pcc/libasm/test.c | 74 +++++++++++++++++++++++++++++++-------- 4 files changed, 98 insertions(+), 17 deletions(-) create mode 100644 pcc/libasm/src/ft_read.s diff --git a/pcc/libasm/Makefile b/pcc/libasm/Makefile index 6f2d7e2..5eb5a87 100644 --- a/pcc/libasm/Makefile +++ b/pcc/libasm/Makefile @@ -3,8 +3,8 @@ AR := ar ARFLAGS := rcs AS := nasm ASFLAGS := -f elf64 -Wall -Werror -SRC := ft_strlen ft_strcpy ft_strcmp ft_write -SRC := $(addsuffix .s,$(addprefix src/,$(SRC))) +SRC := strlen strcpy strcmp write read +SRC := $(addsuffix .s,$(addprefix src/ft_,$(SRC))) OBJ := $(SRC:.s=.o) all: $(NAME) diff --git a/pcc/libasm/src/ft_read.s b/pcc/libasm/src/ft_read.s new file mode 100644 index 0000000..b1fe323 --- /dev/null +++ b/pcc/libasm/src/ft_read.s @@ -0,0 +1,35 @@ +; input: rdi -> file descriptor (int) +; input: rsi -> pointer to data buffer (const void*) +; input: rdx -> count of bytes of data to read (size_t) +; output: rax -> bytes read, -1 on error (size_t) + +section .text + global ft_read + extern __errno_location + +ft_read: + mov rax, 0 ; 0 is the syscall number for sys_read on Linux + ; all data already in place, we just call + syscall + + cmp rax, 0 + jl .error + ret ; after call rax will be nbytes read + +.error: + neg rax ; invert code + + ; Because we called, stack shift is now 8, but Linux tells us it should be + ; divisible by 16 before calling or undefined behaviors will occur. + ; This push both saves the code to rax and alligns the stack. + push rax + call __errno_location + pop rcx ; pop back the saved code into rcx + + ; Technically errno is a 32-bit int. We can move the 32-bit portion of rcx + ; into the mem address pointed by rax. This also means we don't need to + ; zero out the memory pointed by rax. + mov [rax], ecx + + mov rax, -1 + ret diff --git a/pcc/libasm/src/ft_write.s b/pcc/libasm/src/ft_write.s index 1d6dcc1..b434803 100644 --- a/pcc/libasm/src/ft_write.s +++ b/pcc/libasm/src/ft_write.s @@ -14,7 +14,7 @@ ft_write: cmp rax, 0 jl .error - ret ; after call rax will be bytes written count + ret ; after call rax will be nbytes written .error: neg rax ; invert code diff --git a/pcc/libasm/test.c b/pcc/libasm/test.c index e036536..d1776eb 100644 --- a/pcc/libasm/test.c +++ b/pcc/libasm/test.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -7,20 +8,47 @@ #define ASSERT_EQ(actual, expected, label) \ do { \ if ((actual) == (expected)) { \ - printf("[OK] %s\n", label); \ + printf("\033[32mok\033[0m %s\n", label); \ } else { \ - printf("[KO] %s (line %d): Expected %ld, got %ld\n", label, __LINE__, \ - (long)(expected), (long)(actual)); \ + printf("\033[31mKO\033[0m %s (line %d): Expected %ld, got %ld\n", label, \ + __LINE__, (long)(expected), (long)(actual)); \ } \ } while (0) -extern unsigned long ft_strlen(const char *s); -extern const char *ft_strcpy(const char *dst, const char *src); +#define ASSERT_GT(actual, expected, label) \ + do { \ + if ((actual) > (expected)) { \ + printf("\033[32mok\033[0m %s\n", label); \ + } else { \ + printf("\033[31mKO\033[0m %s (line %d): Expected %ld, got %ld\n", label, \ + __LINE__, (long)(expected), (long)(actual)); \ + } \ + } while (0) + +#define ASSERT_LT(actual, expected, label) \ + do { \ + if ((actual) < (expected)) { \ + printf("\033[32mok\033[0m %s\n", label); \ + } else { \ + printf("\033[31mKO\033[0m %s (line %d): Expected %ld, got %ld\n", label, \ + __LINE__, (long)(expected), (long)(actual)); \ + } \ + } while (0) + +extern size_t ft_strlen(const char *s); +extern char *ft_strcpy(const char *dest, const char *src); extern int ft_strcmp(const char *s1, const char *s2); -extern ssize_t ft_write(int fd, const void *buf, size_t count); +extern ssize_t ft_write(int fildes, const void *buf, size_t nbyte); +extern ssize_t ft_read(int fildes, void *buf, size_t nbyte); void comp_ft_strcmp(const char *s1, const char *s2, const char *label) { - ASSERT_EQ(ft_strcmp(s1, s2), strcmp(s1, s2), label); + int my_res = ft_strcmp(s1, s2); + int std_res = strcmp(s1, s2); + + int my_sign = (my_res > 0) - (my_res < 0); + int std_sign = (std_res > 0) - (std_res < 0); + + ASSERT_EQ(my_sign, std_sign, label); } int main(void) { @@ -35,7 +63,7 @@ int main(void) { "Violence is the last refuge of the incompetent - Isaac Asimov"; const char *sn = ft_strcpy(s, o); ASSERT_EQ(*sn, *o, "ft_strcpy should work"); - ASSERT_EQ(sn, s, "ft_strcpy should return dst"); + ASSERT_EQ(sn, s, "ft_strcpy should return dest"); free(s); } @@ -46,15 +74,33 @@ int main(void) { } { - ssize_t ret; + int fd = open("/dev/null", O_WRONLY); + if (fd != -1) { + ASSERT_EQ(ft_write(fd, "Testing ft_write...\n", 20), 20, + "ft_write should print exact number of characters"); - ret = ft_write(2, "Testing ft_write...\n", 20); - ASSERT_EQ(ret, 20, "ft_write should print exact number of characters"); + errno = 0; + ASSERT_EQ(ft_write(-1, "fail", 4), -1, "ft_write should return -1"); + ASSERT_EQ(errno, 9, "ft_write should set errno on error"); + close(fd); + } else + printf("skip: could not open() to test valid write\n"); + } + + { + char buf[50]; + int fd = open("/dev/urandom", O_RDONLY); + if (fd != -1) { + ssize_t ret = ft_read(fd, buf, 10); + ASSERT_EQ(ret, 10, "ft_read should work"); + close(fd); + } else + printf("skip: could not open() to test valid read\n"); errno = 0; - ret = ft_write(-1, "fail", 4); - ASSERT_EQ(ret, -1, "ft_write should return -1"); - ASSERT_EQ(errno, 9, "ft_write should set errno on error"); + ssize_t ret2 = ft_read(-1, buf, 10); + ASSERT_EQ(ret2, -1, "ft_read should error out properly"); + ASSERT_EQ(errno, EBADF, "ft_read should set errno on error"); } return 0; From 795eb488d6b57a427ed7685b1e7d159a68234ed7 Mon Sep 17 00:00:00 2001 From: airone01 <21955960+airone01@users.noreply.github.com> Date: Thu, 26 Feb 2026 20:11:14 +0100 Subject: [PATCH 09/20] docs(libasm): update readme for bonus --- pcc/libasm/README.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pcc/libasm/README.txt b/pcc/libasm/README.txt index 1a0f4e0..08765fb 100644 --- a/pcc/libasm/README.txt +++ b/pcc/libasm/README.txt @@ -22,3 +22,20 @@ The functions provided by this project are the following: - ft_read (man 2 read) - ft_strdup (man 3 strdup, call to malloc allowed) +And the bonus functions are as follow: + +- ft_atoi_base + - `int ft_atoi_base(char *str, char *base);` +- ft_list_push_front + - `void ft_list_push_front(t_list **begin_list, void *data);` +- ft_list_size + - `int ft_list_size(t_list *begin_list);` +- ft_list_sort + - `void ft_list_sort(t_list **begin_list, int (*cmp)());` + - The function pointed to by cmp will be used as: + - `(*cmp)(list_ptr->data, list_other_ptr->data);` +- ft_list_remove_if + - `void ft_list_remove_if(t_list **begin_list, void *data_ref, int (*cmp)(), void (*free_fct)(void *));` + - The functions pointed to by cmp will be used respectively as: + - `(*cmp)(list_ptr->data, data_ref);` + - `(*free_fct)(list_ptr->data);` From bdfe4615584345c2c76cbe15f93231b6872bd1dc Mon Sep 17 00:00:00 2001 From: airone01 <21955960+airone01@users.noreply.github.com> Date: Thu, 26 Feb 2026 20:12:22 +0100 Subject: [PATCH 10/20] docs(libasm): simplify --- pcc/libasm/README.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pcc/libasm/README.txt b/pcc/libasm/README.txt index 08765fb..61761f2 100644 --- a/pcc/libasm/README.txt +++ b/pcc/libasm/README.txt @@ -15,12 +15,12 @@ Provided functions The functions provided by this project are the following: -- ft_strlen (man 3 strlen) -- ft_strcpy (man 3 strcpy) -- ft_strcmp (man 3 strcmp) -- ft_write (man 2 write) -- ft_read (man 2 read) -- ft_strdup (man 3 strdup, call to malloc allowed) +- ft_strlen (strlen.3) +- ft_strcpy (strcpy.3) +- ft_strcmp (strcmp.3) +- ft_write (write.2) +- ft_read (read.2) +- ft_strdup (strdup.3, call to malloc allowed) And the bonus functions are as follow: From 4fbf5936bec92e510aca3299b1c494054629971e Mon Sep 17 00:00:00 2001 From: airone01 <21955960+airone01@users.noreply.github.com> Date: Thu, 26 Feb 2026 20:54:58 +0100 Subject: [PATCH 11/20] feat(libasm): ft_strdup --- pcc/libasm/Makefile | 2 +- pcc/libasm/src/ft_strdup.s | 30 ++++++++++++++++++++++++++++++ pcc/libasm/test.c | 21 +-------------------- 3 files changed, 32 insertions(+), 21 deletions(-) create mode 100644 pcc/libasm/src/ft_strdup.s diff --git a/pcc/libasm/Makefile b/pcc/libasm/Makefile index 5eb5a87..495ec13 100644 --- a/pcc/libasm/Makefile +++ b/pcc/libasm/Makefile @@ -3,7 +3,7 @@ AR := ar ARFLAGS := rcs AS := nasm ASFLAGS := -f elf64 -Wall -Werror -SRC := strlen strcpy strcmp write read +SRC := strlen strcpy strcmp write read strdup SRC := $(addsuffix .s,$(addprefix src/ft_,$(SRC))) OBJ := $(SRC:.s=.o) diff --git a/pcc/libasm/src/ft_strdup.s b/pcc/libasm/src/ft_strdup.s new file mode 100644 index 0000000..eb0d443 --- /dev/null +++ b/pcc/libasm/src/ft_strdup.s @@ -0,0 +1,30 @@ +; input: rdi -> source pointer to NUL-terminated string +; output: rax -> original source pointer + +section .text + global ft_strdup + extern malloc ; byte size into rdi, allign stack + extern ft_strlen + extern ft_strcpy + +ft_strdup: + push rdi ; save og ptr + call ft_strlen + + inc rax ; ptr to NUL + mov rdi, rax ; setup for malloc + + call malloc + cmp rax, 0 ; err handling + je .error + + mov rdi, rax ; setup for strcpy + pop rsi ; get back ptr + + call ft_strcpy + ret + +.error + ; still need to pop to keep the stack balanced + pop rdi + ret diff --git a/pcc/libasm/test.c b/pcc/libasm/test.c index d1776eb..686fee8 100644 --- a/pcc/libasm/test.c +++ b/pcc/libasm/test.c @@ -15,31 +15,12 @@ } \ } while (0) -#define ASSERT_GT(actual, expected, label) \ - do { \ - if ((actual) > (expected)) { \ - printf("\033[32mok\033[0m %s\n", label); \ - } else { \ - printf("\033[31mKO\033[0m %s (line %d): Expected %ld, got %ld\n", label, \ - __LINE__, (long)(expected), (long)(actual)); \ - } \ - } while (0) - -#define ASSERT_LT(actual, expected, label) \ - do { \ - if ((actual) < (expected)) { \ - printf("\033[32mok\033[0m %s\n", label); \ - } else { \ - printf("\033[31mKO\033[0m %s (line %d): Expected %ld, got %ld\n", label, \ - __LINE__, (long)(expected), (long)(actual)); \ - } \ - } while (0) - extern size_t ft_strlen(const char *s); extern char *ft_strcpy(const char *dest, const char *src); extern int ft_strcmp(const char *s1, const char *s2); extern ssize_t ft_write(int fildes, const void *buf, size_t nbyte); extern ssize_t ft_read(int fildes, void *buf, size_t nbyte); +extern char *ft_strdup(const char *s); void comp_ft_strcmp(const char *s1, const char *s2, const char *label) { int my_res = ft_strcmp(s1, s2); From 8855239cfdc972d70d55d037b5aeddcfd868d264 Mon Sep 17 00:00:00 2001 From: "Erwann Lagouche (AirOne01)" <21955960+AirOne01@users.noreply.github.com> Date: Thu, 26 Feb 2026 23:39:06 +0100 Subject: [PATCH 12/20] fix(libasm): good practices and security fixes --- flake.nix | 14 ++++++----- pcc/libasm/Makefile | 14 ++++++++--- pcc/libasm/README.txt | 51 +++++++++++++++++++++++++++++++++++--- pcc/libasm/src/ft_read.s | 24 ++++++++---------- pcc/libasm/src/ft_strcmp.s | 12 ++++++++- pcc/libasm/src/ft_strdup.s | 19 ++++++++------ pcc/libasm/src/ft_write.s | 22 ++++++++-------- 7 files changed, 108 insertions(+), 48 deletions(-) diff --git a/flake.nix b/flake.nix index 5965df8..e74dd59 100644 --- a/flake.nix +++ b/flake.nix @@ -7,7 +7,12 @@ c_formatter_42.url = "github:maix-flake/c_formatter_42"; }; - outputs = { nixpkgs, flake-utils, c_formatter_42, ... }: + outputs = { + nixpkgs, + flake-utils, + c_formatter_42, + ... + }: flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; @@ -29,8 +34,8 @@ openssl lld - # Sharp - stdenv.cc.cc.lib + # ASM + nasm # Rust pkg-config @@ -54,9 +59,6 @@ # Node packages in PATH export PATH="$PWD/node_modules/.bin/:$PATH" - # Sharp - export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${pkgs.stdenv.cc.cc.lib}/lib:${pkgs.xorg.libX11.dev}/lib" - # minilibx export X11_LIB_PATH="${combinedX11}" echo "Starting configure script modification..." diff --git a/pcc/libasm/Makefile b/pcc/libasm/Makefile index 495ec13..1cc3ad6 100644 --- a/pcc/libasm/Makefile +++ b/pcc/libasm/Makefile @@ -7,6 +7,13 @@ SRC := strlen strcpy strcmp write read strdup SRC := $(addsuffix .s,$(addprefix src/ft_,$(SRC))) OBJ := $(SRC:.s=.o) +TNAME := test +TSRC := test.c +# This tells the linker to create the ELF section and explicitly mark it as +# non-executable and non-allocating. Not including a '.note.GNU-stack' section +# in the assembly code implies executable stack. +TLDFLAGS := -Wl,-z,noexecstack + all: $(NAME) $(NAME): $(OBJ) @@ -19,14 +26,13 @@ clean: $(RM) $(OBJ) fclean: clean - $(RM) $(NAME) ./test + $(RM) $(NAME) $(TNAME) re: fclean $(MAKE) all -test: $(NAME) - $(CC) test.c $(NAME) -o test - ./test +$(TNAME): $(NAME) + $(CC) $(TSRC) $(TLDFLAGS) $(NAME) -o $(TNAME) FORCE: diff --git a/pcc/libasm/README.txt b/pcc/libasm/README.txt index 61761f2..a881ffd 100644 --- a/pcc/libasm/README.txt +++ b/pcc/libasm/README.txt @@ -3,12 +3,13 @@ libasm A library of basic functions, written in assembly. -Assembly specifications ------------------------ +Project specifications +====================== -This project is programmed for baseline x86_64 with the System V AMD64 convention, for the GNU/Linux ABI. +This project is programmed for baseline AMD64 with the System V AMD64 convention, for the GNU/Linux ABI. The Intel syntax is the one used, as required by the subject. -This is not programmed for any AVX extension. +My implementation is vanilla, as in it is not programmed with AVX or any AMD64 extension in mind. +It should therefore be portable on AMD64 Linux. Provided functions ------------------ @@ -39,3 +40,45 @@ And the bonus functions are as follow: - The functions pointed to by cmp will be used respectively as: - `(*cmp)(list_ptr->data, data_ref);` - `(*free_fct)(list_ptr->data);` + +Appendix +======== + +Appendix A +---------- + +"Why align the stack?" +Before executing a 'call' instruction, the stack pointer (rsp) must be 16-byte aligned. +When a function is entered, the 'call' instruction pushes an 8-byte return address onto the stack, leaving it misaligned. +To fix this before calling another C function, we must adjust rsp by 8 bytes. +Pushing a dummy register (or saving a register we need) offsets that 8-byte imbalance. +Failing to do this causes modern C library functions (which use strict SSE/AVX instructions) to segfault. + +Appendix B +---------- + +"With Respect to the Procedure Linking Table" +Modern Linux enforces PIE (Position Independent Executables). +Memory addresses for external C library functions (like malloc or __errno_location) are randomized at runtime and cannot be reached via a static 32-bit relative jump. +Appending 'WRT ..plt' (With Respect To Procedure Linkage Table) forces the assembler to generate an R_X86_64_PLT32 relocation. +This routes our call through the PLT trampoline, which dynamically resolves the true memory address via the GOT (Global Offset Table) at runtime. + +The 'default rel' line is used to indicate to the assembler that we are purposely writing position independent code. +AFAIK, 'WRT ..plt' does route the call, but '-Wall' still warns, and we cannot compile because of '-Werror'. Hence, using '[warning -reloc-rel-dword]' to suppress the NASM warning. + +Appendix C +---------- + +Technically errno is a 32-bit integer. +We can move the 32-bit portion of a register someplace else by leveraging the last 32 bits out of the 64 bits of a register. +We can use the convenient 32-bit version of the 64-bit integer for that. For example, EAX for RAX. +We don't need to zero out RAX in this example. + +Resources +========= + +"AMD 64-Bit Technology: The AMD64 x86-64™ Architecture Programmers Overview" by AMD +https://refspecs.linuxbase.org/x86_64-overview.pdf + +"Linux ABI description" +https://docs.kernel.org/admin-guide/abi.html diff --git a/pcc/libasm/src/ft_read.s b/pcc/libasm/src/ft_read.s index b1fe323..a2b54c0 100644 --- a/pcc/libasm/src/ft_read.s +++ b/pcc/libasm/src/ft_read.s @@ -3,33 +3,31 @@ ; input: rdx -> count of bytes of data to read (size_t) ; output: rax -> bytes read, -1 on error (size_t) +; appendix B +default rel +[warning -reloc-rel-dword] + section .text global ft_read extern __errno_location ft_read: - mov rax, 0 ; 0 is the syscall number for sys_read on Linux + mov rax, 0 ; 0 is syscall no. for sys_read on Linux ; all data already in place, we just call syscall cmp rax, 0 - jl .error + jl .lerror ret ; after call rax will be nbytes read -.error: +.lerror: neg rax ; invert code - ; Because we called, stack shift is now 8, but Linux tells us it should be - ; divisible by 16 before calling or undefined behaviors will occur. - ; This push both saves the code to rax and alligns the stack. - push rax - call __errno_location + push rax ; appendix A + call __errno_location WRT ..plt ; appendix B pop rcx ; pop back the saved code into rcx - ; Technically errno is a 32-bit int. We can move the 32-bit portion of rcx - ; into the mem address pointed by rax. This also means we don't need to - ; zero out the memory pointed by rax. - mov [rax], ecx + mov [rax], ecx ; appendix C - mov rax, -1 + mov rax, -1 ; libc errors out with -1 ret diff --git a/pcc/libasm/src/ft_strcmp.s b/pcc/libasm/src/ft_strcmp.s index 9e18075..1c5987c 100644 --- a/pcc/libasm/src/ft_strcmp.s +++ b/pcc/libasm/src/ft_strcmp.s @@ -1,7 +1,17 @@ ; input: rdi -> s1 pointer of NUL-terminated string ; input: rsi -> s2 pointer of NUL-terminated string ; output: rax -> int difference between data in pointers -; note: NULL pointer segfaults + +; Technically strcmp is expected to return: +; * 0 on s1 == s2 +; * any negative value on s1 < s2 +; * any positive value on s1 > s2 +; But this is an implementation closer to that of the libc. We return the +; difference between the first different character of each string. +; i.e. some optimized implementations (like Valgrind's) will return 0, -1 or 1. +; This is the reason you cannot compare the result of different strcmp +; implementations as a test, only the sign matters. +; @see strcmp(3) section .text global ft_strcmp diff --git a/pcc/libasm/src/ft_strdup.s b/pcc/libasm/src/ft_strdup.s index eb0d443..35cf9da 100644 --- a/pcc/libasm/src/ft_strdup.s +++ b/pcc/libasm/src/ft_strdup.s @@ -1,6 +1,10 @@ ; input: rdi -> source pointer to NUL-terminated string ; output: rax -> original source pointer +; appendix B +default rel +[warning -reloc-rel-dword] + section .text global ft_strdup extern malloc ; byte size into rdi, allign stack @@ -8,23 +12,22 @@ section .text extern ft_strcpy ft_strdup: - push rdi ; save og ptr - call ft_strlen + push rdi ; save original ptr + call ft_strlen WRT ..plt inc rax ; ptr to NUL mov rdi, rax ; setup for malloc - call malloc + call malloc WRT ..plt cmp rax, 0 ; err handling - je .error + je .lerror mov rdi, rax ; setup for strcpy pop rsi ; get back ptr - call ft_strcpy + call ft_strcpy WRT ..plt ret -.error - ; still need to pop to keep the stack balanced - pop rdi +.lerror: + pop rdi ; appendix A ret diff --git a/pcc/libasm/src/ft_write.s b/pcc/libasm/src/ft_write.s index b434803..a45a001 100644 --- a/pcc/libasm/src/ft_write.s +++ b/pcc/libasm/src/ft_write.s @@ -3,33 +3,31 @@ ; input: rdx -> count of bytes of data to write (size_t) ; output: rax -> bytes written, -1 on error (size_t) +; appendix B +default rel +[warning -reloc-rel-dword] + section .text global ft_write extern __errno_location ft_write: - mov rax, 1 ; 1 is the syscall number for sys_write on Linux + mov rax, 1 ; 1 is syscall no. for sys_write on Linux ; all data already in place, we just call syscall cmp rax, 0 - jl .error + jl .lerror ret ; after call rax will be nbytes written -.error: +.lerror: neg rax ; invert code - ; Because we called, stack shift is now 8, but Linux tells us it should be - ; divisible by 16 before calling or undefined behaviors will occur. - ; This push both saves the code to rax and alligns the stack. - push rax - call __errno_location + push rax ; appendix A + call __errno_location WRT ..plt ; appendix B pop rcx ; pop back the saved code into rcx - ; Technically errno is a 32-bit int. We can move the 32-bit portion of rcx - ; into the mem address pointed by rax. This also means we don't need to - ; zero out the memory pointed by rax. - mov [rax], ecx + mov [rax], ecx ; appendix C mov rax, -1 ret From 2a2ab682341dcdd9da2736162b58f103d7ff8a47 Mon Sep 17 00:00:00 2001 From: "Erwann Lagouche (AirOne01)" <21955960+AirOne01@users.noreply.github.com> Date: Thu, 26 Feb 2026 23:41:28 +0100 Subject: [PATCH 13/20] feat(libasm): strdup tests --- pcc/libasm/test.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pcc/libasm/test.c b/pcc/libasm/test.c index 686fee8..9725fad 100644 --- a/pcc/libasm/test.c +++ b/pcc/libasm/test.c @@ -84,5 +84,24 @@ int main(void) { ASSERT_EQ(errno, EBADF, "ft_read should set errno on error"); } + { + const char *orig1 = "A language that doesn't have everything is actually " + "easier to program in than some that do"; + char *dup1 = ft_strdup(orig1); + ASSERT_EQ(dup1 != orig1, 1, + "ft_strdup should allocate a new memory address"); + ASSERT_EQ(strcmp(dup1, orig1), 0, + "ft_strdup should copy the exact contents"); + free(dup1); + + const char *orig2 = ""; + char *dup2 = ft_strdup(orig2); + ASSERT_EQ(dup2 != orig2, 1, + "ft_strdup (empty str) should still allocate new memory"); + ASSERT_EQ(strcmp(dup2, orig2), 0, + "ft_strdup (empty str) should cleanly copy the NUL terminator"); + free(dup2); + } + return 0; } From 12e0485fc3b56c9f8623a045b5d8dfdf19d4d43d Mon Sep 17 00:00:00 2001 From: "Erwann Lagouche (AirOne01)" <21955960+AirOne01@users.noreply.github.com> Date: Thu, 26 Feb 2026 23:51:25 +0100 Subject: [PATCH 14/20] feat(libasm): more tests --- pcc/libasm/test.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/pcc/libasm/test.c b/pcc/libasm/test.c index 9725fad..ee02c68 100644 --- a/pcc/libasm/test.c +++ b/pcc/libasm/test.c @@ -41,10 +41,17 @@ int main(void) { { char *s = calloc(128, sizeof(const char)); const char *o = - "Violence is the last refuge of the incompetent - Isaac Asimov"; + "Violence is the last refuge of the incompetent — Isaac Asimov"; const char *sn = ft_strcpy(s, o); - ASSERT_EQ(*sn, *o, "ft_strcpy should work"); + + ASSERT_EQ(strcmp(sn, o), 0, "ft_strcpy should copy full string"); ASSERT_EQ(sn, s, "ft_strcpy should return dest"); + char dest_buf[10] = "overwrite"; + ft_strcpy(dest_buf, ""); + ASSERT_EQ(dest_buf[0], '\0', "ft_strcpy (empty) should place NUL"); + ASSERT_EQ(dest_buf[1], 'v', + "ft_strcpy (empty) should not overwrite past NUL"); + free(s); } @@ -52,6 +59,10 @@ int main(void) { comp_ft_strcmp("hello", "hella", "ft_strcmp should handle positive diff"); comp_ft_strcmp("hella", "hello", "ft_strcmp should handle negative diff"); comp_ft_strcmp("", "", "ft_strcmp should handle equality"); + comp_ft_strcmp("\xff", "\x01", "ft_strcmp should compare as unsigned char"); + comp_ft_strcmp("ᓚᘏᗢ", "ᓚᘏᗢ", "ft_strcmp should handle special characters"); + // well it should really not make any difference for 'special' characters or + // not, but I like the cat :-) } { @@ -59,13 +70,16 @@ int main(void) { if (fd != -1) { ASSERT_EQ(ft_write(fd, "Testing ft_write...\n", 20), 20, "ft_write should print exact number of characters"); - - errno = 0; - ASSERT_EQ(ft_write(-1, "fail", 4), -1, "ft_write should return -1"); - ASSERT_EQ(errno, 9, "ft_write should set errno on error"); + ASSERT_EQ(ft_write(fd, "fail", 0), 0, + "ft_write with 0 bytes should return 0"); close(fd); } else printf("skip: could not open() to test valid write\n"); + + errno = 0; + ASSERT_EQ(ft_write(-1, "fail", 4), -1, + "ft_write should return -1 on error"); + ASSERT_EQ(errno, EBADF, "ft_write should set errno on error"); } { @@ -74,6 +88,7 @@ int main(void) { if (fd != -1) { ssize_t ret = ft_read(fd, buf, 10); ASSERT_EQ(ret, 10, "ft_read should work"); + ASSERT_EQ(ft_read(fd, buf, 0), 0, "ft_read with 0 bytes should return 0"); close(fd); } else printf("skip: could not open() to test valid read\n"); @@ -85,8 +100,9 @@ int main(void) { } { - const char *orig1 = "A language that doesn't have everything is actually " - "easier to program in than some that do"; + const char *orig1 = + "A language that doesn't have everything is actually " + "easier to program in than some that do — Dennis Ritchie"; char *dup1 = ft_strdup(orig1); ASSERT_EQ(dup1 != orig1, 1, "ft_strdup should allocate a new memory address"); From bfd966c1f133ad20689c2cb9b1a78db9cc1e7750 Mon Sep 17 00:00:00 2001 From: airone01 <21955960+airone01@users.noreply.github.com> Date: Fri, 27 Feb 2026 14:53:12 +0100 Subject: [PATCH 15/20] fix(libasm): patch for school computers --- pcc/libasm/README.txt | 10 +++++++++- pcc/libasm/src/ft_read.s | 1 - pcc/libasm/src/ft_strdup.s | 1 - pcc/libasm/src/ft_write.s | 1 - 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/pcc/libasm/README.txt b/pcc/libasm/README.txt index a881ffd..2e46f9b 100644 --- a/pcc/libasm/README.txt +++ b/pcc/libasm/README.txt @@ -10,6 +10,7 @@ This project is programmed for baseline AMD64 with the System V AMD64 convention The Intel syntax is the one used, as required by the subject. My implementation is vanilla, as in it is not programmed with AVX or any AMD64 extension in mind. It should therefore be portable on AMD64 Linux. +Assembling with NASM version 2.15.05. Provided functions ------------------ @@ -64,7 +65,8 @@ Appending 'WRT ..plt' (With Respect To Procedure Linkage Table) forces the assem This routes our call through the PLT trampoline, which dynamically resolves the true memory address via the GOT (Global Offset Table) at runtime. The 'default rel' line is used to indicate to the assembler that we are purposely writing position independent code. -AFAIK, 'WRT ..plt' does route the call, but '-Wall' still warns, and we cannot compile because of '-Werror'. Hence, using '[warning -reloc-rel-dword]' to suppress the NASM warning. +AFAIK, 'WRT ..plt' does route the call, but '-Wall' still warns, and we cannot compile because of '-Werror'. +We would use '[warning -reloc-rel-dword]' to suppress the NASM warning for more recent versions of NASM, but it does not exist in 2.15.05, which is the version installed on 42 Lyon's computers. Appendix C ---------- @@ -82,3 +84,9 @@ https://refspecs.linuxbase.org/x86_64-overview.pdf "Linux ABI description" https://docs.kernel.org/admin-guide/abi.html + +"x64 Cheat Sheet" +https://cs.brown.edu/courses/cs033/docs/guides/x64_cheatsheet.pdf + +"x86 calling conventions" +https://en.wikipedia.org/wiki/X86_calling_conventions diff --git a/pcc/libasm/src/ft_read.s b/pcc/libasm/src/ft_read.s index a2b54c0..e7dab82 100644 --- a/pcc/libasm/src/ft_read.s +++ b/pcc/libasm/src/ft_read.s @@ -5,7 +5,6 @@ ; appendix B default rel -[warning -reloc-rel-dword] section .text global ft_read diff --git a/pcc/libasm/src/ft_strdup.s b/pcc/libasm/src/ft_strdup.s index 35cf9da..4dc808d 100644 --- a/pcc/libasm/src/ft_strdup.s +++ b/pcc/libasm/src/ft_strdup.s @@ -3,7 +3,6 @@ ; appendix B default rel -[warning -reloc-rel-dword] section .text global ft_strdup diff --git a/pcc/libasm/src/ft_write.s b/pcc/libasm/src/ft_write.s index a45a001..8d806b0 100644 --- a/pcc/libasm/src/ft_write.s +++ b/pcc/libasm/src/ft_write.s @@ -5,7 +5,6 @@ ; appendix B default rel -[warning -reloc-rel-dword] section .text global ft_write From 38be679c208cc48a620e803472b42c47cb39de81 Mon Sep 17 00:00:00 2001 From: airone01 <21955960+airone01@users.noreply.github.com> Date: Fri, 27 Feb 2026 15:01:13 +0100 Subject: [PATCH 16/20] fix(libasm): remove bonus --- pcc/libasm/README.txt | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/pcc/libasm/README.txt b/pcc/libasm/README.txt index 2e46f9b..c9ca374 100644 --- a/pcc/libasm/README.txt +++ b/pcc/libasm/README.txt @@ -24,24 +24,6 @@ The functions provided by this project are the following: - ft_read (read.2) - ft_strdup (strdup.3, call to malloc allowed) -And the bonus functions are as follow: - -- ft_atoi_base - - `int ft_atoi_base(char *str, char *base);` -- ft_list_push_front - - `void ft_list_push_front(t_list **begin_list, void *data);` -- ft_list_size - - `int ft_list_size(t_list *begin_list);` -- ft_list_sort - - `void ft_list_sort(t_list **begin_list, int (*cmp)());` - - The function pointed to by cmp will be used as: - - `(*cmp)(list_ptr->data, list_other_ptr->data);` -- ft_list_remove_if - - `void ft_list_remove_if(t_list **begin_list, void *data_ref, int (*cmp)(), void (*free_fct)(void *));` - - The functions pointed to by cmp will be used respectively as: - - `(*cmp)(list_ptr->data, data_ref);` - - `(*free_fct)(list_ptr->data);` - Appendix ======== From 7b702f2be15e6cc91876c9703bd1595d61f501ce Mon Sep 17 00:00:00 2001 From: airone01 <21955960+airone01@users.noreply.github.com> Date: Fri, 27 Feb 2026 15:29:26 +0100 Subject: [PATCH 17/20] feat(bazel): add libasm support --- BUILD.bazel | 31 +++++++++++++++++++++++++------ pcc/libasm/BUILD.bazel | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 pcc/libasm/BUILD.bazel diff --git a/BUILD.bazel b/BUILD.bazel index dea3d54..7795c03 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -11,13 +11,13 @@ alias( actual = "//central/minilibx:mlx", ) -### MILESTONE 0 +### Milestone 0 alias( name = "libft", actual = "//milestone-0/libft:libft", ) -### MILESTONE 1 +### Milestone 1 alias( name = "ft_printf", actual = "//milestone-1/ft_printf:ft_printf", @@ -28,7 +28,7 @@ alias( actual = "//milestone-1/get_next_line:get_next_line", ) -### MILESTONE 2 +### Milestone 2 alias( name = "fdf", actual = "//milestone-2/fdf:fdf", @@ -44,7 +44,7 @@ alias( actual = "//milestone-2/push_swap:push_swap", ) -### MILESTONE 3 +### Milestone 3 alias( name = "minishell", actual = "//milestone-3/minishell:minishell", @@ -55,7 +55,7 @@ alias( actual = "//milestone-3/philosophers:philosophers", ) -### MILESTONE 4 +### Milestone 4 alias( name = "cub3d", actual = "//milestone-4/cub3d:cub3d", @@ -156,7 +156,7 @@ alias( actual = "//milestone-4/cpp04/ex02:abstract", ) -### MILESTONE 5 +### Milestone 5 alias( name = "cpp05-ex00", actual = "//milestone-5/cpp05/ex00:bureaucrat", @@ -231,3 +231,22 @@ alias ( name = "cpp09-ex01", actual = "//milestone-5/cpp09/ex01:RPN" ) + +alias ( + name = "cpp09-ex02", + actual = "//milestone-5/cpp09/ex02:RmergeMe" +) + +### PCC + +#### Compilation branch + +alias ( + name = "libasm", + actual = "//pcc/libasm:libasm" +) +alias ( + name = "libasm-test", + actual = "//pcc/libasm:test" +) + diff --git a/pcc/libasm/BUILD.bazel b/pcc/libasm/BUILD.bazel new file mode 100644 index 0000000..8ce4cdf --- /dev/null +++ b/pcc/libasm/BUILD.bazel @@ -0,0 +1,32 @@ +load("@rules_cc//cc:cc_binary.bzl", "cc_binary") +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +genrule( + name = "compile_libasm", + srcs = glob(["src/*.s"]), + outs = ["libasm.a"], + cmd = """ + OBJS="" + for src in $(SRCS); do + obj="$(@D)/$$(basename $${src%.s}.o)" + nasm -f elf64 -Werror $$src -o $$obj + OBJS="$$OBJS $$obj" + done + ar rcs $@ $$OBJS + """, + message = "Compiling libasm with nasm...", +) + +cc_library( + name = "libasm", + srcs = [":compile_libasm"], + visibility = ["//visibility:public"], +) + +cc_binary( + name = "test", + srcs = ["test.c"], + deps = [":libasm"], + linkopts = ["-Wl,-z,noexecstack"], + visibility = ["//visibility:public"], +) From 55d8d5647a97d1329692096907c3b7070468823d Mon Sep 17 00:00:00 2001 From: "Erwann Lagouche (AirOne01)" <21955960+AirOne01@users.noreply.github.com> Date: Mon, 9 Mar 2026 00:50:10 +0100 Subject: [PATCH 18/20] chore(libasm): release-please cfg --- .release-please-manifest.json | 1 + release-please-config.json | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 70a9c19..01aaf9a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -34,6 +34,7 @@ "milestone-5/cpp08": "1.1.0", "milestone-5/cpp09": "1.0.0", "milestone-5/inception": "1.0.0", + "pcc/libasm": "0.0.0", "rushes/hotrace": "2.1.0", "rushes/libunit": "1.0.0", "tools/push-swap-visualizer-minecraft": "1.0.0" diff --git a/release-please-config.json b/release-please-config.json index 0317ef7..549214f 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -141,6 +141,10 @@ "release-type": "simple", "package-name": "inception" }, + "pcc/libasm": { + "release-type": "simple", + "package-name": "libasm" + }, "rushes/hotrace": { "release-type": "simple", "package-name": "hotrace" From 555b6a90a5e4ae2e57a043b01a9008ca3d01a696 Mon Sep 17 00:00:00 2001 From: "Erwann Lagouche (AirOne01)" <21955960+AirOne01@users.noreply.github.com> Date: Mon, 9 Mar 2026 00:54:24 +0100 Subject: [PATCH 19/20] chore(bazel): typo --- BUILD.bazel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.bazel b/BUILD.bazel index 7795c03..0d2f8fe 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -234,7 +234,7 @@ alias ( alias ( name = "cpp09-ex02", - actual = "//milestone-5/cpp09/ex02:RmergeMe" + actual = "//milestone-5/cpp09/ex02:PmergeMe" ) ### PCC From aaf958929d4aa97772b1fd135b3b212fd64621f4 Mon Sep 17 00:00:00 2001 From: "Erwann Lagouche (AirOne01)" <21955960+AirOne01@users.noreply.github.com> Date: Mon, 9 Mar 2026 01:01:44 +0100 Subject: [PATCH 20/20] ci: add nasm dep --- .github/workflows/bazel.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml index d6858e9..27a9f61 100644 --- a/.github/workflows/bazel.yml +++ b/.github/workflows/bazel.yml @@ -29,7 +29,8 @@ jobs: x11proto-dev \ libxext-dev \ libxrandr-dev \ - libreadline-dev + libreadline-dev \ + nasm - name: Generate libXpm config.h run: |