diff --git a/curs/chap-08-C-asm/01-rand/.gitignore b/curs/chap-08-C-asm/01-rand/.gitignore new file mode 100644 index 00000000..b4916765 --- /dev/null +++ b/curs/chap-08-C-asm/01-rand/.gitignore @@ -0,0 +1 @@ +/rand diff --git a/curs/chap-08-C-asm/01-rand/Makefile b/curs/chap-08-C-asm/01-rand/Makefile new file mode 100644 index 00000000..a8ea8815 --- /dev/null +++ b/curs/chap-08-C-asm/01-rand/Makefile @@ -0,0 +1,22 @@ +AS = nasm +AFILES = main.asm +OBJS = $(AFILES:.asm=.o) +BINARY = rand +ASFLAGS = -f elf64 -g -F dwarf +LD = gcc +LDFLAGS = -no-pie + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.asm + $(AS) $(ASFLAGS) -o $@ $< + +$(BINARY): $(OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/01-rand/README.md b/curs/chap-08-C-asm/01-rand/README.md new file mode 100644 index 00000000..6b7d1c89 --- /dev/null +++ b/curs/chap-08-C-asm/01-rand/README.md @@ -0,0 +1,39 @@ +# Call rand() + +This demo calls the `rand()` function from the C standard library. +`rand()` receives no arguments and returns a pseudo-random integer. +The return value arrives in `rax` following the System V AMD64 ABI. + +## Contents + +* `main.asm`: Assembly `main` that calls `rand()` and prints the result +* `Makefile`: Builds the `rand` binary +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `rand` binary. + +## Run + +```console +./rand +``` + +The program has no output. + +## Understand + +`rand()` is a C library function with no arguments. +In the System V AMD64 ABI, a function with no arguments is called with no register setup — simply issue `call rand`. +The return value is an `int` in `eax` (zero-extended to `rax`): + +```nasm +call rand ; rax = random number +``` + +We ignore the return value in the program, we will use it later. diff --git a/curs/chap-08-C-asm/01-rand/main.asm b/curs/chap-08-C-asm/01-rand/main.asm new file mode 100644 index 00000000..54cab1ae --- /dev/null +++ b/curs/chap-08-C-asm/01-rand/main.asm @@ -0,0 +1,28 @@ +; SPDX-License-Identifier: BSD-3-Clause + +section .note.GNU-stack noalloc noexec nowrite progbits + + +section .rodata + + printf_format db "Random number: %d", 10, 0 + + +section .text + +extern rand +global main + + +main: + push rbp + mov rbp, rsp + + ; Call rand() - no arguments, returns random number in rax (eax). + call rand + + ; We do nothing with the result. + + xor rax, rax ; return 0 + leave + ret diff --git a/curs/chap-08-C-asm/02-rand-srand/.gitignore b/curs/chap-08-C-asm/02-rand-srand/.gitignore new file mode 100644 index 00000000..36e6b16a --- /dev/null +++ b/curs/chap-08-C-asm/02-rand-srand/.gitignore @@ -0,0 +1 @@ +/rand_srand diff --git a/curs/chap-08-C-asm/02-rand-srand/Makefile b/curs/chap-08-C-asm/02-rand-srand/Makefile new file mode 100644 index 00000000..36644d64 --- /dev/null +++ b/curs/chap-08-C-asm/02-rand-srand/Makefile @@ -0,0 +1,22 @@ +AS = nasm +AFILES = main.asm +OBJS = $(AFILES:.asm=.o) +BINARY = rand_srand +ASFLAGS = -f elf64 -g -F dwarf +LD = gcc +LDFLAGS = -no-pie + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.asm + $(AS) $(ASFLAGS) -o $@ $< + +$(BINARY): $(OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/02-rand-srand/README.md b/curs/chap-08-C-asm/02-rand-srand/README.md new file mode 100644 index 00000000..0f566daa --- /dev/null +++ b/curs/chap-08-C-asm/02-rand-srand/README.md @@ -0,0 +1,50 @@ +# Call srand() and rand() + +This demo calls three C standard library functions that together produce a seeded pseudo-random number: + +* `time(NULL)` — takes a `NULL` pointer argument, returns the current Unix epoch time in `rax`. +* `srand(seed)` — takes one integer argument (the seed) in `rdi`, returns `void`. +* `rand()` — takes no arguments, returns a pseudo-random integer in `rax`. + +## Contents + +* `main.asm`: Assembly `main` that chains `time()` → `srand()` → `rand()` and prints the result +* `Makefile`: Builds the `rand_srand` binary +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `rand_srand` binary. + +## Run + +```console +./rand_srand +``` + +The program has no output. + +## Understand + +The three functions demonstrate different calling patterns under the System V AMD64 ABI. + +```nasm +; time(NULL) — argument is NULL (0); return value used as srand seed. +xor rdi, rdi ; NULL pointer in rdi (1st argument) +call time ; rax = current epoch time + +; srand(seed) — seed in rdi, returns void. +mov rdi, rax ; pass time() result as seed +call srand + +; rand() — no arguments, random number in rax. +call rand +``` + +Argument 1 goes in `rdi`, argument 2 in `rsi`, and so on. +The return value is always in `rax`. +Because `srand()` returns `void`, `rax` after `call srand` is undefined — `rand()` is called immediately after with no register setup needed. diff --git a/curs/chap-08-C-asm/02-rand-srand/main.asm b/curs/chap-08-C-asm/02-rand-srand/main.asm new file mode 100644 index 00000000..f54b31e4 --- /dev/null +++ b/curs/chap-08-C-asm/02-rand-srand/main.asm @@ -0,0 +1,39 @@ +; SPDX-License-Identifier: BSD-3-Clause + +section .note.GNU-stack noalloc noexec nowrite progbits + + +section .rodata + + printf_format db "Random number: %d", 10, 0 + + +section .text + +extern time +extern srand +extern rand +global main + + +main: + push rbp + mov rbp, rsp + + ; Call time(NULL) - 1st argument is NULL (0), returns current epoch time in rax. + xor rdi, rdi ; NULL pointer + call time + + ; Call srand(time) - 1st argument is the seed (return value of time in rax). + ; srand() receives an argument and returns void. + mov rdi, rax + call srand + + ; Call rand() - no arguments, returns a random number in rax (eax). + call rand + + ; We do nothing with the result. + + xor rax, rax ; return 0 + leave + ret diff --git a/curs/chap-08-C-asm/03-rand-srand-printf/.gitignore b/curs/chap-08-C-asm/03-rand-srand-printf/.gitignore new file mode 100644 index 00000000..bc525768 --- /dev/null +++ b/curs/chap-08-C-asm/03-rand-srand-printf/.gitignore @@ -0,0 +1 @@ +/rand_srand_printf diff --git a/curs/chap-08-C-asm/03-rand-srand-printf/Makefile b/curs/chap-08-C-asm/03-rand-srand-printf/Makefile new file mode 100644 index 00000000..13280dad --- /dev/null +++ b/curs/chap-08-C-asm/03-rand-srand-printf/Makefile @@ -0,0 +1,22 @@ +AS = nasm +AFILES = main.asm +OBJS = $(AFILES:.asm=.o) +BINARY = rand_srand_printf +ASFLAGS = -f elf64 -g -F dwarf +LD = gcc +LDFLAGS = -no-pie + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.asm + $(AS) $(ASFLAGS) -o $@ $< + +$(BINARY): $(OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/03-rand-srand-printf/README.md b/curs/chap-08-C-asm/03-rand-srand-printf/README.md new file mode 100644 index 00000000..5f2ae550 --- /dev/null +++ b/curs/chap-08-C-asm/03-rand-srand-printf/README.md @@ -0,0 +1,45 @@ +# Call srand(), rand(), and printf() + +This demo extends `02-rand-srand` by also calling `printf()` to print the generated number. +`printf()` is a variadic C function that takes at least two arguments: a format string pointer in `rdi` and the value to print in `rsi`. + +## Contents + +* `main.asm`: Assembly `main` that calls `srand()`, `rand()`, and `printf()` +* `Makefile`: Builds the `rand_srand_printf` binary +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `rand_srand_printf` binary. + +## Run + +```console +./rand_srand_printf +``` + +Example output: + +```text +Random number: 717584553 +``` + +## Understand + +Calling `printf()` requires an extra step compared to regular functions. +The System V AMD64 ABI requires that `rax` holds the number of floating-point arguments passed in vector registers before calling any variadic function. +When no floating-point arguments are present, `rax` must be zero: + +```nasm +lea rdi, [printf_format] ; 1st argument: format string address +mov rsi, rax ; 2nd argument: the random number +xor rax, rax ; 0 floating-point register arguments +call printf +``` + +Omitting `xor rax, rax` can cause `printf` to read undefined XMM register contents, leading to crashes or incorrect output. diff --git a/curs/chap-08-C-asm/03-rand-srand-printf/main.asm b/curs/chap-08-C-asm/03-rand-srand-printf/main.asm new file mode 100644 index 00000000..cd4718b6 --- /dev/null +++ b/curs/chap-08-C-asm/03-rand-srand-printf/main.asm @@ -0,0 +1,45 @@ +; SPDX-License-Identifier: BSD-3-Clause + +section .note.GNU-stack noalloc noexec nowrite progbits + + +section .rodata + + printf_format db "Random number: %d", 10, 0 + + +section .text + +extern time +extern srand +extern rand +extern printf +global main + + +main: + push rbp + mov rbp, rsp + + ; Call time(NULL) to get current epoch time. + xor rdi, rdi ; NULL pointer + call time + + ; Call srand(seed) to initialize the random number generator. + mov rdi, rax + call srand + + ; Call rand() to generate a random number. + call rand + + ; Call printf(format, number) - 2 arguments. + ; rdi = format string (1st argument) + ; rsi = random number (2nd argument) + lea rdi, [printf_format] + mov rsi, rax + xor rax, rax ; no vector register arguments + call printf + + xor rax, rax ; return 0 + leave + ret diff --git a/curs/chap-08-C-asm/04-rand-srand-printf-c/.gitignore b/curs/chap-08-C-asm/04-rand-srand-printf-c/.gitignore new file mode 100644 index 00000000..bc525768 --- /dev/null +++ b/curs/chap-08-C-asm/04-rand-srand-printf-c/.gitignore @@ -0,0 +1 @@ +/rand_srand_printf diff --git a/curs/chap-08-C-asm/04-rand-srand-printf-c/Makefile b/curs/chap-08-C-asm/04-rand-srand-printf-c/Makefile new file mode 100644 index 00000000..9a1272bd --- /dev/null +++ b/curs/chap-08-C-asm/04-rand-srand-printf-c/Makefile @@ -0,0 +1,23 @@ +CC = gcc +CPPFLAGS = -U_FORTIFY_SOURCE +CFILES = main.c +OBJS = $(CFILES:.c=.o) +BINARY = rand_srand_printf +CFLAGS = -fno-PIC -fcf-protection=none -fno-stack-protector -O1 -fno-inline -fno-omit-frame-pointer -Wall -Wno-unused-result -g +LD = gcc +LDFLAGS = -no-pie + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +$(BINARY): $(OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/04-rand-srand-printf-c/README.md b/curs/chap-08-C-asm/04-rand-srand-printf-c/README.md new file mode 100644 index 00000000..dfc276f9 --- /dev/null +++ b/curs/chap-08-C-asm/04-rand-srand-printf-c/README.md @@ -0,0 +1,51 @@ +# Call srand(), rand(), and printf() — C Version + +This demo is the C equivalent of `03-rand-srand-printf`. +It produces identical behaviour to the hand-written assembly version. +By disassembling the binary you can compare the compiler-generated code with the hand-written assembly. + +## Contents + +* `main.c`: C source that calls `srand(time(NULL))`, `rand()`, and `printf()` +* `Makefile`: Builds the `rand_srand_printf` binary +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `rand_srand_printf` binary. + +## Run + +```console +./rand_srand_printf +``` + +Example output: + +```text +Random number: 717584553 +``` + +## Understand + +The C source maps directly onto the assembly from `03-rand-srand-printf`: + +```c +srand(time(NULL)); +printf("Random number: %d\n", rand()); +``` + +To compare the compiler-generated code with the hand-written assembly, disassemble the binary: + +```console +objdump -d -M intel rand_srand_printf | grep -A 30 '
' +``` + +Key things to look for: +* How the compiler represents the `NULL` argument to `time()` — typically `xor edi, edi` (could also be `mov edi, 0`). +* The `xor eax, eax` (or `mov eax, 0`) before `call printf` to clear the floating-point argument count. +* Whether the compiler stores intermediate results in registers or on the stack. diff --git a/curs/chap-08-C-asm/04-rand-srand-printf-c/main.c b/curs/chap-08-C-asm/04-rand-srand-printf-c/main.c new file mode 100644 index 00000000..ae5d2523 --- /dev/null +++ b/curs/chap-08-C-asm/04-rand-srand-printf-c/main.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include +#include +#include + +int main(void) +{ + /* Initialize the random number generator with the current time as seed. */ + srand(time(NULL)); + + /* Generate a random number and print it. */ + printf("Random number: %d\n", rand()); + + return 0; +} diff --git a/curs/chap-08-C-asm/05-sum2-from-as-to-c/.gitignore b/curs/chap-08-C-asm/05-sum2-from-as-to-c/.gitignore new file mode 100644 index 00000000..e11fb7e6 --- /dev/null +++ b/curs/chap-08-C-asm/05-sum2-from-as-to-c/.gitignore @@ -0,0 +1 @@ +/sum2 diff --git a/curs/chap-08-C-asm/05-sum2-from-as-to-c/Makefile b/curs/chap-08-C-asm/05-sum2-from-as-to-c/Makefile new file mode 100644 index 00000000..7a87bceb --- /dev/null +++ b/curs/chap-08-C-asm/05-sum2-from-as-to-c/Makefile @@ -0,0 +1,28 @@ +AS = nasm +CC = gcc +CPPFLAGS = -U_FORTIFY_SOURCE +ASFLAGS = -f elf64 -g -F dwarf +CFLAGS = -fno-PIC -fcf-protection=none -fno-stack-protector -O1 -fno-inline -fno-omit-frame-pointer -Wall -Wno-unused-result -g +LD = gcc +LDFLAGS = -no-pie +BINARY = sum2 +ASM_OBJS = main.o +C_OBJS = add.o + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.asm + $(AS) $(ASFLAGS) -o $@ $< + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +$(BINARY): $(ASM_OBJS) $(C_OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(ASM_OBJS) $(C_OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/05-sum2-from-as-to-c/README.md b/curs/chap-08-C-asm/05-sum2-from-as-to-c/README.md new file mode 100644 index 00000000..f0d1575c --- /dev/null +++ b/curs/chap-08-C-asm/05-sum2-from-as-to-c/README.md @@ -0,0 +1,53 @@ +# Call C Function + +This demo shows assembly code calling a function implemented in C. +The `add()` function is compiled from C and linked with an assembly `main`. + +## Contents + +* `add.c`: Implements `add()` that receives two numbers and returns their sum +* `main.asm`: Assembly `main` that calls `add()` (using the System V ABI) and prints the result +* `Makefile`: Builds the `sum2` binary from both source files +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `sum2` binary by compiling `add.c` and assembling `main.asm`, then linking both object files together. + +## Run + +```console +./sum2 +``` + +Output: + +```text +Sum is: 5 +``` + +## Understand + +`main.asm` declares `add` as an external symbol and calls it following the System V ABI: + +```nasm +extern add + +; Call add(2, 3). +; 1st argument in rdi, 2nd argument in rsi. +mov rdi, 2 +mov rsi, 3 +call add ; rax = 5 + +lea rdi, [printf_format] +mov rsi, rax ; print the result +xor rax, rax +call printf +``` + +The linker resolves `add` to the compiled C object file `add.o`. +This is the standard mechanism for calling C library functions from assembly — the same technique used when calling `printf` or `scanf`. diff --git a/curs/chap-08-C-asm/05-sum2-from-as-to-c/add.c b/curs/chap-08-C-asm/05-sum2-from-as-to-c/add.c new file mode 100644 index 00000000..1a71777f --- /dev/null +++ b/curs/chap-08-C-asm/05-sum2-from-as-to-c/add.c @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: BSD-3-Clause + +/* Implements add() that receives two numbers and returns their sum. */ +long add(long a, long b) +{ + return a + b; +} diff --git a/curs/chap-08-C-asm/05-sum2-from-as-to-c/main.asm b/curs/chap-08-C-asm/05-sum2-from-as-to-c/main.asm new file mode 100644 index 00000000..68f2b685 --- /dev/null +++ b/curs/chap-08-C-asm/05-sum2-from-as-to-c/main.asm @@ -0,0 +1,36 @@ +; SPDX-License-Identifier: BSD-3-Clause + +section .note.GNU-stack noalloc noexec nowrite progbits + + +section .rodata + + printf_format db "Sum is: %ld", 10, 0 + + +section .text + +extern add +extern printf +global main + + +main: + push rbp + mov rbp, rsp + + ; Call add(2, 3) using the System V ABI. + ; 1st argument in rdi, 2nd argument in rsi. + mov rdi, 2 + mov rsi, 3 + call add + + ; Print result. + lea rdi, [printf_format] + mov rsi, rax + xor rax, rax ; no vector register arguments + call printf + + xor rax, rax ; return 0 + leave + ret diff --git a/curs/chap-08-C-asm/06-sum2-from-c-to-as/.gitignore b/curs/chap-08-C-asm/06-sum2-from-c-to-as/.gitignore new file mode 100644 index 00000000..e11fb7e6 --- /dev/null +++ b/curs/chap-08-C-asm/06-sum2-from-c-to-as/.gitignore @@ -0,0 +1 @@ +/sum2 diff --git a/curs/chap-08-C-asm/06-sum2-from-c-to-as/Makefile b/curs/chap-08-C-asm/06-sum2-from-c-to-as/Makefile new file mode 100644 index 00000000..8dfaed4f --- /dev/null +++ b/curs/chap-08-C-asm/06-sum2-from-c-to-as/Makefile @@ -0,0 +1,28 @@ +AS = nasm +CC = gcc +CPPFLAGS = -U_FORTIFY_SOURCE +ASFLAGS = -f elf64 -g -F dwarf +CFLAGS = -fno-PIC -fcf-protection=none -fno-stack-protector -O1 -fno-inline -fno-omit-frame-pointer -Wall -Wno-unused-result -g +LD = gcc +LDFLAGS = -no-pie +BINARY = sum2 +ASM_OBJS = add.o +C_OBJS = main.o + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.asm + $(AS) $(ASFLAGS) -o $@ $< + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +$(BINARY): $(C_OBJS) $(ASM_OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(ASM_OBJS) $(C_OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/06-sum2-from-c-to-as/README.md b/curs/chap-08-C-asm/06-sum2-from-c-to-as/README.md new file mode 100644 index 00000000..7d659dcf --- /dev/null +++ b/curs/chap-08-C-asm/06-sum2-from-c-to-as/README.md @@ -0,0 +1,59 @@ +# Call Assembly Function + +This demo shows C code calling a function implemented in assembly. +The `add()` function is written in NASM assembly and linked with a C `main`. + +## Contents + +* `add.asm`: Assembly implementation of `add()` that receives two numbers and returns their sum +* `main.c`: C `main` that calls `add()` (using the System V ABI) and prints the result +* `Makefile`: Builds the `sum2` binary from both source files +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `sum2` binary by assembling `add.asm` and compiling `main.c`, then linking both object files together. + +## Run + +```console +./sum2 +``` + +Output: + +```text +Sum is: 5 +``` + +## Understand + +`add.asm` exposes `add` as a global symbol with the correct calling convention: + +```nasm +global add + +; long add(long a, long b) +; a is in rdi, b is in rsi, return value in rax. +add: + mov rax, rdi + add rax, rsi + ret +``` + +`main.c` declares the function inline and calls it like any other C function: + +```c +long add(long a, long b); + +long result = add(2, 3); /* result = 5 */ +``` + +The key constraint is that the assembly function must follow the System V AMD64 ABI exactly: +arguments in `rdi`, `rsi`, ... return value in `rax`. + +See `07-sum2-from-c-to-as-header` for the recommended practice of using a header file instead of an inline declaration. diff --git a/curs/chap-08-C-asm/06-sum2-from-c-to-as/add.asm b/curs/chap-08-C-asm/06-sum2-from-c-to-as/add.asm new file mode 100644 index 00000000..663fc2d4 --- /dev/null +++ b/curs/chap-08-C-asm/06-sum2-from-c-to-as/add.asm @@ -0,0 +1,19 @@ +; SPDX-License-Identifier: BSD-3-Clause + +section .note.GNU-stack noalloc noexec nowrite progbits + + +section .text + +global add + + +; long add(long a, long b) +; Returns a + b. +; 1st argument (a) is in rdi. +; 2nd argument (b) is in rsi. +; Return value is in rax. +add: + mov rax, rdi + add rax, rsi + ret diff --git a/curs/chap-08-C-asm/06-sum2-from-c-to-as/main.c b/curs/chap-08-C-asm/06-sum2-from-c-to-as/main.c new file mode 100644 index 00000000..46e09032 --- /dev/null +++ b/curs/chap-08-C-asm/06-sum2-from-c-to-as/main.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include + +/* Declaration of the assembly add() function. */ +long add(long a, long b); + +int main(void) +{ + long result; + + result = add(2, 3); + printf("Sum is: %ld\n", result); + + return 0; +} diff --git a/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/.gitignore b/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/.gitignore new file mode 100644 index 00000000..e11fb7e6 --- /dev/null +++ b/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/.gitignore @@ -0,0 +1 @@ +/sum2 diff --git a/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/Makefile b/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/Makefile new file mode 100644 index 00000000..8dfaed4f --- /dev/null +++ b/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/Makefile @@ -0,0 +1,28 @@ +AS = nasm +CC = gcc +CPPFLAGS = -U_FORTIFY_SOURCE +ASFLAGS = -f elf64 -g -F dwarf +CFLAGS = -fno-PIC -fcf-protection=none -fno-stack-protector -O1 -fno-inline -fno-omit-frame-pointer -Wall -Wno-unused-result -g +LD = gcc +LDFLAGS = -no-pie +BINARY = sum2 +ASM_OBJS = add.o +C_OBJS = main.o + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.asm + $(AS) $(ASFLAGS) -o $@ $< + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +$(BINARY): $(C_OBJS) $(ASM_OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(ASM_OBJS) $(C_OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/README.md b/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/README.md new file mode 100644 index 00000000..9642dc3b --- /dev/null +++ b/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/README.md @@ -0,0 +1,51 @@ +# Call Assembly Function (with Header File) + +This demo is identical to `06-sum2-from-c-to-as` but uses a header file to declare the assembly function. +This is the recommended practice: keep function declarations in a `.h` file and `#include` it wherever the function is called. + +## Contents + +* `add.asm`: Assembly implementation of `add()` that receives two numbers and returns their sum +* `add.h`: Header file that declares the `add()` function +* `main.c`: C `main` that includes `add.h`, calls `add()`, and prints the result +* `Makefile`: Builds the `sum2` binary from all source files +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `sum2` binary. + +## Run + +```console +./sum2 +``` + +Output: + +```text +Sum is: 5 +``` + +## Understand + +`add.h` contains only the function declaration: + +```c +long add(long a, long b); +``` + +`main.c` includes it instead of repeating the declaration: + +```c +#include "add.h" + +long result = add(2, 3); +``` + +This approach scales well: when the signature of `add()` changes, only `add.h` needs to be updated — all callers automatically pick up the new declaration on the next build. +It also prevents mismatches between the declaration used in each C file that calls the function. diff --git a/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/add.asm b/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/add.asm new file mode 100644 index 00000000..663fc2d4 --- /dev/null +++ b/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/add.asm @@ -0,0 +1,19 @@ +; SPDX-License-Identifier: BSD-3-Clause + +section .note.GNU-stack noalloc noexec nowrite progbits + + +section .text + +global add + + +; long add(long a, long b) +; Returns a + b. +; 1st argument (a) is in rdi. +; 2nd argument (b) is in rsi. +; Return value is in rax. +add: + mov rax, rdi + add rax, rsi + ret diff --git a/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/add.h b/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/add.h new file mode 100644 index 00000000..3e0f0b1a --- /dev/null +++ b/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/add.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef ADD_H +#define ADD_H + +/* Declaration of the assembly add() function. */ +long add(long a, long b); + +#endif /* ADD_H */ diff --git a/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/main.c b/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/main.c new file mode 100644 index 00000000..fe262b18 --- /dev/null +++ b/curs/chap-08-C-asm/07-sum2-from-c-to-as-header/main.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include + +#include "add.h" + +int main(void) +{ + long result; + + result = add(2, 3); + printf("Sum is: %ld\n", result); + + return 0; +} diff --git a/curs/chap-08-C-asm/08-sum-array-from-as-to-c/.gitignore b/curs/chap-08-C-asm/08-sum-array-from-as-to-c/.gitignore new file mode 100644 index 00000000..0697f6c0 --- /dev/null +++ b/curs/chap-08-C-asm/08-sum-array-from-as-to-c/.gitignore @@ -0,0 +1 @@ +/sum_array diff --git a/curs/chap-08-C-asm/08-sum-array-from-as-to-c/Makefile b/curs/chap-08-C-asm/08-sum-array-from-as-to-c/Makefile new file mode 100644 index 00000000..001040db --- /dev/null +++ b/curs/chap-08-C-asm/08-sum-array-from-as-to-c/Makefile @@ -0,0 +1,28 @@ +AS = nasm +CC = gcc +CPPFLAGS = -U_FORTIFY_SOURCE +ASFLAGS = -f elf64 -g -F dwarf +CFLAGS = -fno-PIC -fcf-protection=none -fno-stack-protector -O1 -fno-inline -fno-omit-frame-pointer -Wall -Wno-unused-result -g +LD = gcc +LDFLAGS = -no-pie +BINARY = sum_array +ASM_OBJS = main.o +C_OBJS = sum_array.o + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.asm + $(AS) $(ASFLAGS) -o $@ $< + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +$(BINARY): $(ASM_OBJS) $(C_OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(ASM_OBJS) $(C_OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/08-sum-array-from-as-to-c/README.md b/curs/chap-08-C-asm/08-sum-array-from-as-to-c/README.md new file mode 100644 index 00000000..6a108f30 --- /dev/null +++ b/curs/chap-08-C-asm/08-sum-array-from-as-to-c/README.md @@ -0,0 +1,53 @@ +# Call C Function to Add Array Elements + +This demo shows assembly code calling a C function that processes an array. +Passing an array in C means passing a pointer to the first element and the element count as separate arguments. + +## Contents + +* `sum_array.c`: Implements `sum_array(array, length)` that returns the sum of the array elements +* `main.asm`: Assembly `main` that calls `sum_array()` and prints the result +* `Makefile`: Builds the `sum_array` binary from both source files +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `sum_array` binary. + +## Run + +```console +./sum_array +``` + +Output: + +```text +Sum of array elements is: 360 +``` + +## Understand + +An array argument is passed as two values following the System V ABI: + +```nasm +; Call sum_array(num_array, len). +; rdi = pointer to first element (1st argument) +; rsi = number of elements (2nd argument) +mov rdi, num_array ; base address of the array +mov rsi, len ; number of elements (computed at assembly time) +call sum_array ; rax = sum of all elements +``` + +The array data is defined in the `.rodata` section and its length is computed at assembly time using the `$` (current address) trick: + +```nasm +num_array dq 10, 20, 30, 40, 50, 60, 70, 80 +len equ ($-num_array)/8 ; number of 8-byte elements +``` + +On the C side, `sum_array` receives a pointer and a count — it has no way of knowing the array size on its own. diff --git a/curs/chap-08-C-asm/08-sum-array-from-as-to-c/main.asm b/curs/chap-08-C-asm/08-sum-array-from-as-to-c/main.asm new file mode 100644 index 00000000..08773c70 --- /dev/null +++ b/curs/chap-08-C-asm/08-sum-array-from-as-to-c/main.asm @@ -0,0 +1,39 @@ +; SPDX-License-Identifier: BSD-3-Clause + +section .note.GNU-stack noalloc noexec nowrite progbits + + +section .rodata + + num_array dq 10, 20, 30, 40, 50, 60, 70, 80 + len equ ($-num_array)/8 ; number of elements + printf_format db "Sum of array elements is: %lu", 10, 0 + + +section .text + +extern sum_array +extern printf +global main + + +main: + push rbp + mov rbp, rsp + + ; Call sum_array(num_array, len) using the System V ABI. + ; 1st argument (array pointer) in rdi. + ; 2nd argument (number of elements) in rsi. + mov rdi, num_array + mov rsi, len + call sum_array + + ; Print result. + lea rdi, [printf_format] + mov rsi, rax + xor rax, rax ; no vector register arguments + call printf + + xor rax, rax ; return 0 + leave + ret diff --git a/curs/chap-08-C-asm/08-sum-array-from-as-to-c/sum_array.c b/curs/chap-08-C-asm/08-sum-array-from-as-to-c/sum_array.c new file mode 100644 index 00000000..e90c5bb1 --- /dev/null +++ b/curs/chap-08-C-asm/08-sum-array-from-as-to-c/sum_array.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include + +/* Implements sum_array() that receives an array and its length + * and returns the sum of the elements. + */ +unsigned long sum_array(unsigned long *a, size_t num_items) +{ + size_t i; + unsigned long sum; + + sum = 0; + for (i = 0; i < num_items; i++) + sum += a[i]; + + return sum; +} diff --git a/curs/chap-08-C-asm/09-sum-array-from-c-to-as/.gitignore b/curs/chap-08-C-asm/09-sum-array-from-c-to-as/.gitignore new file mode 100644 index 00000000..0697f6c0 --- /dev/null +++ b/curs/chap-08-C-asm/09-sum-array-from-c-to-as/.gitignore @@ -0,0 +1 @@ +/sum_array diff --git a/curs/chap-08-C-asm/09-sum-array-from-c-to-as/Makefile b/curs/chap-08-C-asm/09-sum-array-from-c-to-as/Makefile new file mode 100644 index 00000000..227a30ba --- /dev/null +++ b/curs/chap-08-C-asm/09-sum-array-from-c-to-as/Makefile @@ -0,0 +1,28 @@ +AS = nasm +CC = gcc +CPPFLAGS = -U_FORTIFY_SOURCE +ASFLAGS = -f elf64 -g -F dwarf +CFLAGS = -fno-PIC -fcf-protection=none -fno-stack-protector -O1 -fno-inline -fno-omit-frame-pointer -Wall -Wno-unused-result -g +LD = gcc +LDFLAGS = -no-pie +BINARY = sum_array +ASM_OBJS = sum_array.o +C_OBJS = main.o + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.asm + $(AS) $(ASFLAGS) -o $@ $< + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +$(BINARY): $(C_OBJS) $(ASM_OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(ASM_OBJS) $(C_OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/09-sum-array-from-c-to-as/README.md b/curs/chap-08-C-asm/09-sum-array-from-c-to-as/README.md new file mode 100644 index 00000000..704f6416 --- /dev/null +++ b/curs/chap-08-C-asm/09-sum-array-from-c-to-as/README.md @@ -0,0 +1,52 @@ +# Call Assembly Function to Add Array Elements + +This demo is the reverse of `08-sum-array-from-as-to-c`: the `sum_array()` function is implemented in assembly and called from C. + +## Contents + +* `sum_array.asm`: Assembly implementation of `sum_array(array, length)` +* `sum_array.h`: Header file that declares the `sum_array()` function +* `main.c`: C `main` that calls `sum_array()` and prints the result +* `Makefile`: Builds the `sum_array` binary from all source files +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `sum_array` binary. + +## Run + +```console +./sum_array +``` + +Output: + +```text +Sum of array elements is: 360 +``` + +## Understand + +The assembly `sum_array` receives the array pointer in `rdi` and the element count in `rsi`, then iterates backwards using the `loop` instruction: + +```nasm +; unsigned long sum_array(unsigned long *a, size_t num_items) +sum_array: + mov rbx, rdi ; base pointer + mov rcx, rsi ; counter (num_items down to 1) + xor rax, rax ; accumulator +.again: + add rax, [rbx + rcx*8 - 8] + loopnz .again + ret +``` + +The addressing mode `[rbx + rcx*8 - 8]` accesses elements from index `num_items-1` down to index 0 as `rcx` decrements. + +`rbx` is a **callee-saved** register under the System V ABI, so saving and restoring it is required if this function is called from code that relies on `rbx`. +Here the function is a leaf (it calls nothing else) and its callers happen to not use `rbx`, so no explicit save/restore is needed — but in production code, non-leaf functions must save callee-saved registers they modify. diff --git a/curs/chap-08-C-asm/09-sum-array-from-c-to-as/main.c b/curs/chap-08-C-asm/09-sum-array-from-c-to-as/main.c new file mode 100644 index 00000000..a29e60a7 --- /dev/null +++ b/curs/chap-08-C-asm/09-sum-array-from-c-to-as/main.c @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include + +#include "sum_array.h" + +static unsigned long num_array[] = {10, 20, 30, 40, 50, 60, 70, 80}; + +int main(void) +{ + unsigned long result; + + result = sum_array(num_array, sizeof(num_array) / sizeof(num_array[0])); + printf("Sum of array elements is: %lu\n", result); + + return 0; +} diff --git a/curs/chap-08-C-asm/09-sum-array-from-c-to-as/sum_array.asm b/curs/chap-08-C-asm/09-sum-array-from-c-to-as/sum_array.asm new file mode 100644 index 00000000..015c13bc --- /dev/null +++ b/curs/chap-08-C-asm/09-sum-array-from-c-to-as/sum_array.asm @@ -0,0 +1,31 @@ +; SPDX-License-Identifier: BSD-3-Clause + +section .note.GNU-stack noalloc noexec nowrite progbits + + +section .text + +global sum_array + + +; unsigned long sum_array(unsigned long *a, size_t num_items) +; Returns the sum of num_items elements in the array at address a. +; 1st argument (a) is in rdi. +; 2nd argument (num_items) is in rsi. +; Return value is in rax. +sum_array: + ; Initialize base register to array pointer. + mov rbx, rdi + + ; Initialize counter to num_items. + mov rcx, rsi + + ; Initialize accumulator to 0. + xor rax, rax + + ; Sum all elements. +.again: + add rax, [rbx + rcx*8 - 8] + loopnz .again + + ret diff --git a/curs/chap-08-C-asm/09-sum-array-from-c-to-as/sum_array.h b/curs/chap-08-C-asm/09-sum-array-from-c-to-as/sum_array.h new file mode 100644 index 00000000..3c9486d6 --- /dev/null +++ b/curs/chap-08-C-asm/09-sum-array-from-c-to-as/sum_array.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef SUM_ARRAY_H +#define SUM_ARRAY_H + +#include + +/* Declaration of the assembly sum_array() function. */ +unsigned long sum_array(unsigned long *a, size_t num_items); + +#endif /* SUM_ARRAY_H */ diff --git a/curs/chap-08-C-asm/10-call-n-args-from-as-to-c/.gitignore b/curs/chap-08-C-asm/10-call-n-args-from-as-to-c/.gitignore new file mode 100644 index 00000000..002504e8 --- /dev/null +++ b/curs/chap-08-C-asm/10-call-n-args-from-as-to-c/.gitignore @@ -0,0 +1 @@ +/call_n_args diff --git a/curs/chap-08-C-asm/10-call-n-args-from-as-to-c/Makefile b/curs/chap-08-C-asm/10-call-n-args-from-as-to-c/Makefile new file mode 100644 index 00000000..8167b775 --- /dev/null +++ b/curs/chap-08-C-asm/10-call-n-args-from-as-to-c/Makefile @@ -0,0 +1,28 @@ +AS = nasm +CC = gcc +CPPFLAGS = -U_FORTIFY_SOURCE +ASFLAGS = -f elf64 -g -F dwarf +CFLAGS = -fno-PIC -fcf-protection=none -fno-stack-protector -O1 -fno-inline -fno-omit-frame-pointer -Wall -Wno-unused-result -g +LD = gcc +LDFLAGS = -no-pie +BINARY = call_n_args +ASM_OBJS = main.o +C_OBJS = sums.o + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.asm + $(AS) $(ASFLAGS) -o $@ $< + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +$(BINARY): $(ASM_OBJS) $(C_OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(ASM_OBJS) $(C_OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/10-call-n-args-from-as-to-c/README.md b/curs/chap-08-C-asm/10-call-n-args-from-as-to-c/README.md new file mode 100644 index 00000000..a403d6c3 --- /dev/null +++ b/curs/chap-08-C-asm/10-call-n-args-from-as-to-c/README.md @@ -0,0 +1,75 @@ +# Call C Functions with Multiple Arguments + +This demo shows how to call C functions with 2 to 8 arguments from assembly. +The System V AMD64 ABI passes the first six integer arguments in registers; the seventh and beyond go on the stack. + +## Contents + +* `sums.c`: Implements `sum2()` through `sum8()`, each adding all of its arguments and returning the sum +* `main.asm`: Assembly `main` that calls each function and prints all results +* `Makefile`: Builds the `call_n_args` binary from both source files +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `call_n_args` binary. + +## Run + +```console +./call_n_args +``` + +Output: + +```text +sum2(1,2) = 3 +sum3(1,2,3) = 6 +sum4(1,2,3,4) = 10 +sum5(1,2,3,4,5) = 15 +sum6(1,2,3,4,5,6) = 21 +sum7(1,2,3,4,5,6,7) = 28 +sum8(1,2,3,4,5,6,7,8) = 36 +``` + +## Understand + +The first six integer arguments map to registers in order: + +| Argument | Register | +|----------|----------| +| 1st | `rdi` | +| 2nd | `rsi` | +| 3rd | `rdx` | +| 4th | `rcx` | +| 5th | `r8` | +| 6th | `r9` | +| 7th+ | stack | + +For `sum7` the 7th argument is pushed before the call and the stack is cleaned up afterwards: + +```nasm +push qword 7 ; 7th argument on the stack +mov rdi, 1 +mov rsi, 2 +mov rdx, 3 +mov rcx, 4 +mov r8, 5 +mov r9, 6 +call sum7 +add rsp, 8 ; remove the stack argument +``` + +For `sum8` both the 7th and 8th arguments are pushed in **reverse order** (last argument first) so that argument 7 is at `[rsp+8]` and argument 8 is at `[rsp+16]` when the callee accesses them: + +```nasm +push qword 8 ; 8th argument pushed first +push qword 7 ; 7th argument pushed second (closer to callee's stack top) +; ... load registers ... +call sum8 +add rsp, 16 ; remove both stack arguments +``` diff --git a/curs/chap-08-C-asm/10-call-n-args-from-as-to-c/main.asm b/curs/chap-08-C-asm/10-call-n-args-from-as-to-c/main.asm new file mode 100644 index 00000000..c018959f --- /dev/null +++ b/curs/chap-08-C-asm/10-call-n-args-from-as-to-c/main.asm @@ -0,0 +1,113 @@ +; SPDX-License-Identifier: BSD-3-Clause + +section .note.GNU-stack noalloc noexec nowrite progbits + + +section .rodata + + fmt2 db "sum2(1,2) = %ld", 10, 0 + fmt3 db "sum3(1,2,3) = %ld", 10, 0 + fmt4 db "sum4(1,2,3,4) = %ld", 10, 0 + fmt5 db "sum5(1,2,3,4,5) = %ld", 10, 0 + fmt6 db "sum6(1,2,3,4,5,6) = %ld", 10, 0 + fmt7 db "sum7(1,2,3,4,5,6,7) = %ld", 10, 0 + fmt8 db "sum8(1,2,3,4,5,6,7,8) = %ld", 10, 0 + + +section .text + +extern sum2 +extern sum3 +extern sum4 +extern sum5 +extern sum6 +extern sum7 +extern sum8 +extern printf +global main + + +; Helper macro: print rax with given format string pointer in rbx. +%macro PRINT_RESULT 1 + lea rdi, [%1] + mov rsi, rax + xor rax, rax + call printf +%endmacro + + +main: + push rbp + mov rbp, rsp + + ; Call sum2(1, 2). + ; Arguments 1-6 go in rdi, rsi, rdx, rcx, r8, r9. + mov rdi, 1 + mov rsi, 2 + call sum2 + PRINT_RESULT fmt2 + + ; Call sum3(1, 2, 3). + mov rdi, 1 + mov rsi, 2 + mov rdx, 3 + call sum3 + PRINT_RESULT fmt3 + + ; Call sum4(1, 2, 3, 4). + mov rdi, 1 + mov rsi, 2 + mov rdx, 3 + mov rcx, 4 + call sum4 + PRINT_RESULT fmt4 + + ; Call sum5(1, 2, 3, 4, 5). + mov rdi, 1 + mov rsi, 2 + mov rdx, 3 + mov rcx, 4 + mov r8, 5 + call sum5 + PRINT_RESULT fmt5 + + ; Call sum6(1, 2, 3, 4, 5, 6). + mov rdi, 1 + mov rsi, 2 + mov rdx, 3 + mov rcx, 4 + mov r8, 5 + mov r9, 6 + call sum6 + PRINT_RESULT fmt6 + + ; Call sum7(1, 2, 3, 4, 5, 6, 7). + ; The 7th argument goes on the stack (pushed before the call). + push qword 7 ; 7th argument on the stack + mov rdi, 1 + mov rsi, 2 + mov rdx, 3 + mov rcx, 4 + mov r8, 5 + mov r9, 6 + call sum7 + add rsp, 8 ; clean up the stack argument + PRINT_RESULT fmt7 + + ; Call sum8(1, 2, 3, 4, 5, 6, 7, 8). + ; Arguments 7 and 8 go on the stack (pushed in reverse order). + push qword 8 ; 8th argument + push qword 7 ; 7th argument + mov rdi, 1 + mov rsi, 2 + mov rdx, 3 + mov rcx, 4 + mov r8, 5 + mov r9, 6 + call sum8 + add rsp, 16 ; clean up two stack arguments + PRINT_RESULT fmt8 + + xor rax, rax ; return 0 + leave + ret diff --git a/curs/chap-08-C-asm/10-call-n-args-from-as-to-c/sums.c b/curs/chap-08-C-asm/10-call-n-args-from-as-to-c/sums.c new file mode 100644 index 00000000..06d844a0 --- /dev/null +++ b/curs/chap-08-C-asm/10-call-n-args-from-as-to-c/sums.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BSD-3-Clause + +/* Functions sum2() through sum8() that take 2-8 arguments and return their sum. */ + +long sum2(long a, long b) +{ + return a + b; +} + +long sum3(long a, long b, long c) +{ + return a + b + c; +} + +long sum4(long a, long b, long c, long d) +{ + return a + b + c + d; +} + +long sum5(long a, long b, long c, long d, long e) +{ + return a + b + c + d + e; +} + +long sum6(long a, long b, long c, long d, long e, long f) +{ + return a + b + c + d + e + f; +} + +long sum7(long a, long b, long c, long d, long e, long f, long g) +{ + return a + b + c + d + e + f + g; +} + +long sum8(long a, long b, long c, long d, long e, long f, long g, long h) +{ + return a + b + c + d + e + f + g + h; +} diff --git a/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/.gitignore b/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/.gitignore new file mode 100644 index 00000000..002504e8 --- /dev/null +++ b/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/.gitignore @@ -0,0 +1 @@ +/call_n_args diff --git a/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/Makefile b/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/Makefile new file mode 100644 index 00000000..2ace58c7 --- /dev/null +++ b/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/Makefile @@ -0,0 +1,28 @@ +AS = nasm +CC = gcc +CPPFLAGS = -U_FORTIFY_SOURCE +ASFLAGS = -f elf64 -g -F dwarf +CFLAGS = -fno-PIC -fcf-protection=none -fno-stack-protector -O1 -fno-inline -fno-omit-frame-pointer -Wall -Wno-unused-result -g +LD = gcc +LDFLAGS = -no-pie +BINARY = call_n_args +ASM_OBJS = sums.o +C_OBJS = main.o + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.asm + $(AS) $(ASFLAGS) -o $@ $< + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +$(BINARY): $(C_OBJS) $(ASM_OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(ASM_OBJS) $(C_OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/README.md b/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/README.md new file mode 100644 index 00000000..c1dc0179 --- /dev/null +++ b/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/README.md @@ -0,0 +1,58 @@ +# Call Assembly Functions with Multiple Arguments + +This demo is the reverse of `10-call-n-args-from-as-to-c`: `sum2()` through `sum8()` are implemented in assembly and called from C. + +## Contents + +* `sums.asm`: Assembly implementations of `sum2()` through `sum8()` +* `sums.h`: Header file that declares all sum functions +* `main.c`: C `main` that calls each function and prints the results +* `Makefile`: Builds the `call_n_args` binary from all source files +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `call_n_args` binary. + +## Run + +```console +./call_n_args +``` + +Output: + +```text +sum2(1,2) = 3 +sum3(1,2,3) = 6 +sum4(1,2,3,4) = 10 +sum5(1,2,3,4,5) = 15 +sum6(1,2,3,4,5,6) = 21 +sum7(1,2,3,4,5,6,7) = 28 +sum8(1,2,3,4,5,6,7,8) = 36 +``` + +## Understand + +In assembly, functions with 7 or more arguments read the extra arguments from the stack at `[rsp+8]`, `[rsp+16]`, and so on (the return address occupies `[rsp+0]`): + +```nasm +; long sum8(a, b, c, d, e, f, g, h) +; rdi=a, rsi=b, rdx=c, rcx=d, r8=e, r9=f, [rsp+8]=g, [rsp+16]=h +sum8: + mov rax, rdi + add rax, rsi + add rax, rdx + add rax, rcx + add rax, r8 + add rax, r9 + add rax, [rsp+8] ; 7th argument + add rax, [rsp+16] ; 8th argument + ret +``` + +The C compiler handles the argument layout automatically; the assembly must replicate it manually for the function to be callable from both C and assembly code. diff --git a/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/main.c b/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/main.c new file mode 100644 index 00000000..d92e7c5c --- /dev/null +++ b/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/main.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include + +#include "sums.h" + +int main(void) +{ + printf("sum2(1,2) = %ld\n", sum2(1, 2)); + printf("sum3(1,2,3) = %ld\n", sum3(1, 2, 3)); + printf("sum4(1,2,3,4) = %ld\n", sum4(1, 2, 3, 4)); + printf("sum5(1,2,3,4,5) = %ld\n", sum5(1, 2, 3, 4, 5)); + printf("sum6(1,2,3,4,5,6) = %ld\n", sum6(1, 2, 3, 4, 5, 6)); + printf("sum7(1,2,3,4,5,6,7) = %ld\n", sum7(1, 2, 3, 4, 5, 6, 7)); + printf("sum8(1,2,3,4,5,6,7,8) = %ld\n", sum8(1, 2, 3, 4, 5, 6, 7, 8)); + + return 0; +} diff --git a/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/sums.asm b/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/sums.asm new file mode 100644 index 00000000..727841a4 --- /dev/null +++ b/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/sums.asm @@ -0,0 +1,85 @@ +; SPDX-License-Identifier: BSD-3-Clause + +section .note.GNU-stack noalloc noexec nowrite progbits + + +section .text + +global sum2 +global sum3 +global sum4 +global sum5 +global sum6 +global sum7 +global sum8 + + +; long sum2(long a, long b) +; Arguments: rdi=a, rsi=b +sum2: + mov rax, rdi + add rax, rsi + ret + +; long sum3(long a, long b, long c) +; Arguments: rdi=a, rsi=b, rdx=c +sum3: + mov rax, rdi + add rax, rsi + add rax, rdx + ret + +; long sum4(long a, long b, long c, long d) +; Arguments: rdi=a, rsi=b, rdx=c, rcx=d +sum4: + mov rax, rdi + add rax, rsi + add rax, rdx + add rax, rcx + ret + +; long sum5(long a, long b, long c, long d, long e) +; Arguments: rdi=a, rsi=b, rdx=c, rcx=d, r8=e +sum5: + mov rax, rdi + add rax, rsi + add rax, rdx + add rax, rcx + add rax, r8 + ret + +; long sum6(long a, long b, long c, long d, long e, long f) +; Arguments: rdi=a, rsi=b, rdx=c, rcx=d, r8=e, r9=f +sum6: + mov rax, rdi + add rax, rsi + add rax, rdx + add rax, rcx + add rax, r8 + add rax, r9 + ret + +; long sum7(long a, long b, long c, long d, long e, long f, long g) +; Arguments: rdi=a, rsi=b, rdx=c, rcx=d, r8=e, r9=f, [rsp+8]=g +sum7: + mov rax, rdi + add rax, rsi + add rax, rdx + add rax, rcx + add rax, r8 + add rax, r9 + add rax, [rsp+8] ; 7th argument is on the stack + ret + +; long sum8(long a, long b, long c, long d, long e, long f, long g, long h) +; Arguments: rdi=a, rsi=b, rdx=c, rcx=d, r8=e, r9=f, [rsp+8]=g, [rsp+16]=h +sum8: + mov rax, rdi + add rax, rsi + add rax, rdx + add rax, rcx + add rax, r8 + add rax, r9 + add rax, [rsp+8] ; 7th argument is on the stack + add rax, [rsp+16] ; 8th argument is on the stack + ret diff --git a/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/sums.h b/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/sums.h new file mode 100644 index 00000000..069854ad --- /dev/null +++ b/curs/chap-08-C-asm/11-call-n-args-from-c-to-as/sums.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef SUMS_H +#define SUMS_H + +/* Declarations of the assembly sum functions. */ +long sum2(long a, long b); +long sum3(long a, long b, long c); +long sum4(long a, long b, long c, long d); +long sum5(long a, long b, long c, long d, long e); +long sum6(long a, long b, long c, long d, long e, long f); +long sum7(long a, long b, long c, long d, long e, long f, long g); +long sum8(long a, long b, long c, long d, long e, long f, long g, long h); + +#endif /* SUMS_H */ diff --git a/curs/chap-08-C-asm/12-fibonacci-from-as-to-c/.gitignore b/curs/chap-08-C-asm/12-fibonacci-from-as-to-c/.gitignore new file mode 100644 index 00000000..7cc59a68 --- /dev/null +++ b/curs/chap-08-C-asm/12-fibonacci-from-as-to-c/.gitignore @@ -0,0 +1 @@ +/fibonacci diff --git a/curs/chap-08-C-asm/12-fibonacci-from-as-to-c/Makefile b/curs/chap-08-C-asm/12-fibonacci-from-as-to-c/Makefile new file mode 100644 index 00000000..070eb35e --- /dev/null +++ b/curs/chap-08-C-asm/12-fibonacci-from-as-to-c/Makefile @@ -0,0 +1,28 @@ +AS = nasm +CC = gcc +CPPFLAGS = -U_FORTIFY_SOURCE +ASFLAGS = -f elf64 -g -F dwarf +CFLAGS = -fno-PIC -fcf-protection=none -fno-stack-protector -O1 -fno-inline -fno-omit-frame-pointer -Wall -Wno-unused-result -g +LD = gcc +LDFLAGS = -no-pie +BINARY = fibonacci +ASM_OBJS = main.o +C_OBJS = fibonacci.o + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.asm + $(AS) $(ASFLAGS) -o $@ $< + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +$(BINARY): $(ASM_OBJS) $(C_OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(ASM_OBJS) $(C_OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/12-fibonacci-from-as-to-c/README.md b/curs/chap-08-C-asm/12-fibonacci-from-as-to-c/README.md new file mode 100644 index 00000000..bca8cd1d --- /dev/null +++ b/curs/chap-08-C-asm/12-fibonacci-from-as-to-c/README.md @@ -0,0 +1,54 @@ +# Call C Fibonacci Function + +This demo shows assembly code calling a recursive function implemented in C. +The `fibonacci()` function is compiled from C and linked with an assembly `main`. + +## Contents + +* `fibonacci.c`: Implements the recursive `fibonacci()` function +* `main.asm`: Assembly `main` that reads N, calls `fibonacci(N)`, and prints the result +* `Makefile`: Builds the `fibonacci` binary from both source files +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `fibonacci` binary. + +## Run + +```console +./fibonacci +``` + +Example interaction: + +```text +Introduce N: 10 +Fibonacci(10) = 89 +``` + +## Understand + +The assembly `main` uses a local variable on the stack to store N between the `scanf` call and the `fibonacci` call: + +```nasm +sub rsp, 16 ; allocate space; N is at [rbp-16] + +lea rdi, [prompt] +xor rax, rax +call printf + +lea rdi, [scanf_format] +lea rsi, [rbp-16] ; pass address of local N to scanf +xor rax, rax +call scanf + +mov rdi, [rbp-16] ; load N as argument to fibonacci +call fibonacci ; rax = fibonacci(N) +``` + +The local variable is needed because `rax` (which would hold N if stored in a register) is clobbered by `call printf`. diff --git a/curs/chap-08-C-asm/12-fibonacci-from-as-to-c/fibonacci.c b/curs/chap-08-C-asm/12-fibonacci-from-as-to-c/fibonacci.c new file mode 100644 index 00000000..f1b221c5 --- /dev/null +++ b/curs/chap-08-C-asm/12-fibonacci-from-as-to-c/fibonacci.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: BSD-3-Clause + +/* Recursive Fibonacci implementation in C. + * Returns 1 for N < 2, fibonacci(N-1) + fibonacci(N-2) otherwise. + */ +unsigned long fibonacci(unsigned long N) +{ + if (N < 2) + return 1; + return fibonacci(N - 1) + fibonacci(N - 2); +} diff --git a/curs/chap-08-C-asm/12-fibonacci-from-as-to-c/main.asm b/curs/chap-08-C-asm/12-fibonacci-from-as-to-c/main.asm new file mode 100644 index 00000000..e046115d --- /dev/null +++ b/curs/chap-08-C-asm/12-fibonacci-from-as-to-c/main.asm @@ -0,0 +1,52 @@ +; SPDX-License-Identifier: BSD-3-Clause + +section .note.GNU-stack noalloc noexec nowrite progbits + + +section .rodata + + prompt db "Introduce N: ", 0 + scanf_format db "%lu", 0 + result_msg db "Fibonacci(%lu) = %lu", 10, 0 + + +section .text + +extern fibonacci +extern printf +extern scanf +global main + + +main: + push rbp + mov rbp, rsp + + ; Allocate space for local variable N at [rbp-16]. + sub rsp, 16 + + ; Print prompt. + lea rdi, [prompt] + xor rax, rax + call printf + + ; Read N with scanf. + lea rdi, [scanf_format] + lea rsi, [rbp-16] + xor rax, rax + call scanf + + ; Call fibonacci(N). + mov rdi, [rbp-16] + call fibonacci + + ; Print result. + lea rdi, [result_msg] + mov rsi, [rbp-16] + mov rdx, rax + xor rax, rax + call printf + + xor rax, rax ; return 0 + leave + ret diff --git a/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/.gitignore b/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/.gitignore new file mode 100644 index 00000000..7cc59a68 --- /dev/null +++ b/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/.gitignore @@ -0,0 +1 @@ +/fibonacci diff --git a/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/Makefile b/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/Makefile new file mode 100644 index 00000000..e56a2ce1 --- /dev/null +++ b/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/Makefile @@ -0,0 +1,28 @@ +AS = nasm +CC = gcc +CPPFLAGS = -U_FORTIFY_SOURCE +ASFLAGS = -f elf64 -g -F dwarf +CFLAGS = -fno-PIC -fcf-protection=none -fno-stack-protector -O1 -fno-inline -fno-omit-frame-pointer -Wall -Wno-unused-result -g +LD = gcc +LDFLAGS = -no-pie +BINARY = fibonacci +ASM_OBJS = fibonacci.o +C_OBJS = main.o + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.asm + $(AS) $(ASFLAGS) -o $@ $< + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +$(BINARY): $(C_OBJS) $(ASM_OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(ASM_OBJS) $(C_OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/README.md b/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/README.md new file mode 100644 index 00000000..c4adfa5a --- /dev/null +++ b/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/README.md @@ -0,0 +1,62 @@ +# Call Assembly Fibonacci Function + +This demo is the reverse of `12-fibonacci-from-as-to-c`: the `fibonacci()` function is implemented in assembly and called from C. + +## Contents + +* `fibonacci.asm`: Assembly implementation of the recursive `fibonacci()` function +* `fibonacci.h`: Header file that declares `fibonacci()` +* `main.c`: C `main` that reads N, calls `fibonacci(N)`, and prints the result +* `Makefile`: Builds the `fibonacci` binary from all source files +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `fibonacci` binary. + +## Run + +```console +./fibonacci +``` + +Example interaction: + +```text +Introduce N: 10 +Fibonacci(10) = 89 +``` + +## Understand + +The assembly `fibonacci` uses push/pop to preserve `rdi` (N) and `rax` (intermediate result) across recursive calls: + +```nasm +fibonacci: + cmp rdi, 2 + jge .continue + + mov rax, 1 ; base case + jmp .out + +.continue: + dec rdi + push rdi ; save N-1 before first recursive call + call fibonacci ; rax = fibonacci(N-1) + pop rdi ; restore rdi = N-1 + + push rax ; save fibonacci(N-1) + dec rdi ; rdi = N-2 + call fibonacci ; rax = fibonacci(N-2) + + pop rdx ; rdx = fibonacci(N-1) + add rax, rdx ; rax = fibonacci(N) +.out: + ret +``` + +The order of `pop rdi` (before `push rax`) is critical: it must happen before the second `dec rdi` so that `rdi` contains the correct `N-1` value to decrement to `N-2`. diff --git a/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/fibonacci.asm b/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/fibonacci.asm new file mode 100644 index 00000000..18e658f1 --- /dev/null +++ b/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/fibonacci.asm @@ -0,0 +1,39 @@ +; SPDX-License-Identifier: BSD-3-Clause + +section .note.GNU-stack noalloc noexec nowrite progbits + + +section .text + +global fibonacci + + +; unsigned long fibonacci(unsigned long N) +; Returns 1 for N < 2, fibonacci(N-1) + fibonacci(N-2) otherwise. +; Argument N is in rdi. Return value in rax. +fibonacci: + cmp rdi, 2 + jge .continue + + ; Base case: N < 2 → return 1. + mov rax, 1 + jmp .out + +.continue: + ; Save N-1 on the stack and compute fibonacci(N-1). + dec rdi + push rdi + call fibonacci + pop rdi ; restore rdi = N-1 + + ; Save fibonacci(N-1) and compute fibonacci(N-2). + push rax + dec rdi + call fibonacci + + ; rax = fibonacci(N-2); add saved fibonacci(N-1). + pop rdx + add rax, rdx + +.out: + ret diff --git a/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/fibonacci.h b/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/fibonacci.h new file mode 100644 index 00000000..0c1c5e1b --- /dev/null +++ b/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/fibonacci.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef FIBONACCI_H +#define FIBONACCI_H + +/* Declaration of the assembly fibonacci() function. */ +unsigned long fibonacci(unsigned long N); + +#endif /* FIBONACCI_H */ diff --git a/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/main.c b/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/main.c new file mode 100644 index 00000000..0fa325b7 --- /dev/null +++ b/curs/chap-08-C-asm/13-fibonacci-from-c-to-as/main.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include + +#include "fibonacci.h" + +int main(void) +{ + unsigned long N; + + printf("Introduce N: "); + scanf("%lu", &N); + printf("Fibonacci(%lu) = %lu\n", N, fibonacci(N)); + + return 0; +} diff --git a/curs/chap-08-C-asm/14-inline-as-cpuid/.gitignore b/curs/chap-08-C-asm/14-inline-as-cpuid/.gitignore new file mode 100644 index 00000000..a396bc19 --- /dev/null +++ b/curs/chap-08-C-asm/14-inline-as-cpuid/.gitignore @@ -0,0 +1 @@ +/cpuid diff --git a/curs/chap-08-C-asm/14-inline-as-cpuid/Makefile b/curs/chap-08-C-asm/14-inline-as-cpuid/Makefile new file mode 100644 index 00000000..a1be51e9 --- /dev/null +++ b/curs/chap-08-C-asm/14-inline-as-cpuid/Makefile @@ -0,0 +1,23 @@ +CC = gcc +CPPFLAGS = -U_FORTIFY_SOURCE +CFILES = main.c +OBJS = $(CFILES:.c=.o) +BINARY = cpuid +CFLAGS = -fno-PIC -fcf-protection=none -fno-stack-protector -O1 -fno-inline -fno-omit-frame-pointer -Wall -Wno-unused-result -g +LD = gcc +LDFLAGS = -no-pie + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +$(BINARY): $(OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/14-inline-as-cpuid/README.md b/curs/chap-08-C-asm/14-inline-as-cpuid/README.md new file mode 100644 index 00000000..b3982a5a --- /dev/null +++ b/curs/chap-08-C-asm/14-inline-as-cpuid/README.md @@ -0,0 +1,54 @@ +# Inline Assembly: cpuid + +This demo calls the `cpuid` assembly instruction directly from C source code using GCC inline assembly syntax. +`cpuid` returns processor identification and feature information in registers `eax`, `ebx`, `ecx`, `edx`. + +## Contents + +* `main.c`: C source that calls `cpuid` via inline assembly and prints the result +* `Makefile`: Builds the `cpuid` binary +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `cpuid` binary. + +## Run + +```console +./cpuid +``` + +Example output on an Intel processor: + +```text +cpuid leaf 0: + eax = 0x00000023 + ebx = 0x756e6547 + ecx = 0x6c65746e + edx = 0x49656e69 +Vendor: GenuineIntel +``` + +## Understand + +GCC inline assembly uses the `__asm__` keyword with constraints to describe inputs, outputs, and clobbered registers: + +```c +__asm__ volatile ( + "cpuid" + : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) + : "a"(0) +); +``` + +* `"=a"(eax)` — output constraint: write `eax` register into C variable `eax`. +* `"a"(0)` — input constraint: load the value `0` into `eax` before the instruction. +* `volatile` — prevent the compiler from reordering or eliminating the instruction. + +The leaf number in `eax` selects which information `cpuid` returns. +Leaf 0 returns the maximum supported leaf number in `eax` and the vendor string split across `ebx`, `edx`, `ecx` (in that order). diff --git a/curs/chap-08-C-asm/14-inline-as-cpuid/main.c b/curs/chap-08-C-asm/14-inline-as-cpuid/main.c new file mode 100644 index 00000000..19503331 --- /dev/null +++ b/curs/chap-08-C-asm/14-inline-as-cpuid/main.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: BSD-3-Clause + +/* Inline assembly example: call the cpuid instruction from C. + * cpuid returns processor identification and feature information. + * Input: eax = leaf (function number) + * Output: eax, ebx, ecx, edx contain the result. + */ + +#include + +int main(void) +{ + unsigned int eax, ebx, ecx, edx; + + /* Call cpuid with leaf 0 (vendor string). */ + __asm__ volatile ( + "cpuid" + : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) + : "a"(0) + ); + + printf("cpuid leaf 0:\n"); + printf(" eax = 0x%08x\n", eax); + printf(" ebx = 0x%08x\n", ebx); + printf(" ecx = 0x%08x\n", ecx); + printf(" edx = 0x%08x\n", edx); + + /* The vendor string is in ebx, edx, ecx (in that order). */ + printf("Vendor: %.4s%.4s%.4s\n", + (char *)&ebx, (char *)&edx, (char *)&ecx); + + return 0; +} diff --git a/curs/chap-08-C-asm/15-inline-as-rdtsc/.gitignore b/curs/chap-08-C-asm/15-inline-as-rdtsc/.gitignore new file mode 100644 index 00000000..2bc3764d --- /dev/null +++ b/curs/chap-08-C-asm/15-inline-as-rdtsc/.gitignore @@ -0,0 +1 @@ +/rdtsc diff --git a/curs/chap-08-C-asm/15-inline-as-rdtsc/Makefile b/curs/chap-08-C-asm/15-inline-as-rdtsc/Makefile new file mode 100644 index 00000000..6ae5ce04 --- /dev/null +++ b/curs/chap-08-C-asm/15-inline-as-rdtsc/Makefile @@ -0,0 +1,23 @@ +CC = gcc +CPPFLAGS = -U_FORTIFY_SOURCE +CFILES = main.c +OBJS = $(CFILES:.c=.o) +BINARY = rdtsc +CFLAGS = -fno-PIC -fcf-protection=none -fno-stack-protector -O1 -fno-inline -fno-omit-frame-pointer -Wall -Wno-unused-result -g +LD = gcc +LDFLAGS = -no-pie + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +$(BINARY): $(OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/15-inline-as-rdtsc/README.md b/curs/chap-08-C-asm/15-inline-as-rdtsc/README.md new file mode 100644 index 00000000..f96ef656 --- /dev/null +++ b/curs/chap-08-C-asm/15-inline-as-rdtsc/README.md @@ -0,0 +1,52 @@ +# Inline Assembly: rdtsc + +This demo calls the `rdtsc` (Read Time-Stamp Counter) assembly instruction directly from C source code using GCC inline assembly syntax. +`rdtsc` returns the 64-bit CPU cycle counter split across `edx` (high 32 bits) and `eax` (low 32 bits). + +## Contents + +* `main.c`: C source that calls `rdtsc` via inline assembly and prints the TSC value +* `Makefile`: Builds the `rdtsc` binary +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `rdtsc` binary. + +## Run + +```console +./rdtsc +``` + +Example output (the value depends on when the binary is run): + +```text +TSC value: 511044251076362 +``` + +## Understand + +`rdtsc` places the high 32 bits of the counter in `edx` and the low 32 bits in `eax`. +The inline assembly reads both and combines them into a 64-bit C variable: + +```c +uint32_t lo, hi; + +__asm__ volatile ( + "rdtsc" + : "=a"(lo), "=d"(hi) +); + +uint64_t tsc = ((uint64_t)hi << 32) | lo; +``` + +* `"=a"(lo)` — output constraint: write `eax` into `lo`. +* `"=d"(hi)` — output constraint: write `edx` into `hi`. + +The TSC increments at a fixed rate (the nominal CPU frequency on modern processors) and can be used to measure elapsed time between two readings. +See demo `16-inline-measure` for a measurement example. diff --git a/curs/chap-08-C-asm/15-inline-as-rdtsc/main.c b/curs/chap-08-C-asm/15-inline-as-rdtsc/main.c new file mode 100644 index 00000000..ee3a0539 --- /dev/null +++ b/curs/chap-08-C-asm/15-inline-as-rdtsc/main.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: BSD-3-Clause + +/* Inline assembly example: call the rdtsc instruction from C. + * rdtsc (Read Time-Stamp Counter) returns the number of CPU cycles + * since reset in edx:eax (high 32 bits in edx, low 32 bits in eax). + */ + +#include +#include + +int main(void) +{ + uint32_t lo, hi; + uint64_t tsc; + + /* Call rdtsc; result in edx:eax. */ + __asm__ volatile ( + "rdtsc" + : "=a"(lo), "=d"(hi) + ); + + tsc = ((uint64_t)hi << 32) | lo; + printf("TSC value: %lu\n", tsc); + + return 0; +} diff --git a/curs/chap-08-C-asm/16-inline-measure/.gitignore b/curs/chap-08-C-asm/16-inline-measure/.gitignore new file mode 100644 index 00000000..cab4ee5e --- /dev/null +++ b/curs/chap-08-C-asm/16-inline-measure/.gitignore @@ -0,0 +1 @@ +/measure diff --git a/curs/chap-08-C-asm/16-inline-measure/Makefile b/curs/chap-08-C-asm/16-inline-measure/Makefile new file mode 100644 index 00000000..39dffed1 --- /dev/null +++ b/curs/chap-08-C-asm/16-inline-measure/Makefile @@ -0,0 +1,23 @@ +CC = gcc +CPPFLAGS = -U_FORTIFY_SOURCE +CFILES = main.c +OBJS = $(CFILES:.c=.o) +BINARY = measure +CFLAGS = -fno-PIC -fcf-protection=none -fno-stack-protector -O1 -fno-inline -fno-omit-frame-pointer -Wall -Wno-unused-result -g +LD = gcc +LDFLAGS = -no-pie + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +$(BINARY): $(OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/16-inline-measure/README.md b/curs/chap-08-C-asm/16-inline-measure/README.md new file mode 100644 index 00000000..df2c510f --- /dev/null +++ b/curs/chap-08-C-asm/16-inline-measure/README.md @@ -0,0 +1,54 @@ +# Inline Assembly: Measure with rdtsc + +This demo uses `rdtsc` called twice from inline assembly to measure the CPU cycles consumed by a `fibonacci()` function call. + +## Contents + +* `main.c`: C source with inline-assembly `rdtsc` wrapper and measurement of `fibonacci()` +* `Makefile`: Builds the `measure` binary +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `measure` binary. + +## Run + +```console +./measure +``` + +Example interaction: + +```text +Introduce N: 10 +fibonacci(10) took 3266 cycles +``` + +## Understand + +The measurement pattern reads the TSC before and after the function call and subtracts: + +```c +static uint64_t read_tsc(void) +{ + uint32_t lo, hi; + __asm__ volatile ("rdtsc" : "=a"(lo), "=d"(hi)); + return ((uint64_t)hi << 32) | lo; +} + +uint64_t start = read_tsc(); +fibonacci(N); +uint64_t end = read_tsc(); + +printf("fibonacci(%lu) took %lu cycles\n", N, end - start); +``` + +The cycle count includes the function call overhead and all recursive calls inside `fibonacci`. +For small `N` (e.g. 10) the count is on the order of a few thousand cycles; for `N=30` the recursive version can take millions of cycles due to its exponential time complexity. + +See demo `17-measure-as-func` for the same measurement using a reusable assembly `read_cycles()` function. diff --git a/curs/chap-08-C-asm/16-inline-measure/main.c b/curs/chap-08-C-asm/16-inline-measure/main.c new file mode 100644 index 00000000..e067064b --- /dev/null +++ b/curs/chap-08-C-asm/16-inline-measure/main.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: BSD-3-Clause + +/* Inline assembly example: use rdtsc to measure cycles taken by fibonacci(). */ + +#include +#include + +static unsigned long fibonacci(unsigned long N) +{ + if (N < 2) + return 1; + return fibonacci(N - 1) + fibonacci(N - 2); +} + +static uint64_t read_tsc(void) +{ + uint32_t lo, hi; + + __asm__ volatile ( + "rdtsc" + : "=a"(lo), "=d"(hi) + ); + return ((uint64_t)hi << 32) | lo; +} + +int main(void) +{ + unsigned long N; + uint64_t start, end; + + printf("Introduce N: "); + scanf("%lu", &N); + + start = read_tsc(); + fibonacci(N); + end = read_tsc(); + + printf("fibonacci(%lu) took %lu cycles\n", N, end - start); + + return 0; +} diff --git a/curs/chap-08-C-asm/17-measure-as-func/.gitignore b/curs/chap-08-C-asm/17-measure-as-func/.gitignore new file mode 100644 index 00000000..cab4ee5e --- /dev/null +++ b/curs/chap-08-C-asm/17-measure-as-func/.gitignore @@ -0,0 +1 @@ +/measure diff --git a/curs/chap-08-C-asm/17-measure-as-func/Makefile b/curs/chap-08-C-asm/17-measure-as-func/Makefile new file mode 100644 index 00000000..c901d47a --- /dev/null +++ b/curs/chap-08-C-asm/17-measure-as-func/Makefile @@ -0,0 +1,28 @@ +AS = nasm +CC = gcc +CPPFLAGS = -U_FORTIFY_SOURCE +ASFLAGS = -f elf64 -g -F dwarf +CFLAGS = -fno-PIC -fcf-protection=none -fno-stack-protector -O1 -fno-inline -fno-omit-frame-pointer -Wall -Wno-unused-result -g +LD = gcc +LDFLAGS = -no-pie +BINARY = measure +ASM_OBJS = read_cycles.o +C_OBJS = main.o + +.PHONY: all clean + +all: $(BINARY) + +%.o: %.asm + $(AS) $(ASFLAGS) -o $@ $< + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +$(BINARY): $(C_OBJS) $(ASM_OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + -rm -f $(ASM_OBJS) $(C_OBJS) + -rm -f $(BINARY) + -rm -f *~ diff --git a/curs/chap-08-C-asm/17-measure-as-func/README.md b/curs/chap-08-C-asm/17-measure-as-func/README.md new file mode 100644 index 00000000..983c9240 --- /dev/null +++ b/curs/chap-08-C-asm/17-measure-as-func/README.md @@ -0,0 +1,59 @@ +# rdtsc in Separate Assembly File + +This demo factors the `rdtsc` measurement into a reusable `read_cycles()` function implemented in a separate assembly file. +It measures the CPU cycles consumed by a `fibonacci()` call and prints the result. + +## Contents + +* `read_cycles.asm`: Assembly implementation of `read_cycles()` that returns the current CPU cycle count +* `read_cycles.h`: Header file that declares `read_cycles()` +* `main.c`: C `main` that calls `read_cycles()` twice around `fibonacci()` to measure elapsed cycles +* `Makefile`: Builds the `measure` binary from all source files +* `README.md`: This file + +## Build + +```console +make +``` + +This creates the `measure` binary. + +## Run + +```console +./measure +``` + +Example interaction: + +```text +Introduce N: 10 +fibonacci(10) took 3596 cycles +``` + +## Understand + +`read_cycles()` in assembly is straightforward — it reads `rdtsc`, combines `edx:eax` into a 64-bit `rax`, and returns: + +```nasm +read_cycles: + rdtsc ; edx:eax = timestamp counter + shl rdx, 32 ; shift high 32 bits up + or rax, rdx ; combine into 64-bit value + ret ; return in rax +``` + +In `main.c` the function is called like any other C function: + +```c +#include "read_cycles.h" + +uint64_t start = read_cycles(); +fibonacci(N); +uint64_t end = read_cycles(); + +printf("fibonacci(%lu) took %lu cycles\n", N, end - start); +``` + +Factoring the measurement into a named function makes the intent clear and allows reuse across multiple measurement sites without duplicating the inline assembly. diff --git a/curs/chap-08-C-asm/17-measure-as-func/main.c b/curs/chap-08-C-asm/17-measure-as-func/main.c new file mode 100644 index 00000000..b04cbc11 --- /dev/null +++ b/curs/chap-08-C-asm/17-measure-as-func/main.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include +#include + +#include "read_cycles.h" + +static unsigned long fibonacci(unsigned long N) +{ + if (N < 2) + return 1; + return fibonacci(N - 1) + fibonacci(N - 2); +} + +int main(void) +{ + unsigned long N; + uint64_t start, end; + + printf("Introduce N: "); + scanf("%lu", &N); + + start = read_cycles(); + fibonacci(N); + end = read_cycles(); + + printf("fibonacci(%lu) took %lu cycles\n", N, end - start); + + return 0; +} diff --git a/curs/chap-08-C-asm/17-measure-as-func/read_cycles.asm b/curs/chap-08-C-asm/17-measure-as-func/read_cycles.asm new file mode 100644 index 00000000..8d590f44 --- /dev/null +++ b/curs/chap-08-C-asm/17-measure-as-func/read_cycles.asm @@ -0,0 +1,18 @@ +; SPDX-License-Identifier: BSD-3-Clause + +section .note.GNU-stack noalloc noexec nowrite progbits + + +section .text + +global read_cycles + + +; uint64_t read_cycles(void) +; Returns the current CPU cycle count read via the rdtsc instruction. +; Result: high 32 bits in edx, low 32 bits in eax; combined into rax. +read_cycles: + rdtsc ; edx:eax = current timestamp counter + shl rdx, 32 ; shift high 32 bits to upper half of rdx + or rax, rdx ; combine into 64-bit value in rax + ret diff --git a/curs/chap-08-C-asm/17-measure-as-func/read_cycles.h b/curs/chap-08-C-asm/17-measure-as-func/read_cycles.h new file mode 100644 index 00000000..c0501ff7 --- /dev/null +++ b/curs/chap-08-C-asm/17-measure-as-func/read_cycles.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef READ_CYCLES_H +#define READ_CYCLES_H + +#include + +/* Declaration of the assembly read_cycles() function. + * Returns the current CPU cycle count via the rdtsc instruction. + */ +uint64_t read_cycles(void); + +#endif /* READ_CYCLES_H */ diff --git a/curs/chap-08-C-asm/README.md b/curs/chap-08-C-asm/README.md new file mode 100644 index 00000000..39b4894c --- /dev/null +++ b/curs/chap-08-C-asm/README.md @@ -0,0 +1,551 @@ +# Chapter 08: Application Binary Interface and C-Assembly Interaction + +The Application Binary Interface (ABI) defines the low-level contract between separately compiled program components — how functions are called, how arguments and return values are exchanged, which registers each side must preserve, and how the stack is managed. + +This chapter focuses on the **System V AMD64 ABI** used on Linux x86-64 and demonstrates how assembly and C code can call each other seamlessly through 17 progressive demos. + +![Chapter 08 Overview](overview.png) + +--- + +## Table of Contents + +1. [The Application Binary Interface](#1-the-application-binary-interface) +1. [Calling Convention Summary](#2-calling-convention-summary) +1. [Demo 01: Call rand()](#demo-01-call-rand) +1. [Demo 02: Call srand() and rand()](#demo-02-call-srand-and-rand) +1. [Demo 03: Call srand(), rand(), and printf()](#demo-03-call-srand-rand-and-printf) +1. [Demo 04: C Reference — Disassembly Comparison](#demo-04-c-reference--disassembly-comparison) +1. [Calling Between C and Assembly](#3-calling-between-c-and-assembly) +1. [Demo 05: Assembly Calls C add()](#demo-05-assembly-calls-c-add) +1. [Demo 06: C Calls Assembly add()](#demo-06-c-calls-assembly-add) +1. [Demo 07: C Calls Assembly add() (with Header File)](#demo-07-c-calls-assembly-add-with-header-file) +1. [Passing Arrays Across Languages](#4-passing-arrays-across-languages) +1. [Demo 08: Assembly Calls C sum_array()](#demo-08-assembly-calls-c-sum_array) +1. [Demo 09: C Calls Assembly sum_array()](#demo-09-c-calls-assembly-sum_array) +1. [Functions with More Than Six Arguments](#5-functions-with-more-than-six-arguments) +1. [Demo 10: Assembly Calls C sum2()–sum8()](#demo-10-assembly-calls-c-sum2sum8) +1. [Demo 11: C Calls Assembly sum2()–sum8()](#demo-11-c-calls-assembly-sum2sum8) +1. [Recursive Functions Across Languages](#6-recursive-functions-across-languages) +1. [Demo 12: Assembly Calls C fibonacci()](#demo-12-assembly-calls-c-fibonacci) +1. [Demo 13: C Calls Assembly fibonacci()](#demo-13-c-calls-assembly-fibonacci) +1. [Inline Assembly](#7-inline-assembly) +1. [Demo 14: Inline cpuid](#demo-14-inline-cpuid) +1. [Demo 15: Inline rdtsc](#demo-15-inline-rdtsc) +1. [Demo 16: Measuring with Inline rdtsc](#demo-16-measuring-with-inline-rdtsc) +1. [Demo 17: read_cycles() in a Separate Assembly File](#demo-17-read_cycles-in-a-separate-assembly-file) +1. [Summary](#summary) + +--- + +## 1. The Application Binary Interface + +When two separately compiled modules (e.g. a `.c` file and a `.asm` file) are linked together and one calls a function in the other, they must agree on: + +* **Which registers hold arguments** — so the caller loads registers the callee will read. +* **Which register holds the return value** — so the callee writes it and the caller reads it. +* **Which registers each side may freely modify** (*caller-saved*) and which must be left unchanged (*callee-saved*). +* **Stack alignment** — `rsp` must be 16-byte aligned at the point of a `call` instruction. +* **Stack argument layout** — when there are more than six arguments, the extras are placed on the stack in a defined order. + +This contract is the **System V AMD64 ABI**, the standard for Linux x86-64. + +--- + +## 2. Calling Convention Summary + +![System V AMD64 ABI Calling Convention](abi-calling-convention.png) + +### Argument Registers + +| Argument | Register | +|----------|----------| +| 1st | `rdi` | +| 2nd | `rsi` | +| 3rd | `rdx` | +| 4th | `rcx` | +| 5th | `r8` | +| 6th | `r9` | +| 7th+ | stack (right-to-left, i.e. pushed in reverse order) | + +### Return Value + +Integer and pointer return values are passed in **`rax`**. + +### Register Preservation + +**Caller-saved** (the callee may overwrite these freely): +`rax`, `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`, `r10`, `r11` + +**Callee-saved** (the callee must save and restore these if it uses them): +`rbx`, `rbp`, `r12`, `r13`, `r14`, `r15` + +### Variadic Functions (`printf`, `scanf`) + +Before calling a variadic function, **`rax` must be set to the number of floating-point arguments** passed in vector (XMM/YMM) registers. +When no floating-point arguments are passed, use: + +```nasm +xor rax, rax ; 0 floating-point arguments +call printf +``` + +--- + +## Demo 01: Call rand() + +**Directory:** [`01-rand/`](01-rand/) + +The simplest possible call: `rand()` takes no arguments and returns a value in `rax`. + +```nasm +call rand ; rax = random number + +lea rdi, [printf_format] +mov rsi, rax +xor rax, rax +call printf +``` + +**Build and run:** + +```console +cd 01-rand && make && ./rand +``` + +Output: `Random number: 1804289383` (fixed — no seed set) + +--- + +## Demo 02: Call srand() and rand() + +**Directory:** [`02-rand-srand/`](02-rand-srand/) + +Chains `time()` → `srand()` → `rand()`, showing three different call patterns: + +```nasm +xor rdi, rdi ; NULL → time(NULL) +call time ; rax = epoch time + +mov rdi, rax ; seed → srand(seed) — returns void +call srand + +call rand ; rax = seeded random number +``` + +**Build and run:** + +```console +cd 02-rand-srand && make && ./rand_srand +``` + +--- + +## Demo 03: Call srand(), rand(), and printf() + +**Directory:** [`03-rand-srand-printf/`](03-rand-srand-printf/) + +Extends demo 02 to also call `printf()`. +Key point: `xor rax, rax` is required before every call to a variadic function. + +**Build and run:** + +```console +cd 03-rand-srand-printf && make && ./rand_srand_printf +``` + +--- + +## Demo 04: C Reference — Disassembly Comparison + +**Directory:** [`04-rand-srand-printf-c/`](04-rand-srand-printf-c/) + +The identical program written in C. +Disassemble it to compare with the hand-written assembly: + +```console +cd 04-rand-srand-printf-c && make +objdump -d -M intel rand_srand_printf | grep -A 30 '
' +``` + +--- + +## 3. Calling Between C and Assembly + +The linker treats object files identically regardless of whether they were compiled from C or assembled from NASM. +As long as a function's symbol is `global` (in assembly) or declared with proper prototype (in C), and both sides follow the System V ABI, the call succeeds. + +--- + +## Demo 05: Assembly Calls C add() + +**Directory:** [`05-sum2-from-as-to-c/`](05-sum2-from-as-to-c/) + +`add.c` provides `long add(long a, long b)`. +`main.asm` calls it: + +```nasm +extern add + +mov rdi, 2 +mov rsi, 3 +call add ; rax = 5 +``` + +**Build and run:** + +```console +cd 05-sum2-from-as-to-c && make && ./sum2 +``` + +Output: `Sum is: 5` + +--- + +## Demo 06: C Calls Assembly add() + +**Directory:** [`06-sum2-from-c-to-as/`](06-sum2-from-c-to-as/) + +`add.asm` implements `add` following the ABI. +`main.c` calls it with an inline declaration: + +```c +long add(long a, long b); +printf("Sum is: %ld\n", add(2, 3)); +``` + +**Build and run:** + +```console +cd 06-sum2-from-c-to-as && make && ./sum2 +``` + +--- + +## Demo 07: C Calls Assembly add() (with Header File) + +**Directory:** [`07-sum2-from-c-to-as-header/`](07-sum2-from-c-to-as-header/) + +Same as demo 06 but the declaration is in `add.h`. +This is best practice — all callers `#include "add.h"` instead of redeclaring the function. + +**Build and run:** + +```console +cd 07-sum2-from-c-to-as-header && make && ./sum2 +``` + +--- + +## 4. Passing Arrays Across Languages + +An array is passed as two arguments: a **pointer to the first element** (`rdi`) and the **element count** (`rsi`). +Neither C nor assembly has any built-in way to determine the length of an array from a pointer alone. + +--- + +## Demo 08: Assembly Calls C sum_array() + +**Directory:** [`08-sum-array-from-as-to-c/`](08-sum-array-from-as-to-c/) + +```nasm +mov rdi, num_array ; pointer to array +mov rsi, len ; number of elements +call sum_array ; rax = sum +``` + +**Build and run:** + +```console +cd 08-sum-array-from-as-to-c && make && ./sum_array +``` + +Output: `Sum of array elements is: 360` + +--- + +## Demo 09: C Calls Assembly sum_array() + +**Directory:** [`09-sum-array-from-c-to-as/`](09-sum-array-from-c-to-as/) + +```c +#include "sum_array.h" +result = sum_array(num_array, sizeof(num_array) / sizeof(num_array[0])); +``` + +**Build and run:** + +```console +cd 09-sum-array-from-c-to-as && make && ./sum_array +``` + +--- + +## 5. Functions with More Than Six Arguments + +The first six integer arguments go in registers; the seventh and beyond are placed on the stack. +The caller pushes them in **reverse order** (last argument first) so that argument 7 is at `[rsp+8]` after the `call` pushes the return address. + +--- + +## Demo 10: Assembly Calls C sum2()–sum8() + +**Directory:** [`10-call-n-args-from-as-to-c/`](10-call-n-args-from-as-to-c/) + +Shows the full register sequence plus stack placement for 7 and 8 arguments: + +```nasm +push qword 8 ; 8th argument (pushed first) +push qword 7 ; 7th argument +mov rdi, 1 +; ... rsi=2, rdx=3, rcx=4, r8=5, r9=6 ... +call sum8 +add rsp, 16 ; clean up the two stack arguments +``` + +**Build and run:** + +```console +cd 10-call-n-args-from-as-to-c && make && ./call_n_args +``` + +--- + +## Demo 11: C Calls Assembly sum2()–sum8() + +**Directory:** [`11-call-n-args-from-c-to-as/`](11-call-n-args-from-c-to-as/) + +The assembly `sum7` and `sum8` access stack arguments at `[rsp+8]` and `[rsp+16]` because the return address occupies `[rsp+0]`: + +```nasm +sum8: + mov rax, rdi + add rax, rsi + ; ... rdx, rcx, r8, r9 ... + add rax, [rsp+8] ; 7th argument + add rax, [rsp+16] ; 8th argument + ret +``` + +**Build and run:** + +```console +cd 11-call-n-args-from-c-to-as && make && ./call_n_args +``` + +--- + +## 6. Recursive Functions Across Languages + +Recursive functions work across language boundaries without any special treatment — the ABI is followed by each individual call, and the call stack grows and unwinds normally. + +--- + +## Demo 12: Assembly Calls C fibonacci() + +**Directory:** [`12-fibonacci-from-as-to-c/`](12-fibonacci-from-as-to-c/) + +`main.asm` uses a local stack variable to hold N between calls to `printf`/`scanf` and `fibonacci`: + +```nasm +sub rsp, 16 ; N at [rbp-16] +; ... scanf into [rbp-16] ... +mov rdi, [rbp-16] +call fibonacci ; rax = fibonacci(N) +``` + +**Build and run:** + +```console +cd 12-fibonacci-from-as-to-c && make +echo 10 | ./fibonacci +``` + +Output: `Fibonacci(10) = 89` + +--- + +## Demo 13: C Calls Assembly fibonacci() + +**Directory:** [`13-fibonacci-from-c-to-as/`](13-fibonacci-from-c-to-as/) + +The assembly `fibonacci` saves `rdi` and `rax` on the stack at each level of recursion: + +```nasm +.continue: + dec rdi + push rdi ; save N-1 + call fibonacci ; compute fibonacci(N-1) + pop rdi ; restore N-1 + + push rax ; save fibonacci(N-1) + dec rdi + call fibonacci ; compute fibonacci(N-2) + + pop rdx + add rax, rdx ; fibonacci(N) = fibonacci(N-1) + fibonacci(N-2) +``` + +**Build and run:** + +```console +cd 13-fibonacci-from-c-to-as && make +echo 10 | ./fibonacci +``` + +--- + +## 7. Inline Assembly + +GCC provides `__asm__` to embed assembly instructions directly inside C source. +This is useful for accessing CPU instructions not exposed by C (such as `cpuid` and `rdtsc`). + +![GCC Inline Assembly Syntax](inline-assembly.png) + +The general form is: + +```c +__asm__ volatile ( + "instruction" + : output_constraints + : input_constraints + : clobbered_registers +); +``` + +--- + +## Demo 14: Inline cpuid + +**Directory:** [`14-inline-as-cpuid/`](14-inline-as-cpuid/) + +```c +__asm__ volatile ( + "cpuid" + : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) + : "a"(0) +); +``` + +`"=a"(eax)` means: write the `eax` register into C variable `eax` after the instruction. +`"a"(0)` means: load the value `0` into `eax` before the instruction. + +**Build and run:** + +```console +cd 14-inline-as-cpuid && make && ./cpuid +``` + +Output (on an Intel processor): `Vendor: GenuineIntel` + +--- + +## Demo 15: Inline rdtsc + +**Directory:** [`15-inline-as-rdtsc/`](15-inline-as-rdtsc/) + +```c +uint32_t lo, hi; +__asm__ volatile ("rdtsc" : "=a"(lo), "=d"(hi)); +uint64_t tsc = ((uint64_t)hi << 32) | lo; +``` + +`rdtsc` writes the 64-bit Time-Stamp Counter into `edx:eax`. +The two halves are then combined in C. + +**Build and run:** + +```console +cd 15-inline-as-rdtsc && make && ./rdtsc +``` + +--- + +## Demo 16: Measuring with Inline rdtsc + +**Directory:** [`16-inline-measure/`](16-inline-measure/) + +Read the TSC before and after `fibonacci(N)` and subtract to get elapsed cycles: + +```c +uint64_t start = read_tsc(); +fibonacci(N); +uint64_t end = read_tsc(); +printf("fibonacci(%lu) took %lu cycles\n", N, end - start); +``` + +**Build and run:** + +```console +cd 16-inline-measure && make +echo 10 | ./measure +``` + +--- + +## Demo 17: read_cycles() in a Separate Assembly File + +**Directory:** [`17-measure-as-func/`](17-measure-as-func/) + +The inline assembly is factored into a reusable `read_cycles()` function in `read_cycles.asm`: + +```nasm +read_cycles: + rdtsc ; edx:eax = timestamp counter + shl rdx, 32 + or rax, rdx ; combine into 64-bit rax + ret +``` + +C code calls it like any other function: + +```c +#include "read_cycles.h" + +uint64_t start = read_cycles(); +fibonacci(N); +uint64_t end = read_cycles(); +``` + +**Build and run:** + +```console +cd 17-measure-as-func && make +echo 10 | ./measure +``` + +--- + +## Summary + +| Demo | Direction | Concept | +|------|-----------|---------| +| `01-rand` | ASM → C lib | No-argument call; return value in `rax` | +| `02-rand-srand` | ASM → C lib | `void` return; `NULL` argument; chained calls | +| `03-rand-srand-printf` | ASM → C lib | Variadic call; `xor rax, rax` before `printf` | +| `04-rand-srand-printf-c` | C reference | Disassembly comparison with hand-written ASM | +| `05-sum2-from-as-to-c` | ASM → C | Two-argument call; `extern` declaration | +| `06-sum2-from-c-to-as` | C → ASM | `global` symbol; inline declaration | +| `07-sum2-from-c-to-as-header` | C → ASM | Header file best practice | +| `08-sum-array-from-as-to-c` | ASM → C | Array as pointer + count | +| `09-sum-array-from-c-to-as` | C → ASM | Assembly array traversal | +| `10-call-n-args-from-as-to-c` | ASM → C | 7th and 8th args pushed on stack (reversed) | +| `11-call-n-args-from-c-to-as` | C → ASM | Stack args at `[rsp+8]`, `[rsp+16]` | +| `12-fibonacci-from-as-to-c` | ASM → C | Recursive call; local var to preserve N | +| `13-fibonacci-from-c-to-as` | C → ASM | Recursive ASM; push/pop for register save | +| `14-inline-as-cpuid` | Inline | `__asm__` with input/output constraints | +| `15-inline-as-rdtsc` | Inline | `rdtsc`; `edx:eax` → 64-bit value | +| `16-inline-measure` | Inline | TSC before/after measurement | +| `17-measure-as-func` | C → ASM | `rdtsc` factored into reusable ASM function | + +### Key Takeaways + +* **The System V AMD64 ABI** is the shared contract that makes C and assembly code interoperable on Linux x86-64. +* **Argument registers** (`rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`) and the **return register** (`rax`) are the same regardless of whether the caller or callee is written in C or assembly. +* **Arguments beyond the sixth** are placed on the stack, pushed in reverse order; the callee accesses them at `[rsp+8]`, `[rsp+16]`, … (because `[rsp+0]` holds the return address). +* **Caller-saved registers** may be freely overwritten by the callee; **callee-saved registers** must be preserved. +* **Variadic functions** (`printf`, `scanf`) require `rax = 0` before the call to indicate no floating-point arguments. +* **Header files** are the correct mechanism for sharing function declarations between C and assembly; they prevent mismatches and make maintenance easier. +* **Inline assembly** (`__asm__`) allows individual CPU instructions to be used from within C code without a separate assembly file, at the cost of using GCC's constraint syntax instead of plain NASM. + +![Chapter 08 Overview](overview.png) diff --git a/curs/chap-08-C-asm/abi-calling-convention.png b/curs/chap-08-C-asm/abi-calling-convention.png new file mode 100644 index 00000000..10a01f31 Binary files /dev/null and b/curs/chap-08-C-asm/abi-calling-convention.png differ diff --git a/curs/chap-08-C-asm/abi-calling-convention.svg b/curs/chap-08-C-asm/abi-calling-convention.svg new file mode 100644 index 00000000..28d79fa1 --- /dev/null +++ b/curs/chap-08-C-asm/abi-calling-convention.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + System V AMD64 ABI: Argument Passing + How the caller passes arguments and the callee returns results + + + + Caller + + + + ; Prepare arguments + mov rdi, arg1 + mov rsi, arg2 + mov rdx, arg3 + mov rcx, arg4 + mov r8, arg5 + mov r9, arg6 + push arg8 ; 8th (reverse) + push arg7 ; 7th + + + call function + + + ; Return value is in rax + rax ← return value + Caller-saved (may be clobbered): + rax rdi rsi rdx rcx r8 r9 r10 r11 + + + + Arg → Register + + + + Arg + Register + + + 1st + rdi + + + 2nd + rsi + + + 3rd + rdx + + + 4th + rcx + + + 5th + r8 + + + 6th + r9 + + + 7th+ + stack + + + return + rax + + + + Callee + + + ; Receive arguments + ; rdi = arg1 + ; rsi = arg2 + ; rdx = arg3 + ; [rsp+8] = arg7 + ; [rsp+16] = arg8 + ; (after call, rsp + ; points at ret addr) + + + ; Return value + mov rax, result + ret + + Callee-saved (must preserve): + rbx rbp r12 r13 r14 r15 + + + + + + + + Stack (7th+ args) + higher addr → + + arg8 + + arg7 + ← lower addr (rsp after call) + diff --git a/curs/chap-08-C-asm/abi.png b/curs/chap-08-C-asm/abi.png new file mode 100644 index 00000000..0203ba11 Binary files /dev/null and b/curs/chap-08-C-asm/abi.png differ diff --git a/curs/chap-08-C-asm/abi.svg b/curs/chap-08-C-asm/abi.svg new file mode 100644 index 00000000..d13aca45 --- /dev/null +++ b/curs/chap-08-C-asm/abi.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + ABI: Application Binary Interface + + + Defines the runtime interaction between binary components — a contract enforced by the compiler + + + + Component A + (object file / library) + + // compiled binary + 48 89 e5 48 83 + ec 20 89 7d fc + 89 75 f8 8b 45 + fc 03 45 f8 5d + compiled by Compiler A + (e.g. GCC, Clang) + or written in ASM + or a different language + + + + Component B + (object file / library) + + // compiled binary + 55 48 89 e5 89 + 7d fc 89 75 f8 + 8b 55 fc 8b 45 + f8 01 d0 5d c3 + compiled by Compiler B + (e.g. MSVC, ICC) + or written in ASM + or a different language + + + + ABI Contract + both components must agree on: + + + + Calling Convention + how functions pass arguments and return values + + + Data Type Sizes + size of int, long, pointer; struct layout / padding + + + Executable / Object Format + ELF (Linux) · PE (Windows) · Mach-O (macOS) + + + Name Mangling + how symbol names are encoded (used in C++) + + + + + + + + The ABI is a binary-level contract — relevant at link time and runtime + + API → source-level contract + (compile time) + ABI → binary-level contract + (link time / runtime) + The compiler's job is to generate ABI-compatible code + diff --git a/curs/chap-08-C-asm/api-vs-abi.png b/curs/chap-08-C-asm/api-vs-abi.png new file mode 100644 index 00000000..56cd7723 Binary files /dev/null and b/curs/chap-08-C-asm/api-vs-abi.png differ diff --git a/curs/chap-08-C-asm/api-vs-abi.svg b/curs/chap-08-C-asm/api-vs-abi.svg new file mode 100644 index 00000000..74ff9400 --- /dev/null +++ b/curs/chap-08-C-asm/api-vs-abi.svg @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + API vs. ABI + + + + API — Application Programming Interface + + + ABI — Application Binary Interface + + + + VS + + + + What it is + Source-level contract between a caller + and a component (library / module) + + + What it is + Binary-level contract between compiled + components (object files, libraries) + + + + When + Compile time (writing & building source code) + + + When + Link time & runtime (binary execution) + + + + Expressed as + Header files (.h): function signatures, + data types, constants, macros + + + Expressed as + Calling convention, data sizes, struct layout, + executable format, name mangling + + + + Enforced by + The compiler (type checking, includes) + + + Enforced by + The compiler + linker + OS loader + + + + Breaking it causes + Compile error — caller must be updated + and recompiled against new API + + + Breaking it causes + Runtime crash / wrong behaviour — even if + the source compiles without errors + + + + Example + int add(int a, int b); // in mylib.h + caller #includes this declaration + + + Example + arg in RDI, return in RAX, stack 16-byte aligned + compiler generates machine code accordingly + + + + + + + A stable ABI lets you replace a library's implementation without recompiling callers — only the binary needs to be compatible + diff --git a/curs/chap-08-C-asm/api.png b/curs/chap-08-C-asm/api.png new file mode 100644 index 00000000..f053eaeb Binary files /dev/null and b/curs/chap-08-C-asm/api.png differ diff --git a/curs/chap-08-C-asm/api.svg b/curs/chap-08-C-asm/api.svg new file mode 100644 index 00000000..10a1cb62 --- /dev/null +++ b/curs/chap-08-C-asm/api.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + API: Application Programming Interface + + + + Caller + (application source code) + + + #include "mylib.h" + + int main() { + int r = add(3,4); + Point p; + p.x = MAX_SIZE; + ... + } + + + + uses + + + + API (mylib.h) + interface exposed to the programmer + + + + Function Signatures + int add(int a, int b); + void sort(int *arr, int n); + declarations of callable functions + + + + Data Type Declarations + typedef struct { + int x, y; + } Point; + + + + Constants / Macros + #define MAX_SIZE 1024 + #define PI 3.14159 + + + + backs + + + + Implementation + (library source files) + + + // mylib.c + + int add(int a, int b) + { + return a + b; + } + void sort(int *a, int n) + { ... } + + + + The API is a source-level contract — used at compile time + The caller only needs the header (.h); the implementation can change without affecting the caller's source code + The binary-level contract (at runtime) is the ABI + diff --git a/curs/chap-08-C-asm/calling-convention.png b/curs/chap-08-C-asm/calling-convention.png new file mode 100644 index 00000000..8706c869 Binary files /dev/null and b/curs/chap-08-C-asm/calling-convention.png differ diff --git a/curs/chap-08-C-asm/calling-convention.svg b/curs/chap-08-C-asm/calling-convention.svg new file mode 100644 index 00000000..9790cb59 --- /dev/null +++ b/curs/chap-08-C-asm/calling-convention.svg @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + Calling Convention + + + A calling convention defines how functions interact across module boundaries — they must agree on the same contract + + + + Caller + (function making the call) + + + // before call + 1. place arguments + (registers or stack) + 2. save caller-saved regs + 3. CALL instruction + // after return + 4. read return value + (register or stack) + 5. clean up stack args + (caller-cleanup conv.) + 6. restore saved regs + + + + Callee + (function being called) + + + // function prologue + 1. set up stack frame + 2. save callee-saved regs + 3. read arguments + // function body + 4. execute logic + // function epilogue + 5. place return value + 6. restore callee-saved regs + 7. tear down stack frame + 8. RET instruction + + + + + + Argument Passing + how parameters are transferred to callee + + + + + + Return Value Placement + where the result is placed for caller to read + + + + + + Stack Management + who sets up / tears down the stack frame + + + + + + Register Saving + Caller-saved: caller preserves before CALL + Callee-saved: callee preserves and restores + + + + + + Calling convention is part of the ABI — functions from different modules must use the same one + + Registers (fast) + arguments passed in CPU registers + (first N args; rest go on stack) + Stack (overflow) + extra arguments pushed onto the stack + before the CALL instruction + diff --git a/curs/chap-08-C-asm/inline-assembly.png b/curs/chap-08-C-asm/inline-assembly.png new file mode 100644 index 00000000..396cd06e Binary files /dev/null and b/curs/chap-08-C-asm/inline-assembly.png differ diff --git a/curs/chap-08-C-asm/inline-assembly.svg b/curs/chap-08-C-asm/inline-assembly.svg new file mode 100644 index 00000000..ecb4c561 --- /dev/null +++ b/curs/chap-08-C-asm/inline-assembly.svg @@ -0,0 +1,87 @@ + + + + + + + GCC Inline Assembly Syntax + __asm__ volatile ("instruction" : outputs : inputs : clobbers) + + + + __asm__ volatile + ( + "rdtsc" + : + "=a"(lo), "=d"(hi) + : + /* no inputs */ + ); + + + + + instruction + (NASM or GAS syntax) + + + + output constraints + "=a" → write eax to var + + + + input constraints + "a"(val) → load val to eax + + + + Common Register Constraints + + + Letter + Register + + + "a" + eax / rax + + + "b" + ebx / rbx + + + "c" + ecx / rcx + + + "d" + edx / rdx + + + "r" + any general-purpose register + + + "m" + memory operand + + + "i" + immediate (constant) value + + + + cpuid Example + + /* Input: leaf in eax */ + __asm__ volatile ( + "cpuid" + : "=a"(eax), + "=b"(ebx), + "=c"(ecx), + "=d"(edx) + : "a"(0) + ); + /* leaf=0 → vendor string */ + diff --git a/curs/chap-08-C-asm/lecture-overview.png b/curs/chap-08-C-asm/lecture-overview.png new file mode 100644 index 00000000..f44cc9b5 Binary files /dev/null and b/curs/chap-08-C-asm/lecture-overview.png differ diff --git a/curs/chap-08-C-asm/lecture-overview.svg b/curs/chap-08-C-asm/lecture-overview.svg new file mode 100644 index 00000000..ecf693ef --- /dev/null +++ b/curs/chap-08-C-asm/lecture-overview.svg @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + Lecture Overview: ABI and C+Assembly Interaction + + + + + + Modules + source file = + compilation unit + → object file (.o) + + + + + + + Libraries + collection of modules + expose an interface + for reuse + + + + + + + API + source-level contract + header files (.h) + compile-time + + + + + + + ABI + binary-level contract + calling conv + sizes + + format + mangling + + + + + + + Calling Convention + argument passing + return value · stack + register saving + + + + + + Demos: C ↔ Assembly Interaction + + + + ASM calls C functions + (assembly → C) + 01-rand + 02-rand-srand + 03-rand-srand-printf + 05-sum2-from-as-to-c + 08-sum-array-from-as-to-c + 10-call-n-args-from-as-to-c + 12-fibonacci-from-as-to-c + + + + C calls ASM functions + (C → assembly) + 04-rand-srand-printf-c + 06-sum2-from-c-to-as + 07-sum2-from-c-to-as-header + 09-sum-array-from-c-to-as + 11-call-n-args-from-c-to-as + 13-fibonacci-from-c-to-as + + + + Inline Assembly + (ASM inside C source) + 14-inline-as-cpuid + 15-inline-as-rdtsc + 16-inline-measure + 17-measure-as-func + + + + Build Pipeline (Mixed C + Assembly) + + + + source.c + C source + + + source.asm + + + + + + gcc / nasm + compile/assemble + + + + + source.o + source.o + object files + + + + + Linker + + + + + program + executable + + + + ABI ensures + C and ASM modules + can call each other + using same calling + convention + diff --git a/curs/chap-08-C-asm/libraries.png b/curs/chap-08-C-asm/libraries.png new file mode 100644 index 00000000..b2e77e24 Binary files /dev/null and b/curs/chap-08-C-asm/libraries.png differ diff --git a/curs/chap-08-C-asm/libraries.svg b/curs/chap-08-C-asm/libraries.svg new file mode 100644 index 00000000..1e57d3e8 --- /dev/null +++ b/curs/chap-08-C-asm/libraries.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + Libraries + + + + Application + calls library + functions + + + + uses + + + + Library + + + + Interface (API) + exposed to callers — defined in header files (.h) + + + + Function signatures + int add(int a, int b); + + + Data types + typedef struct { ... }; + + + Constants / macros + #define MAX_SIZE 1024 + + + + Implementation (hidden from callers) + + + + module_a.o + object module + (from module_a.c) + can be updated + independently + + + module_b.o + object module + (from module_b.c) + can be updated + independently + + + module_c.o + object module + (from module_c.asm) + can be updated + independently + + + + Purpose: Code reuse — same library used by many applications + Static library (.a / .lib) — Dynamic / shared library (.so / .dll / .dylib) + diff --git a/curs/chap-08-C-asm/modules.png b/curs/chap-08-C-asm/modules.png new file mode 100644 index 00000000..32e9264b Binary files /dev/null and b/curs/chap-08-C-asm/modules.png differ diff --git a/curs/chap-08-C-asm/modules.svg b/curs/chap-08-C-asm/modules.svg new file mode 100644 index 00000000..09094335 --- /dev/null +++ b/curs/chap-08-C-asm/modules.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + Modules and Modularity + + + Source Files + Object Files + + + + module_a.c + compilation unit + + + module_b.c + compilation unit + + + module_c.asm + compilation unit + + + + + + + + + Compiler + / Assembler + + + + + + + + + module_a.o + object module + + + module_b.o + object module + + + module_c.o + object module + + + + + + + + + Linker + + + + + + + program + executable + + + + Advantages of Modularity + + + Update / fix one module only + Modules can be reused across projects + Each source file = one compilation unit + (no need to rebuild everything) + (libraries, packages) + (C, C++, Go, D, ...) + diff --git a/curs/chap-08-C-asm/01-simple-c-asm/.gitignore b/curs/chap-08-C-asm/old/01-simple-c-asm/.gitignore similarity index 100% rename from curs/chap-08-C-asm/01-simple-c-asm/.gitignore rename to curs/chap-08-C-asm/old/01-simple-c-asm/.gitignore diff --git a/curs/chap-08-C-asm/01-simple-c-asm/Makefile b/curs/chap-08-C-asm/old/01-simple-c-asm/Makefile similarity index 100% rename from curs/chap-08-C-asm/01-simple-c-asm/Makefile rename to curs/chap-08-C-asm/old/01-simple-c-asm/Makefile diff --git a/curs/chap-08-C-asm/01-simple-c-asm/main.c b/curs/chap-08-C-asm/old/01-simple-c-asm/main.c similarity index 100% rename from curs/chap-08-C-asm/01-simple-c-asm/main.c rename to curs/chap-08-C-asm/old/01-simple-c-asm/main.c diff --git a/curs/chap-08-C-asm/01-simple-c-asm/sum_n.asm b/curs/chap-08-C-asm/old/01-simple-c-asm/sum_n.asm similarity index 100% rename from curs/chap-08-C-asm/01-simple-c-asm/sum_n.asm rename to curs/chap-08-C-asm/old/01-simple-c-asm/sum_n.asm diff --git a/curs/chap-08-C-asm/02-c-asm-dandamudi/.vscode/launch.json b/curs/chap-08-C-asm/old/02-c-asm-dandamudi/.vscode/launch.json similarity index 100% rename from curs/chap-08-C-asm/02-c-asm-dandamudi/.vscode/launch.json rename to curs/chap-08-C-asm/old/02-c-asm-dandamudi/.vscode/launch.json diff --git a/curs/chap-08-C-asm/02-c-asm-dandamudi/.vscode/tasks.json b/curs/chap-08-C-asm/old/02-c-asm-dandamudi/.vscode/tasks.json similarity index 100% rename from curs/chap-08-C-asm/02-c-asm-dandamudi/.vscode/tasks.json rename to curs/chap-08-C-asm/old/02-c-asm-dandamudi/.vscode/tasks.json diff --git a/curs/chap-08-C-asm/02-c-asm-dandamudi/Makefile b/curs/chap-08-C-asm/old/02-c-asm-dandamudi/Makefile similarity index 100% rename from curs/chap-08-C-asm/02-c-asm-dandamudi/Makefile rename to curs/chap-08-C-asm/old/02-c-asm-dandamudi/Makefile diff --git a/curs/chap-08-C-asm/02-c-asm-dandamudi/hll_arraysum2a.asm b/curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_arraysum2a.asm similarity index 100% rename from curs/chap-08-C-asm/02-c-asm-dandamudi/hll_arraysum2a.asm rename to curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_arraysum2a.asm diff --git a/curs/chap-08-C-asm/02-c-asm-dandamudi/hll_arraysum2c.c b/curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_arraysum2c.c similarity index 100% rename from curs/chap-08-C-asm/02-c-asm-dandamudi/hll_arraysum2c.c rename to curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_arraysum2c.c diff --git a/curs/chap-08-C-asm/02-c-asm-dandamudi/hll_arraysum_inline.c b/curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_arraysum_inline.c similarity index 100% rename from curs/chap-08-C-asm/02-c-asm-dandamudi/hll_arraysum_inline.c rename to curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_arraysum_inline.c diff --git a/curs/chap-08-C-asm/02-c-asm-dandamudi/hll_arraysum_inline2.c b/curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_arraysum_inline2.c similarity index 100% rename from curs/chap-08-C-asm/02-c-asm-dandamudi/hll_arraysum_inline2.c rename to curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_arraysum_inline2.c diff --git a/curs/chap-08-C-asm/02-c-asm-dandamudi/hll_arraysum_inline_intrinsics.c b/curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_arraysum_inline_intrinsics.c similarity index 100% rename from curs/chap-08-C-asm/02-c-asm-dandamudi/hll_arraysum_inline_intrinsics.c rename to curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_arraysum_inline_intrinsics.c diff --git a/curs/chap-08-C-asm/02-c-asm-dandamudi/hll_arraysuma.asm b/curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_arraysuma.asm similarity index 100% rename from curs/chap-08-C-asm/02-c-asm-dandamudi/hll_arraysuma.asm rename to curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_arraysuma.asm diff --git a/curs/chap-08-C-asm/02-c-asm-dandamudi/hll_arraysumc.c b/curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_arraysumc.c similarity index 100% rename from curs/chap-08-C-asm/02-c-asm-dandamudi/hll_arraysumc.c rename to curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_arraysumc.c diff --git a/curs/chap-08-C-asm/02-c-asm-dandamudi/hll_ex1_inline.c b/curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_ex1_inline.c similarity index 100% rename from curs/chap-08-C-asm/02-c-asm-dandamudi/hll_ex1_inline.c rename to curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_ex1_inline.c diff --git a/curs/chap-08-C-asm/02-c-asm-dandamudi/hll_ex1c.c b/curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_ex1c.c similarity index 100% rename from curs/chap-08-C-asm/02-c-asm-dandamudi/hll_ex1c.c rename to curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_ex1c.c diff --git a/curs/chap-08-C-asm/02-c-asm-dandamudi/hll_minmaxa.asm b/curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_minmaxa.asm similarity index 100% rename from curs/chap-08-C-asm/02-c-asm-dandamudi/hll_minmaxa.asm rename to curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_minmaxa.asm diff --git a/curs/chap-08-C-asm/02-c-asm-dandamudi/hll_minmaxc.c b/curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_minmaxc.c similarity index 100% rename from curs/chap-08-C-asm/02-c-asm-dandamudi/hll_minmaxc.c rename to curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_minmaxc.c diff --git a/curs/chap-08-C-asm/02-c-asm-dandamudi/hll_test.asm b/curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_test.asm similarity index 100% rename from curs/chap-08-C-asm/02-c-asm-dandamudi/hll_test.asm rename to curs/chap-08-C-asm/old/02-c-asm-dandamudi/hll_test.asm diff --git a/curs/chap-08-C-asm/02-simple-c-asm/.gitignore b/curs/chap-08-C-asm/old/02-simple-c-asm/.gitignore similarity index 100% rename from curs/chap-08-C-asm/02-simple-c-asm/.gitignore rename to curs/chap-08-C-asm/old/02-simple-c-asm/.gitignore diff --git a/curs/chap-08-C-asm/02-simple-c-asm/Makefile b/curs/chap-08-C-asm/old/02-simple-c-asm/Makefile similarity index 100% rename from curs/chap-08-C-asm/02-simple-c-asm/Makefile rename to curs/chap-08-C-asm/old/02-simple-c-asm/Makefile diff --git a/curs/chap-08-C-asm/02-simple-c-asm/main.c b/curs/chap-08-C-asm/old/02-simple-c-asm/main.c similarity index 100% rename from curs/chap-08-C-asm/02-simple-c-asm/main.c rename to curs/chap-08-C-asm/old/02-simple-c-asm/main.c diff --git a/curs/chap-08-C-asm/02-simple-c-asm/proc.asm b/curs/chap-08-C-asm/old/02-simple-c-asm/proc.asm similarity index 100% rename from curs/chap-08-C-asm/02-simple-c-asm/proc.asm rename to curs/chap-08-C-asm/old/02-simple-c-asm/proc.asm diff --git a/curs/chap-08-C-asm/overview.png b/curs/chap-08-C-asm/overview.png new file mode 100644 index 00000000..cf620b57 Binary files /dev/null and b/curs/chap-08-C-asm/overview.png differ diff --git a/curs/chap-08-C-asm/overview.svg b/curs/chap-08-C-asm/overview.svg new file mode 100644 index 00000000..5eb29467 --- /dev/null +++ b/curs/chap-08-C-asm/overview.svg @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + Chapter 08: Application Binary Interface and C-Assembly Interaction + Storyline Overview + + + + + Assembly calls C + + + + C calls Assembly + + + + Inline Assembly + + + + Calling C Library Functions from Assembly + + + + 01-rand + rand() — no args, + return value in rax + + + + 02-rand-srand + time() / srand() / rand() + void return, NULL arg + + + + 03-rand-srand-printf + printf() variadic: + xor rax, rax required + + + + 04-rand-srand-printf-c + C reference — compare + with objdump -d + + + + + + + + Calling Between C and Assembly — Simple Functions + + + + 05-sum2-from-as-to-c + ASM calls C add() + 2 register args + + + + 06-sum2-from-c-to-as + C calls ASM add() + inline declaration + + + + 07-sum2-from-c-to-as + -header + C calls ASM add() + via header file (.h) + + + + + + + + Passing Arrays (Pointer + Count) + + + + 08-sum-array-from-as-to-c + ASM calls C sum_array(ptr, n) + rdi=ptr, rsi=count + + + + 09-sum-array-from-c-to-as + C calls ASM sum_array(ptr, n) + with header file + + + + Stack Arguments (7th+ Beyond Registers) + + + + 10-call-n-args-from-as-to-c + ASM calls C sum2()…sum8() + 7th+ args pushed on stack + + + + 11-call-n-args-from-c-to-as + C calls ASM sum2()…sum8() + 7th+ args at [rsp+8], [rsp+16] + + + + Recursive Functions Across Languages + + + + 12-fibonacci-from-as-to-c + ASM calls C fibonacci(N) + local var on stack for N + + + + 13-fibonacci-from-c-to-as + C calls ASM fibonacci(N) + push/pop for register save + + + + GCC Inline Assembly (__asm__) + + + + 14-inline-as-cpuid + cpuid instruction in C + input/output constraints + + + + 15-inline-as-rdtsc + rdtsc: read cycle counter + edx:eax → 64-bit value + + + + 16-inline-measure + rdtsc before & after call + measure fibonacci() cycles + + + + 17-measure-as-func + read_cycles() in .asm + reusable measurement fn + + + + + + + + + + Assembly → C + + C → Assembly + + Inline Assembly in C +