Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/bazel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ jobs:
x11proto-dev \
libxext-dev \
libxrandr-dev \
libreadline-dev
libreadline-dev \
nasm

- name: Generate libXpm config.h
run: |
Expand Down
1 change: 1 addition & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
31 changes: 25 additions & 6 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -44,7 +44,7 @@ alias(
actual = "//milestone-2/push_swap:push_swap",
)

### MILESTONE 3
### Milestone 3
alias(
name = "minishell",
actual = "//milestone-3/minishell:minishell",
Expand All @@ -55,7 +55,7 @@ alias(
actual = "//milestone-3/philosophers:philosophers",
)

### MILESTONE 4
### Milestone 4
alias(
name = "cub3d",
actual = "//milestone-4/cub3d:cub3d",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -231,3 +231,22 @@ alias (
name = "cpp09-ex01",
actual = "//milestone-5/cpp09/ex01:RPN"
)

alias (
name = "cpp09-ex02",
actual = "//milestone-5/cpp09/ex02:PmergeMe"
)

### PCC

#### Compilation branch

alias (
name = "libasm",
actual = "//pcc/libasm:libasm"
)
alias (
name = "libasm-test",
actual = "//pcc/libasm:test"
)

Binary file removed external-libs/libXpm/doc/xpm.PS.gz
Binary file not shown.
Binary file not shown.
14 changes: 8 additions & 6 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -29,8 +34,8 @@
openssl
lld

# Sharp
stdenv.cc.cc.lib
# ASM
nasm

# Rust
pkg-config
Expand All @@ -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..."
Expand Down
2 changes: 2 additions & 0 deletions pcc/libasm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test

32 changes: 32 additions & 0 deletions pcc/libasm/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -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"],
)
41 changes: 41 additions & 0 deletions pcc/libasm/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
NAME := libasm.a
AR := ar
ARFLAGS := rcs
AS := nasm
ASFLAGS := -f elf64 -Wall -Werror
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)
$(AR) $(ARFLAGS) $(NAME) $(OBJ)

%.o: %.s Makefile FORCE
$(AS) $(ASFLAGS) $< -o $@

clean:
$(RM) $(OBJ)

fclean: clean
$(RM) $(NAME) $(TNAME)

re: fclean
$(MAKE) all

$(TNAME): $(NAME)
$(CC) $(TSRC) $(TLDFLAGS) $(NAME) -o $(TNAME)

FORCE:

.PHONY: all clean fclean re test FORCE


74 changes: 74 additions & 0 deletions pcc/libasm/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
libasm
======

A library of basic functions, written in assembly.

Project specifications
======================

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.
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
------------------

The functions provided by this project are the following:

- 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)

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'.
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
----------

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

"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
32 changes: 32 additions & 0 deletions pcc/libasm/src/ft_read.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
; 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)

; appendix B
default rel

section .text
global ft_read
extern __errno_location

ft_read:
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 .lerror
ret ; after call rax will be nbytes read

.lerror:
neg rax ; invert code

push rax ; appendix A
call __errno_location WRT ..plt ; appendix B
pop rcx ; pop back the saved code into rcx

mov [rax], ecx ; appendix C

mov rax, -1 ; libc errors out with -1
ret
46 changes: 46 additions & 0 deletions pcc/libasm/src/ft_strcmp.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
; input: rdi -> s1 pointer of NUL-terminated string
; input: rsi -> s2 pointer of NUL-terminated string
; output: rax -> int difference between data in pointers

; 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

ft_strcmp:
; xor is a fast way to bzero a reg
xor rax, rax
xor rcx, rcx

.find_loop:
mov al, byte [rdi]
mov cl, byte [rsi]

; 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

inc rdi
inc rsi
jmp .find_loop

.done:
; 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

27 changes: 27 additions & 0 deletions pcc/libasm/src/ft_strcpy.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
; input: rdi -> destination pointer
; input: rsi -> 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

Loading
Loading