diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 0c85ceab5..b999f8f65 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - x86_64 jobs: checkpatch: diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index dba362c16..a9fce74c1 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -4,7 +4,7 @@ on: push: branches: - main - - x86_64 + - x86 jobs: deploy: @@ -16,11 +16,11 @@ jobs: with: path: main - - name: Checkout `x86_64` branch + - name: Checkout `x86` branch uses: actions/checkout@v4 with: - ref: x86_64 - path: x86_64 + ref: x86 + path: x86 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 @@ -36,7 +36,7 @@ jobs: cache-from: type=gha cache-to: type=gha - - name: Build Jekyll site for x86 + - name: Build Jekyll site for x86_64 run: | echo "url: https://${{ github.repository_owner }}.github.io" >> _config.yaml echo "baseurl: /hardware-software-interface" >> _config.yaml @@ -48,21 +48,21 @@ jobs: jekyll-image bundle exec jekyll build -s /base/app -d /base/app/_site working-directory: main - - name: Build Jekyll site for x64 + - name: Build Jekyll site for x86 run: | echo "url: https://${{ github.repository_owner }}.github.io" >> _config.yaml - echo "baseurl: /hardware-software-interface/x86_64" >> _config.yaml + echo "baseurl: /hardware-software-interface/x86" >> _config.yaml - # Build Jekyll site for x86_64 - # Note: The x86_64 site is built in a separate directory. + # Build Jekyll site for x86 + # Note: The x86 site is built in a separate directory. docker run --rm \ -v $(pwd):/base/app \ -u $(id -u):$(id -g) \ jekyll-image bundle exec jekyll build -s /base/app -d /base/app/_site - # Move the x86_64 site under the main site directory - mv _site ${{ github.workspace }}/main/_site/x86_64 - working-directory: x86_64 + # Move the x86 site under the main site directory + mv _site ${{ github.workspace }}/main/_site/x86 + working-directory: x86 - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v3 diff --git a/.gitignore b/.gitignore index e61cd7168..e74e73a3e 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,9 @@ _site/ .view/ +*.ref +.gdb_history + # Ruby files for the local Jekyll server .sass-cache/ .jekyll-cache/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..3623fd8ad --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "*.c": "c", + "param.h": "c" + } +} \ No newline at end of file diff --git a/labs/lab-02/media/arit.svg b/labs/lab-02/media/arit.svg index bee8b721c..a20916734 100644 --- a/labs/lab-02/media/arit.svg +++ b/labs/lab-02/media/arit.svg @@ -1,4 +1,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/labs/lab-02/reading/memory-operations.md b/labs/lab-02/reading/memory-operations.md index 92dfb1a5a..92f9b47f5 100644 --- a/labs/lab-02/reading/memory-operations.md +++ b/labs/lab-02/reading/memory-operations.md @@ -44,10 +44,12 @@ For example: char *char_ptr = 1000; short *short_ptr = 2000; int *int_ptr = 3000; +long long *long_long_ptr = 4000; ++char_ptr; /* char_ptr will point to address 1001 */ ++short_ptr; /* short_ptr points to address 2002 */ ++int_ptr; /* int_ptr points to address 3004 */ +++long_long_ptr; /* long_long_ptr points to address 4008 */ ``` ![A diagram which visualizes arithmetic operations on pointers](../media/arit.svg) diff --git a/labs/lab-02/tasks/iterate/README.md b/labs/lab-02/tasks/iterate/README.md index ee80a47b0..13011fa55 100644 --- a/labs/lab-02/tasks/iterate/README.md +++ b/labs/lab-02/tasks/iterate/README.md @@ -13,14 +13,14 @@ Here is the given piece of C code: #include int main() { - int v[] = {0xCAFEBABE, 0xDEADBEEF, 0x0B00B135, 0xBAADF00D, 0xDEADC0DE}; + int v[] = {0xCAFEBABE, 0xDEADBEEF, 0x0B00B135, 0xBAADF00D, 0xDEADC0DE, 0x1EE71EE7}; return 0; } ``` Display the addresses of the elements in the `v` array along with their values. -Iterate through the addresses in `v` byte by byte, two bytes at a time, and four bytes at a time. +Iterate through the addresses in `v` byte by byte, two bytes at a time, four bytes at a time, and eight bytes at a time. > **TIP:** You can iterate through memory byte by byte starting from a specific address using a pointer of type `unsigned char*` (since the `char` type is represented by one byte). > @@ -43,9 +43,10 @@ make check In case of a correct solution, you will get an output such as: ```text -test_chars ........................ passed ... 33 -test_shorts ........................ passed ... 33 -test_ints ........................ passed ... 34 +test_chars ........................ passed ... 25 +test_shorts ........................ passed ... 25 +test_ints ........................ passed ... 25 +test_long_longs ........................ passed ... 25 Total: 100/100 ``` diff --git a/labs/lab-02/tasks/iterate/solution/array.c b/labs/lab-02/tasks/iterate/solution/array.c index 807a6c684..3d2ec435e 100644 --- a/labs/lab-02/tasks/iterate/solution/array.c +++ b/labs/lab-02/tasks/iterate/solution/array.c @@ -2,4 +2,4 @@ #include "array.h" -int v[5] = {0xCAFEBABE, 0xDEADBEEF, 0x0B00B135, 0xBAADF00D, 0xDEADC0DE}; +int v[6] = {0xCAFEBABE, 0xDEADBEEF, 0x0B00B135, 0xBAADF00D, 0xDEADC0DE, 0x1EE71EE7}; diff --git a/labs/lab-02/tasks/iterate/solution/array.h b/labs/lab-02/tasks/iterate/solution/array.h index d23697eb6..e6a72e8b2 100644 --- a/labs/lab-02/tasks/iterate/solution/array.h +++ b/labs/lab-02/tasks/iterate/solution/array.h @@ -3,6 +3,6 @@ #ifndef ARRAY_H #define ARRAY_H 1 -extern int v[5]; +extern int v[6]; #endif // ARRAY_H diff --git a/labs/lab-02/tasks/iterate/solution/iterate.c b/labs/lab-02/tasks/iterate/solution/iterate.c index 29064040a..0d24fa0c4 100644 --- a/labs/lab-02/tasks/iterate/solution/iterate.c +++ b/labs/lab-02/tasks/iterate/solution/iterate.c @@ -53,3 +53,18 @@ void print_ints_ref(void) } printf("-------------------------------\n"); } + +void print_long_longs_ref(void) +{ + unsigned long long *long_long_ptr = (unsigned long long *) &v; + unsigned long long i; + /** + * Iterate through 8 bytes at a time, we have only a quarter of the steps because we are + * displaying 8 bytes at each step. + */ + for (i = 0 ; i < sizeof(v) / sizeof((*long_long_ptr)); ++i) { + printf("%p -> %p\n", long_long_ptr, *long_long_ptr); + ++long_long_ptr; + } + printf("-------------------------------\n"); +} diff --git a/labs/lab-02/tasks/iterate/solution/iterate.h b/labs/lab-02/tasks/iterate/solution/iterate.h index 2be037300..330ad34db 100644 --- a/labs/lab-02/tasks/iterate/solution/iterate.h +++ b/labs/lab-02/tasks/iterate/solution/iterate.h @@ -6,5 +6,6 @@ void print_chars_ref(void); void print_shorts_ref(void); void print_ints_ref(void); +void print_long_longs_ref(void); #endif // ITERATE_REF_H diff --git a/labs/lab-02/tasks/iterate/solution/main.c b/labs/lab-02/tasks/iterate/solution/main.c index 3e2d705a7..2ce8c8e42 100644 --- a/labs/lab-02/tasks/iterate/solution/main.c +++ b/labs/lab-02/tasks/iterate/solution/main.c @@ -13,6 +13,7 @@ int main(void) print_chars_ref(); print_shorts_ref(); print_ints_ref(); + print_long_longs_ref(); return 0; } diff --git a/labs/lab-02/tasks/iterate/support/array.c b/labs/lab-02/tasks/iterate/support/array.c index 807a6c684..3d2ec435e 100644 --- a/labs/lab-02/tasks/iterate/support/array.c +++ b/labs/lab-02/tasks/iterate/support/array.c @@ -2,4 +2,4 @@ #include "array.h" -int v[5] = {0xCAFEBABE, 0xDEADBEEF, 0x0B00B135, 0xBAADF00D, 0xDEADC0DE}; +int v[6] = {0xCAFEBABE, 0xDEADBEEF, 0x0B00B135, 0xBAADF00D, 0xDEADC0DE, 0x1EE71EE7}; diff --git a/labs/lab-02/tasks/iterate/support/array.h b/labs/lab-02/tasks/iterate/support/array.h index d23697eb6..e6a72e8b2 100644 --- a/labs/lab-02/tasks/iterate/support/array.h +++ b/labs/lab-02/tasks/iterate/support/array.h @@ -3,6 +3,6 @@ #ifndef ARRAY_H #define ARRAY_H 1 -extern int v[5]; +extern int v[6]; #endif // ARRAY_H diff --git a/labs/lab-02/tasks/iterate/support/iterate.c b/labs/lab-02/tasks/iterate/support/iterate.c index 68b030454..369f9f083 100644 --- a/labs/lab-02/tasks/iterate/support/iterate.c +++ b/labs/lab-02/tasks/iterate/support/iterate.c @@ -34,3 +34,13 @@ void print_ints(void) printf("-------------------------------\n"); } + +void print_long_longs(void) +{ + /** + * TODO: Implement function + */ + (void) v; + + printf("-------------------------------\n"); +} diff --git a/labs/lab-02/tasks/iterate/support/iterate.h b/labs/lab-02/tasks/iterate/support/iterate.h index d3171c6d1..56a68264e 100644 --- a/labs/lab-02/tasks/iterate/support/iterate.h +++ b/labs/lab-02/tasks/iterate/support/iterate.h @@ -6,5 +6,6 @@ void print_chars(void); void print_shorts(void); void print_ints(void); +void print_long_longs(void); #endif // ITERATE_H diff --git a/labs/lab-02/tasks/iterate/support/main.c b/labs/lab-02/tasks/iterate/support/main.c index a79ea9bc3..a374126eb 100644 --- a/labs/lab-02/tasks/iterate/support/main.c +++ b/labs/lab-02/tasks/iterate/support/main.c @@ -14,6 +14,7 @@ int main(void) print_chars(); print_shorts(); print_ints(); + print_long_longs(); return 0; } diff --git a/labs/lab-02/tasks/iterate/tests/test_iterate.c b/labs/lab-02/tasks/iterate/tests/test_iterate.c index 5d6a85b15..a9fb833c5 100644 --- a/labs/lab-02/tasks/iterate/tests/test_iterate.c +++ b/labs/lab-02/tasks/iterate/tests/test_iterate.c @@ -14,9 +14,11 @@ #define CHAR_OUTPUT "char.out" #define SHORT_OUTPUT "short.out" #define INT_OUTPUT "int.out" +#define LONG_LONG_OUTPUT "long_long.out" #define CHAR_REF "char.ref" #define SHORT_REF "short.ref" #define INT_REF "int.ref" +#define LONG_LONG_REF "long_long.ref" static int fd, stdout_fd; @@ -120,10 +122,29 @@ static int test_ints(void) return status == 0 ? 1 : 0; } +static int test_long_longs(void) +{ + int status; + + prep_io(LONG_LONG_OUTPUT); + print_long_longs(); + fflush(stdout); + restore_io(); + + prep_io(LONG_LONG_REF); + print_long_longs_ref(); + fflush(stdout); + restore_io(); + + status = system("diff -q " LONG_LONG_OUTPUT " " LONG_LONG_REF); + return status == 0 ? 1 : 0; +} + static struct graded_test all_tests[] = { - { test_chars, "test_chars", 33 }, - { test_shorts, "test_shorts", 33}, - { test_ints, "test_ints", 34}, + { test_chars, "test_chars", 25 }, + { test_shorts, "test_shorts", 25}, + { test_ints, "test_ints", 25}, + { test_long_longs, "test_long_long", 25}, }; int main(void) diff --git a/labs/lab-04/guides/discovering-assembly/README.md b/labs/lab-04/guides/discovering-assembly/README.md index 322920571..b79486e1a 100644 --- a/labs/lab-04/guides/discovering-assembly/README.md +++ b/labs/lab-04/guides/discovering-assembly/README.md @@ -8,28 +8,28 @@ nav_order: 10 To follow this guide, you will need to navigate to the `guides/discovering-assembly/support` directory. 1. Open the `ex1.asm` file and read the comments. -Assemble it by using the `make` utility and run it. -Using gdb, go through the program line by line (the `start` command followed by `next`) and observe the changes in register values after executing the `mov` and `add` instructions. -Ignore the sequence of `PRINTF32` instructions. + Assemble it by using the `make` utility and run it. + Using gdb, go through the program line by line (the `start` command followed by `next`) and observe the changes in register values after executing the `mov` and `add` instructions. + Ignore the sequence of `PRINTF64` instructions. 1. Open the `ex2.asm` file and read the comments. -Assemble it by using the `make` utility and run it. -Using gdb, observe the change in the `eip` register when executing the `jmp` instruction. -To skip the `PRINTF32` instructions, add a breakpoint at the `jump_incoming` label (the `break` command followed by `run`). + Assemble it by using the `make` utility and run it. + Using gdb, observe the change in the `rip` register when executing the `jmp` instruction. + To skip the `PRINTF64` instructions, add a breakpoint at the `jump_incoming` label (the `break` command followed by `run`). 1. Open the `ex3.asm` file and read the comments. -Assemble it by using the `make` utility and run it. -Using gdb, navigate through the program using breakpoints. -Follow the program flow. -Why is `15` displayed first and then `3`? -Because of the jump at line 9. -Where does the jump at line 25 point to? -To the `zone1` label. + Assemble it by using the `make` utility and run it. + Using gdb, navigate through the program using breakpoints. + Follow the program flow. + Why is `15` displayed first and then `3`? + Because of the jump at line 9. + Where does the jump at line 25 point to? + To the `zone1` label. 1. Open the `ex4.asm` file and read the comments. -Assemble it by using the `make` utility and run it. -Using gdb, go through the program. -Why isn't the jump at line 12 taken? -Because the `je` instruction jumps if the `ZF` bit in the `FLAGS` register is set. -This bit is set by the `cmp` instruction, which calculates the difference between the values of the `eax` and `ebx` registers without storing the result. -However, the `add` instruction at line 11 clears this flag because the result of the operation is different from 0. + Assemble it by using the `make` utility and run it. + Using gdb, go through the program. + Why isn't the jump at line 12 taken? + Because the `je` instruction jumps if the `ZF` bit in the `FLAGS` register is set. + This bit is set by the `cmp` instruction, which calculates the difference between the values of the `eax` and `ebx` registers without storing the result. + However, the `add` instruction at line 11 clears this flag because the result of the operation is different from 0. diff --git a/labs/lab-04/guides/discovering-assembly/support/Makefile b/labs/lab-04/guides/discovering-assembly/support/Makefile index dbcca12d6..eaaa1452d 100644 --- a/labs/lab-04/guides/discovering-assembly/support/Makefile +++ b/labs/lab-04/guides/discovering-assembly/support/Makefile @@ -5,11 +5,11 @@ RM = rm SRCS := $(shell find . -name "*.asm") OBJS := $(SRCS:.asm=.o) -UTILSDIR := ..utils/ +UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -no-pie all: ex1 ex2 ex3 ex4 diff --git a/labs/lab-04/guides/discovering-assembly/support/ex1.asm b/labs/lab-04/guides/discovering-assembly/support/ex1.asm index 8c8bfeffb..a1de6fc40 100644 --- a/labs/lab-04/guides/discovering-assembly/support/ex1.asm +++ b/labs/lab-04/guides/discovering-assembly/support/ex1.asm @@ -1,14 +1,20 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .text global main extern printf main: - mov eax, 7 ; load the eax register with the value 7 - mov ebx, 8 ; load the ebx register with the value 8 - add eax, ebx ; add the value from ebx to the value from eax + push rbp + mov rbp, rsp + + mov rax, 7 ; load the eax register with the value 7 + mov r8, 8 ; load the ebx register with the value 8 + add rax, r8 ; add the value from ebx to the value from eax ; and store the result in eax - PRINTF32 `%d\n\x0`, eax ; print the value from the eax register + PRINTF64 `%d\n\x0`, rax ; print the value from the eax register + + leave + ret diff --git a/labs/lab-04/guides/discovering-assembly/support/ex2.asm b/labs/lab-04/guides/discovering-assembly/support/ex2.asm index 3847d1496..3af1bf3a8 100644 --- a/labs/lab-04/guides/discovering-assembly/support/ex2.asm +++ b/labs/lab-04/guides/discovering-assembly/support/ex2.asm @@ -1,22 +1,26 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .text global main extern printf main: - mov eax, 4 - PRINTF32 `%d\n\x0`, eax + push rbp + mov rbp, rsp + + mov r9, 4 + PRINTF64 `%d\n\x0`, r9 jump_incoming: jmp exit ; unconditional jump to the "exit" label - mov eax, 7 ; this code is unreachable, therefore not executed - mov ebx, 8 - add eax, ebx - PRINTF32 `%d\n\x0`, eax + mov r9, 7 ; this code is unreachable, therefore not executed + mov rbx, 8 + add r9, rbx + PRINTF64 `%d\n\x0`, r9 exit: + leave ret diff --git a/labs/lab-04/guides/discovering-assembly/support/ex3.asm b/labs/lab-04/guides/discovering-assembly/support/ex3.asm index 3e7d75520..3c131bf09 100644 --- a/labs/lab-04/guides/discovering-assembly/support/ex3.asm +++ b/labs/lab-04/guides/discovering-assembly/support/ex3.asm @@ -1,31 +1,35 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .text global main extern printf main: - mov eax, zone2 - jmp eax ; unconditional jump to the address stored + push rbp + mov rbp, rsp + + mov rax, zone2 + jmp rax ; unconditional jump to the address stored ; in the eax register zone1: - mov eax, 1 - mov ebx, 2 - add eax, ebx - PRINTF32 `%d\n\x0`, eax + mov rax, 1 + mov r10, 2 + add rax, r10 + PRINTF64 `%d\n\x0`, rax jump1: jmp exit zone2: - mov eax, 7 - mov ebx, 8 - add eax, ebx - PRINTF32 `%d\n\x0`, eax + mov rax, 7 + mov r10, 8 + add rax, r10 + PRINTF64 `%d\n\x0`, rax jump2: - jmp $-0x4A ; relative jump with a negative offset + jmp $-0x82 ; relative jump with a negative offset ; to the address of the previous instruction exit: + leave ret diff --git a/labs/lab-04/guides/discovering-assembly/support/ex4.asm b/labs/lab-04/guides/discovering-assembly/support/ex4.asm index a4aef2a97..894514f98 100644 --- a/labs/lab-04/guides/discovering-assembly/support/ex4.asm +++ b/labs/lab-04/guides/discovering-assembly/support/ex4.asm @@ -1,19 +1,26 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .text global main extern printf main: - mov eax, 1 - mov ebx, 1 - cmp eax, ebx - add ecx, 1 ; Comment out this line + push rbp + mov rbp, rsp + + mov rax, 1 + mov rbx, 1 + cmp rax, rbx + add r15, 1 ; Comment out this line je print + + leave ret print: - PRINTF32 `%d\n\x0`, eax + PRINTF64 `%d\n\x0`, rax + + leave ret diff --git a/labs/lab-04/guides/discovering-assembly/utils/printf32.asm b/labs/lab-04/guides/discovering-assembly/utils/printf32.asm deleted file mode 100644 index 61a930ed7..000000000 --- a/labs/lab-04/guides/discovering-assembly/utils/printf32.asm +++ /dev/null @@ -1,22 +0,0 @@ -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-04/guides/discovering-assembly/utils/printf64.asm b/labs/lab-04/guides/discovering-assembly/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-04/guides/discovering-assembly/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-04/guides/instructions/README.md b/labs/lab-04/guides/instructions/README.md index 501c36d70..6cfb4631c 100644 --- a/labs/lab-04/guides/instructions/README.md +++ b/labs/lab-04/guides/instructions/README.md @@ -28,13 +28,13 @@ Having in mind what the `popf` instruction does, try to guess what would adding jnc mystery_label ``` -Moving on, we can see that the `0` value is set to the `eax` register using the `mov` instruction. -Can you give example of another two ways of setting the value in `eax` to `0` without using `mov` ? +Moving on, we can see that the `0` value is set to the `r9` register using the `mov` instruction. +Can you give example of another two ways of setting the value in `r9` to `0` without using `mov` ? > **HINT**: Think about the [logical operators](../../reading/x86-architecture-family.md). -Next, by using the `test` instruction we can set the `flags` based on the output of the `logical and` between `eax` and itself. +Next, by using the `test` instruction we can set the `flags` based on the output of the `logical and` between `r9` and itself. -After resetting the flags, we store `0xffffffff` in the `ebx` register(which is actually the largest number it can store before setting the carry flag) and then use the `test` instruction yet again. +After resetting the flags, we store `0xffffffffffffffff` in the `rbx` register(which is actually the largest number it can store before setting the carry flag) and then use the `test` instruction yet again. Similarly, what do you think adding the following line of code after the `test` instruction would produce ? ```assembly @@ -42,12 +42,12 @@ jnz mystery_label ``` We reset the flags once again and now we take a look at working with the smaller portions of the `eax` register. -Can you guess the output of the following command, put right under the `add al, bl` instruction ? +Can you guess the output of the following command, put right under the `add r9b, bl` instruction ? What about the flags ? Which flag has been set ? ```assembly -PRINTF32 `%d\n\x0`, eax +PRINTF64 `%d\n\x0`, r9 ``` Similarly, try to answer the same questions from above, but considering the next portions of the code. diff --git a/labs/lab-04/guides/instructions/support/Makefile b/labs/lab-04/guides/instructions/support/Makefile index ade99a100..818b7d923 100644 --- a/labs/lab-04/guides/instructions/support/Makefile +++ b/labs/lab-04/guides/instructions/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -no-pie TARGET_EXEC = instructions diff --git a/labs/lab-04/guides/instructions/support/instructions.asm b/labs/lab-04/guides/instructions/support/instructions.asm index 5381fc28f..731146397 100644 --- a/labs/lab-04/guides/instructions/support/instructions.asm +++ b/labs/lab-04/guides/instructions/support/instructions.asm @@ -1,53 +1,58 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .text global main extern printf main: + push rbp + mov rbp, rsp + push 0 popf - mov eax, 0 - test eax, eax + mov r9, 0 + test r9, r9 push 0 popf - mov ebx, 0xffffffff - test ebx, ebx + mov rbx, 0xffffffffffffffff + test rbx, rbx push 0 popf - mov al, 250 + mov r9b, 250 mov bl, 10 - add al, bl + add r9b, bl push 0 popf - mov al, 0 + mov r9b, 0 mov bl, 1 - sub al, bl + sub r9b, bl push 0 popf - mov al, 120 + mov r9b, 120 mov bl, 120 - add al, bl + add r9b, bl push 0 popf - mov al, 129 + mov r9b, 129 mov bl, 129 - add al, bl + add r9b, bl + + PRINTF64 `%d\n\x0`, r9 - PRINTF32 `%d\n\x0`, eax + leave ret diff --git a/labs/lab-04/guides/instructions/utils/printf32.asm b/labs/lab-04/guides/instructions/utils/printf32.asm deleted file mode 100644 index 61a930ed7..000000000 --- a/labs/lab-04/guides/instructions/utils/printf32.asm +++ /dev/null @@ -1,22 +0,0 @@ -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-04/guides/instructions/utils/printf64.asm b/labs/lab-04/guides/instructions/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-04/guides/instructions/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-04/reading/introduction.md b/labs/lab-04/reading/introduction.md index d0c9a55a0..8bcbef6fe 100644 --- a/labs/lab-04/reading/introduction.md +++ b/labs/lab-04/reading/introduction.md @@ -33,8 +33,8 @@ Most assembly languages provide a direct correspondence between instructions. Fo ```assembly mov al, 12 <-> '\xB0\x0C' -xor dx, dx <-> '\x67\x31\xD2' -jmp esp <-> '\xFF\xE4' +xor dx, dx <-> '\x66\x31\xD2' +jmp rsp <-> '\xFF\xE4' ``` > **NOTE**: Because assembly language depends on architecture, it is generally not portable. diff --git a/labs/lab-04/reading/x86-architecture-family.md b/labs/lab-04/reading/x86-architecture-family.md index af609eb34..02bfaab1d 100644 --- a/labs/lab-04/reading/x86-architecture-family.md +++ b/labs/lab-04/reading/x86-architecture-family.md @@ -17,26 +17,27 @@ These processors are highly backward compatible, with most instructions unchange The basic working units for x86 processors are registers. These are a suite of locations within the processor through which it interacts with memory, I/O, etc. -x86 processors have 8 such 32-bit registers. +x86 processors have 16 such 64-bit registers. Although any of these can be used in operations, for historical reasons, each register has a specific role. Name | Role ----- | --- -`eax` | accumulator; system calls, I/O, arithmetic -`ebx` | base register; used for memory-based addressing -`ecx` | counter in loop instructions -`edx` | data register, used for I/O, arithmetic, interrupt values; can extend eax to 64 bits -`esi` | source in string operations -`edi` | destination in string operations -`ebp` | base or frame pointer; points to the current stack frame -`esp` | stack pointer; points to the top of the stack - -In addition to these, there are some special registers that cannot be directly accessed by the programmer, such as `eflags` and `eip` (Instruction Pointer). - -`eip` is a register that holds the address of the current instruction to be executed. +`rax` | accumulator; system calls, I/O, arithmetic +`rbx` | base register; used for memory-based addressing +`rcx` | counter in loop instructions +`rdx` | data register, used for I/O, arithmetic, interrupt values; can extend rax to 128 bits +`rsi` | source in string operations +`rdi` | destination in string operations +`rbp` | base or frame pointer; points to the current stack frame +`rsp` | stack pointer; points to the top of the stack +`r8-15` | general purpose registers + +In addition to these, there are some special registers that cannot be directly accessed by the programmer, such as `rflags` and `rip` (Instruction Pointer). + +`rip` is a register that holds the address of the current instruction to be executed. It cannot be directly modified, programmatically, but indirectly through jump, call, and ret instructions. -The `eflags` register contains `32` bits used as status indicators or condition variables. +The `rflags` register contains `64` bits used as status indicators or condition variables. We say that a flag is set if its value is `1`. The ones commonly used by programmers are: Name | Expanded Name | Description @@ -49,13 +50,10 @@ Name | Expanded Name | Description `OF` | Overflow Flag | Set if the result exceeds the maximum (or minimum) signed integer value >**NOTE**: If you follow the evolution of registers from 8086, you'll see that initially they were named `ax`, `bx`, `cx` etc., and were 16 bits in size. ->From 80386, Intel extended these registers to 32 bits (i.e., "extended" `ax` → `eax`). +>From 80386, Intel extended these registers to 32 bits (i.e., "extended" `ax` → `eax`), and again with the release of "Prescott" and "Nocona" processors. ## Instruction Classes -Although the current set of instructions for Intel processors has [hundreds of instructions](https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf), we will only look at a [small portion of them](http://css.csail.mit.edu/6.858/2015/readings/i386.pdf). -More precisely, some of the 80386 instructions. - All x86 processors instructions can fit into 3 categories : - data movement instructions @@ -107,16 +105,16 @@ We are talking about an unsigned subtraction, without storing the result. Therefore, when talking about the code: > ```assembly -> cmp eax, 0 +> cmp rax, 0 > jl negative > ``` The jump to the `negative` label will be made only if the value in eax is less than `0`. -The `eax - 0` subtraction is evaluated and if the result is negative(and so, eax is negative), the jump will be made.\ +The `rax - 0` subtraction is evaluated and if the result is negative (and so, rax is negative), the jump will be made.\ When have comparisons with `0`, the same thing can be done more efficiently using the `test` instruction: > ```assembly -> test eax, eax +> test rax, rax > jl negative >``` diff --git a/labs/lab-04/tasks/cf-of/solution/cf.asm b/labs/lab-04/tasks/cf-of/solution/cf.asm index 9258188ad..2ff06fd28 100644 --- a/labs/lab-04/tasks/cf-of/solution/cf.asm +++ b/labs/lab-04/tasks/cf-of/solution/cf.asm @@ -1,21 +1,27 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .text global main extern printf main: + push rbp + mov rbp, rsp + mov al, 0xFF - PRINTF32 `the Carry Flag is not active\n\x0` + PRINTF64 `the Carry Flag is not active\n\x0` test al, al add al, 1 jc carry_flag jmp end carry_flag: - PRINTF32 `the Carry Flag is active\n\x0` + PRINTF64 `the Carry Flag is active\n\x0` end: + xor rax, rax + + leave ret diff --git a/labs/lab-04/tasks/cf-of/solution/cf_of.asm b/labs/lab-04/tasks/cf-of/solution/cf_of.asm index ef80271b6..e7e6f67fd 100644 --- a/labs/lab-04/tasks/cf-of/solution/cf_of.asm +++ b/labs/lab-04/tasks/cf-of/solution/cf_of.asm @@ -1,15 +1,17 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .text global main extern printf main: + push rbp + mov rbp, rsp mov al, 128 - PRINTF32 `the Carry Flag and the Overflow Flag are not active\n\x0` + PRINTF64 `the Carry Flag and the Overflow Flag are not active\n\x0` test al, al ; Any value between 128 and 255 will set the carry flag @@ -23,8 +25,10 @@ cf_on: jmp end cf_of_on: - PRINTF32 `the Carry Flag and the Overflow Flag are active\n\x0` + PRINTF64 `the Carry Flag and the Overflow Flag are active\n\x0` end: - ret + xor rax, rax + leave + ret diff --git a/labs/lab-04/tasks/cf-of/solution/of.asm b/labs/lab-04/tasks/cf-of/solution/of.asm index 967e0bdbd..10e582c41 100644 --- a/labs/lab-04/tasks/cf-of/solution/of.asm +++ b/labs/lab-04/tasks/cf-of/solution/of.asm @@ -1,22 +1,27 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .text global main extern printf main: + push rbp + mov rbp, rsp mov al, 0x7F - PRINTF32 `the Overflow Flag is not active\n\x0` + PRINTF64 `the Overflow Flag is not active\n\x0` test al, al add al, 1 jo overflow_flag jmp end overflow_flag: - PRINTF32 `the Overflow Flag is active\n\x0` + PRINTF64 `the Overflow Flag is active\n\x0` end: + xor rax, rax + + leave ret diff --git a/labs/lab-04/tasks/cf-of/support/Makefile b/labs/lab-04/tasks/cf-of/support/Makefile index 549af5412..af71359ee 100644 --- a/labs/lab-04/tasks/cf-of/support/Makefile +++ b/labs/lab-04/tasks/cf-of/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -no-pie all: of cf cf_of diff --git a/labs/lab-04/tasks/cf-of/support/cf.asm b/labs/lab-04/tasks/cf-of/support/cf.asm index ee8180415..344fb9631 100644 --- a/labs/lab-04/tasks/cf-of/support/cf.asm +++ b/labs/lab-04/tasks/cf-of/support/cf.asm @@ -1,14 +1,17 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .text global main extern printf main: + push rbp + mov rbp, rsp + mov al, 0xFF - PRINTF32 `the Carry Flag is not active\n\x0` + PRINTF64 `the Carry Flag is not active\n\x0` test al, al ;TODO: activate the Carry Flag @@ -16,7 +19,10 @@ main: jmp end carry_flag: - PRINTF32 `the Carry Flag is active\n\x0` + PRINTF64 `the Carry Flag is active\n\x0` end: + xor rax, rax + + leave ret diff --git a/labs/lab-04/tasks/cf-of/support/cf_of.asm b/labs/lab-04/tasks/cf-of/support/cf_of.asm index b0d1b9ca4..78a832cbb 100644 --- a/labs/lab-04/tasks/cf-of/support/cf_of.asm +++ b/labs/lab-04/tasks/cf-of/support/cf_of.asm @@ -1,14 +1,17 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .text global main extern printf main: + push rbp + mov rbp, rsp + mov al, 128 - PRINTF32 `the Carry Flag and the Overflow Flag are not active\n\x0` + PRINTF64 `the Carry Flag and the Overflow Flag are not active\n\x0` test al, al ;TODO: activate the Carry Flag and the Overflow Flag @@ -20,8 +23,10 @@ cf_on: jmp end cf_of_on: - PRINTF32 `the Carry Flag and the Overflow Flag are active\n\x0` + PRINTF64 `the Carry Flag and the Overflow Flag are active\n\x0` end: - ret + xor rax, rax + leave + ret diff --git a/labs/lab-04/tasks/cf-of/support/of.asm b/labs/lab-04/tasks/cf-of/support/of.asm index 0b79eb95f..92d529261 100644 --- a/labs/lab-04/tasks/cf-of/support/of.asm +++ b/labs/lab-04/tasks/cf-of/support/of.asm @@ -1,22 +1,28 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .text global main extern printf main: + push rbp + mov rbp, rsp mov al, 0x7F - PRINTF32 `the Overflow Flag is not active\n\x0` + PRINTF64 `the Overflow Flag is not active\n\x0` test al, al ;TODO: activate the Overflow Flag + jo overflow_flag jmp end overflow_flag: - PRINTF32 `the Overflow Flag is active\n\x0` + PRINTF64 `the Overflow Flag is active\n\x0` end: + xor rax, rax + + leave ret diff --git a/labs/lab-04/tasks/cf-of/utils/printf32.asm b/labs/lab-04/tasks/cf-of/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-04/tasks/cf-of/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-04/tasks/cf-of/utils/printf64.asm b/labs/lab-04/tasks/cf-of/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-04/tasks/cf-of/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-04/tasks/conditional-jumps/README.md b/labs/lab-04/tasks/conditional-jumps/README.md index 05d584ca3..f508100d2 100644 --- a/labs/lab-04/tasks/conditional-jumps/README.md +++ b/labs/lab-04/tasks/conditional-jumps/README.md @@ -7,12 +7,12 @@ nav_order: 1 You will solve the exercises starting from the `hello_world.asm` file located in the `tasks/conditional-jumps` directory. -1. Modify the program so that the message is displayed only if the content of the `eax` register is greater than that of `ebx`. +1. Modify the program so that the message is displayed only if the content of the `rax` register is greater than that of `r8`. Also, modify the values of the registers to continue displaying the message `"Hello, World!"`. 1. Modify the program to also display `"Goodbye, World!"` at the end. -1. Using jump instructions, modify the program to display `"Hello, World!"` N times, where N is given through the `ecx` register. +1. Using jump instructions, modify the program to display `"Hello, World!"` N times, where N is given through the `rcx` register. Avoid infinite looping. > **TIP**: After a successful completion (with `N = 6`), the program should display: diff --git a/labs/lab-04/tasks/conditional-jumps/solution/hello_world.asm b/labs/lab-04/tasks/conditional-jumps/solution/hello_world.asm index 3267e2e46..464730656 100644 --- a/labs/lab-04/tasks/conditional-jumps/solution/hello_world.asm +++ b/labs/lab-04/tasks/conditional-jumps/solution/hello_world.asm @@ -1,10 +1,10 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data myString: db "Hello, World!", 0 - N: dd 6 ; N = 6 + N: dq 6 ; N = 6 myGoodbyeString: db "Goodbye, World!", 0 section .text @@ -12,22 +12,30 @@ section .text extern printf main: - mov ecx, DWORD [N] ; N = the value stored in ecx - PRINTF32 `%d\n\x0`, ecx ; DO NOT REMOVE/MODIFY THIS LINE + push rbp + mov rbp, rsp - mov eax, 2 - mov ebx, 1 - cmp eax, ebx - jg print ; TODO1: eax > ebx? + mov rcx, QWORD [N] ; N = the value stored in rcx + PRINTF64 `%d\n\x0`, rcx ; DO NOT REMOVE/MODIFY THIS LINE + + mov rax, 2 + mov r8, 1 + cmp rax, r8 + jg print ; TODO1: rax > r8? + xor rax, rax + + leave ret print: - PRINTF32 `%s\n\x0`, myString + PRINTF64 `%s\n\x0`, myString ; TODO2.2: print "Hello, World!" N times - dec ecx - cmp ecx, 0 + dec rcx + cmp rcx, 0 jg print ; TODO2.1: print "Goodbye, World!" - PRINTF32 `%s\n\x0`, myGoodbyeString + PRINTF64 `%s\n\x0`, myGoodbyeString + xor rax, rax + leave ret diff --git a/labs/lab-04/tasks/conditional-jumps/support/Makefile b/labs/lab-04/tasks/conditional-jumps/support/Makefile index a1e94397b..ef889026a 100644 --- a/labs/lab-04/tasks/conditional-jumps/support/Makefile +++ b/labs/lab-04/tasks/conditional-jumps/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -no-pie TARGET_EXEC = hello_world diff --git a/labs/lab-04/tasks/conditional-jumps/support/hello_world.asm b/labs/lab-04/tasks/conditional-jumps/support/hello_world.asm index 6e9362cdc..4b9af08b5 100644 --- a/labs/lab-04/tasks/conditional-jumps/support/hello_world.asm +++ b/labs/lab-04/tasks/conditional-jumps/support/hello_world.asm @@ -1,28 +1,36 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data myString: db "Hello, World!", 0 - N: dd 6 ; N = 6 + N: dq 6 ; N = 6 section .text global main extern printf main: - mov ecx, DWORD [N] ; ecx will store the value of N - PRINTF32 `%d\n\x0`, ecx ; DO NOT REMOVE/MODIFY THIS LINE + push rbp + mov rbp, rsp - mov eax, 2 - mov ebx, 1 - cmp eax, ebx - je print ; TODO1: eax > ebx? + mov rcx, QWORD [N] ; rcx will store the value of N + PRINTF64 `%d\n\x0`, rcx ; DO NOT REMOVE/MODIFY THIS LINE + + mov rax, 2 + mov r8, 1 + cmp rax, r8 + je print ; TODO1: rax > r8? + xor rax, rax + + leave ret print: - PRINTF32 `%s\n\x0`, myString + PRINTF64 `%s\n\x0`, myString ; TODO2.2: print "Hello, World!" N times ; TODO2.1: print "Goodbye, World!" + xor rax, rax + leave ret diff --git a/labs/lab-04/tasks/conditional-jumps/utils/printf32.asm b/labs/lab-04/tasks/conditional-jumps/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-04/tasks/conditional-jumps/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-04/tasks/conditional-jumps/utils/printf64.asm b/labs/lab-04/tasks/conditional-jumps/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-04/tasks/conditional-jumps/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-04/tasks/fibonacci/README.md b/labs/lab-04/tasks/fibonacci/README.md index af737ecb9..29137feb7 100644 --- a/labs/lab-04/tasks/fibonacci/README.md +++ b/labs/lab-04/tasks/fibonacci/README.md @@ -7,13 +7,13 @@ nav_order: 5 You will solve this exercise starting from the `fibonacci.asm` file located in the `tasks/fibonacci` directory. -Calculate the Nth Fibonacci number, where N is given through the `eax` register. +Calculate the Nth Fibonacci number, where N is given through the `rcx` register. >**NOTE**: The Nth Fibonacci number is `F[N]` for our chosen N. > By definition, `F[0] = 0` and `F[1] = F[2] = 1`. > Each following value is calculated with the formula `F[i] = F[i - 1] + F[i - 2]`. > ->**TIP**: For example, if the value stored in `ecx` is equal to `5`, a correct solution will display `5` and for `7`, it will display `13`. +>**TIP**: For example, if the value stored in `rcx` is equal to `5`, a correct solution will display `5` and for `7`, it will display `13`. To test the implementation, enter the `tests/` directory and run: diff --git a/labs/lab-04/tasks/fibonacci/solution/fibonacci.asm b/labs/lab-04/tasks/fibonacci/solution/fibonacci.asm index f02d13d68..f6d341cd8 100644 --- a/labs/lab-04/tasks/fibonacci/solution/fibonacci.asm +++ b/labs/lab-04/tasks/fibonacci/solution/fibonacci.asm @@ -1,31 +1,36 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data - N: dd 7 ; N-th fibonacci number to calculate + N: dq 7 ; N-th fibonacci number to calculate section .text global main extern printf main: - mov ecx, DWORD [N] ; we want to find the N-th fibonacci number; N = ECX = 7 + push rbp + mov rbp, rsp + + mov rcx, QWORD [N] ; we want to find the N-th fibonacci number; N = RCX = 7 ; TODO: calculate the N-th fibonacci number (f(0) = 0, f(1) = 1) - PRINTF32 `%d\n\x0`, ecx ; DO NOT REMOVE/MODIFY THIS LINE + PRINTF64 `%d\n\x0`, rcx ; DO NOT REMOVE/MODIFY THIS LINE - mov eax, 0 - mov ebx, 1 + mov rax, 0 + mov r9, 1 fibonacci: - dec ecx - test ecx, ecx + dec rcx + test rcx, rcx je print - add eax, ebx - xchg eax, ebx + add rax, r9 + xchg rax, r9 jmp fibonacci print: - PRINTF32 `%d\n\x0`, ebx - xor eax, eax + PRINTF64 `%d\n\x0`, r9 + xor rax, rax + + leave ret diff --git a/labs/lab-04/tasks/fibonacci/support/Makefile b/labs/lab-04/tasks/fibonacci/support/Makefile index 31d4caede..2fb5e3a50 100644 --- a/labs/lab-04/tasks/fibonacci/support/Makefile +++ b/labs/lab-04/tasks/fibonacci/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -no-pie TARGET_EXEC = fibonacci diff --git a/labs/lab-04/tasks/fibonacci/support/fibonacci.asm b/labs/lab-04/tasks/fibonacci/support/fibonacci.asm index dc6ffa125..18bdf59b0 100644 --- a/labs/lab-04/tasks/fibonacci/support/fibonacci.asm +++ b/labs/lab-04/tasks/fibonacci/support/fibonacci.asm @@ -1,18 +1,24 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data - N: dd 7 ; N-th fibonacci number to calculate + N: dq 7 ; N-th fibonacci number to calculate section .text global main extern printf main: - mov ecx, DWORD [N] ; we want to find the N-th fibonacci number; N = ECX = 7 - PRINTF32 `%d\n\x0`, ecx ; DO NOT REMOVE/MODIFY THIS LINE + push rbp + mov rbp, rsp + + mov rcx, QWORD [N] ; we want to find the N-th fibonacci number; N = RCX = 7 + PRINTF64 `%d\n\x0`, rcx ; DO NOT REMOVE/MODIFY THIS LINE ; TODO: calculate the N-th fibonacci number (f(0) = 0, f(1) = 1) + xor rax, rax + + leave ret diff --git a/labs/lab-04/tasks/fibonacci/utils/printf32.asm b/labs/lab-04/tasks/fibonacci/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-04/tasks/fibonacci/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-04/tasks/fibonacci/utils/printf64.asm b/labs/lab-04/tasks/fibonacci/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-04/tasks/fibonacci/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-04/tasks/grumpy-jumps/README.md b/labs/lab-04/tasks/grumpy-jumps/README.md index d6e05c694..1495b4271 100644 --- a/labs/lab-04/tasks/grumpy-jumps/README.md +++ b/labs/lab-04/tasks/grumpy-jumps/README.md @@ -7,13 +7,13 @@ nav_order: 2 You will solve the exercises starting from the `grumpy_jumps.asm` file located in the `tasks/grumpy-jumps` directory. -1. Modify the values of the `eax` and `ebx` registers so that when the program is run, the message `Well done!` is displayed. +1. Modify the values of the `rax` and `rbx` registers so that when the program is run, the message `Well done!` is displayed. Follow the `TODO` comments. 1. Why does the wrong message still appear? Modify the source so that the wrong message is not displayed anymore. -> **TIP**: To determine the necessary values for the `eax` and `ebx` registers, we recommend using GDB. +> **TIP**: To determine the necessary values for the `rax` and `rbx` registers, we recommend using GDB. To test the implementation, enter the `tests/` directory and run: diff --git a/labs/lab-04/tasks/grumpy-jumps/solution/grumpy_jumps.asm b/labs/lab-04/tasks/grumpy-jumps/solution/grumpy_jumps.asm index 4a56ce184..216167a85 100644 --- a/labs/lab-04/tasks/grumpy-jumps/solution/grumpy_jumps.asm +++ b/labs/lab-04/tasks/grumpy-jumps/solution/grumpy_jumps.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data wrong: db 'Not today, son.',0 @@ -11,21 +11,30 @@ section .text extern printf main: - mov eax, 0x1 ; TODO3.1: modify eax register - mov ebx, 0x4; ; TODO3.1: modify ebx register - mov ecx, 0x5 ; hardcoded; DO NOT change - cmp eax, ebx + push rbp + mov rbp, rsp + + mov rax, 0x1 ; TODO3.1: modify rax register + mov rbx, 0x4; ; TODO3.1: modify rbx register + mov rcx, 0x5 ; hardcoded; DO NOT change + cmp rax, rbx jns bad - cmp ecx, ebx + cmp rcx, rbx jb bad - add eax, ebx - xor eax, ecx + add rax, rbx + xor rax, rcx jnz bad good: - PRINTF32 `%s\n\x0`, right + PRINTF64 `%s\n\x0`, right + xor rax, rax + + leave ret bad: - PRINTF32 `%s\n\x0`, wrong + PRINTF64 `%s\n\x0`, wrong + xor rax, rax + + leave ret diff --git a/labs/lab-04/tasks/grumpy-jumps/support/Makefile b/labs/lab-04/tasks/grumpy-jumps/support/Makefile index 5572f3474..d3783114e 100644 --- a/labs/lab-04/tasks/grumpy-jumps/support/Makefile +++ b/labs/lab-04/tasks/grumpy-jumps/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -no-pie TARGET_EXEC = grumpy_jumps diff --git a/labs/lab-04/tasks/grumpy-jumps/support/grumpy_jumps.asm b/labs/lab-04/tasks/grumpy-jumps/support/grumpy_jumps.asm index aa7dc661b..63d584a42 100644 --- a/labs/lab-04/tasks/grumpy-jumps/support/grumpy_jumps.asm +++ b/labs/lab-04/tasks/grumpy-jumps/support/grumpy_jumps.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data wrong: db 'Not today, son.',0 @@ -11,20 +11,30 @@ section .text extern printf main: - mov eax, 0xdeadc0de ; TODO3.1: modify eax register - mov ebx, 0x1337ca5e ; TODO3.1: modify ebx register - mov ecx, 0x5 ; hardcoded; DO NOT change - cmp eax, ebx + push rbp + mov rbp, rsp + + mov rax, 0xdeadc0de ; TODO3.1: modify rax register + mov rbx, 0x1337ca5e ; TODO3.1: modify rbx register + mov rcx, 0x5 ; hardcoded; DO NOT change + cmp rax, rbx jns bad - cmp ecx, ebx + cmp rcx, rbx jb bad - add eax, ebx - xor eax, ecx + add rax, rbx + xor rax, rcx jnz bad good: - PRINTF32 `%s\n\x0`, right + PRINTF64 `%s\n\x0`, right + xor rax, rax + + leave + ret bad: - PRINTF32 `%s\n\x0`, wrong + PRINTF64 `%s\n\x0`, wrong + xor rax, rax + + leave ret diff --git a/labs/lab-04/tasks/grumpy-jumps/utils/printf32.asm b/labs/lab-04/tasks/grumpy-jumps/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-04/tasks/grumpy-jumps/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-04/tasks/grumpy-jumps/utils/printf64.asm b/labs/lab-04/tasks/grumpy-jumps/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-04/tasks/grumpy-jumps/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-04/tasks/min/README.md b/labs/lab-04/tasks/min/README.md index ad6901faf..8f9f8bf82 100644 --- a/labs/lab-04/tasks/min/README.md +++ b/labs/lab-04/tasks/min/README.md @@ -7,7 +7,7 @@ nav_order: 4 You will solve this exercise starting from the `min.asm` file located in the `tasks/min` directory. -Calculate the minimum of the numbers in 2 registers (`eax` and `ebx`) using a comparison instruction, a jump instruction, and the `xchg` instruction. +Calculate the minimum of the numbers in 2 registers (`r9` and `r10`) using a comparison instruction, a jump instruction, and the `xchg` instruction. To test the implementation, enter the `tests/` directory and run: diff --git a/labs/lab-04/tasks/min/solution/min.asm b/labs/lab-04/tasks/min/solution/min.asm index 31b0b67a8..dac870bfa 100644 --- a/labs/lab-04/tasks/min/solution/min.asm +++ b/labs/lab-04/tasks/min/solution/min.asm @@ -1,27 +1,32 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data - FIRST_NUMBER dd 4 ; The first number - SECOND_NUMBER dd 1 ; The second number + FIRST_NUMBER dq 4 ; The first number + SECOND_NUMBER dq 1 ; The second number section .text global main extern printf main: - ; The two numbers can be found in the registers eax and ebx - mov eax, DWORD [FIRST_NUMBER] ; The first number - mov ebx, DWORD [SECOND_NUMBER] ; The second number - PRINTF32 `%d\n\x0`, eax ; print the first number - PRINTF32 `%d\n\x0`, ebx ; print the second number + push rbp + mov rbp, rsp - ; TODO: find the minimum of the two numbers and store it in eax - cmp eax, ebx + ; The two numbers can be found in the registers r9 and r10 + mov r9, QWORD [FIRST_NUMBER] ; The first number + mov r10, QWORD [SECOND_NUMBER] ; The second number + PRINTF64 `%d\n\x0`, r9 ; print the first number + PRINTF64 `%d\n\x0`, r10 ; print the second number + + ; TODO: find the minimum of the two numbers and store it in r9 + cmp r9, r10 jl print_min - xchg eax, ebx + xchg r9, r10 print_min: - PRINTF32 `%d\n\x0`, eax ; print the minimum number - xor eax, eax + PRINTF64 `%d\n\x0`, r9 ; print the minimum number + xor rax, rax + + leave ret diff --git a/labs/lab-04/tasks/min/support/Makefile b/labs/lab-04/tasks/min/support/Makefile index 6b08351ac..490c697ee 100644 --- a/labs/lab-04/tasks/min/support/Makefile +++ b/labs/lab-04/tasks/min/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -no-pie TARGET_EXEC = min diff --git a/labs/lab-04/tasks/min/support/min.asm b/labs/lab-04/tasks/min/support/min.asm index c99549cc8..3d5acf3da 100644 --- a/labs/lab-04/tasks/min/support/min.asm +++ b/labs/lab-04/tasks/min/support/min.asm @@ -1,25 +1,30 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data - FIRST_NUMBER dd 4 ; The first number - SECOND_NUMBER dd 1 ; The second number + FIRST_NUMBER dq 4 ; The first number + SECOND_NUMBER dq 1 ; The second number section .text global main extern printf main: - ; The two numbers can be found in the registers eax and ebx - mov eax, DWORD [FIRST_NUMBER] ; The first number - mov ebx, DWORD [SECOND_NUMBER] ; The second number - PRINTF32 `%d\n\x0`, eax ; print the first number + push rbp + mov rbp, rsp + + ; The two numbers can be found in the registers r9 and r10 + mov r9, QWORD [FIRST_NUMBER] ; The first number + mov r10, QWORD [SECOND_NUMBER] ; The second number + PRINTF64 `%d\n\x0`, r9 ; print the first number ; DO NOT REMOVE/MODIFY THIS LINE - PRINTF32 `%d\n\x0`, ebx ; print the second number + PRINTF64 `%d\n\x0`, r10 ; print the second number ; DO NOT REMOVE/MODIFY THIS LINE - ; TODO: find the minimum of the two numbers and store it in eax - PRINTF32 `%d\n\x0`, eax ; print the minimum + ; TODO: find the minimum of the two numbers and store it in r9 + PRINTF64 `%d\n\x0`, r9 ; print the minimum + xor rax, rax + leave ret diff --git a/labs/lab-04/tasks/min/utils/printf32.asm b/labs/lab-04/tasks/min/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-04/tasks/min/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-04/tasks/min/utils/printf64.asm b/labs/lab-04/tasks/min/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-04/tasks/min/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-04/tasks/sets/README.md b/labs/lab-04/tasks/sets/README.md index 88e07f053..3bd86ae94 100644 --- a/labs/lab-04/tasks/sets/README.md +++ b/labs/lab-04/tasks/sets/README.md @@ -7,11 +7,11 @@ nav_order: 3 You will solve the exercises starting from the `sets.asm` file located in the `tasks/sets` directory. -You need to implement operations on sets that can contain elements between 0 and 31. +You need to implement operations on sets that can contain elements between 0 and 63. An efficient way to do this (both in terms of space and speed) would be to represent sets so that a register represents a set. Each bit in the register represents an element in the set (if bit i is set, then the set contains element i). -> **TIP**: For example: if `eax` contains the representation of the set `{0, 2, 4}`, the register value would be `2^0 + 2^2 + 2^4 = 1 + 4 + 16 = 21`. +> **TIP**: For example: if `rax` contains the representation of the set `{0, 2, 4}`, the register value would be `2^0 + 2^2 + 2^4 = 1 + 4 + 16 = 21`. > Educate yourself about the available instructions on the [x86 architecture](http://www.cs.virginia.edu/~evans/cs216/guides/x86.html). - You have two defined sets. @@ -19,23 +19,23 @@ What values do they contain? You do not need to make the code print the actual values stored in the sets. Perform the union of the two sets. -- Use the `or` instruction to add two new elements to the first set (the set stored in the `eax` register). +- Use the `or` instruction to add two new elements to the first set (the set stored in the `rax` register). -> **NOTE**: Do not reset the `eax` register after this exercise. +> **NOTE**: Do not reset the `rax` register after this exercise. > Its new value will be the one used by the checker for further evaluation. > -> **TIP**: Take advantage of the fact that the current sets, although they have "space" for 32 bits, only use 8 bits. +> **TIP**: Take advantage of the fact that the current sets, although they have "space" for 64 bits, only use 8 bits. > If you `or` with a number greater than 255 (`0xff`, `2^8-1`) which has two active bits, you will effectively add two new elements to the set. - Perform the intersection of the two sets. -- Determine the elements missing from the `eax` set for it to be complete. +- Determine the elements missing from the `rax` set for it to be complete. > **TIP**: You need to take the complement of the number using the `not` instruction. -- Remove an element from the first set (the set stored in the `eax` register). +- Remove an element from the first set (the set stored in the `rax` register). -> **NOTE**: Do not reset the `eax` register after this exercise. +> **NOTE**: Do not reset the `rax` register after this exercise. > Its new value will be the one used by the checker for further evaluation. - Find the difference between the sets. diff --git a/labs/lab-04/tasks/sets/solution/sets.asm b/labs/lab-04/tasks/sets/solution/sets.asm index bf71668d0..f3b186e64 100644 --- a/labs/lab-04/tasks/sets/solution/sets.asm +++ b/labs/lab-04/tasks/sets/solution/sets.asm @@ -1,54 +1,59 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data - FIRST_SET: dd 139 ; The first set - SECOND_SET: dd 169 ; The second set + FIRST_SET: dq 139 ; The first set + SECOND_SET: dq 169 ; The second set section .text global main extern printf main: - ; The two sets can be found in the registers eax and ebx - mov eax, DWORD [FIRST_SET] - mov ebx, DWORD [SECOND_SET] - PRINTF32 `%u\n\x0`, eax ; print the first set - PRINTF32 `%u\n\x0`, ebx ; print the second set + push rbp + mov rbp, rsp + + ; The two sets can be found in the registers rax and rbx + mov rax, QWORD [FIRST_SET] + mov rbx, QWORD [SECOND_SET] + PRINTF64 `%u\n\x0`, rax ; print the first set + PRINTF64 `%u\n\x0`, rbx ; print the second set ; TODO1: reunion of the two sets - mov edx, eax - or edx, ebx - PRINTF32 `%u\n\x0`, edx + mov rdx, rax + or rdx, rbx + PRINTF64 `%u\n\x0`, rdx ; TODO2: adding an element to a set - or eax, 0x300 ; We will add the elements 8 and 9 to the set + or rax, 0x300 ; We will add the elements 8 and 9 to the set ; 0x300 <==> ...11|0000|0000 - PRINTF32 `%u\n\x0`, eax + PRINTF64 `%u\n\x0`, rax ; TODO3: intersection of the two sets - mov edx, eax - and edx, ebx - PRINTF32 `%u\n\x0`, edx + mov rdx, rax + and rdx, rbx + PRINTF64 `%u\n\x0`, rdx ; TODO4: the complement of a set - mov edx, eax - not edx - PRINTF32 `%u\n\x0`, edx + mov rdx, rax + not rdx + PRINTF64 `%u\n\x0`, rdx ; TODO5: removal of an element from a set ; We will remove the 3rd element from the set - mov edx, 1 - shl edx, 3 - not edx - and eax, edx - PRINTF32 `%u\n\x0`, eax + mov rdx, 1 + shl rdx, 3 + not rdx + and rax, rdx + PRINTF64 `%u\n\x0`, rax ; TODO6: difference of two sets - not ebx - and eax, ebx - PRINTF32 `%u\n\x0`, eax + not rbx + and rax, rbx + PRINTF64 `%u\n\x0`, rax + + xor rax, rax - xor eax, eax + leave ret diff --git a/labs/lab-04/tasks/sets/support/Makefile b/labs/lab-04/tasks/sets/support/Makefile index a4dfb815d..2b8bb3bb5 100644 --- a/labs/lab-04/tasks/sets/support/Makefile +++ b/labs/lab-04/tasks/sets/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -no-pie TARGET_EXEC = sets diff --git a/labs/lab-04/tasks/sets/support/sets.asm b/labs/lab-04/tasks/sets/support/sets.asm index 9485dee75..e034c29c0 100644 --- a/labs/lab-04/tasks/sets/support/sets.asm +++ b/labs/lab-04/tasks/sets/support/sets.asm @@ -1,21 +1,24 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data - FIRST_SET: dd 139 ; The first set - SECOND_SET: dd 169 ; The second set + FIRST_SET: dq 139 ; The first set + SECOND_SET: dq 169 ; The second set section .text global main extern printf main: + push rbp + mov rbp, rsp + ; The two sets can be found in the FIRST_SET and SECOND_SET variables - mov eax, DWORD [FIRST_SET] - mov ebx, DWORD [SECOND_SET] - PRINTF32 `%u\n\x0`, eax ; print the first set - PRINTF32 `%u\n\x0`, ebx ; print the second set + mov rax, QWORD [FIRST_SET] + mov rbx, QWORD [SECOND_SET] + PRINTF64 `%u\n\x0`, rax ; print the first set + PRINTF64 `%u\n\x0`, rbx ; print the second set ; TODO1: reunion of the two sets @@ -34,6 +37,7 @@ main: ; TODO6: difference of two sets + xor rax, rax - xor eax, eax + leave ret diff --git a/labs/lab-04/tasks/sets/utils/printf32.asm b/labs/lab-04/tasks/sets/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-04/tasks/sets/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-04/tasks/sets/utils/printf64.asm b/labs/lab-04/tasks/sets/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-04/tasks/sets/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-05/guides/addressing-arrays/README.md b/labs/lab-05/guides/addressing-arrays/README.md index b20e0b65b..2a16647ba 100644 --- a/labs/lab-05/guides/addressing-arrays/README.md +++ b/labs/lab-05/guides/addressing-arrays/README.md @@ -9,5 +9,5 @@ To follow this guide, you'll need to use the `addressing_arrays.asm` file locate The program increments the values of an array of 10 integers by 1 and iterates through the array before and after to show the changes. -> **Note:** `ecx` is used as the loop counter. +> **Note:** `rcx` is used as the loop counter. Since the array contains `dwords` (4 bytes), the loop counter is multiplied by 4 to get the address of the next element in the array. diff --git a/labs/lab-05/guides/addressing-arrays/support/Makefile b/labs/lab-05/guides/addressing-arrays/support/Makefile index 290330081..5a7a42a8b 100644 --- a/labs/lab-05/guides/addressing-arrays/support/Makefile +++ b/labs/lab-05/guides/addressing-arrays/support/Makefile @@ -6,9 +6,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGETS := $(OBJS:.o=) diff --git a/labs/lab-05/guides/addressing-arrays/support/addressing_arrays.asm b/labs/lab-05/guides/addressing-arrays/support/addressing_arrays.asm index 2f98ca9e4..9523cdff3 100644 --- a/labs/lab-05/guides/addressing-arrays/support/addressing_arrays.asm +++ b/labs/lab-05/guides/addressing-arrays/support/addressing_arrays.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" %define ARRAY_SIZE 10 @@ -12,45 +12,46 @@ section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - xor eax, eax + xor rax, rax - ; ecx - loop counter/array index, starting from 0 - ; [my_array + ecx*4] - since our array stores double-words (4 bytes) - ; we multiply `ecx` by 4 to index the `ecx`-th element - ; cmp ecx, ARRAY_SIZE - compare `ecx` with ARRAY_SIZE, sets EFLAGS accordingly - ; jl for_print_before - `jl` instruction checks if `ecx` < ARRAY_SIZE (based on EFLAGS) + ; rcx - loop counter/array index, starting from 0 + ; [my_array + rcx*4] - since our array stores double-words (4 bytes) + ; we multiply `rcx` by 4 to index the `rcx`-th element + ; cmp rcx, ARRAY_SIZE - compare `rcx` with ARRAY_SIZE, sets RFLAGS accordingly + ; jl for_print_before - `jl` instruction checks if `rcx` < ARRAY_SIZE (based on RFLAGS) ; and it jumps to the `for_print_before` label, otherwise it ; continues to the next instruction ; print out the array before incrementing its elements - xor ecx, ecx + xor rcx, rcx for_print_before: - PRINTF32 `my_array[%d]: %d\n\x0`, ecx, [my_array + ecx*4] - inc ecx - cmp ecx, ARRAY_SIZE + mov rdx, [my_array + rcx*4] ; Load value into rdx for printing + PRINTF64 `my_array[%d]: %d\n\x0`, rcx, rdx + inc rcx + cmp rcx, ARRAY_SIZE jl for_print_before - PRINTF32 `\n\x0` + PRINTF64 `\n\x0` ; increment each element of the array - xor ecx, ecx + xor rcx, rcx for: - inc dword [my_array + ecx*4] - inc ecx - cmp ecx, ARRAY_SIZE + inc dword [my_array + rcx*4] + inc rcx + cmp rcx, ARRAY_SIZE jl for ; print out the array after incrementing its elements - xor ecx, ecx + xor rcx, rcx for_print_after: - PRINTF32 `my_array[%d]: %d\n\x0`, ecx, [my_array + ecx*4] - inc ecx - cmp ecx, ARRAY_SIZE + mov rdx, [my_array + rcx*4] ; Load value into rdx for printing + PRINTF64 `my_array[%d]: %d\n\x0`, rcx, rdx + inc rcx + cmp rcx, ARRAY_SIZE jl for_print_after leave ret - diff --git a/labs/lab-05/guides/addressing-arrays/utils/printf32.asm b/labs/lab-05/guides/addressing-arrays/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-05/guides/addressing-arrays/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-05/guides/addressing-arrays/utils/printf64.asm b/labs/lab-05/guides/addressing-arrays/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-05/guides/addressing-arrays/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-05/guides/declarations/support/Makefile b/labs/lab-05/guides/declarations/support/Makefile index 290330081..5a7a42a8b 100644 --- a/labs/lab-05/guides/declarations/support/Makefile +++ b/labs/lab-05/guides/declarations/support/Makefile @@ -6,9 +6,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGETS := $(OBJS:.o=) diff --git a/labs/lab-05/guides/declarations/support/declarations.asm b/labs/lab-05/guides/declarations/support/declarations.asm index fb00d7e58..017f9fc6f 100644 --- a/labs/lab-05/guides/declarations/support/declarations.asm +++ b/labs/lab-05/guides/declarations/support/declarations.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" %define ARRAY_SIZE 13 %define DECIMAL_PLACES 5 @@ -25,9 +25,10 @@ section .data decimal_point db ".",0 var db 64 ; Declare a byte with value 64 db 10 ; Declare a byte with value 10. - ; This byte can be found at address (var + 1) + ; This byte can be found at address (var + 1) Y dd 3000 ; Declare a double word (4 bytes) Z dd 1, 2, 3 ; Declare a double word array + big_number dq 9000000000 ; Declare a quad word (8 bytes) var5 equ 2 my_array times 3 db 8, 9, 10 @@ -36,15 +37,15 @@ section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - xor eax, eax - mov ecx, ARRAY_SIZE + xor rax, rax + mov rcx, ARRAY_SIZE mov al, [var] - PRINTF32 `var: %d\n\x0`, eax + PRINTF64 `var: %d\n\x0`, rax leave ret diff --git a/labs/lab-05/guides/declarations/utils/printf32.asm b/labs/lab-05/guides/declarations/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-05/guides/declarations/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-05/guides/declarations/utils/printf64.asm b/labs/lab-05/guides/declarations/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-05/guides/declarations/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-05/guides/floating-point-exception/support/Makefile b/labs/lab-05/guides/floating-point-exception/support/Makefile index 43acbff58..01d412455 100644 --- a/labs/lab-05/guides/floating-point-exception/support/Makefile +++ b/labs/lab-05/guides/floating-point-exception/support/Makefile @@ -6,9 +6,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGETS := $(OBJS:.o=) diff --git a/labs/lab-05/guides/floating-point-exception/support/floating_point_exception.asm b/labs/lab-05/guides/floating-point-exception/support/floating_point_exception.asm index 60a57d029..468eac6d6 100644 --- a/labs/lab-05/guides/floating-point-exception/support/floating_point_exception.asm +++ b/labs/lab-05/guides/floating-point-exception/support/floating_point_exception.asm @@ -1,34 +1,33 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .text extern printf global main main: - push ebp - mov ebp, esp - xor eax, eax - xor ebx, ebx + push rbp + mov rbp, rsp + xor rax, rax + xor rbx, rbx ; using 8 bit unsigned division, the result/quotient needs to be in the range [0, 255] ; 22891 / 2 = 11445, thus we will have an overflow, causing a floating point exception ; Tip: 1) find out what is the minimum value for `bl` such that it does not overflow - ; 2) change the register sizes such that the division does not overflow + ; 2) change the register sizes such that the division does not overflow mov ax, 22891 mov bl, 2 div bl ; qutoient is stored in al - PRINTF32 `Result is: %hhu\n\0`, eax + PRINTF64 `Result is: %hhu\n\0`, rax ; remainder is stored in ah, thus we need to shift it to the right by 8 bits - shr eax, 8 - PRINTF32 `Result is: %hhu\n\0`, eax + shr rax, 8 + PRINTF64 `Result is: %hhu\n\0`, rax leave ret - diff --git a/labs/lab-05/guides/floating-point-exception/utils/printf32.asm b/labs/lab-05/guides/floating-point-exception/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-05/guides/floating-point-exception/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-05/guides/floating-point-exception/utils/printf64.asm b/labs/lab-05/guides/floating-point-exception/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-05/guides/floating-point-exception/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-05/guides/loop/README.md b/labs/lab-05/guides/loop/README.md index 50d204269..e918ca2ed 100644 --- a/labs/lab-05/guides/loop/README.md +++ b/labs/lab-05/guides/loop/README.md @@ -10,6 +10,6 @@ To follow this guide, you'll need to use the `loop.asm` file located in the `gui This program illustrates how to use the `loop` instruction, as well as how to index an array of `dwords`. >**Note**: The `loop` instruction jumps to the given label when the `count` register is not equal to 0. -In the case of `x86` the `count` register is `ecx`. +In the case of `x86` the `count` register is `rcx`. > >**Note**: For a detailed description of the `loop` instruction check out the [documentation](https://www.felixcloutier.com/x86/loop:loopcc). diff --git a/labs/lab-05/guides/loop/support/Makefile b/labs/lab-05/guides/loop/support/Makefile index 290330081..5a7a42a8b 100644 --- a/labs/lab-05/guides/loop/support/Makefile +++ b/labs/lab-05/guides/loop/support/Makefile @@ -6,9 +6,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGETS := $(OBJS:.o=) diff --git a/labs/lab-05/guides/loop/support/loop.asm b/labs/lab-05/guides/loop/support/loop.asm index 782c8bac2..584ab2465 100644 --- a/labs/lab-05/guides/loop/support/loop.asm +++ b/labs/lab-05/guides/loop/support/loop.asm @@ -1,4 +1,4 @@ -%include "printf32.asm" +%include "printf64.asm" %define ARRAY_SIZE 9 ; loop = loope @@ -11,39 +11,40 @@ section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - xor eax, eax + xor rax, rax ; for simplicity, we display the array as it's starting from 1 - ; the true index is `ecx - 1`, one less then the one displayed + ; the true index is `rcx - 1`, one less then the one displayed - ; ecx - loop counter - ; [my_array + ecx*4 - 4] - the `-4` is required for + ; rcx - loop counter + ; [my_array + rcx*4 - 4] - the `-4` is required for ; indexing in the bounds of the array ; print the array before incrementing - mov ecx, ARRAY_SIZE + mov rcx, ARRAY_SIZE for_print_before: - PRINTF32 `my_array[%d]: %d\n\x0`, ecx, [my_array + ecx*4 - 4] + mov rdx, rcx ; Move loop counter to rdx for printing + mov rsi, [my_array + rcx*4 - 4] ; Move array value to rsi for printing + PRINTF64 `my_array[%d]: %d\n\x0`, rdx, rsi loop for_print_before ; increment all array elements except the first 3 - mov ecx, ARRAY_SIZE + mov rcx, ARRAY_SIZE for: - cmp ecx, 3 + cmp rcx, 3 jle label1 - inc dword [my_array + ecx*4 - 4] + inc dword [my_array + rcx*4 - 4] loop for ; print the array after incrementing label1: - mov ecx, ARRAY_SIZE + mov rcx, ARRAY_SIZE for_print_after: - PRINTF32 `my_array[%d]: %d\n\x0`, ecx, [my_array + ecx*4 - 4] + PRINTF64 `my_array[%d]: %d\n\x0`, rcx, qword [my_array + rcx*4 - 4] loop for_print_after leave ret - diff --git a/labs/lab-05/guides/loop/utils/printf32.asm b/labs/lab-05/guides/loop/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-05/guides/loop/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-05/guides/loop/utils/printf64.asm b/labs/lab-05/guides/loop/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-05/guides/loop/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-05/guides/multiply-divide/support/Makefile b/labs/lab-05/guides/multiply-divide/support/Makefile index 290330081..5a7a42a8b 100644 --- a/labs/lab-05/guides/multiply-divide/support/Makefile +++ b/labs/lab-05/guides/multiply-divide/support/Makefile @@ -6,9 +6,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGETS := $(OBJS:.o=) diff --git a/labs/lab-05/guides/multiply-divide/support/multiply_divide.asm b/labs/lab-05/guides/multiply-divide/support/multiply_divide.asm index b9d9e318f..59ca7d714 100644 --- a/labs/lab-05/guides/multiply-divide/support/multiply_divide.asm +++ b/labs/lab-05/guides/multiply-divide/support/multiply_divide.asm @@ -1,36 +1,36 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - ; edx:eax = eax * r/m32, where r/m32 is a 32-bit register or memory location - mov eax, 10 - mov ebx, 30 - mul ebx + ; rdx:rax = rax * r/m64, where r/m64 is a 64-bit register or memory location + mov rax, 10 + mov rbx, 30 + mul rbx - PRINTF32 `Result is: %u\n\x0`, eax + PRINTF64 `Result is: %u\n\x0`, rax - ; make sure to always clear the edx register before performing a division + ; make sure to always clear the rdx register before performing a division ; there might be leftover data from previous operations - xor edx, edx + xor rdx, rdx - ; qutotient: eax = edx:eax / r/m32 - ; remainder: edx = edx:eax % r/m32 + ; qutotient: rax = rdx:rax / r/m64 + ; remainder: rdx = rdx:rax % r/m64 ; both operations are performed at the same time, using the div instruction:w - ; in this case the divisor is stored in ebx - mov eax, 10 - mov ebx, 3 - div ebx + ; in this case the divisor is stored in rbx + mov rax, 10 + mov rbx, 3 + div rbx - PRINTF32 `Quotient is: %u\n\x0`, eax - PRINTF32 `Remainder is: %u\n\x0`, edx + PRINTF64 `Quotient is: %u\n\x0`, rax + PRINTF64 `Remainder is: %u\n\x0`, rdx leave ret diff --git a/labs/lab-05/guides/multiply-divide/utils/printf32.asm b/labs/lab-05/guides/multiply-divide/utils/printf32.asm deleted file mode 100644 index c32da02eb..000000000 --- a/labs/lab-05/guides/multiply-divide/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -;dSPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-05/guides/multiply-divide/utils/printf64.asm b/labs/lab-05/guides/multiply-divide/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-05/guides/multiply-divide/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-05/media/registers-x86_64.svg b/labs/lab-05/media/registers-x86_64.svg new file mode 100644 index 000000000..70d613824 --- /dev/null +++ b/labs/lab-05/media/registers-x86_64.svg @@ -0,0 +1,4 @@ + + + +
8 bits
8 bits
16 bits
32 bits
AX
AL
AH
EAX
RAX
BX
BL
BH
EBX
RBX
CX
CL
CH
ECX
RCX
BP
EBP
RBP
DX
DL
DH
EDX
RDX
DI
EDI
RDI
SI
ESI
RSI
SP
ESP
RSP
R8W
R8B
R8D
R8
R15W
R15B
R15D
R15
\ No newline at end of file diff --git a/labs/lab-05/media/registers.svg b/labs/lab-05/media/registers.svg deleted file mode 100644 index 62225e9fd..000000000 --- a/labs/lab-05/media/registers.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - -
AH
AL
AX
EAX
BH
BL
BX
EBX
CH
CL
CX
ECX
DH
DL
DX
EDX
BP
EBP
SI
ESI
DI
EDI
SP
ESP
16 bits
8 bits
8 bits
\ No newline at end of file diff --git a/labs/lab-05/reading/memory-addressing.md b/labs/lab-05/reading/memory-addressing.md index ee2c2a9f2..5a3894d33 100644 --- a/labs/lab-05/reading/memory-addressing.md +++ b/labs/lab-05/reading/memory-addressing.md @@ -5,24 +5,24 @@ parent: Lab 5 - Registers and Memory Addressing # Reading: Memory Addressing -Modern x86 processors can address up to 2^32 bytes of memory, which means memory addresses are represented on 32 bits. +Modern x86_64 processors can address up to 2^64 bytes of memory, which means memory addresses are represented on 64 bits. To address memory, the processor uses addresses (implicitly, each label is translated into a corresponding memory address). Besides labels, there are other forms of addressing memory: ```Assembly -mov eax, [0xcafebab3] ; direct (displacement) -mov eax, [esi] ; indirect (base) -mov eax, [ebp-8] ; based (base + displacement) -mov eax, [ebx*4 + 0xdeadbeef] ; indexed (index * scale + displacement) -mov eax, [edx + ebx + 12] ; based and indexed without scale (base + index + displacement) -mov eax, [edx + ebx*4 + 42] ; based and indexed with scale (base + index * scale + displacement) +mov rax, [0xcafebabe12345678] ; direct (displacement) +mov rax, [rsi] ; indirect (base) +mov rax, [rbp-8] ; based (base + displacement) +mov rax, [rbx*4 + 0xdeadbeef] ; indexed (index * scale + displacement) +mov rax, [rdx + rbx + 12] ; based and indexed without scale (base + index + displacement) +mov rax, [rdx + r8*4 + 42] ; based and indexed with scale (base + index * scale + displacement) ``` > **WARNING**: The following addressing modes are invalid: > > ```Assembly ->mov eax, [ebx-ecx] ; Registers can only be added ->mov [eax+esi+edi], ebx ; The address calculation can involve at most 2 registers +>mov rax, [rbx-rcx] ; Registers can only be added +>mov [rax+rsi+rdi], rbx ; The address calculation can involve at most 2 registers > ``` ## Size Directives @@ -32,42 +32,44 @@ For example, in the above addressing modes, the size of the values could be infe Let's consider the following instruction: ```Assembly -mov [ebx], 2 +mov [rbx], 2 ``` -As seen, it intends to store the value 2 at the address contained in the `ebx` register. -The size of the register is 4 bytes. -The value 2 can be represented on both 1 and 4 bytes. -In this case, since both interpretations are valid, the processor needs additional information on how to treat this value. +As seen, it intends to store the value 2 at the address contained in the `rbx` register. +The size of the register is 8 bytes. +The value 2 can be represented on 1, 2, 4, or 8 bytes. +In this case, since multiple interpretations are valid, the processor needs additional information on how to treat this value. This can be done through size directives: ```Assembly -mov byte [ebx], 2 ; Move the value 2 into the byte at the address contained in ebx. -mov word [ebx], 2 ; Move the entire 2 represented in 16 bits into the 2 bytes - ; starting from the address contained in ebx. -mov dword [ebx], 2 ; Move the entire 2 represented in 32 bits into the 4 bytes - ; starting from the address contained in ebx. +mov byte [rbx], 2 ; Move the value 2 into the byte at the address contained in rbx. +mov word [rbx], 2 ; Move the entire 2 represented in 16 bits into the 2 bytes + ; starting from the address contained in rbx. +mov dword [rbx], 2 ; Move the entire 2 represented in 32 bits into the 4 bytes + ; starting from the address contained in rbx. +mov qword [rbx], 2 ; Move the entire 2 represented in 64 bits into the 8 bytes + ; starting from the address contained in rbx. ``` ## Loop Instruction -The loop instruction is used for loops with a predetermined number of iterations, loaded into the `ecx` register. +The loop instruction is used for loops with a predetermined number of iterations, loaded into the `rcx` register. Its syntax is as follows: ```Assembly -mov ecx, 10 ; Initialize ecx with the number of iterations +mov rcx, 10 ; Initialize rcx with the number of iterations label: ; loop content loop label ``` -At each iteration, the `ecx` register is decremented, and if it's not equal to 0, the execution jumps to the specified label. +At each iteration, the `rcx` register is decremented, and if it's not equal to 0, the execution jumps to the specified label. There are other forms of the instruction that additionally check the `ZF` flag: Mnemonic | Description --------------------- | ----------- -`loope/loopz label` | Decrement `ecx`, jump to label if `ecx != 0` and `ZF == 1` -`loopne/loopnz label` | Decrement `ecx`, jump to label if `ecx != 0` and `ZF != 1` +`loope/loopz label` | Decrement `rcx`, jump to label if `rcx != 0` and `ZF == 1` +`loopne/loopnz label` | Decrement `rcx`, jump to label if `rcx != 0` and `ZF != 1` > **NOTE**: When using jumps in an assembly language program, it's important to consider the difference between a `short jump` (near jump) and a `long jump` (far jump). > diff --git a/labs/lab-05/reading/registers.md b/labs/lab-05/reading/registers.md index 50d5a0ed2..8f097d4d5 100644 --- a/labs/lab-05/reading/registers.md +++ b/labs/lab-05/reading/registers.md @@ -8,20 +8,20 @@ parent: Lab 5 - Registers and Memory Addressing Registers are the primary "tools" used to write programs in assembly language. They are like variables built into the processor. Using registers instead of direct memory addressing makes developing and reading assembly-written programs faster and easier. -The only disadvantage of programming in x86 assembly language is that there are few registers. -Modern x86 processors have 8 general-purpose registers whose size is 32 bits. -The names of the registers are of historical nature (for example: `eax` was called the accumulator register because it is used by a series of arithmetic instructions, such as [idiv](https://www.felixcloutier.com/x86/idiv)). -While most registers have lost their special purpose, becoming "general purpose" in the modern ISA (`eax`, `ebx`, `ecx`, `edx`, `esi`, `edi`), by convention, 2 have retained their initial purpose: `esp` (stack pointer) and `ebp` (base pointer). +Modern x86_64 processors have 16 general-purpose registers whose size is 64 bits. +Some of these are just extensions of the original 32-bit registers, while others are newly added (`r8` to `r15`). +The names of the registers are of historical nature (for example: `rax`'s ancestor, `eax`, was called the accumulator register because it is used by a series of arithmetic instructions, such as [idiv](https://www.felixcloutier.com/x86/idiv)). +While most registers have lost their special purpose, becoming "general purpose" in the modern ISA (`rax`, `rbx`, `rcx`, `rdx`, `rsi`, `rdi`, `rsp`, `rbp`), by convention, 2 have retained their initial purpose: `rsp` (stack pointer) and `rbp` (base pointer). ## Register Subsections -In certain cases, we want to manipulate values that are represented in less than 4 bytes (for example, working with character strings). -For these situations, x86 processors offer us the possibility to work with subsections of 1 and 2 bytes of the `eax`, `ebx`, `ecx`, `edx` registers. +In certain cases, we want to manipulate values that are represented in less than 8 bytes. +For these situations, x86_64 processors offer us the possibility to work with subsections of the registers. The image below represents the registers, their subsections, and their sizes. -![x86_32 Registers](../media/registers.svg) +![x86_64 Registers](../media/registers-x86_64.svg) >**WARNING**: Subsections are part of registers, which means that if we modify a register, we implicitly modify the value of the subsection. > @@ -33,36 +33,42 @@ The image below represents the registers, their subsections, and their sizes. ## Static Memory Region Declarations -Static memory declarations (analogous to declaring global variables) in the x86 world are made through special assembly directives. +Static memory declarations (analogous to declaring global variables) in the x86_64 world are made through special assembly directives. These declarations are made in the data section (the `.data` region). Names can be attached to the declared memory portions through a label to easily reference them later in the program. Follow the example below: ```Assembly .DATA - var `db` 64 ; Declares a byte containing the value 64. Labels - ; the memory location as "var". - var2 `db` ? ; Declares an uninitialized byte labeled "var2". - `db` 10 ; Declares an unlabeled byte, initialized with 10. This - ; byte will be placed at the address (var2 + 1). - X `dw` ? ; Declares an uninitialized word (2 bytes), labeled "X". - Y `dd` 3000 ; Declares a double word (4 bytes) labeled "Y", - ; initialized with the value 3000. - Z `dd` 1,2,3 ; Declares 3 double words (each 4 bytes) - ; starting from address "Z" and initialized with 1, 2, and 3, respectively. - ; For example, 3 will be placed at the address (Z + 8). + var db 64 ; Declares a byte containing the value 64. Labels + ; the memory location as "var". + var2 db ? ; Declares an uninitialized byte labeled "var2". + db 10 ; Declares an unlabeled byte, initialized with 10. This + ; byte will be placed at the address (var2 + 1). + X dw ? ; Declares an uninitialized word (2 bytes), labeled "X". + Y dd 3000 ; Declares a double word (4 bytes) labeled "Y", + ; initialized with the value 3000. + Z dq 4294967297 ; Declares a quad word (8 bytes) labeled "Z", + ; initialized with the value 4294967297 (2^32 + 1). + ARR dd 1,2,3 ; Declares 3 double words (each 4 bytes) + ; starting from address "ARR" and initialized with 1, 2, and 3, respectively. + ; For example, 3 will be placed at the address (ARR + 8). + ARR64 dq 1,2,3 ; Declares 3 quad words (each 8 bytes) + ; starting from address "ARR64" and initialized with 1, 2, and 3, respectively. + ; For example, 3 will be placed at the address (ARR64 + 16). ``` -> **NOTE**: DB, DW, DD are directives used to specify the size of the portion: +> **NOTE**: DB, DW, DD, DQ are directives used to specify the size of the portion: > > Directive | Role | Size > ----------- | ------------------ | ---- -> `db` | Define Byte | 1 bytes (8 bits) +> `db` | Define Byte | 1 byte (8 bits) > `dw` | Define Word | 2 bytes (16 bits) > `dd` | Define Double Word | 4 bytes (32 bits) +> `dq` | Define Quad Word | 8 bytes (64 bits) > > **NOTE**: There are multiple types of memory regions as can be seen in the image below: > > ![Memory Sections](../media/sections.jpg) -The last declaration in the above example represents the declaration of an array. +The last declarations in the above example represent the declaration of arrays. Unlike higher-level languages, where arrays can have multiple dimensions and their elements are accessed by indices, in assembly language, arrays are represented as a number of cells located in a contiguous area of memory. diff --git a/labs/lab-05/tasks/div/README.md b/labs/lab-05/tasks/div/README.md index 2812d9470..040d6d814 100644 --- a/labs/lab-05/tasks/div/README.md +++ b/labs/lab-05/tasks/div/README.md @@ -5,37 +5,46 @@ parent: Lab 5 - Registers and Memory Addressing # Task: Division of Two Numbers -You will solve this exercise starting from the `divide.asm` file located in the `tasks/div/support` directory. +You will solve this exercise starting from the `divide_64.asm` file located in the `tasks/div/support` directory. -In the `divide.asm` program, the quotient and remainder of two numbers represented as bytes are calculated. -Update the area marked with `TODO` to perform divisions `dividend2 / divisor2` (word-type divisor) and `dividend3 / divisor3` (dword-type divisor). +In the `divide.asm` program, you'll implement division operations for various data sizes. +Update the area marked with `TODO` in the code to perform the division of the numbers mentioned in the comments. Similar to the `mul` instruction, the registers where the dividend is placed vary depending on the representation size of the divisor. The divisor is passed as an argument to the `div` mnemonic. -> **TIP**: If the divisor is of type `byte` (8 bits), the components are arranged as follows: +> **TIP**: The division operation works as follows for different sizes: > -> - the dividend is placed in the `ax` register -> - the argument of the `div` instruction is 8 bits and can be represented by a register or an immediate value -> - the quotient is placed in `al` -> - the remainder is placed in `ah` +> **For byte (8-bit) division:** > -> If the divisor is of type `word` (16 bits), the components are arranged as follows: +> - The dividend is placed in the `ax` register +> - The divisor is 8 bits (e.g., `bl`) +> - The quotient is placed in `al` +> - The remainder is placed in `ah` > -> - the dividend is arranged in the `dx:ax` pair, meaning its `high` part is in the `dx` register, and the `low` part is in `ax` -> - the argument of the `div` instruction is 16 bits and can be represented by a register or an immediate value -> - the quotient is placed in `ax` -> - the remainder is placed in `dx` +> **For word (16-bit) division:** > -> If the divisor is of type `dword` (32 bits), the components are arranged as follows: +> - The dividend is arranged in the `dx:ax` pair (high part in `dx`, low part in `ax`) +> - The divisor is 16 bits (e.g., `bx`) +> - The quotient is placed in `ax` +> - The remainder is placed in `dx` > -> - the dividend is arranged in the `edx:eax` pair, meaning its `high` part is in the `edx` register, and the `low` part is in `eax` -> - the argument of the `div` instruction is 32 bits and can be represented by a register or an immediate value -> - the quotient is placed in `eax` -> - the remainder is placed in `edx` +> **For double word (32-bit) division:** +> +> - The dividend is arranged in the `edx:eax` pair (high part in `edx`, low part in `eax`) +> - The divisor is 32 bits (e.g., `ebx`) +> - The quotient is placed in `eax` +> - The remainder is placed in `edx` +> +> **For quad word (64-bit) division:** +> +> - The dividend is arranged in the `rdx:rax` pair (high part in `rdx`, low part in `rax`) +> - The divisor is 64 bits (e.g., `rbx`) +> - The quotient is placed in `rax` +> - The remainder is placed in `rdx` > > **TIP**: If the program gives you a `SIGFPE`. -Arithmetic exception," you most likely forgot to initialize the upper part of the dividend (`ah`, `dx`, or `edx`). +Arithmetic exception," you most likely forgot to initialize the upper part of the dividend (`ah`, `dx`, `edx`, `rdx`). ## Testing @@ -48,9 +57,10 @@ make check In case of a correct solution, you will get an output such as: ```text -test_first_div ........................ passed ... 33 -test_second_div ........................ passed ... 33 -test_third_div ........................ passed ... 34 +test_first_div ........................ passed ... 25 +test_second_div ........................ passed ... 25 +test_third_div ........................ passed ... 25 +test_fourth_div ........................ passed ... 25 ======================================================================== diff --git a/labs/lab-05/tasks/div/solution/divide.asm b/labs/lab-05/tasks/div/solution/divide.asm index 4b6c56e78..501086829 100644 --- a/labs/lab-05/tasks/div/solution/divide.asm +++ b/labs/lab-05/tasks/div/solution/divide.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" ; https://en.wikibooks.org/wiki/X86_Assembly/Arithmetic @@ -10,49 +10,60 @@ section .data dividend2 dd 67254 divisor2 dw 1349 dividend3 dq 69094148 - divisor3 dd 87621 + divisor3 dq 12345678 + dividend4 dq 0x00000000FFFFFFFF, 0x0000000000000000 + divisor4 dq 0x0000000000010000 section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - xor eax, eax + xor rax, rax mov al, byte [dividend1] mov bl, byte [divisor1] div bl - xor ebx, ebx + xor rbx, rbx mov bl, al - PRINTF32 `Quotient: %hhu\n\x0`, ebx + PRINTF64 `Quotient: %hhu\n\x0`, rbx - xor ebx, ebx + xor rbx, rbx mov bl, ah - PRINTF32 `Remainder: %hhu\n\x0`, ebx + PRINTF64 `Remainder: %hhu\n\x0`, rbx - mov edx, [dividend2] - mov ax, dx - shr edx, 16 + xor rdx, rdx + mov eax, dword [dividend2] + mov dx, ax + shr eax, 16 + xchg ax, dx mov bx, word [divisor2] div bx - xor ebx, ebx + xor rbx, rbx mov bx, ax - PRINTF32 `Quotient: %hu\n\x0`, ebx + PRINTF64 `Quotient: %hu\n\x0`, rbx - xor ebx, ebx + xor rbx, rbx mov bx, dx - PRINTF32 `Remainder: %hu\n\x0`, ebx + PRINTF64 `Remainder: %hu\n\x0`, rbx - mov eax, dword [dividend3] - mov edx, dword [dividend3 + 4] + mov rax, qword [dividend3] + xor rdx, rdx mov ebx, dword [divisor3] div ebx - PRINTF32 `Quotient: %u\nRemainder: %u\n\x0`, eax, edx + PRINTF64 `Quotient: %lu\nRemainder: %lu\n\x0`, rax, rdx + + mov rax, qword [dividend4] + mov rdx, qword [dividend4 + 8] + mov rbx, qword [divisor4] + div rbx + + PRINTF64 `Quotient: 0x%lx\nRemainder: 0x%lx\n\x0`, rax, rdx leave - ret + ret \ No newline at end of file diff --git a/labs/lab-05/tasks/div/support/Makefile b/labs/lab-05/tasks/div/support/Makefile index 290330081..5a7a42a8b 100644 --- a/labs/lab-05/tasks/div/support/Makefile +++ b/labs/lab-05/tasks/div/support/Makefile @@ -6,9 +6,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGETS := $(OBJS:.o=) diff --git a/labs/lab-05/tasks/div/tests/test.sh b/labs/lab-05/tasks/div/tests/test.sh index 7820ef45d..cdf2f3171 100755 --- a/labs/lab-05/tasks/div/tests/test.sh +++ b/labs/lab-05/tasks/div/tests/test.sh @@ -17,6 +17,10 @@ remainder2=$(echo "$OUTPUT" | grep "Remainder:" | sed -n '2p' | awk '{print $2}' quotient3=$(echo "$OUTPUT" | grep "Quotient:" | sed -n '3p' | awk '{print $2}') remainder3=$(echo "$OUTPUT" | grep "Remainder:" | sed -n '3p' | awk '{print $2}') +quotient4=$(echo "$OUTPUT" | grep "Quotient:" | sed -n '4p' | awk '{print $2}') +remainder4=$(echo "$OUTPUT" | grep "Remainder:" | sed -n '4p' | awk '{print $2}') + + test_first_div() { if [[ $quotient1 -eq 3 && $remainder1 -eq 10 ]]; then exit 0 @@ -34,7 +38,15 @@ test_second_div() { } test_third_div() { - if [[ $quotient3 -eq 788 && $remainder3 -eq 48800 ]]; then + if [[ $quotient3 -eq 5 && $remainder3 -eq 7365758 ]]; then + exit 0 + else + exit 1 + fi +} + +test_fourth_div() { + if [[ $quotient4 -eq $((0xffff)) && $remainder4 -eq $((0xffff)) ]]; then exit 0 else exit 1 @@ -42,9 +54,9 @@ test_third_div() { } run_tests() { - local tests=(test_first_div test_second_div test_third_div) - local scores=(33 33 34) - for i in {0..2}; do + local tests=(test_first_div test_second_div test_third_div test_fourth_div) + local scores=(25 25 25 25) + for i in {0..3}; do run_test "${tests[i]}" "${scores[i]}" done } diff --git a/labs/lab-05/tasks/div/utils/printf32.asm b/labs/lab-05/tasks/div/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-05/tasks/div/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-05/tasks/div/utils/printf64.asm b/labs/lab-05/tasks/div/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-05/tasks/div/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-05/tasks/mul/README.md b/labs/lab-05/tasks/mul/README.md index 7c752f1a5..924da514e 100644 --- a/labs/lab-05/tasks/mul/README.md +++ b/labs/lab-05/tasks/mul/README.md @@ -5,7 +5,7 @@ parent: Lab 5 - Registers and Memory Addressing # Task: Multiplying Two Numbers -## Multiplying Two Numbers represented as Bytes +## Multiplying Two Numbers of Different Sizes You will solve this exercise starting from the `multiply.asm` file located in the `tasks/mul/support` directory. @@ -19,30 +19,47 @@ When performing multiplication, the process is as follows, as described [here](h - if we're operating on a byte (8 bits, one byte), we place the multiplicand in the `al` register; - if we're operating on a word (16 bits, 2 bytes), we place the multiplicand in the `ax` register; - if we're operating on a double word (32 bits, 4 bytes), we place the multiplicand in the `eax` register. + - if we're operating on a quad word (64 bits, 8 bytes), we place the multiplicand in the `rax` register. 1. The multiplier is passed as an argument to the `mul` mnemonic. The multiplier must have the same size as the multiplicand. 1. The result is placed in two registers (the high part and the low part). -## Multiplying Two Numbers represented as Words / Double Words +## Implementing Multiplication for Different Data Types -Update the area marked with `TODO` in the file `multiply.asm` to allow multiplication of `word` and `dword` numbers, namely `num1_dw` with `num2_dw`, and `num1_dd` with `num2_dd`. +The file `multiply.asm` already contains the code for multiplying bytes. +Update the area marked with `TODO` to implement multiplication for word (16-bit), double word (32-bit), and quad word (64-bit) numbers. -> **TIP**: For multiplying word numbers (16 bits), the components are arranged as follows: +You'll need to multiply: + +- `num1_w` with `num2_w` (word) +- `num1_d` with `num2_d` (double word) +- `num1_q` with `num2_q` (quad word) + +> **TIP**: For each data type, the components are arranged as follows: +> +> **For word (16-bit) multiplication:** > > - Place the multiplicand in the `ax` register. -> - The argument of the `mul` instruction, the multiplier (possibly another register), is 16 bits (either a value or a register such as `bx`, `cx`, `dx`). -> - The result of the multiplication is arranged in the pair `dx:ax`, where the high part of the result is in the `dx` register, and the low part of the result is in the `ax` register. +> - The multiplier should be 16 bits (e.g., `bx`). +> - The result will be in `dx:ax` (high part in `dx`, low part in `ax`). > -> For multiplying `dword` numbers (32 bits), the components are arranged as follows: +> **For double word (32-bit) multiplication:** > > - Place the multiplicand in the `eax` register. -> - The argument of the `mul` instruction, the multiplier (possibly another register), is 32 bits (either a value or a register such as `ebx`, `ecx`, `edx`). -> - The result of the multiplication is arranged in the pair `edx:eax`, where the high part of the result is in the `edx` register, and the low part of the result is in the `eax` register. +> - The multiplier should be 32 bits (e.g., `ebx`). +> - The result will be in `edx:eax` (high part in `edx`, low part in `eax`). +> +> **For quad word (64-bit) multiplication:** +> +> - Place the multiplicand in the `rax` register. +> - The multiplier should be 64 bits (e.g., `rbx`). +> - The result will be in `rdx:rax` (high part in `rdx`, low part in `rax`). > -> **NOTE**: When displaying the result, use the `PRINTF32` macro to display the two registers containing the result: +> **NOTE**: When displaying the results, use the appropriate format specifiers with the PRINTF64 macro: > -> - Registers `dx` and `ax` for multiplying word numbers. -> - Registers `edx` and `eax` for multiplying dword numbers. +> - For word multiplication: `PRINTF64 \`Result is: 0x%hx%hx\n\x0\`, rdx, rax` +> - For double word multiplication: `PRINTF64 \`Result is: 0x%x%x\n\x0\`, rdx, rax` +> - For quad word multiplication: `PRINTF64 \`Result is: 0x%lx%016lx\n\x0\`, rdx, rax` ## Testing @@ -55,9 +72,10 @@ make check In case of a correct solution, you will get an output such as: ```text -test_byte_mul ........................ passed ... 33 -test_short_mul ........................ passed ... 33 -test_int_mul ........................ passed ... 34 +test_byte_mul ........................ passed ... 25 +test_short_mul ........................ passed ... 25 +test_int_mul ........................ passed ... 25 +test_long_mul ........................ passed ... 25 ======================================================================== diff --git a/labs/lab-05/tasks/mul/solution/multiply.asm b/labs/lab-05/tasks/mul/solution/multiply.asm index 81728ce83..5eb30d77e 100644 --- a/labs/lab-05/tasks/mul/solution/multiply.asm +++ b/labs/lab-05/tasks/mul/solution/multiply.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" ; https://en.wikibooks.org/wiki/X86_Assembly/Arithmetic @@ -11,13 +11,15 @@ section .data num2_w dw 9949 num1_d dd 134932 num2_d dd 994912 + num1_q dq 9223372036854775800 + num2_q dq 12345678 section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp ; Multiplication for db mov al, byte [num1] @@ -26,9 +28,9 @@ main: ; Print result in hexa ; NOTE: The `%hx` format specifier tells printf to print only the lower half - ; (lower 16 bits) of eax - i.e., the ax register - by treating the value as + ; (lower 16 bits) of rax - i.e., the ax register - by treating the value as ; a 16-bit unsigned short. - PRINTF32 `Result is: 0x%hx\n\x0`, eax + PRINTF64 `Result is: 0x%hx\n\x0`, rax ; Multiplication for dw @@ -38,8 +40,8 @@ main: ; Print result in hexa ; NOTE: The `%hx` format specifier is used in the same way as in the - ; previous PRINTF32 statement, only now for both dx and ax registers - PRINTF32 `Result is: 0x%hx%hx\n\x0`, edx, eax + ; previous PRINTF64 statement, only now for both dx and ax registers + PRINTF64 `Result is: 0x%hx%hx\n\x0`, rdx, rax ; Multiplication for dd @@ -48,7 +50,18 @@ main: mul ebx ; Print result in hexa - PRINTF32 `Result is: 0x%x%x\n\x0`, edx, eax + PRINTF64 `Result is: 0x%x%x\n\x0`, rdx, rax + + + ; Multiplication for dq + mov rax, qword [num1_q] + mov rbx, qword [num2_q] + mul rbx + + ; Print result in hexa + ; NOTE: For 64-bit multiplication, the 128-bit result is stored in rdx:rax + ; We use %lx format specifier for 64-bit long values + PRINTF64 `Result is: 0x%lx%016lx\n\x0`, rdx, rax leave - ret + ret \ No newline at end of file diff --git a/labs/lab-05/tasks/mul/support/Makefile b/labs/lab-05/tasks/mul/support/Makefile index 43acbff58..01d412455 100644 --- a/labs/lab-05/tasks/mul/support/Makefile +++ b/labs/lab-05/tasks/mul/support/Makefile @@ -6,9 +6,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGETS := $(OBJS:.o=) diff --git a/labs/lab-05/tasks/mul/support/multiply.asm b/labs/lab-05/tasks/mul/support/multiply.asm index e70455c18..4948cf412 100644 --- a/labs/lab-05/tasks/mul/support/multiply.asm +++ b/labs/lab-05/tasks/mul/support/multiply.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" ; https://en.wikibooks.org/wiki/X86_Assembly/Arithmetic @@ -11,13 +11,15 @@ section .data num2_w dw 9949 num1_d dd 134932 num2_d dd 994912 + num1_q dq 9223372036854775800 + num2_q dq 12345678 section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp ; Multiplication for db mov al, byte [num1] @@ -25,10 +27,11 @@ main: mul bl ; Print result in hexa - PRINTF32 `Result is: 0x%hx\n\x0`, eax + PRINTF64 `Result is: 0x%hx\n\x0`, rax - ; TODO: Implement multiplication for dw and dd data types. + ; TODO: Implement multiplication for dw, dd and dq data types. + leave ret diff --git a/labs/lab-05/tasks/mul/tests/test.sh b/labs/lab-05/tasks/mul/tests/test.sh index e593c804b..420223996 100755 --- a/labs/lab-05/tasks/mul/tests/test.sh +++ b/labs/lab-05/tasks/mul/tests/test.sh @@ -11,6 +11,7 @@ OUTPUT=$($binary) result1=$(echo "$OUTPUT" | grep "R" | sed -n '1p' | awk '{print $3}') result2=$(echo "$OUTPUT" | grep "R" | sed -n '2p' | awk '{print $3}') result3=$(echo "$OUTPUT" | grep "R" | sed -n '3p' | awk '{print $3}') +result4=$(echo "$OUTPUT" | grep "R" | sed -n '4p' | awk '{print $3}') test_byte_mul() { if [[ -z "$result1" ]]; then @@ -48,10 +49,22 @@ test_int_mul() { fi } +test_long_mul() { + if [[ -z "$result4" ]]; then + exit 1 + fi + + if [[ $result4 -eq $((0x5e30a6fffffffffa1cf590)) ]]; then + exit 0 + else + exit 1 + fi +} + run_tests() { - local tests=(test_byte_mul test_short_mul test_int_mul) - local scores=(33 33 34) - for i in {0..2}; do + local tests=(test_byte_mul test_short_mul test_int_mul test_long_mul) + local scores=(25 25 25 25) + for i in {0..3}; do run_test "${tests[i]}" "${scores[i]}" done } diff --git a/labs/lab-05/tasks/mul/utils/printf32.asm b/labs/lab-05/tasks/mul/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-05/tasks/mul/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-05/tasks/mul/utils/printf64.asm b/labs/lab-05/tasks/mul/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-05/tasks/mul/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-05/tasks/sum-array/README.md b/labs/lab-05/tasks/sum-array/README.md index 3726ada70..747deaa81 100644 --- a/labs/lab-05/tasks/sum-array/README.md +++ b/labs/lab-05/tasks/sum-array/README.md @@ -7,20 +7,23 @@ parent: Lab 5 - Registers and Memory Addressing ## Introduction -You will solve this exercise starting from the `sum-array.asm` file located in the `tasks/sum-array/support` directory. +You will solve this exercise starting from the `sum_array.asm` file located in the `tasks/sum-array/support` directory. -In the `sum-array.asm` file the sum of elements in an array of bytes (8-bit representation) is calculated. +In the `sum_array.asm` file, the sum of elements in arrays of different data types is calculated. -Follow the code, observe the constructions and registers specific for working with bytes. +Follow the code, observe the constructions and registers specific for working with different data types in 64-bit mode. Run the code. > **IMPORTANT**: Proceed to the next step only after thoroughly understanding what the code does. It will be difficult for you to complete the following exercises if you have difficulty understanding the current exercise. -## Sum of Elements in an Array of types word and dword +## Working with Arrays of Different Sizes -In the `TODO` section of the `sum-array.asm` file, complete the code to calculate the sum of arrays with elements of type word (16 bits) and dword (32 bits); -namely, the `word_array` and `dword_array`. +In the `TODO` section of the `sum-array.asm` file, complete the code to calculate the sum of: + +1. `word_array` (16-bit elements) +1. `dword_array` (32-bit elements) +1. `qword_array` (64-bit elements) > **TIP**: When calculating the address of an element in an array, you will use a construction like: > @@ -28,39 +31,31 @@ namely, the `word_array` and `dword_array`. > > In the construction above: > -> - base is the address of the array (i.e., `word_array` or `dword_array`) +> - base is the address of the array (i.e., `word_array`, `dword_array` etc.) > - size is the length of the array element (i.e., 2 for a word array (16 bits, 2 bytes) and 4 for a dword array (32 bits, 4 bytes)) > - index is the current index within the array > -> **NOTE**: The sum of elements in the three arrays should be: +> **NOTE**: The expected sums for each array are: > > - `sum(byte_array): 575` > - `sum(word_array): 65799` -> - `sum(dword_array): 74758117` - -## Sum of Squares of Elements in an Array +> - `sum(dword_array): 140975` +> - `sum(qword_array): 49782475293` -Starting from the program in the previous exercise, calculate the sum of squares of elements in an array. +## 128-bit Addition Exercise -> **NOTE**: You can use the `dword_array2` array, ensuring that the sum of squares of the contained elements can be represented in 32 bits. -> -> **NOTE**: If you use the construction below (array with 10 elements) -> -> ```Assembly -> dword_array dd 1392, 12544, 7992, 6992, 7202, 27187, 28789, 17897, 12988, 17992 -> ``` -> -> the sum of squares will be 2704560839. +The final part of this lab requires you to implement a 128-bit addition operation. -## 64 Bits Sum of Squares +You need to add three very large 64-bit values stored in the `big_qword_array`: -Compute the sum of squares of the elements from `big_numbers_array`. +```assembly +big_qword_array dq 9223372036854775800, 8223372036854775800, 7223372036854775800 +``` -> **NOTE**: The sum of the array can be represented on 64 bits, but we only have 32 bits registers. +> **NOTE**: When adding these values, the sum will exceed the capacity of a single 64-bit register (rax). +> You will also have to track and handle the carry bits. > -> **HINT**: Split the sum in 2 variables (the mul operator for 32 bits multiplication). -> -> the sum of squares will be 1610691026282151079. +> **NOTE**: The expected result of adding these three values is: `0x1565ddbe509d3ffe8`. ## Testing @@ -73,14 +68,16 @@ make check In case of a correct solution, you will get an output such as: ```text -first_test ........................ passed ... 20 -second_test ........................ passed ... 20 -third_test ........................ passed ... 30 -fourth_test ........................ passed ... 30 +word_sum_test ........................ passed ... 20 +dword_sum_test ........................ passed ... 20 +qword_sum_test ........................ passed ... 30 +big_sum_test ........................ passed ... 30 ======================================================================== Total: 100/100 ``` +## Additional Resources + If you're having difficulties solving this exercise, go through [this](/reading/memory-addressing.md) reading material. diff --git a/labs/lab-05/tasks/sum-array/solution/.gitignore b/labs/lab-05/tasks/sum-array/solution/.gitignore index 8c0c005dd..ddeb385af 100644 --- a/labs/lab-05/tasks/sum-array/solution/.gitignore +++ b/labs/lab-05/tasks/sum-array/solution/.gitignore @@ -1,3 +1 @@ -sum-array-squared-64 -sum-array-squared -sum-array +sum-_array diff --git a/labs/lab-05/tasks/sum-array/solution/sum_array.asm b/labs/lab-05/tasks/sum-array/solution/sum_array.asm index 8d2a7dccb..ad0a8a06a 100644 --- a/labs/lab-05/tasks/sum-array/solution/sum_array.asm +++ b/labs/lab-05/tasks/sum-array/solution/sum_array.asm @@ -1,89 +1,86 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" %define ARRAY_SIZE 10 section .data byte_array db 8, 19, 12, 3, 6, 200, 128, 19, 78, 102 word_array dw 1893, 9773, 892, 891, 3921, 8929, 7299, 720, 2590, 28891 - dword_array dd 1392849, 12544, 879923, 8799279, 7202277, 971872, 28789292, 17897892, 12988, 8799201 - dword_array2 dd 1392, 12544, 7992, 6992, 7202, 27187, 28789, 17897, 12988, 17992 ; for squares - big_numbers_array dd 20000001, 3000000, 3000000, 23456789, 56789123, 123456789, 987654321, 56473829, 87564836, 777777777 - low_bits dd 0 - high_bits dd 0 + dword_array dd 1392, 12544, 7992, 6992, 7202, 27187, 28789, 17897, 12988, 17992 + qword_array dq 1392849893, 1254400000, 8799230000, 8799279000, 7202277000, 9718720000, 2878929200, 1789789200, 1298800000, 8799201000 + big_qword_array dq 9223372036854775800, 8223372036854775800, 7223372036854775800 section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - mov ecx, ARRAY_SIZE ; Use ecx as loop counter. - xor eax, eax ; Use eax to store the sum. - xor edx, edx ; Store current value in dl; zero entire edx. + ; Byte array sum (original implementation) + mov rcx, ARRAY_SIZE ; Use rcx as loop counter + xor rax, rax ; Use rax to store the sum + xor rdx, rdx ; Store current value in dl; zero entire rdx add_byte_array_element: - mov dl, byte [byte_array + ecx - 1] - add eax, edx - loop add_byte_array_element ; Decrement ecx, if not zero, add another element. + mov dl, byte [byte_array + rcx - 1] + add rax, rdx + loop add_byte_array_element - PRINTF32 `Array sum is %u\n\x0`, eax + PRINTF64 `Byte array sum: %u\n\x0`, rax + + ; Word array sum + mov rcx, ARRAY_SIZE + xor rax, rax + xor rdx, rdx - mov ecx, ARRAY_SIZE - xor eax, eax - xor edx, edx add_word_array_element: - mov dx, word [word_array + 2 * ecx - 2] - add eax, edx - loop add_word_array_element ; Decrement ecx, if not zero, add another element. + mov dx, word [word_array + 2 * rcx - 2] + add rax, rdx + loop add_word_array_element - PRINTF32 `Array sum is %u\n\x0`, eax + PRINTF64 `Word array sum: %u\n\x0`, rax - mov ecx, ARRAY_SIZE - xor eax, eax - xor edx, edx + ; Dword array sum + mov rcx, ARRAY_SIZE + xor rax, rax + xor rdx, rdx add_dword_array_element: - mov edx, dword [dword_array + 4 * ecx - 4] - add eax, edx - loop add_dword_array_element ; Decrement ecx, if not zero, add another element. - - PRINTF32 `Array sum is %u\n\x0`, eax - - xor eax, eax - mov ecx, ARRAY_SIZE - xor eax, eax - xor edx, edx - -add_dword_array_element_squares: - push eax - mov eax, dword [dword_array2 + 4 * ecx - 4] - mov edx, dword [dword_array2 + 4 * ecx - 4] - mul edx - mov edx, eax - pop eax - add eax, edx - loop add_dword_array_element_squares - - PRINTF32 `Array sum is %u\n\x0`, eax - - xor eax, eax - mov ecx, ARRAY_SIZE - xor eax, eax - xor ebx, ebx - -add_dword_array_element_big: - xor edx, edx - mov eax, dword [big_numbers_array + 4 * ecx - 4] - mov ebx, dword [big_numbers_array + 4 * ecx - 4] - mul ebx ; Compute array element square. - add [low_bits], eax ; Add lowest 32 bits of squared element to final result. - adc [high_bits], edx ; Add higher 32 bits of squared element with carry to final result. - loop add_dword_array_element_big - - PRINTF32 `Array sum is %lld\n\x0`, [low_bits], [high_bits] + mov edx, dword [dword_array + 4 * rcx - 4] + add rax, rdx + loop add_dword_array_element + + PRINTF64 `Dword array sum: %u\n\x0`, rax + + ; Qword array sum + mov rcx, ARRAY_SIZE + xor rax, rax + +add_qword_array_element: + add rax, qword [qword_array + 8 * rcx - 8] + loop add_qword_array_element + + PRINTF64 `Qword array sum: %lu\n\x0`, rax + + ; 128-bit addition example (sum of all three big_qword values) + mov r8, 0 ; Use r8 to track carries + + mov rax, qword [big_qword_array] ; Load first value + add rax, qword [big_qword_array + 8] ; Add second value + jnc no_first_carry + inc r8 ; Increment carry counter +no_first_carry: + + add rax, qword [big_qword_array + 16] ; Add third value + jnc no_second_carry + inc r8 ; Increment carry counter +no_second_carry: + + mov rdx, r8 ; Move final carry count to rdx for output + + PRINTF64 `128-bit addition example: 0x%lx%016lx\n\x0`, rdx, rax leave - ret + ret \ No newline at end of file diff --git a/labs/lab-05/tasks/sum-array/solution/sum_array_square.asm b/labs/lab-05/tasks/sum-array/solution/sum_array_square.asm deleted file mode 100644 index 7fa139510..000000000 --- a/labs/lab-05/tasks/sum-array/solution/sum_array_square.asm +++ /dev/null @@ -1,35 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -%include "printf32.asm" - -%define ARRAY_SIZE 10 - -section .data - dword_array dd 1392, 12544, 7992, 6992, 7202, 27187, 28789, 17897, 12988, 17992 - -section .text -extern printf -global main -main: - push ebp - mov ebp, esp - xor eax, eax - - mov ecx, ARRAY_SIZE - xor eax, eax - xor edx, edx - -add_dword_array_element: - push eax - mov eax, dword [dword_array + 4 * ecx - 4] - mov edx, dword [dword_array + 4 * ecx - 4] - mul edx - mov edx, eax - pop eax - add eax, edx - loop add_dword_array_element - - PRINTF32 `Array sum is %u\n\x0`, eax - - leave - ret diff --git a/labs/lab-05/tasks/sum-array/solution/sum_array_square_64.asm b/labs/lab-05/tasks/sum-array/solution/sum_array_square_64.asm deleted file mode 100644 index 39d75571a..000000000 --- a/labs/lab-05/tasks/sum-array/solution/sum_array_square_64.asm +++ /dev/null @@ -1,36 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -%include "printf32.asm" - -%define ARRAY_SIZE 10 - -section .data - big_numbers_array dd 20000001, 3000000, 3000000, 23456789, 56789123, 123456789, 987654321, 56473829, 87564836, 777777777 - low_bits dd 0 - high_bits dd 0 - -section .text -extern printf -global main -main: - push ebp - mov ebp, esp - - xor eax, eax - mov ecx, ARRAY_SIZE - xor eax, eax - xor ebx, ebx - -add_dword_array_element: - xor edx, edx - mov eax, dword [big_numbers_array + 4 * ecx - 4] - mov ebx, dword [big_numbers_array + 4 * ecx - 4] - mul ebx ; Compute array element square. - add [low_bits], eax ; Add lowest 32 bits of squared element to final result. - adc [high_bits], edx ; Add higher 32 bits of squared element with carry to final result. - loop add_dword_array_element ; Decrement ecx, if not zero, go to next element. - - PRINTF32 `Array sum is %lld\n\x0`, [low_bits], [high_bits] - - leave - ret diff --git a/labs/lab-05/tasks/sum-array/support/.gitignore b/labs/lab-05/tasks/sum-array/support/.gitignore index 8c0c005dd..c8142c975 100644 --- a/labs/lab-05/tasks/sum-array/support/.gitignore +++ b/labs/lab-05/tasks/sum-array/support/.gitignore @@ -1,3 +1 @@ -sum-array-squared-64 -sum-array-squared -sum-array +sum_array diff --git a/labs/lab-05/tasks/sum-array/support/Makefile b/labs/lab-05/tasks/sum-array/support/Makefile index 43acbff58..01d412455 100644 --- a/labs/lab-05/tasks/sum-array/support/Makefile +++ b/labs/lab-05/tasks/sum-array/support/Makefile @@ -6,9 +6,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGETS := $(OBJS:.o=) diff --git a/labs/lab-05/tasks/sum-array/tests/test.sh b/labs/lab-05/tasks/sum-array/tests/test.sh index 20d3c7202..91519b54c 100755 --- a/labs/lab-05/tasks/sum-array/tests/test.sh +++ b/labs/lab-05/tasks/sum-array/tests/test.sh @@ -8,12 +8,12 @@ binary="../support/sum_array" OUTPUT=$($binary) -word_sum=$(echo "$OUTPUT" | grep "A" | sed -n '2p' | awk '{print $4}') -dword_sum=$(echo "$OUTPUT" | grep "A" | sed -n '3p' | awk '{print $4}') -squares_sum=$(echo "$OUTPUT" | grep "A" | sed -n '4p' | awk '{print $4}') -big_sum=$(echo "$OUTPUT" | grep "A" | sed -n '5p' | awk '{print $4}') +word_sum=$(echo "$OUTPUT" | grep "Word array sum:" | awk '{print $4}') +dword_sum=$(echo "$OUTPUT" | grep "Dword array sum:" | awk '{print $4}') +qword_sum=$(echo "$OUTPUT" | grep "Qword array sum:" | awk '{print $4}') +big_sum=$(echo "$OUTPUT" | grep "128-bit addition example:" | awk '{print $4}') -first_test() { +word_sum_test() { if [[ -z $word_sum ]]; then exit 1 fi @@ -25,36 +25,36 @@ first_test() { fi } -second_test() { +dword_sum_test() { if [[ -z $dword_sum ]]; then exit 1 fi - if [[ $dword_sum -eq 74758117 ]]; then + if [[ $dword_sum -eq 140975 ]]; then exit 0 else exit 1 fi } -third_test() { - if [[ -z $squares_sum ]]; then +qword_sum_test() { + if [[ -z $qword_sum ]]; then exit 1 fi - if [[ $squares_sum -eq 2704560839 ]]; then + if [[ $qword_sum -eq 51933475293 ]]; then exit 0 else exit 1 fi } -fourth_test() { +big_sum_test() { if [[ -z $big_sum ]]; then exit 1 fi - if [[ $big_sum -eq 1610691026282151079 ]]; then + if [[ $big_sum -eq $((0x1565ddbe509d3ffe8)) ]]; then exit 0 else exit 1 @@ -62,7 +62,7 @@ fourth_test() { } run_tests() { - local tests=(first_test second_test third_test fourth_test) + local tests=(word_sum_test dword_sum_test qword_sum_test big_sum_test) local scores=(20 20 30 30) for i in {0..3}; do run_test "${tests[i]}" "${scores[i]}" diff --git a/labs/lab-05/tasks/sum-array/utils/printf32.asm b/labs/lab-05/tasks/sum-array/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-05/tasks/sum-array/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-05/tasks/sum-array/utils/printf64.asm b/labs/lab-05/tasks/sum-array/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-05/tasks/sum-array/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-05/tasks/sum-squared/README.md b/labs/lab-05/tasks/sum-squared/README.md index 383cad8be..8aa061c2f 100644 --- a/labs/lab-05/tasks/sum-squared/README.md +++ b/labs/lab-05/tasks/sum-squared/README.md @@ -9,25 +9,25 @@ You will solve this exercise starting from the `sum_n.asm` file located in the ` In the `sum_n.asm` program, the sum of the first `num` natural numbers is calculated. -Follow the code, observe the constructions and registers specific to working with bytes. +Follow the code, observe the constructions and registers specific to working with 64-bit values. Run the code. > **IMPORTANT**: Proceed to the next step only after you have understood very well what the code does. It will be difficult for you to do the next exercise if you have difficulties understanding the current one. -Start with the program `sum_n.asm` and create a program `sum_n_squared.asm` that calculates the sum of squares of the first `num` natural numbers (`num` <= 100). +Start with the program `sum_n.asm` and create a program `sum_n_squared.asm` that calculates the sum of the squares of the first `num = 100000` natural numbers. -> **TIP**: You will use the `eax` and `edx` registers for multiplication to compute the squares (using the `mul` instruction). -Therefore, you cannot easily use the `eax` register to store the sum of squares. To retain the sum of squares, you have two options: +> **TIP**: You will use the `rax` and `rdx` registers for multiplication to compute the squares (using the `mul` instruction). +Therefore, you cannot easily use the `rax` register to store the sum of squares. To retain the sum of squares, you have two options: > -> 1. (easier) Use the `ebx` register to store the sum of squares. -> 1. (more complex) Before performing operations on the `eax` register, save its value on the stack (using the `push` instruction), then perform the necessary operations, and finally restore the saved value (using the `pop` instruction). +> 1. (easier) Use the `rbx` register to store the sum of squares. +> 1. (more complex) Before performing operations on the `rax` register, save its value on the stack (using the `push` instruction), then perform the necessary operations, and finally restore the saved value (using the `pop` instruction). > -> **NOTE**: For verification, the sum of squares of the first 100 natural numbers is `338350`. +> **NOTE**: For verification, the sum of squares of the first 100,000 natural numbers is `333,338,333,350,000`, which requires 64-bit registers to represent. ## Testing -To test the implementation, enter the `tests/` directory and run: +To test your implementation, enter the `tests/` directory and run: ```console make check @@ -36,8 +36,8 @@ make check In case of a correct solution, you will get an output such as: ```text -test_sum_100 ........................ passed ... 50 -test_sum_squares_100 ........................ passed ... 50 +test_sum ........................ passed ... 50 +test_sum_squares ........................ passed ... 50 ======================================================================== diff --git a/labs/lab-05/tasks/sum-squared/solution/.gitignore b/labs/lab-05/tasks/sum-squared/solution/.gitignore index 2d6888e7d..6375b9439 100644 --- a/labs/lab-05/tasks/sum-squared/solution/.gitignore +++ b/labs/lab-05/tasks/sum-squared/solution/.gitignore @@ -1,3 +1,2 @@ -sum_n_squared_stack sum_n_squared sum_n diff --git a/labs/lab-05/tasks/sum-squared/solution/sum_n.asm b/labs/lab-05/tasks/sum-squared/solution/sum_n.asm index 13120dcfb..7f435286d 100644 --- a/labs/lab-05/tasks/sum-squared/solution/sum_n.asm +++ b/labs/lab-05/tasks/sum-squared/solution/sum_n.asm @@ -1,26 +1,26 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data - num dd 100 + num dq 100000 section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - mov ecx, [num] ; Use ecx as counter for computing the sum. - xor eax, eax ; Use eax to store the sum. Start from 0. + mov rcx, [num] ; Use rcx as counter for computing the sum. + xor rax, rax ; Use rax to store the sum. Start from 0. add_to_sum: - add eax, ecx - loop add_to_sum ; Decrement ecx. If not zero, add it to sum. + add rax, rcx + loop add_to_sum ; Decrement rcx. If not zero, add it to sum. - mov ecx, [num] - PRINTF32 `Sum(%u): %u\n\x0`, ecx, eax + mov rcx, [num] + PRINTF64 `Sum(%lu): %lu\n\x0`, rcx, rax leave ret diff --git a/labs/lab-05/tasks/sum-squared/solution/sum_n_squared.asm b/labs/lab-05/tasks/sum-squared/solution/sum_n_squared.asm index a59538b1d..404a89575 100644 --- a/labs/lab-05/tasks/sum-squared/solution/sum_n_squared.asm +++ b/labs/lab-05/tasks/sum-squared/solution/sum_n_squared.asm @@ -1,28 +1,28 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data - num dd 100 + num dq 100000 section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - mov ecx, [num] ; Use ecx as counter for computing the sum. - xor ebx, ebx ; Use ebx to store the sum. Start from 0. + mov rcx, [num] ; Use rcx as counter. + xor rbx, rbx ; Use rbx to store the sum of squares. Start from 0. -add_to_sum: - mov eax, ecx - mul eax - add ebx, eax - loop add_to_sum ; Decrement ecx. If not zero, add it to sum. +add_square_to_sum: + mov rax, rcx ; Move current number to rax for multiplication + mul rax ; Square the number (rax = rax * rax) + add rbx, rax ; Add the square to our sum + loop add_square_to_sum ; Decrement rcx. If not zero, continue. - mov ecx, [num] - PRINTF32 `Sum(%u): %u\n\x0`, ecx, ebx + mov rcx, [num] + PRINTF64 `Sum of squares(%lu): %lu\n\x0`, rcx, rbx leave ret diff --git a/labs/lab-05/tasks/sum-squared/solution/sum_n_squared_stack.asm b/labs/lab-05/tasks/sum-squared/solution/sum_n_squared_stack.asm index 07a45215d..1bf6242a9 100644 --- a/labs/lab-05/tasks/sum-squared/solution/sum_n_squared_stack.asm +++ b/labs/lab-05/tasks/sum-squared/solution/sum_n_squared_stack.asm @@ -1,31 +1,31 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data - num dd 100 + num dq 100000 section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - mov ecx, [num] ; Use ecx as counter for computing the sum. - xor eax, eax ; Use eax to store the sum. Start from 0. + mov rcx, [num] ; Use rcx as counter for computing the sum. + xor rax, rax ; Use rax to store the sum. Start from 0. add_to_sum: - push eax - mov eax, ecx - mul eax - mov edx, eax - pop eax - add eax, edx - loop add_to_sum ; Decrement ecx. If not zero, add it to sum. + push rax ; Save current sum on stack + mov rax, rcx ; Move counter to rax + mul rax ; Square the value (rax = rax * rax) + mov rdx, rax ; Move squared value to rdx + pop rax ; Restore sum from stack + add rax, rdx ; Add squared value to sum + loop add_to_sum ; Decrement rcx. If not zero, continue. - mov ecx, [num] - PRINTF32 `Sum(%u): %u\n\x0`, ecx, eax + mov rcx, [num] + PRINTF64 `Sum of squares(%lu): %lu\n\x0`, rcx, rax leave ret diff --git a/labs/lab-05/tasks/sum-squared/support/.gitignore b/labs/lab-05/tasks/sum-squared/support/.gitignore index 2d6888e7d..6375b9439 100644 --- a/labs/lab-05/tasks/sum-squared/support/.gitignore +++ b/labs/lab-05/tasks/sum-squared/support/.gitignore @@ -1,3 +1,2 @@ -sum_n_squared_stack sum_n_squared sum_n diff --git a/labs/lab-05/tasks/sum-squared/support/Makefile b/labs/lab-05/tasks/sum-squared/support/Makefile index 43acbff58..01d412455 100644 --- a/labs/lab-05/tasks/sum-squared/support/Makefile +++ b/labs/lab-05/tasks/sum-squared/support/Makefile @@ -6,9 +6,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGETS := $(OBJS:.o=) diff --git a/labs/lab-05/tasks/sum-squared/support/sum_n.asm b/labs/lab-05/tasks/sum-squared/support/sum_n.asm index 13120dcfb..7f435286d 100644 --- a/labs/lab-05/tasks/sum-squared/support/sum_n.asm +++ b/labs/lab-05/tasks/sum-squared/support/sum_n.asm @@ -1,26 +1,26 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data - num dd 100 + num dq 100000 section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - mov ecx, [num] ; Use ecx as counter for computing the sum. - xor eax, eax ; Use eax to store the sum. Start from 0. + mov rcx, [num] ; Use rcx as counter for computing the sum. + xor rax, rax ; Use rax to store the sum. Start from 0. add_to_sum: - add eax, ecx - loop add_to_sum ; Decrement ecx. If not zero, add it to sum. + add rax, rcx + loop add_to_sum ; Decrement rcx. If not zero, add it to sum. - mov ecx, [num] - PRINTF32 `Sum(%u): %u\n\x0`, ecx, eax + mov rcx, [num] + PRINTF64 `Sum(%lu): %lu\n\x0`, rcx, rax leave ret diff --git a/labs/lab-05/tasks/sum-squared/tests/test.sh b/labs/lab-05/tasks/sum-squared/tests/test.sh index 16ab8d397..6f4605694 100755 --- a/labs/lab-05/tasks/sum-squared/tests/test.sh +++ b/labs/lab-05/tasks/sum-squared/tests/test.sh @@ -15,27 +15,27 @@ else OUTPUT2="Not yet" fi -sum_100=$(echo "$OUTPUT1" | grep "S" | sed -n '1p' | awk '{print $2}') -sum_squares_100=$(echo "$OUTPUT2" | grep "S" | sed -n '1p' | awk '{print $2}') +sum=$(echo "$OUTPUT1" | grep "Sum" | awk '{print $2}' | tr -d '():') +sum_squares=$(echo "$OUTPUT2" | grep "Sum" | awk '{print $4}' | tr -d '():') -test_sum_100() { - if [[ -z $sum_100 ]]; then +test_sum() { + if [[ -z $sum ]]; then exit 1 fi - if [[ $sum_100 -eq 5050 ]]; then + if [[ $sum -eq 5000050000 ]]; then exit 0 else exit 1 fi } -test_sum_squares_100() { - if [[ -z $sum_squares_100 ]]; then +test_sum_squares() { + if [[ -z $sum_squares ]]; then exit 1 fi - if [[ $sum_squares_100 -eq 338350 ]]; then + if [[ $sum_squares -eq 333338333350000 ]]; then exit 0 else exit 1 @@ -43,7 +43,7 @@ test_sum_squares_100() { } run_tests() { - local tests=(test_sum_100 test_sum_squares_100) + local tests=(test_sum test_sum_squares) local scores=(50 50) for i in {0..1}; do run_test "${tests[i]}" "${scores[i]}" diff --git a/labs/lab-05/tasks/sum-squared/utils/printf32.asm b/labs/lab-05/tasks/sum-squared/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-05/tasks/sum-squared/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-05/tasks/sum-squared/utils/printf64.asm b/labs/lab-05/tasks/sum-squared/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-05/tasks/sum-squared/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-05/tasks/vec-count-if/solution/count_even_odd.asm b/labs/lab-05/tasks/vec-count-if/solution/count_even_odd.asm index 50d17fb46..d554d82ea 100644 --- a/labs/lab-05/tasks/vec-count-if/solution/count_even_odd.asm +++ b/labs/lab-05/tasks/vec-count-if/solution/count_even_odd.asm @@ -1,38 +1,42 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" %define ARRAY_SIZE 10 section .data - dword_array dd 1392, 12544, 7991, 6992, 7202, 27187, 28789, 17897, 12988, 17992 + qword_array dq 1392, 12544, 7991, 6992, 7202, 27187, 28789, 17897, 12988, 17992 + fmt_str db "Num even is %lu, num odd is %lu", 10, 0 section .text extern printf global main main: - mov ecx, ARRAY_SIZE ; Use ecx as loop counter. - xor esi, esi ; Store even number in `esi`. - xor edi, edi ; Store odd number in `edi`. -next_element: - - ; We need to initialize the dividend (edx:eax) to 0:array_element - xor edx, edx - mov eax, dword [dword_array + ecx*4 - 4] - ; Store the divisor (2) in `ebx`. - mov ebx, 2 - div ebx + push rbp + mov rbp, rsp - ; edx stores remainder. If remainder is 0, number is even, otherwise number is odd. - test edx, edx + mov rcx, ARRAY_SIZE ; Use rcx as loop counter. + xor rsi, rsi ; Store even number in `rsi`. + xor rdi, rdi ; Store odd number in `rdi`. +next_element: + ; We need to initialize the dividend (rdx:rax) to 0:array_element + xor rdx, rdx + mov rax, qword [qword_array + rcx*8 - 8] + ; Store the divisor (2) in `rbx`. + mov rbx, 2 + div rbx + + ; rdx stores remainder. If remainder is 0, number is even, otherwise number is odd. + test rdx, rdx jne add_to_odd - inc esi + inc rsi jmp test_end add_to_odd: - inc edi + inc rdi test_end: - loop next_element ; Decrement `ecx`, if not zero, go to next element. + loop next_element ; Decrement `rcx`, if not zero, go to next element. - PRINTF32 `Num even is %u, num odd is %u\n\x0`, esi, edi + PRINTF64 `Num even is %u, num odd is %u\n\x0`, rsi, rdi + leave ret diff --git a/labs/lab-05/tasks/vec-count-if/solution/count_pos_neg.asm b/labs/lab-05/tasks/vec-count-if/solution/count_pos_neg.asm index 5b1dfe9af..f1d555cbb 100644 --- a/labs/lab-05/tasks/vec-count-if/solution/count_pos_neg.asm +++ b/labs/lab-05/tasks/vec-count-if/solution/count_pos_neg.asm @@ -1,30 +1,35 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "../utils/printf32.asm" +%include "printf64.asm" %define ARRAY_SIZE 10 section .data - dword_array dd 1392, -12544, -7992, -6992, 7202, 27187, 28789, -17897, 12988, 17992 + qword_array dq 1392, -12544, -7992, -6992, 7202, 27187, 28789, -17897, 12988, 17992 + fmt_str db "Num pos is %lu, num neg is %lu", 10, 0 section .text extern printf global main main: - mov ecx, ARRAY_SIZE ; Use ecx as loop counter. - xor ebx, ebx ; Store positive number in ebx. - xor edx, edx ; Store negative number in edx. + push rbp + mov rbp, rsp + + mov rcx, ARRAY_SIZE ; Use rcx as loop counter. + xor rbx, rbx ; Store positive number in rbx. + xor rdx, rdx ; Store negative number in rdx. next_element: - mov eax, dword [dword_array + ecx*4 - 4] - cmp eax, 0 + mov rax, qword [qword_array + rcx*8 - 8] + cmp rax, 0 jl add_to_neg - inc ebx + inc rbx jmp test_end add_to_neg: - inc edx + inc rdx test_end: - loop next_element ; Decrement ecx, if not zero, go to next element. + loop next_element ; Decrement rcx, if not zero, go to next element. - PRINTF32 `Num pos is %u, num neg is %u\n\x0`, ebx, edx + PRINTF64 `Num even is %u, num odd is %u\n\x0`, rbx, rdx + leave ret diff --git a/labs/lab-05/tasks/vec-count-if/support/.gitignore b/labs/lab-05/tasks/vec-count-if/support/.gitignore index d551f7ae7..daf3965a4 100644 --- a/labs/lab-05/tasks/vec-count-if/support/.gitignore +++ b/labs/lab-05/tasks/vec-count-if/support/.gitignore @@ -1,2 +1,2 @@ -count-even-odd -count-pos-neg +count_even_odd +count_pos_neg diff --git a/labs/lab-05/tasks/vec-count-if/support/Makefile b/labs/lab-05/tasks/vec-count-if/support/Makefile index 43acbff58..01d412455 100644 --- a/labs/lab-05/tasks/vec-count-if/support/Makefile +++ b/labs/lab-05/tasks/vec-count-if/support/Makefile @@ -6,9 +6,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGETS := $(OBJS:.o=) diff --git a/labs/lab-05/tasks/vec-count-if/support/count_pos_neg.asm b/labs/lab-05/tasks/vec-count-if/support/count_pos_neg.asm index bea90c37b..d5f2a263e 100644 --- a/labs/lab-05/tasks/vec-count-if/support/count_pos_neg.asm +++ b/labs/lab-05/tasks/vec-count-if/support/count_pos_neg.asm @@ -1,11 +1,11 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" %define ARRAY_SIZE 10 section .data - dword_array dd 1392, -12544, -7992, -6992, 7202, 27187, 28789, -17897, 12988, 17992 + qword_array dq 1392, -12544, -7992, -6992, 7202, 27187, 28789, -17897, 12988, 17992 section .text extern printf diff --git a/labs/lab-05/tasks/vec-count-if/utils/printf32.asm b/labs/lab-05/tasks/vec-count-if/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-05/tasks/vec-count-if/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-05/tasks/vec-count-if/utils/printf64.asm b/labs/lab-05/tasks/vec-count-if/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-05/tasks/vec-count-if/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-06/guides/max/README.md b/labs/lab-06/guides/max/README.md index 6445c8410..2c041dec9 100644 --- a/labs/lab-06/guides/max/README.md +++ b/labs/lab-06/guides/max/README.md @@ -9,6 +9,6 @@ To follow this guide, you'll need to use the `max.asm` file located in the `guid The program finds the maximum value in an array of 16-bit integers (`array`). It iterates through the array, updating the maximum value (`dx`) when it finds a larger value. -Finally, it prints the maximum value using the `printf()` function. +Finally, it prints the maximum value using the `PRINTF64` macro. >**Note**: For a detailed description of the instruction, check out the following page: [Assembly Arrays Tutorial](https://www.tutorialspoint.com/assembly_programming/assembly_arrays.htm) diff --git a/labs/lab-06/guides/max/support/Makefile b/labs/lab-06/guides/max/support/Makefile index 6851e023d..a7119bcd2 100644 --- a/labs/lab-06/guides/max/support/Makefile +++ b/labs/lab-06/guides/max/support/Makefile @@ -1,9 +1,9 @@ NASM = nasm AFILES = max.asm OBJS = $(AFILES:.asm=.o) -ASM_FLAGS = -f elf32 -g +ASM_FLAGS = -f elf64 -g LD=gcc -LDFLAGS = -m32 -g +LDFLAGS = -g -no-pie BINARIES = max all : $(BINARIES) diff --git a/labs/lab-06/guides/max/support/max.asm b/labs/lab-06/guides/max/support/max.asm index 8b98a2e90..034b4e1d6 100644 --- a/labs/lab-06/guides/max/support/max.asm +++ b/labs/lab-06/guides/max/support/max.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "../utils/printf32.asm" +%include "../utils/printf64.asm" section .data array dw 10, 20, 30, 17, 277, 127, 17792, 1781, 2891, 2129 @@ -11,29 +11,29 @@ extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - ; ecx -> iterator through vector (ecx -> 0..9) - xor ecx, ecx + ; rcx -> iterator through vector (rcx -> 0..9) + xor rcx, rcx - ; edx -> store max - xor edx, edx + ; rdx -> store max + xor rdx, rdx again: - cmp dx, [array + 2*ecx] + cmp dx, [array + 2*rcx] ja noaction - ; Load new value in edx (max). - mov dx, [array + 2*ecx] + ; Load new value in rdx (max). + mov dx, [array + 2*rcx] noaction: - inc ecx - cmp ecx, len + inc rcx + cmp rcx, len jb again - PRINTF32 `%u\x0`, edx + PRINTF64 `%lu\n\x0`, rdx - xor eax, eax + xor rax, rax leave ret diff --git a/labs/lab-06/guides/max/utils/printf32.asm b/labs/lab-06/guides/max/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-06/guides/max/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-06/guides/max/utils/printf64.asm b/labs/lab-06/guides/max/utils/printf64.asm new file mode 100644 index 000000000..358881592 --- /dev/null +++ b/labs/lab-06/guides/max/utils/printf64.asm @@ -0,0 +1,73 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + ;sub rsp, 8 + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq + ;add rsp, 8 +%endmacro diff --git a/labs/lab-06/guides/students/support/Makefile b/labs/lab-06/guides/students/support/Makefile index 5abc99d22..88e5c7275 100644 --- a/labs/lab-06/guides/students/support/Makefile +++ b/labs/lab-06/guides/students/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -no-pie TARGET_EXEC = students diff --git a/labs/lab-06/guides/students/support/students.asm b/labs/lab-06/guides/students/support/students.asm index 10a2c68a0..6b9aaa13b 100644 --- a/labs/lab-06/guides/students/support/students.asm +++ b/labs/lab-06/guides/students/support/students.asm @@ -1,48 +1,48 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" extern printf section .bss: ; the structure for a student struc student_t - name: resb 10 ; char[10] - student name - id_course: resd 1 ; integer - the id of the course where a student is assigned - check: resd 1 ; "bool" - check if the student is assigned to the course + name: resb 10 ; char[10] - student name + id_course: resq 1 ; integer - the id of the course where a student is assigned + check: resd 1 ; "bool" - check if the student is assigned to the course endstruc section .data - v_students_count: dd 5 + v_students_count: dq 5 students: istruc student_t at name, db "Vlad", 0 - at id_course, dd 0 + at id_course, dq 0 at check, dd 1 iend istruc student_t at name, db "Andrew", 0 - at id_course, dd 1 + at id_course, dq 1 at check, dd 1 iend istruc student_t at name, db "Kim", 0 - at id_course, dd 1 + at id_course, dq 1 at check, dd 1 iend istruc student_t at name, db "George", 0 - at id_course, dd 2 + at id_course, dq 2 at check, dd 1 iend istruc student_t at name, db "Kate", 0 - at id_course, dd 0 + at id_course, dq 0 at check, dd 0 iend @@ -50,19 +50,24 @@ section .text global main main: - push ebp - mov ebp, esp - PRINTF32 `The students list is:\n\x0` - xor ecx, ecx - afisare: - mov ebx, students ; save the starting address of the vector in ebx - mov edx, ecx ; save the index in edx - imul edx, student_t_size ; multiply the index by the size of the student_t structure - add ebx, edx ; save the starting address of the structure with the index ecx from the list of structures in ebx - add ebx, name ; extract the field where the student's name is stored in each structure - PRINTF32 `%s\n\x0`, ebx - inc ecx - cmp ecx, [v_students_count] + push rbp + mov rbp, rsp + PRINTF64 `The students list is:\n\x0` + xor rcx, rcx + print: + mov rbx, students ; save the starting address of the vector in rbx + + mov rdx, rcx ; save the index in rdx + + imul rdx, student_t_size ; multiply the index by the size of the student_t structure + + add rbx, rdx ; save the starting address of the structure with the index rcx from the list of structures in rbx + + add rbx, name ; extract the field where the student's name is stored in each structure + + PRINTF64 `%s\n\x0`, rbx + inc rcx + cmp rcx, [v_students_count] jl afisare leave ret diff --git a/labs/lab-06/guides/students/utils/printf32.asm b/labs/lab-06/guides/students/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-06/guides/students/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-06/guides/students/utils/printf64.asm b/labs/lab-06/guides/students/utils/printf64.asm new file mode 100644 index 000000000..358881592 --- /dev/null +++ b/labs/lab-06/guides/students/utils/printf64.asm @@ -0,0 +1,73 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + ;sub rsp, 8 + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq + ;add rsp, 8 +%endmacro diff --git a/labs/lab-06/reading/arrays.md b/labs/lab-06/reading/arrays.md index db29ad2dd..59084aef5 100644 --- a/labs/lab-06/reading/arrays.md +++ b/labs/lab-06/reading/arrays.md @@ -77,19 +77,20 @@ section .bss As mentioned before, to access a field of an element in an array, we need to use normal addressing (particularly "based-indexed with scale" addressing). The formula to find the address of the element is `base_of_array + i * size_of_struct`. -Assuming we have the start address of the array in the `ebx` register and the index of the element we want to access in the `eax` register, the following example demonstrates printing the value of the y field of this element. +Assuming we have the start address of the array in the `rbx` register and the index of the element we want to access in the `rax` register, the following example demonstrates printing the value of the y field of this element. ```Assembly -mov ebx, point_array ; Move the start address of the array into ebx -mov eax, 13 ; Assume we want the 14th element -mov edx, [ebx + point_size * eax + point.y] ; Calculate the address of the desired field between [] - ; and then transfer the value from that address - ; into the edx register - -PRINTF32 `%u\n\x0`, edx +mov rbx, point_array ; Move the start address of the array into rbx +mov rax, 13 ; Assume we want the 14th element +xor rax, rax ; Clear the rax register +mov rdx, dword [rbx + point_size * rax + point.y] ; Calculate the address of the desired field between [] + ; and then transfer the value from that address + ; into the rdx register + +PRINTF64 `%lu\n\x0`, rdx ``` -We traverse the array, having the current index in the eax register at each iteration. +We traverse the array, having the current index in the rax register at each iteration. We can print the values from both fields of each element in the array with the following program: ```Assembly @@ -105,21 +106,21 @@ section .text global CMAIN CMAIN: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - xor edx, edx - xor eax, eax + xor rdx, rdx + xor rax, rax label: - mov edx, [point_array + point_size * eax + point.x] ; access x member - PRINTF32 `%u\n\x0`, edx + mov edx, dword [point_array + point_size * rax + point.x] ; access x member + PRINTF64 `%lu\n\x0`, rdx - mov edx, [point_array + point_size * eax + point.y] ; access y member - PRINTF32 `%u\n\x0`, edx + mov edx, dword [point_array + point_size * rax + point.y] ; access y member + PRINTF64 `%lu\n\x0`, rdx - inc eax ; increment input index - cmp eax, 100 + inc rax ; increment input index + cmp rax, 100 jl label leave diff --git a/labs/lab-06/reading/structures.md b/labs/lab-06/reading/structures.md index 65144dbf5..efb3906da 100644 --- a/labs/lab-06/reading/structures.md +++ b/labs/lab-06/reading/structures.md @@ -26,7 +26,7 @@ struc mystruct a: resw 1 ; a will refer to a single word-sized element b: resd 1 ; b will refer to a single double-word-sized element c: resb 1 ; c will refer to a single byte-sized element - d: resd 1 ; d will refer to a single double-word-sized element + d: resq 1 ; d will refer to a single quad-word-sized element e: resb 6 ; e will refer to 6 byte-sized elements endstruc ``` @@ -66,7 +66,7 @@ struct_var: at a, dw -1 at b, dd 0x12345678 at c, db ' ' - at d, dd 23 + at d, dq 23 at e, db 'Gary', 0 iend ``` @@ -79,7 +79,7 @@ struct_var: at mystruct.a, dw -1 at mystruct.b, dd 0x12345678 at mystruct.c, db ' ' - at mystruct.d, dd 23 + at mystruct.d, dq 23 at mystruct.e, db 'Gary', 0 iend ``` @@ -91,12 +91,12 @@ struct_var: To access and/or modify a specific member of the instantiated structure, we need to know its address. This address can be obtained by calculating the sum of the starting address of the structure and the offset within the structure of the desired member. -The following code sequence demonstrates setting a value in the `b` field of the structure and subsequently displaying the value of this field. +The following code sequence demonstrates setting a value in the `d` field of the structure and subsequently displaying the value of this field. ```Assembly -mov eax, 12345 -mov dword [mystruct + b], eax ; the address of field b is the base address of the statically instantiated structure + the offset of the field (given by the label 'b') +mov rax, 12345 +mov [mystruct + d], rax ; the address of field d is the base address of the statically instantiated structure + the offset of the field (given by the label 'd') -mov ebx, dword [mystruct + b] ; putting the value from field b into the ebx register for display -PRINTF32 `%d\n\x0`, ebx +mov rbx, [mystruct + d] ; putting the value from field d into the rbx register for display +PRINTF64 `%ld\n\x0`, rbx ``` diff --git a/labs/lab-06/tasks/courses/solution/courses.asm b/labs/lab-06/tasks/courses/solution/courses.asm index 0a820c5c6..e75f7b8b4 100644 --- a/labs/lab-06/tasks/courses/solution/courses.asm +++ b/labs/lab-06/tasks/courses/solution/courses.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" extern printf @@ -8,65 +8,65 @@ section .bss ; the structure for a student struc student_t name: resb 10 ; char[10] - student name - id_course: resd 1 ; integer - the id of the course where a student is assigned + id_course: resq 1 ; integer - the id of the course where a student is assigned check: resd 1 ; "bool" - check if the student is assigned to any course endstruc ; the structure for a course struc course_t - id: resd 1 ; id = index in courses (the list of courses) + id: resq 1 ; id = index in courses (the list of courses) name_course: resb 15 ; char[10] - the name of the course endstruc section .data unassigned: db "Student unassigned :(", 0 - v_students_count: dd 5 - v_courses_count: dd 3 + v_students_count: dq 5 + v_courses_count: dq 3 students: istruc student_t at name, db "Vlad", 0 - at id_course, dd 0 + at id_course, dq 0 at check, dd 1 iend istruc student_t at name, db "Andrew", 0 - at id_course, dd 1 + at id_course, dq 1 at check, dd 1 iend istruc student_t at name, db "Kim", 0 - at id_course, dd 1 + at id_course, dq 1 at check, dd 1 iend istruc student_t at name, db "George", 0 - at id_course, dd 2 + at id_course, dq 2 at check, dd 1 iend istruc student_t at name, db "Kate", 0 - at id_course, dd 0 + at id_course, dq 0 at check, dd 0 iend courses: istruc course_t - at id, dd 0 + at id, dq 0 at name_course, db "Assembly", 0 iend istruc course_t - at id, dd 1 + at id, dq 1 at name_course, db "Linear Algebra", 0 iend istruc course_t - at id, dd 2 + at id, dq 2 at name_course, db "Physics", 0 iend @@ -74,43 +74,44 @@ section .text global main main: - push ebp - mov ebp, esp - PRINTF32 `The students list is:\n\x0` + push rbp + mov rbp, rsp + PRINTF64 `The students list is:\n\x0` - xor ecx, ecx + xor rcx, rcx final_print: - mov ebx, students ; put the address of students array into ebx - mov edx, ecx ; save the index value in edx - imul edx, student_t_size ; multiply the index with the size of the student_t structure - add ebx, edx ; this addition save into ebx the address of the structure from an index in the students array - mov eax, ebx ; move this address into ebx to use ebx for something else and to have the address saved in eax - add ebx, name ; add to ebx `name` so that to get the name of the student - ; it is possible to add name because in ebx is saved the address of start of an element from students array + mov rbx, students ; put the address of students array into rbx + mov rdx, rcx ; save the index value in rdx + imul rdx, student_t_size ; multiply the index with the size of the student_t structure + add rbx, rdx ; this addition save into rbx the address of the structure from an index in the students array + mov rax, rbx ; move this address into rbx to use rbx for something else and to have the address saved in rax + add rbx, name ; add to rbx `name` so that to get the name of the student + ; it is possible to add name because in rbx is saved the address of start of an element from students array ; and student_t structure contains name field - PRINTF32 `%s ----\x0`, ebx + PRINTF64 `%s ----\x0`, rbx - mov ebx, eax - add ebx, check - mov ebx, [ebx] ; extract the value from check field (it's by default a dword) - cmp ebx, 0 + mov r8, rax + add r8, check + xor rbx, rbx + mov ebx, dword [r8] ; extract the value from check field (it's by default a dword) + cmp rbx, 0 jne step - PRINTF32 ` %s\n\x0`, unassigned + PRINTF64 ` %s\n\x0`, unassigned jmp finish step: - mov ebx, eax - add ebx, id_course - mov ebx, [ebx] ; extract the index of the course in the courses array - mov esi, courses - imul ebx, course_t_size - add esi, ebx - add esi, name_course ; get the name of the student - PRINTF32 ` %s\n\x0`, esi + mov rbx, rax + add rbx, id_course + mov rbx, [rbx] ; extract the index of the course in the courses array + mov rsi, courses + imul rbx, course_t_size + add rsi, rbx + add rsi, name_course ; get the name of the student + PRINTF64 ` %s\n\x0`, rsi finish: - inc ecx - cmp ecx, [v_students_count] ; it's converted by default to dword + inc rcx + cmp rcx, [v_students_count] ; it's converted by default to dword jl final_print leave diff --git a/labs/lab-06/tasks/courses/support/Makefile b/labs/lab-06/tasks/courses/support/Makefile index cf1acae85..365240ec6 100644 --- a/labs/lab-06/tasks/courses/support/Makefile +++ b/labs/lab-06/tasks/courses/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGET_EXEC = courses diff --git a/labs/lab-06/tasks/courses/support/courses.asm b/labs/lab-06/tasks/courses/support/courses.asm index 1a890245c..378094386 100644 --- a/labs/lab-06/tasks/courses/support/courses.asm +++ b/labs/lab-06/tasks/courses/support/courses.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" extern printf @@ -8,65 +8,65 @@ section .bss: ; the structure for a student struc student_t name: resb 10 ; char[10] - student name - id_course: resd 1 ; integer - the id of the course where a student is assigned + id_course: resq 1 ; integer - the id of the course where a student is assigned check: resd 1 ; "bool" - check if the student is assigned to any course endstruc ; the structure for a course struc course_t - id: resd 1 ; id = index in courses (the list of courses) + id: resq 1 ; id = index in courses (the list of courses) name_course: resb 15 ; char[10] - the name of the course endstruc section .data unassigned: db "Student unassigned :(", 0 - v_students_count: dd 5 - v_courses_count: dd 3 + v_students_count: dq 5 + v_courses_count: dq 3 students: istruc student_t at name, db "Vlad", 0 - at id_course, dd 0 + at id_course, dq 0 at check, dd 1 iend istruc student_t at name, db "Andrew", 0 - at id_course, dd 1 + at id_course, dq 1 at check, dd 1 iend istruc student_t at name, db "Kim", 0 - at id_course, dd 1 + at id_course, dq 1 at check, dd 1 iend istruc student_t at name, db "George", 0 - at id_course, dd 2 + at id_course, dq 2 at check, dd 1 iend istruc student_t at name, db "Kate", 0 - at id_course, dd 0 + at id_course, dq 0 at check, dd 0 iend courses: istruc course_t - at id, dd 0 + at id, dq 0 at name_course, db "Assembly", 0 iend istruc course_t - at id, dd 1 + at id, dq 1 at name_course, db "Linear Algebra", 0 iend istruc course_t - at id, dd 2 + at id, dq 2 at name_course, db "Physics", 0 iend @@ -74,9 +74,9 @@ section .text global main main: - push ebp - mov ebp, esp - PRINTF32 `The students list is:\n\x0` + push rbp + mov rbp, rsp + PRINTF64 `The students list is:\n\x0` ; TODO: Print the list of students and the courses where they are assigned leave ret diff --git a/labs/lab-06/tasks/courses/utils/printf32.asm b/labs/lab-06/tasks/courses/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-06/tasks/courses/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-06/tasks/courses/utils/printf64.asm b/labs/lab-06/tasks/courses/utils/printf64.asm new file mode 100644 index 000000000..358881592 --- /dev/null +++ b/labs/lab-06/tasks/courses/utils/printf64.asm @@ -0,0 +1,73 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + ;sub rsp, 8 + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq + ;add rsp, 8 +%endmacro diff --git a/labs/lab-06/tasks/fibonacci/solution/fibo_sum.asm b/labs/lab-06/tasks/fibonacci/solution/fibo_sum.asm index 3c64a0bd8..5a4078c2b 100644 --- a/labs/lab-06/tasks/fibonacci/solution/fibo_sum.asm +++ b/labs/lab-06/tasks/fibonacci/solution/fibo_sum.asm @@ -1,47 +1,43 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data - N dd 9 ; compute the sum of the first N fibonacci numbers - sum_print_format db "Sum first %d fibonacci numbers is %d", 10, 0 + N dq 9 ; compute the sum of the first N fibonacci numbers + sum_print_format db "Sum first %ld fibonacci numbers is %ld", 10, 0 section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - ; The calling convention requires saving and restoring `ebx` if modified - push ebx + ; The calling convention requires saving and restoring `rbx` if modified + push rbx - xor eax, eax ;store the sum in eax - mov ecx, [N] - mov ebx, 0 - mov edx, 1 + xor rax, rax ;store the sum in rax + mov rcx, [N] + mov rbx, 0 + mov rdx, 1 calc_fibo: - add eax, ebx - add ebx, edx - xchg ebx, edx + add rax, rbx + add rbx, rdx + xchg rbx, rdx ; The `xhcg` above is equivalent to the following: - ; push eax - ; mov eax, ebx - ; mov ebx, edx - ; mov edx, eax - ; pop eax + ; push rax + ; mov rax, rbx + ; mov rbx, rdx + ; mov rdx, rax + ; pop rax loop calc_fibo - push eax - push dword [N] - push sum_print_format - call printf - add esp, 12 + PRINTF64 `Sum first %d fibonacci numbers is %d\n\x0`, qword [N], rax - ; Restore the `ebx` that was previously saved - pop ebx + ; Restore the `rbx` that was previously saved + pop rbx - xor eax, eax + xor rax, rax leave ret diff --git a/labs/lab-06/tasks/fibonacci/support/Makefile b/labs/lab-06/tasks/fibonacci/support/Makefile index 3f2aff5c9..e6a9c37cd 100644 --- a/labs/lab-06/tasks/fibonacci/support/Makefile +++ b/labs/lab-06/tasks/fibonacci/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGET_EXEC = fibo_sum diff --git a/labs/lab-06/tasks/fibonacci/support/fibo_sum.asm b/labs/lab-06/tasks/fibonacci/support/fibo_sum.asm index 0de69efc8..08340b863 100644 --- a/labs/lab-06/tasks/fibonacci/support/fibo_sum.asm +++ b/labs/lab-06/tasks/fibonacci/support/fibo_sum.asm @@ -1,9 +1,9 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data - N dd 9 ; DO NOT MOFIDY THIS LINE EXCEPT FOR THE VALUE OF N! + N dq 9 ; DO NOT MOFIDY THIS LINE EXCEPT FOR THE VALUE OF N! ; compute the sum of the first N fibonacci numbers sum_print_format db "Sum first %d fibonacci numbers is %d", 10, 0 @@ -11,21 +11,20 @@ section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp ; TODO: calculate the sum of first N fibonacci numbers ; (f(0) = 0, f(1) = 1) - xor eax, eax ;store the sum in eax + xor rax, rax ;store the sum in rax ; Use the loop instruction - push eax - push dword [N] - push sum_print_format + mov rdx, rax + mov rsi, qword [N] + mov rdi, sum_print_format call printf - add esp, 12 - xor eax, eax + xor rax, rax leave ret diff --git a/labs/lab-06/tasks/fibonacci/tests/tests.sh b/labs/lab-06/tasks/fibonacci/tests/tests.sh index 29b722392..fa90c438b 100755 --- a/labs/lab-06/tasks/fibonacci/tests/tests.sh +++ b/labs/lab-06/tasks/fibonacci/tests/tests.sh @@ -20,7 +20,7 @@ test_fibo_sum() SOL=$2 # Modify the assembly code's N value - sed -i "s/N dd [0-9]\+/N dd $N/w sedlog" "$SRC_PATH"/fibo_sum.asm + sed -i "s/N dq [0-9]\+/N dq $N/w sedlog" "$SRC_PATH"/fibo_sum.asm if ! [ -s sedlog ] ; then return 2 fi @@ -71,7 +71,7 @@ run_test "test_fibo_sum_1_0" 25 run_test "test_fibo_sum_40_165580140" 25 if [ $? -eq 2 ] ; then - printf "\nERROR: Make sure the declaration of variable N in section .data follows the format \"N dd \"\n" + printf "\nERROR: Make sure the declaration of variable N in section .data follows the format \"N dq \"\n" fi mv ./fibo_sum.asm "$SRC_PATH" diff --git a/labs/lab-06/tasks/fibonacci/utils/printf32.asm b/labs/lab-06/tasks/fibonacci/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-06/tasks/fibonacci/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-06/tasks/fibonacci/utils/printf64.asm b/labs/lab-06/tasks/fibonacci/utils/printf64.asm new file mode 100644 index 000000000..358881592 --- /dev/null +++ b/labs/lab-06/tasks/fibonacci/utils/printf64.asm @@ -0,0 +1,73 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + ;sub rsp, 8 + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq + ;add rsp, 8 +%endmacro diff --git a/labs/lab-06/tasks/find-substring/solution/find_substring.asm b/labs/lab-06/tasks/find-substring/solution/find_substring.asm index dd67e40f3..0e5954e36 100644 --- a/labs/lab-06/tasks/find-substring/solution/find_substring.asm +++ b/labs/lab-06/tasks/find-substring/solution/find_substring.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data source_text: db "ABCABCBABCBABCBBBABABBCBABCBAAACCCB", 0 @@ -12,39 +12,45 @@ section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - mov esi, source_text - mov edi, substring + mov r8, source_text + mov r9, substring source_loop: - mov eax, esi - mov edx, edi + mov rax, r8 + mov rdx, r9 substr_loop: - cmp byte [edx], 0 + cmp byte [rdx], 0 je found_substr - mov bl, byte [eax] - cmp byte [edx], bl + mov bl, byte [rax] + cmp byte [rdx], bl jne exit_substr_loop - inc eax - inc edx + inc rax + inc rdx jmp substr_loop found_substr: - pusha - mov ecx, esi - sub ecx, source_text - push ecx - push print_format - call printf - add esp, 8 - popa + push rax ; Save the registers that may be modified by printf according to the + push rdx ; calling convention. + push r8 ; Make sure to write and extra push-pop pair in case of segfault + push r9 ; (because calling convention states the stack must be have a 16 or 32 + mov rsi, r8 ; byte alignment) + sub rsi, source_text + mov rdi, print_format + xor al, al ; al is used to indicate the number of vector arguments passed + ; to a function requiring a variable number of arguments + call printf ; MORE ON CALLING CONVENTIONS IN THE FUNCTIONS LAB + pop r9 + pop r8 + pop rdx + pop rax exit_substr_loop: - inc esi - cmp byte [esi], 0 + inc r8 + cmp byte [r8], 0 je exit jmp source_loop diff --git a/labs/lab-06/tasks/find-substring/support/Makefile b/labs/lab-06/tasks/find-substring/support/Makefile index 1e1b21ea8..d56fe8478 100644 --- a/labs/lab-06/tasks/find-substring/support/Makefile +++ b/labs/lab-06/tasks/find-substring/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGET_EXEC = find_substring diff --git a/labs/lab-06/tasks/find-substring/support/find_substring.asm b/labs/lab-06/tasks/find-substring/support/find_substring.asm index c544fbb17..46bc74454 100644 --- a/labs/lab-06/tasks/find-substring/support/find_substring.asm +++ b/labs/lab-06/tasks/find-substring/support/find_substring.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .data source_text: db "ABCABCBABCBABCBBBABABBCBABCBAAACCCB", 0 ; DO NOT MODIFY THIS LINE EXCEPT FOR THE STRING IN QUOTES @@ -12,8 +12,8 @@ section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp ; TODO: Print the start indices for all occurrences of the substring in source_text diff --git a/labs/lab-06/tasks/find-substring/utils/printf32.asm b/labs/lab-06/tasks/find-substring/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-06/tasks/find-substring/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-06/tasks/find-substring/utils/printf64.asm b/labs/lab-06/tasks/find-substring/utils/printf64.asm new file mode 100644 index 000000000..358881592 --- /dev/null +++ b/labs/lab-06/tasks/find-substring/utils/printf64.asm @@ -0,0 +1,73 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + ;sub rsp, 8 + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq + ;add rsp, 8 +%endmacro diff --git a/labs/lab-06/tasks/getters-setters/solution/getter_setter_printf.asm b/labs/lab-06/tasks/getters-setters/solution/getter_setter_printf.asm index 0d698d648..457032b75 100644 --- a/labs/lab-06/tasks/getters-setters/solution/getter_setter_printf.asm +++ b/labs/lab-06/tasks/getters-setters/solution/getter_setter_printf.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" struc my_struct int_x: resb 4 @@ -25,33 +25,33 @@ extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - PRINTF32 `int_x: %d\n\x0`, [sample_obj + int_x] - PRINTF32 `char_y: %c\n\x0`, [sample_obj + char_y] - lea eax, [sample_obj + string_s] - PRINTF32 `string_s: %s\n\x0`, eax + PRINTF64 `int_x: %d\n\x0`, qword [sample_obj + int_x] + PRINTF64 `char_y: %c\n\x0`, qword [sample_obj + char_y] + lea rax, [sample_obj + string_s] + PRINTF64 `string_s: %s\n\x0`, rax - mov eax, [new_int] - mov [sample_obj + int_x], eax + mov rax, [new_int] + mov [sample_obj + int_x], rax mov al, [new_char] mov [sample_obj + char_y], al - mov ecx, 0 + mov rcx, 0 copy: - mov bl, [new_string + ecx] - mov [sample_obj + string_s + ecx], bl - inc ecx + mov bl, [new_string + rcx] + mov [sample_obj + string_s + rcx], bl + inc rcx cmp bl, 0 jnz copy - PRINTF32 `int_x: %d\n\x0`, [sample_obj + int_x] - PRINTF32 `char_y: %c\n\x0`, [sample_obj + char_y] - lea eax, [sample_obj + string_s] - PRINTF32 `string_s: %s\n\x0`, eax + PRINTF64 `int_x: %d\n\x0`, qword [sample_obj + int_x] + PRINTF64 `char_y: %c\n\x0`, qword [sample_obj + char_y] + lea rax, [sample_obj + string_s] + PRINTF64 `string_s: %s\n\x0`, rax - xor eax, eax + xor rax, rax leave ret diff --git a/labs/lab-06/tasks/getters-setters/support/Makefile b/labs/lab-06/tasks/getters-setters/support/Makefile index d81d7cb61..f6071ff13 100644 --- a/labs/lab-06/tasks/getters-setters/support/Makefile +++ b/labs/lab-06/tasks/getters-setters/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGET_EXEC = getter_setter_printf diff --git a/labs/lab-06/tasks/getters-setters/support/getter_setter_printf.asm b/labs/lab-06/tasks/getters-setters/support/getter_setter_printf.asm index 086fa0521..c5a83fa28 100644 --- a/labs/lab-06/tasks/getters-setters/support/getter_setter_printf.asm +++ b/labs/lab-06/tasks/getters-setters/support/getter_setter_printf.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" struc my_struct int_x: resb 4 @@ -25,8 +25,8 @@ extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp ; Print all three values (int_x, char_y, string_s) from sample_obj. ; Hint: use "lea reg, [base + offset]" to save the result of @@ -41,6 +41,6 @@ main: ; TODO: print all three values again to validate the results of the ; three set operations above. - xor eax, eax + xor rax, rax leave ret diff --git a/labs/lab-06/tasks/getters-setters/utils/printf32.asm b/labs/lab-06/tasks/getters-setters/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-06/tasks/getters-setters/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-06/tasks/getters-setters/utils/printf64.asm b/labs/lab-06/tasks/getters-setters/utils/printf64.asm new file mode 100644 index 000000000..358881592 --- /dev/null +++ b/labs/lab-06/tasks/getters-setters/utils/printf64.asm @@ -0,0 +1,73 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + ;sub rsp, 8 + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq + ;add rsp, 8 +%endmacro diff --git a/labs/lab-06/tasks/mul-arrays/solution/mul_arrays.asm b/labs/lab-06/tasks/mul-arrays/solution/mul_arrays.asm index 763c17778..2f59a00df 100644 --- a/labs/lab-06/tasks/mul-arrays/solution/mul_arrays.asm +++ b/labs/lab-06/tasks/mul-arrays/solution/mul_arrays.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .bss array3: resw 10 @@ -14,34 +14,33 @@ extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - xor ecx, ecx + xor rcx, rcx iterate_arrays: - xor eax, eax - xor ebx, ebx - xor edx, edx - mov al, byte[array1 + ecx] - mov bl, byte[array2 + ecx] + xor rax, rax + xor rbx, rbx + xor rdx, rdx + mov al, [array1 + rcx] + mov bl, [array2 + rcx] mul bl - mov word[array3 + ecx * 2], ax - inc ecx - cmp ecx, 10 + mov word[array3 + rcx * 2], ax + inc rcx + cmp rcx, 10 jl iterate_arrays - PRINTF32 `The array that results from the product of the corresponding elements in array1 and array2 is:\n\x0` + PRINTF64 `The array that results from the product of the corresponding elements in array1 and array2 is:\n\x0` - xor ecx, ecx - xor eax, eax + xor rcx, rcx + xor rax, rax display: - mov ax, word[array3 + ecx * 2] - PRINTF32 `%hu \x0`, eax - inc ecx - cmp ecx, 10 + PRINTF64 `%hu \x0`, qword [array3 + rcx * 2] + inc rcx + cmp rcx, 10 jl display - PRINTF32 `\n\x0` + PRINTF64 `\n\x0` leave ret diff --git a/labs/lab-06/tasks/mul-arrays/support/Makefile b/labs/lab-06/tasks/mul-arrays/support/Makefile index a2b26d676..e9f9955a0 100644 --- a/labs/lab-06/tasks/mul-arrays/support/Makefile +++ b/labs/lab-06/tasks/mul-arrays/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGET_EXEC = mul_arrays diff --git a/labs/lab-06/tasks/mul-arrays/support/mul_arrays.asm b/labs/lab-06/tasks/mul-arrays/support/mul_arrays.asm index 7fe864a8f..4d73a846b 100644 --- a/labs/lab-06/tasks/mul-arrays/support/mul_arrays.asm +++ b/labs/lab-06/tasks/mul-arrays/support/mul_arrays.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" section .bss array3: resw 10 @@ -14,11 +14,11 @@ extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp ; TODO: Traversing array1 and array2 and putting the result in array3 - PRINTF32 `The array that results from the product of the corresponding elements in array1 and array2 is:\n\x0` + PRINTF64 `The array that results from the product of the corresponding elements in array1 and array2 is:\n\x0` ; TODO: Traversing array3 and displaying its elements - PRINTF32 `\n\x0` + PRINTF64 `\n\x0` leave ret diff --git a/labs/lab-06/tasks/mul-arrays/utils/printf32.asm b/labs/lab-06/tasks/mul-arrays/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-06/tasks/mul-arrays/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-06/tasks/mul-arrays/utils/printf64.asm b/labs/lab-06/tasks/mul-arrays/utils/printf64.asm new file mode 100644 index 000000000..358881592 --- /dev/null +++ b/labs/lab-06/tasks/mul-arrays/utils/printf64.asm @@ -0,0 +1,73 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + ;sub rsp, 8 + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq + ;add rsp, 8 +%endmacro diff --git a/labs/lab-06/tasks/print-structure/solution/print_structure.asm b/labs/lab-06/tasks/print-structure/solution/print_structure.asm index 34b4b37c8..44c76464f 100644 --- a/labs/lab-06/tasks/print-structure/solution/print_structure.asm +++ b/labs/lab-06/tasks/print-structure/solution/print_structure.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" struc stud_struct name: resb 32 @@ -33,48 +33,36 @@ section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp mov word [sample_student + birth_year], 1993 mov byte [sample_student + age], 22 mov byte [sample_student + group + 2], '3' - lea eax, [sample_student + name] - push eax - push format_name + lea rsi, [sample_student + name] + mov rdi, format_name call printf - add esp, 8 - lea eax, [sample_student + surname] - push eax - push format_surname + lea rsi, [sample_student + surname] + mov rdi, format_surname call printf - add esp, 8 - movzx eax, byte [sample_student + age] - push eax - push format_age + movzx rsi, byte [sample_student + age] + mov rdi, format_age call printf - add esp, 8 - lea eax, [sample_student + group] - push eax - push format_group + lea rsi, [sample_student + group] + mov rdi, format_group call printf - add esp, 8 - movzx eax, byte [sample_student + gender] - push eax - push format_gender + movzx rsi, byte [sample_student + gender] + mov rdi, format_gender call printf - add esp, 8 - movzx eax, word [sample_student + birth_year] - push eax - push format_year + movzx rsi, word [sample_student + birth_year] + mov rdi, format_year call printf - add esp, 8 leave ret diff --git a/labs/lab-06/tasks/print-structure/support/Makefile b/labs/lab-06/tasks/print-structure/support/Makefile index c4f4eb079..dd62bac98 100644 --- a/labs/lab-06/tasks/print-structure/support/Makefile +++ b/labs/lab-06/tasks/print-structure/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGET_EXEC = print_structure diff --git a/labs/lab-06/tasks/print-structure/support/print_structure.asm b/labs/lab-06/tasks/print-structure/support/print_structure.asm index 132034430..bc112d03d 100644 --- a/labs/lab-06/tasks/print-structure/support/print_structure.asm +++ b/labs/lab-06/tasks/print-structure/support/print_structure.asm @@ -1,6 +1,6 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "printf32.asm" +%include "printf64.asm" struc stud_struct name: resb 32 @@ -33,49 +33,37 @@ section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp ; TODO: Update name, surname, birth_year, gender and age such that: ; birth_year is 1993 ; age is 22 ; group is '323CA' - lea eax, [sample_student + name] - push eax - push format_name + lea rsi, [sample_student + name] + mov rdi, format_name call printf - add esp, 8 - lea eax, [sample_student + surname] - push eax - push format_surname + lea rsi, [sample_student + surname] + mov rdi, format_surname call printf - add esp, 8 - movzx eax, byte [sample_student + age] - push eax - push format_age + movzx rsi, byte [sample_student + age] + mov rdi, format_age call printf - add esp, 8 - lea eax, [sample_student + group] - push eax - push format_group + lea rsi, [sample_student + group] + mov rdi, format_group call printf - add esp, 8 - movzx eax, byte [sample_student + gender] - push eax - push format_gender + movzx rsi, byte [sample_student + gender] + mov rdi, format_gender call printf - add esp, 8 - movzx eax, word [sample_student + birth_year] - push eax - push format_year + movzx rsi, word [sample_student + birth_year] + mov rdi, format_year call printf - add esp, 8 leave ret diff --git a/labs/lab-06/tasks/print-structure/utils/printf32.asm b/labs/lab-06/tasks/print-structure/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-06/tasks/print-structure/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-06/tasks/print-structure/utils/printf64.asm b/labs/lab-06/tasks/print-structure/utils/printf64.asm new file mode 100644 index 000000000..358881592 --- /dev/null +++ b/labs/lab-06/tasks/print-structure/utils/printf64.asm @@ -0,0 +1,73 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + ;sub rsp, 8 + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq + ;add rsp, 8 +%endmacro diff --git a/labs/lab-07/guides/stack-addressing/README.md b/labs/lab-07/guides/stack-addressing/README.md index c347a2de4..8e4c8669e 100644 --- a/labs/lab-07/guides/stack-addressing/README.md +++ b/labs/lab-07/guides/stack-addressing/README.md @@ -10,49 +10,46 @@ The `stack_addressing.asm` file demonstrates how data is stored on the stack, an Here's what an usual output for the compiled program would be: ```c -0xff99fba8: 0xf7f46020 -0xff99fba4: 0xa -0xff99fba0: 0xb -0xff99fb9c: 0xc -0xff99fb98: 0xd +0x7fff124f4830: 0x7fff124f48d0 +0x7fff124f4828: 0xa +0x7fff124f4820: 0xb +0x7fff124f4818: 0xc +0x7fff124f4810: 0xd ``` > **Note:** The last 4 values are the ones we pushed on stack. > What is the first one? > -> **Answer:** It is the old EBP we push at the start of the function. +> **Answer:** It is the old RBP we push at the start of the function. For convenience, here's the contents of the file. To play around with it, download the lab locally. ```assembly -%include "printf32.asm" +%include "printf64.asm" section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - push dword 10 - push dword 11 - push dword 12 - push dword 13 + push qword 10 + push qword 11 + push qword 12 + push qword 13 - mov eax, ebp + mov rax, rbp print_stack: - PRINTF32 `0x\x0` - PRINTF32 `%x\x0`, eax - PRINTF32 `: 0x\x0` - PRINTF32 `%x\n\x0`, [eax] + PRINTF64 `%p: %p\n\x0`, rax, [rax] - sub eax, 4 - cmp eax, esp + sub rax, 8 + cmp rax, rsp jge print_stack - xor eax, eax + xor rax, rax leave ret ``` diff --git a/labs/lab-07/guides/stack-addressing/support/Makefile b/labs/lab-07/guides/stack-addressing/support/Makefile index 4afa1bf60..bf86e0c0d 100644 --- a/labs/lab-07/guides/stack-addressing/support/Makefile +++ b/labs/lab-07/guides/stack-addressing/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGET_EXEC = stack-addressing diff --git a/labs/lab-07/guides/stack-addressing/support/stack-addressing.asm b/labs/lab-07/guides/stack-addressing/support/stack-addressing.asm index 1c9d2e442..b0c6568ce 100644 --- a/labs/lab-07/guides/stack-addressing/support/stack-addressing.asm +++ b/labs/lab-07/guides/stack-addressing/support/stack-addressing.asm @@ -1,26 +1,26 @@ -%include "printf32.asm" +%include "printf64.asm" section .text extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - push dword 10 - push dword 11 - push dword 12 - push dword 13 + push qword 10 + push qword 11 + push qword 12 + push qword 13 - mov eax, ebp + mov rax, rbp print_stack: - PRINTF32 `%p: %p\n\x0`, eax, [eax] + PRINTF64 `%p: %p\n\x0`, rax, qword [rax] - sub eax, 4 - cmp eax, esp + sub rax, 8 + cmp rax, rsp jge print_stack - xor eax, eax + xor rax, rax leave ret diff --git a/labs/lab-07/guides/stack-addressing/utils/printf32.asm b/labs/lab-07/guides/stack-addressing/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-07/guides/stack-addressing/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-07/guides/stack-addressing/utils/printf64.asm b/labs/lab-07/guides/stack-addressing/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-07/guides/stack-addressing/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-07/guides/stack-operations/README.md b/labs/lab-07/guides/stack-operations/README.md index ce2bc00a3..98e7e125b 100644 --- a/labs/lab-07/guides/stack-operations/README.md +++ b/labs/lab-07/guides/stack-operations/README.md @@ -11,64 +11,60 @@ The main focus is to show how to manipulate the stack by pushing and popping val > **Note:** Notice how `push` and `pop` are just syntactic sugar for the simpler `sub`, `add`, and `mov` instructions. For convenience, here's the contents of the file. -To play around with it, download the lab locally. +To play around with it, clone the repository locally. ```assembly -%include "printf32.asm" +%include "printf64.asm" section .data - var: dd ? + var: dq ? section .text -; esp -> stack pointer -; ebp -> base pointer +; rsp -> stack pointer +; rbp -> base pointer extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - push dword 10 ; sub esp, 4; mov [esp], 10; - push dword 11 ; sub esp, 4; mov [esp], 11; - push dword 12 ; sub esp, 4; mov [esp], 12; - push dword 13 ; sub esp, 4; mov [esp], 13; - push dword 14 ; sub esp, 4; mov [esp], 13; - - - pusha ; push all registers on the stack - popa ; pop all registers from the stack + push qword 10 ; same as: sub rsp, 8 followed by: mov [rsp], 10 + push qword 11 ; same as: sub rsp, 8 followed by: mov [rsp], 11 + push qword 12 ; same as: sub rsp, 8 followed by: mov [rsp], 12 + push qword 13 ; same as: sub rsp, 8 followed by: mov [rsp], 13 + push qword 14 ; same as: sub rsp, 8 followed by: mov [rsp], 13 ; Version 1 - pop eax; ; mov eax, [esp]; add esp, 4 - pop eax; ; mov eax, [esp]; add esp, 4 - pop eax; ; mov eax, [esp]; add esp, 4 - pop eax; ; mov eax, [esp]; add esp, 4 - pop eax; ; mov eax, [esp]; add esp, 4 + pop rax ; same as: mov rax, [rsp] followed by: add rsp, 8 + pop rax ; same as: mov rax, [rsp] followed by: add rsp, 8 + pop rax ; same as: mov rax, [rsp] followed by: add rsp, 8 + pop rax ; same as: mov rax, [rsp] followed by: add rsp, 8 + pop rax ; same as: mov rax, [rsp] followed by: add rsp, 8 ; Version 2 - ; add esp, 20 ; 4 * number_of_push + ; add rsp, 40 ; 8 * number_of_push ; Version 3 - ; mov esp, ebp + ; mov rsp, rbp - ; sub esp <-> add esp -> use to allocate/deallocate memory + ; sub rsp <-> add rsp -> use to allocate/deallocate memory - ; Aloc 8 bytes <-> 2 int - ; sub esp, 8 - ; mov [esp], 10 - ; mov [esp + 4], 12 + ; Aloc 16 bytes <-> 2 long + ; sub rsp, 16 + ; mov [rsp], 10 + ; mov [rsp + 8], 12 ; Push/Pop from global variable - mov dword [var], 1337 + mov qword [var], 1337 - push dword [var] - pop dword [var] + push qword [var] + pop qword [var] - mov eax, [var] - PRINTF32 `VAR: %d\n\x0`, eax + mov rax, [var] + PRINTF64 `VAR: %d\n\x0`, rax leave diff --git a/labs/lab-07/guides/stack-operations/support/Makefile b/labs/lab-07/guides/stack-operations/support/Makefile index 552ecfada..cf1ec93b2 100644 --- a/labs/lab-07/guides/stack-operations/support/Makefile +++ b/labs/lab-07/guides/stack-operations/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGET_EXEC = stack-operations diff --git a/labs/lab-07/guides/stack-operations/support/stack-operations.asm b/labs/lab-07/guides/stack-operations/support/stack-operations.asm index 5911cce8e..308980b27 100644 --- a/labs/lab-07/guides/stack-operations/support/stack-operations.asm +++ b/labs/lab-07/guides/stack-operations/support/stack-operations.asm @@ -1,58 +1,54 @@ -%include "printf32.asm" +%include "printf64.asm" section .data - var: dd ? + var: dq ? section .text -; esp -> stack pointer -; ebp -> base pointer +; rsp -> stack pointer +; rbp -> base pointer extern printf global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - push dword 10 ; sub esp, 4; mov [esp], 10; - push dword 11 ; sub esp, 4; mov [esp], 11; - push dword 12 ; sub esp, 4; mov [esp], 12; - push dword 13 ; sub esp, 4; mov [esp], 13; - push dword 14 ; sub esp, 4; mov [esp], 13; - - - pusha ; push all registers on the stack - popa ; pop all registers from the stack + push qword 10 ; same as: sub rsp, 8 followed by: mov [rsp], 10 + push qword 11 ; same as: sub rsp, 8 followed by: mov [rsp], 11 + push qword 12 ; same as: sub rsp, 8 followed by: mov [rsp], 12 + push qword 13 ; same as: sub rsp, 8 followed by: mov [rsp], 13 + push qword 14 ; same as: sub rsp, 8 followed by: mov [rsp], 13 ; Version 1 - pop eax; ; mov eax, [esp]; add esp, 4 - pop eax; ; mov eax, [esp]; add esp, 4 - pop eax; ; mov eax, [esp]; add esp, 4 - pop eax; ; mov eax, [esp]; add esp, 4 - pop eax; ; mov eax, [esp]; add esp, 4 + pop rax ; same as: mov rax, [rsp] followed by: add rsp, 8 + pop rax ; same as: mov rax, [rsp] followed by: add rsp, 8 + pop rax ; same as: mov rax, [rsp] followed by: add rsp, 8 + pop rax ; same as: mov rax, [rsp] followed by: add rsp, 8 + pop rax ; same as: mov rax, [rsp] followed by: add rsp, 8 ; Version 2 - ; add esp, 20 ; 4 * number_of_push + ; add rsp, 40 ; 8 * number_of_push ; Version 3 - ; mov esp, ebp + ; mov rsp, rbp - ; sub esp <-> add esp -> use to allocate/deallocate memory + ; sub rsp <-> add rsp -> use to allocate/deallocate memory - ; Aloc 8 bytes <-> 2 int - ; sub esp, 8 - ; mov [esp], 10 - ; mov [esp + 4], 12 + ; Aloc 16 bytes <-> 2 long + ; sub rsp, 16 + ; mov [rsp], 10 + ; mov [rsp + 8], 12 ; Push/Pop from global variable - mov dword [var], 1337 + mov qword [var], 1337 - push dword [var] - pop dword [var] + push qword [var] + pop qword [var] - mov eax, [var] - PRINTF32 `VAR: %d\n\x0`, eax + mov rax, [var] + PRINTF64 `VAR: %ld\n\x0`, rax leave diff --git a/labs/lab-07/guides/stack-operations/utils/printf32.asm b/labs/lab-07/guides/stack-operations/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-07/guides/stack-operations/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-07/guides/stack-operations/utils/printf64.asm b/labs/lab-07/guides/stack-operations/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-07/guides/stack-operations/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-07/reading/stack.md b/labs/lab-07/reading/stack.md index 55fd4dd41..ab8b999dc 100644 --- a/labs/lab-07/reading/stack.md +++ b/labs/lab-07/reading/stack.md @@ -33,12 +33,12 @@ As the above image suggests, the order in which items are inserted and removed f ## So, Why is it Useful? In the previous chapters we learned how to work with the basics of assembly. -A pretty big limitation we have imposed on ourselves by using such a low-level language is the small number of values we can work with at a time. -For anything but small programs, having just the 6 registers (`eax`, `ebx`, `ecx`, `edx`, `esi`, `edi`) is usually not enough, and creating global variables for temporary values is not memory efficient and, at some point, we'll struggle to even name them something reasonable! +The x86_64 architecture provides plenty general-purpose registers for us to use in our programs, but sometimes we need to store and reuse multiple values. +Using up all our registers for this purpose could prove to be a waste of resources. +Likewise, creating global variables for temporary values is not memory efficient and, at some point, we'll struggle to even name them something reasonable! You might have also felt the absence of functions. The stack will help us out as it provides a nice place to store: -- the arguments, - the values of registers before entering a function so they can be restored on exit, - and some metadata useful for when we want to exit out of a function. @@ -66,18 +66,18 @@ section .text global CMAIN CMAIN: - mov eax, 7 - mov ebx, 8 - add eax, ebx - push eax ; push the value of the eax register onto the stack - mov eax, 10 ; we can now use the eax register, as its value is saved on the stack - PRINTF32 `%d \n\x0`, eax ; 10 + mov rax, 7 + mov rbx, 8 + add rax, rbx + push rax ; push the value of the rax register onto the stack + mov rax, 10 ; we can now use the rax register, as its value is saved on the stack + PRINTF64 `%d \n\x0`, rax ; 10 - pop eax ; retrieve the value of the eax register from the stack - PRINTF32 `%d \n\x0`, eax ; 15 + pop rax ; retrieve the value of the rax register from the stack + PRINTF64 `%d \n\x0`, rax ; 15 ``` -1. By directly accessing the memory with the help of a special register in which the top of the stack is held - `esp` also known as the "stack pointer register". +1. By directly accessing the memory with the help of a special register in which the top of the stack is held - `rsp` also known as the "stack pointer register". ```assembly %include "io.asm" @@ -85,20 +85,20 @@ CMAIN: section .text global CMAIN CMAIN: - mov eax, 7 - mov ebx, 8 - add eax, ebx - sub esp, 4 ; reserve 4 bytes on the stack - mov [esp], eax ; move the contents of the eax register to the new address pointed to by esp - mov eax, 10 - PRINTF32 `%d \n\x0`, eax - - mov eax, [esp] ; retrieve the value from the stack - add esp, 4 ; restore the value of the esp register - PRINTF32 `%d \n\x0`, eax + mov rax, 7 + mov rbx, 8 + add rax, rbx + sub rsp, 8 ; reserve 8 bytes on the stack + mov [rsp], rax ; move the contents of the rax register to the new address pointed to by rsp + mov rax, 10 + PRINTF64 `%d \n\x0`, rax + + mov rax, [rsp] ; retrieve the value from the stack + add rsp, 8 ; restore the value of the rsp register + PRINTF64 `%d \n\x0`, rax ``` -> **IMPORTANT:** Comment out the instructions `sub esp, 4` and `add esp, 4`. +> **IMPORTANT:** Comment out the instructions `sub rsp, 4` and `add rsp, 4`. > What happens? > Why? > @@ -111,18 +111,18 @@ CMAIN: Some processors do not have support for stack operations: for example, MIPS processors do not have `push` and `pop` instructions and do not have a special register for the stack pointer. Thus, if we want to implement stack operations on a MIPS processor, we would do it exactly as in the example above, but we can choose any register to keep track of the stack pointer. -Therefore, the `push eax` instruction on an x86 processor is equivalent to: +Therefore, the `push rax` instruction on an x86_64 processor is equivalent to: ```assembly -sub esp, 4 -mov [esp], eax +sub rsp, 8 +mov [rsp], rax ``` -And the `pop eax` is equivalent to: +And the `pop rax` is equivalent to: ```assembly -mov eax, [esp] -add esp, 4 +mov rax, [rsp] +add rsp, 8 ``` > **IMPORTANT:** We need to be careful with the amount of data allocated on the stack because the size of the stack is limited. @@ -156,19 +156,19 @@ Given that the stack is used for function calls, it is very important that when section .text global CMAIN CMAIN: - mov eax, 5 - mov ebx, 6 - mov ecx, 7 + mov rax, 5 + mov rbx, 6 + mov rcx, 7 - push eax - push ebx - push ecx + push rax + push rbx + push rcx - add esp, 12 ; equivalent to using 3 consecutive pop-s + add rsp, 24 ; equivalent to using 3 consecutive pop-s ret ``` -1. An alternative method is to save the current stack pointer value in a separate register, such as `ebp`, before performing any `push` operations. +1. An alternative method is to save the current stack pointer value in a separate register, such as `rbp`, before performing any `push` operations. This allows us to easily restore the stack pointer value at the end of the function, without having to keep track of the number of `push` operations performed. ```assembly @@ -176,22 +176,22 @@ section .text global CMAIN CMAIN: - mov ebp, esp ; save current stack pointer value in ebp + mov rbp, rsp ; save current stack pointer value in rbp - mov eax, 5 - mov ebx, 6 - mov ecx, 7 + mov rax, 5 + mov rbx, 6 + mov rcx, 7 - push eax - push ebx - push ecx + push rax + push rbx + push rcx - mov esp, ebp ; restore stack pointer value + mov rsp, rbp ; restore stack pointer value ret ``` -> **IMPORTANT:** What is the primary use of the `ebp` register? +> **IMPORTANT:** What is the primary use of the `rbp` register? -As we can observe, the `ebp` register defines the stack frame for each function. -Similarly to how we can address local variables using the `esp` register, we can do the same with `ebp`. -Additionally, we will see that function parameters are addressed using `ebp`. +As we can observe, the `rbp` register defines the stack frame for each function. +Similarly to how we can address local variables using the `rsp` register, we can do the same with `rbp`. +Additionally, we will see that, on 32-bit systems, function parameters are addressed using its 32-bit equivalent, `ebp`. diff --git a/labs/lab-07/tasks/gcd/README.md b/labs/lab-07/tasks/gcd/README.md index f2791e308..433ca7b3b 100644 --- a/labs/lab-07/tasks/gcd/README.md +++ b/labs/lab-07/tasks/gcd/README.md @@ -6,7 +6,7 @@ parent: Lab 7 - The Stack # Task: GCD - Greatest Common Divisor Open `gcd.asm` and run the program. -The code calculates the greatest common divisor (GCD) of two numbers given as parameters using the `eax` and `edx` registers, and then stores the calculated value back in the `eax` register. +The code calculates the greatest common divisor (GCD) of two numbers given as parameters using the `rax` and `rdx` registers, and then stores the calculated value back in the `rax` register. 1. Make the necessary modifications so that the error message - `Segmentation fault (core dumped)` - no longer appears. 1. Within the `print` label, display the result in the following format: diff --git a/labs/lab-07/tasks/gcd/solution/gcd.asm b/labs/lab-07/tasks/gcd/solution/gcd.asm index 77d733242..2e620cbc8 100644 --- a/labs/lab-07/tasks/gcd/solution/gcd.asm +++ b/labs/lab-07/tasks/gcd/solution/gcd.asm @@ -1,45 +1,49 @@ ; SPDX-License-Identifier: BSD-3-Clause -%include "../utils/printf32.asm" +%include "../utils/printf64.asm" section .text extern printf global main main: - ; Input values (eax, edx) : the 2 numbers to compute the gcd for. - mov eax, 49 - mov edx, 28 + sub rsp, 8 ;; Necessary for stack alignment - push eax - push edx + ; Input values (rax, rdx) : the 2 numbers to compute the gcd for. + mov rax, 49 + mov rdx, 28 + + push rax + push rdx gcd: - neg eax + neg rax je gcd_end swap_values: - neg eax - push eax - push edx - pop eax - pop edx + neg rax + push rax + push rdx + pop rax + pop rdx subtract_values: - sub eax,edx + sub rax,rdx jg subtract_values jne swap_values gcd_end: - add eax,edx + add rax,rdx jne print - inc eax + inc rax print: - pop edx - pop ebx + pop rdx + pop rbx + + PRINTF64 `gcd(%ld, %ld) = %ld\n\x0`, rbx, rdx, rax ; rax = greatest common divisor - PRINTF32 `gcd(%d, %d) = %d\n\x0`, ebx, edx, eax ; eax = greatest common divisor + xor rax, rax - xor eax, eax + add rsp, 8 ;; Necessary for stack alignment ret diff --git a/labs/lab-07/tasks/gcd/support/Makefile b/labs/lab-07/tasks/gcd/support/Makefile index 2e9237490..947975190 100644 --- a/labs/lab-07/tasks/gcd/support/Makefile +++ b/labs/lab-07/tasks/gcd/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGET_EXEC = gcd diff --git a/labs/lab-07/tasks/gcd/support/gcd.asm b/labs/lab-07/tasks/gcd/support/gcd.asm index 6a3331b70..9f1c4b891 100644 --- a/labs/lab-07/tasks/gcd/support/gcd.asm +++ b/labs/lab-07/tasks/gcd/support/gcd.asm @@ -1,44 +1,48 @@ -%include "printf32.asm" +%include "printf64.asm" section .text extern printf global main main: - ; input values (eax, edx): the 2 numbers to compute the gcd for - mov eax, 49 - mov edx, 28 + sub rsp, 8 ;; Necessary for stack alignment - push eax - push edx + ; input values (rax, rdx): the 2 numbers to compute the gcd for + mov rax, 49 + mov rdx, 28 + + push rax + push rdx gcd: - neg eax + neg rax je gcd_end swap_values: - neg eax - push eax - push edx - pop eax - pop edx + neg rax + push rax + push rdx + pop rax + pop rdx subtract_values: - sub eax,edx + sub rax,rdx jg subtract_values jne swap_values gcd_end: - add eax,edx + add rax,rdx jne print - inc eax + inc rax print: ; TODO 1: solve the 'Segmentation fault!' error - ; TODO 2: print the result in the form of: "gdc(eax, edx)=7" with PRINTF32 macro - ; output value in eax + ; TODO 2: print the result in the form of: "gdc(rax, rdx)=7" with PRINTF64 macro + ; output value in rax + + xor rax, rax - xor eax, eax + add rsp, 8 ;; Necessary for stack alignment ret diff --git a/labs/lab-07/tasks/gcd/tests/test.sh b/labs/lab-07/tasks/gcd/tests/test.sh index 6b43c50e7..ca418e0f3 100755 --- a/labs/lab-07/tasks/gcd/tests/test.sh +++ b/labs/lab-07/tasks/gcd/tests/test.sh @@ -17,8 +17,8 @@ test_segfault() { touch "$out" "$ref" - ./"$binary" > "$out" 2>&1 - ./"$ref_binary" > "$ref" 2>&1 + { ./"$binary"; } > "$out" 2>&1 + { ./"$ref_binary"; } > "$ref" 2>&1 if grep -q "Segmentation fault" "$out"; then exit 1 diff --git a/labs/lab-07/tasks/gcd/utils/printf32.asm b/labs/lab-07/tasks/gcd/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-07/tasks/gcd/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-07/tasks/gcd/utils/printf64.asm b/labs/lab-07/tasks/gcd/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-07/tasks/gcd/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-07/tasks/local-var/solution/local-var.asm b/labs/lab-07/tasks/local-var/solution/local-var.asm index 4a7187730..86cea41d2 100644 --- a/labs/lab-07/tasks/local-var/solution/local-var.asm +++ b/labs/lab-07/tasks/local-var/solution/local-var.asm @@ -1,4 +1,4 @@ -%include "../utils/printf32.asm" +%include "../utils/printf64.asm" section .data @@ -16,91 +16,99 @@ extern printf global main main: - mov ebp, esp - sub esp, 4 * ARRAY_1_LEN - mov eax, esp - mov edx, 0 -array_1_on_stack: - mov ecx, [array_1 + 4 * edx] - mov [eax], ecx - inc edx - add eax, 4 - cmp edx, ARRAY_1_LEN - jl array_1_on_stack - mov eax, esp - - sub esp, 4 * ARRAY_2_LEN - mov ebx, esp - mov edx, 0 -array_2_on_stack: - mov ecx, [array_2 + 4 * edx] - mov [ebx], ecx - inc edx - add ebx, 4 - cmp edx, ARRAY_2_LEN - jl array_2_on_stack - mov ebx, esp - sub esp, 4 * ARRAY_OUTPUT_LEN - mov ecx, esp + push rbp + mov rbp, rsp + + sub rsp, 4 * ARRAY_1_LEN + and rsp, -16 ;; Align the stack to 16 bytes + + ; Copy array_1 to stack + mov rdi, 0 +copy_array_1_to_stack: + mov ecx, [array_1 + 4 * rdi] + mov [rsp + 4 * rdi], ecx + inc rdi + cmp rdi, ARRAY_1_LEN + jl copy_array_1_to_stack + + mov r8, rsp ; r8 = start of array_1 on stack + + sub rsp, 4 * ARRAY_2_LEN + and rsp, -16 ;; Align the stack to 16 bytes + + ; Copy array_2 to stack + mov rdi, 0 +copy_array_2_to_stack: + mov ecx, [array_2 + 4 * rdi] + mov [rsp + 4 * rdi], ecx + inc rdi + cmp rdi, ARRAY_2_LEN + jl copy_array_2_to_stack + + mov r9, rsp ; r9 = start of array_2 on stack + + sub rsp, 4 * ARRAY_OUTPUT_LEN + and rsp, -16 ;; Align the stack to 16 bytes + mov r10, rsp ; r10 = start of output array on stack + + ; Initialize indices + mov rax, 0 ; index for array_1 + mov rbx, 0 ; index for array_2 + mov rcx, 0 ; index for output array merge_arrays: - mov edx, [eax] - cmp edx, [ebx] + cmp rax, ARRAY_1_LEN + jge copy_array_2 + cmp rbx, ARRAY_2_LEN + jge copy_array_1 + + mov edx, [r8 + 4 * rax] ; element from array_1 + mov edi, [r9 + 4 * rbx] ; element from array_2 + cmp edx, edi jg array_2_lower array_1_lower: - mov [ecx], edx ; The element from array_1 is lower - add eax, 4 - add ecx, 4 - jmp verify_array_end + mov [r10 + 4 * rcx], edx ; The element from array_1 is lower + inc rax + inc rcx + jmp merge_arrays array_2_lower: - mov edx, [ebx] - mov [ecx], edx ; The elements of the array_2 is lower - add ebx, 4 - add ecx, 4 - -verify_array_end: - mov edx, ebp - cmp eax, edx - jge copy_array_2 - sub edx, 4 * ARRAY_1_LEN - cmp ebx, ebp - jge copy_array_1 + mov [r10 + 4 * rcx], edi ; The elements of the array_2 is lower + inc rbx + inc rcx jmp merge_arrays copy_array_1: - xor edx, edx - mov eax, [eax] - mov [ecx], edx - add ecx, 4 - add eax, 4 - cmp eax, ebp - jb copy_array_1 - jmp print_array + cmp rax, ARRAY_1_LEN + jge print_array + mov edx, [r8 + 4 * rax] + mov [r10 + 4 * rcx], edx + inc rax + inc rcx + jmp copy_array_1 + copy_array_2: - xor edx, edx - mov edx, [ebx] - mov [ecx], edx - add ecx, 4 - add ebx, 4 - mov edx, ebp - sub edx, 4 * ARRAY_1_LEN - cmp ebx, edx - jb copy_array_2 + cmp rbx, ARRAY_2_LEN + jge print_array + mov edx, [r9 + 4 * rbx] + mov [r10 + 4 * rcx], edx + inc rbx + inc rcx + jmp copy_array_2 print_array: - PRINTF32 `Array merged:\n\x0` - xor eax, eax - xor ecx, ecx + PRINTF64 `Array merged:\n\x0` + xor rcx, rcx print: - mov eax, [esp] - PRINTF32 `%d \x0`, eax - add esp, 4 - inc ecx - cmp ecx, ARRAY_OUTPUT_LEN + mov edx, [r10 + 4 * rcx] + PRINTF64 `%d \x0`, rdx + inc rcx + cmp rcx, ARRAY_OUTPUT_LEN jb print - PRINTF32 `\n\x0` - xor eax, eax - mov esp, ebp - ret \ No newline at end of file + PRINTF64 `\n\x0` + xor rax, rax + mov rsp, rbp + + leave + ret diff --git a/labs/lab-07/tasks/local-var/support/Makefile b/labs/lab-07/tasks/local-var/support/Makefile index 6c5d5d988..f2eda186d 100644 --- a/labs/lab-07/tasks/local-var/support/Makefile +++ b/labs/lab-07/tasks/local-var/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGET_EXEC = local-var diff --git a/labs/lab-07/tasks/local-var/support/local-var.asm b/labs/lab-07/tasks/local-var/support/local-var.asm index 397230808..da13d3f5b 100644 --- a/labs/lab-07/tasks/local-var/support/local-var.asm +++ b/labs/lab-07/tasks/local-var/support/local-var.asm @@ -1,4 +1,4 @@ -%include "printf32.asm" +%include "printf64.asm" %define ARRAY_1_LEN 5 %define ARRAY_2_LEN 7 @@ -16,58 +16,64 @@ section .text extern printf global main main: - mov eax, 0 ; counter used for array_1 - mov ebx, 0 ; counter used for array_2 - mov ecx, 0 ; counter used for the output array + push rbp + mov rbp, rsp + + mov rax, 0 ; counter used for array_1 + mov rbx, 0 ; counter used for array_2 + mov rcx, 0 ; counter used for the output array merge_arrays: - mov edx, [array_1 + 4 * eax] - cmp edx, [array_2 + 4 * ebx] + mov edx, [array_1 + 4 * rax] + cmp edx, [array_2 + 4 * rbx] jg array_2_lower array_1_lower: - mov [array_output + 4 * ecx], edx - inc eax - inc ecx + mov [array_output + 4 * rcx], edx + inc rax + inc rcx jmp verify_array_end array_2_lower: - mov edx, [array_2 + 4 * ebx] - mov [array_output + 4 * ecx], edx - inc ecx - inc ebx + mov edx, [array_2 + 4 * rbx] + mov [array_output + 4 * rcx], edx + inc rcx + inc rbx verify_array_end: - cmp eax, ARRAY_1_LEN + cmp rax, ARRAY_1_LEN jge copy_array_2 - cmp ebx, ARRAY_2_LEN + cmp rbx, ARRAY_2_LEN jge copy_array_1 jmp merge_arrays copy_array_1: - mov edx, [array_1 + 4 * eax] - mov [array_output + 4 * ecx], edx - inc ecx - inc eax - cmp eax, ARRAY_1_LEN + mov edx, [array_1 + 4 * rax] + mov [array_output + 4 * rcx], edx + inc rcx + inc rax + cmp rax, ARRAY_1_LEN jb copy_array_1 jmp print_array copy_array_2: - mov edx, [array_2 + 4 * ebx] - mov [array_output + 4 * ecx], edx - inc ecx - inc ebx - cmp ebx, ARRAY_2_LEN + mov edx, [array_2 + 4 * rbx] + mov [array_output + 4 * rcx], edx + inc rcx + inc rbx + cmp rbx, ARRAY_2_LEN jb copy_array_2 print_array: - PRINTF32 `Array merged:\n\x0` - mov ecx, 0 + PRINTF64 `Array merged:\n\x0` + mov rcx, 0 print: - mov eax, [array_output + 4 * ecx] - PRINTF32 `%d \x0`, eax - inc ecx - cmp ecx, ARRAY_OUTPUT_LEN + mov eax, [array_output + 4 * rcx] + PRINTF64 `%d \x0`, rax + inc rcx + cmp rcx, ARRAY_OUTPUT_LEN jb print - PRINTF32 `\n\x0` - xor eax, eax - ret \ No newline at end of file + PRINTF64 `\n\x0` + xor rax, rax + + mov rsp, rbp + pop rbp + ret diff --git a/labs/lab-07/tasks/local-var/tests/test.sh b/labs/lab-07/tasks/local-var/tests/test.sh index b33baeae4..d248c8e90 100755 --- a/labs/lab-07/tasks/local-var/tests/test.sh +++ b/labs/lab-07/tasks/local-var/tests/test.sh @@ -20,8 +20,8 @@ test_local_var() ./"$binary" > "$out" 2>&1 ./"$ref_binary" > "$ref" 2>&1 - if grep -q "sub esp, .*" "${binary}.asm" && - grep -q "mov .*, [esp]" "${binary}.asm"; then + if grep -q "sub rsp, .*" "${binary}.asm" && + grep -q "mov .*, [rsp]" "${binary}.asm"; then if diff -q "$out" "$ref"; then exit 0 else diff --git a/labs/lab-07/tasks/local-var/utils/printf32.asm b/labs/lab-07/tasks/local-var/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-07/tasks/local-var/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-07/tasks/local-var/utils/printf64.asm b/labs/lab-07/tasks/local-var/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-07/tasks/local-var/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-07/tasks/max/README.md b/labs/lab-07/tasks/max/README.md index 6d6039bf1..5aac52ab2 100644 --- a/labs/lab-07/tasks/max/README.md +++ b/labs/lab-07/tasks/max/README.md @@ -5,7 +5,7 @@ parent: Lab 7 - The Stack # Task: Max -Calculate the maximum between two numbers in two registers (`eax` and `ebx`) using a comparison instruction, a jump instruction, and push/pop instructions. +Calculate the maximum between two numbers in two registers (`rax` and `rbx`) using a comparison instruction, a jump instruction, and push/pop instructions. > **TIP:** Consider how you can swap two registers using the stack. diff --git a/labs/lab-07/tasks/max/solution/max.asm b/labs/lab-07/tasks/max/solution/max.asm index 593b1601d..7266fbb11 100644 --- a/labs/lab-07/tasks/max/solution/max.asm +++ b/labs/lab-07/tasks/max/solution/max.asm @@ -1,22 +1,26 @@ -%include "../utils/printf32.asm" +%include "../utils/printf64.asm" section .text extern printf global main main: + push rbp + mov rbp, rsp + ; Numbers are placed in these two registers. - mov eax, 1 - mov ebx, 4 + mov rax, 1 + mov rbx, 4 - cmp eax, ebx + cmp rax, rbx ja print_max - push eax - push ebx - pop eax - pop ebx + push rax + push rbx + pop rax + pop rbx print_max: - PRINTF32 `Max value is: %d\n\x0`, eax + PRINTF64 `Max value is: %d\n\x0`, rax - ret \ No newline at end of file + leave + ret diff --git a/labs/lab-07/tasks/max/support/Makefile b/labs/lab-07/tasks/max/support/Makefile index 2227e8f5a..36b067a1d 100644 --- a/labs/lab-07/tasks/max/support/Makefile +++ b/labs/lab-07/tasks/max/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGET_EXEC = max diff --git a/labs/lab-07/tasks/max/support/max.asm b/labs/lab-07/tasks/max/support/max.asm index 04427d919..a14f62e23 100644 --- a/labs/lab-07/tasks/max/support/max.asm +++ b/labs/lab-07/tasks/max/support/max.asm @@ -1,16 +1,21 @@ -%include "printf32.asm" +%include "printf64.asm" section .text extern printf global main main: + push rbp + mov rbp, rsp + ; numbers are placed in these two registers - mov eax, 1 - mov ebx, 4 + mov rax, 1 + mov rbx, 4 ; TODO: get maximum value. You are only allowed to use one conditional jump and push/pop instructions. - PRINTF32 `Max value is: %d\n\x0`, eax ; print maximum value + PRINTF64 `Max value is: %ld\n\x0`, rax ; print maximum value + mov rsp, rbp + pop rbp ret diff --git a/labs/lab-07/tasks/max/tests/test.sh b/labs/lab-07/tasks/max/tests/test.sh index 4c6b2a091..589f725ee 100755 --- a/labs/lab-07/tasks/max/tests/test.sh +++ b/labs/lab-07/tasks/max/tests/test.sh @@ -20,7 +20,7 @@ test_max() ./"$binary" > "$out" 2>&1 ./"$ref_binary" > "$ref" 2>&1 - if grep -q -E 'cmp eax, ebx|cmp ebx, eax' "${binary}.asm" && + if grep -q -E 'cmp rax, rbx|cmp rbx, rax' "${binary}.asm" && grep -q -E 'jl|jg|jb|ja|jle|jge|jbe|jae' "${binary}.asm" && grep -q -E 'push' "${binary}.asm" && grep -q -E 'pop' "${binary}.asm"; then diff --git a/labs/lab-07/tasks/max/utils/printf32.asm b/labs/lab-07/tasks/max/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-07/tasks/max/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-07/tasks/max/utils/printf64.asm b/labs/lab-07/tasks/max/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-07/tasks/max/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-07/tasks/reverse-array/solution/reverse-array.asm b/labs/lab-07/tasks/reverse-array/solution/reverse-array.asm index c1607b507..75e0ec95a 100644 --- a/labs/lab-07/tasks/reverse-array/solution/reverse-array.asm +++ b/labs/lab-07/tasks/reverse-array/solution/reverse-array.asm @@ -1,38 +1,42 @@ -%include "../utils/printf32.asm" +%include "../utils/printf64.asm" %define ARRAY_LEN 7 section .data -input dd 122, 184, 199, 242, 263, 845, 911 -output times ARRAY_LEN dd 0 +input dq 122, 184, 199, 242, 263, 845, 911 +output times ARRAY_LEN dq 0 section .text extern printf global main main: + push rbp + mov rbp, rsp push ARRAY_LEN - pop ecx + pop rcx push_elem: - push dword [input + 4 * (ecx - 1)] + push qword [input + 8 * (rcx - 1)] loop push_elem push ARRAY_LEN - pop ecx + pop rcx pop_elem: - pop dword [output + 4 * (ecx - 1)] + pop qword [output + 8 * (rcx - 1)] loop pop_elem - PRINTF32 `Reversed array: \n\x0` - xor ecx, ecx + PRINTF64 `Reversed array: \n\x0` + xor rcx, rcx print_array: - mov edx, [output + 4 * ecx] - PRINTF32 `%d\n\x0`, edx - inc ecx - cmp ecx, ARRAY_LEN + mov rdx, [output + 8 * rcx] + PRINTF64 `%ld\n\x0`, rdx + inc rcx + cmp rcx, ARRAY_LEN jb print_array - xor eax, eax + xor rax, rax + + leave ret diff --git a/labs/lab-07/tasks/reverse-array/support/Makefile b/labs/lab-07/tasks/reverse-array/support/Makefile index b15f88097..bd860e714 100644 --- a/labs/lab-07/tasks/reverse-array/support/Makefile +++ b/labs/lab-07/tasks/reverse-array/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGET_EXEC = reverse-array diff --git a/labs/lab-07/tasks/reverse-array/support/reverse-array.asm b/labs/lab-07/tasks/reverse-array/support/reverse-array.asm index 8319189b9..137038a68 100644 --- a/labs/lab-07/tasks/reverse-array/support/reverse-array.asm +++ b/labs/lab-07/tasks/reverse-array/support/reverse-array.asm @@ -1,29 +1,34 @@ -%include "printf32.asm" +%include "printf64.asm" %define ARRAY_LEN 7 section .data -input dd 122, 184, 199, 242, 263, 845, 911 -output times ARRAY_LEN dd 0 +input dq 122, 184, 199, 242, 263, 845, 911 +output times ARRAY_LEN dq 0 section .text extern printf global main main: + push rbp + mov rbp, rsp ; TODO push the elements of the array on the stack ; TODO retrieve the elements (pop) from the stack into the output array - PRINTF32 `Reversed array: \n\x0` - xor ecx, ecx + PRINTF64 `Reversed array: \n\x0` + xor rcx, rcx print_array: - mov edx, [output + 4 * ecx] - PRINTF32 `%d\n\x0`, edx - inc ecx - cmp ecx, ARRAY_LEN + mov rdx, [output + 8 * rcx] + PRINTF64 `%ld\n\x0`, rdx + inc rcx + cmp rcx, ARRAY_LEN jb print_array - xor eax, eax + xor rax, rax + + mov rsp, rbp + pop rbp ret diff --git a/labs/lab-07/tasks/reverse-array/utils/printf32.asm b/labs/lab-07/tasks/reverse-array/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-07/tasks/reverse-array/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-07/tasks/reverse-array/utils/printf64.asm b/labs/lab-07/tasks/reverse-array/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-07/tasks/reverse-array/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-07/tasks/stack-addressing/README.md b/labs/lab-07/tasks/stack-addressing/README.md index 1781a4e6d..0a39a83c6 100644 --- a/labs/lab-07/tasks/stack-addressing/README.md +++ b/labs/lab-07/tasks/stack-addressing/README.md @@ -11,7 +11,7 @@ The `stack-addressing.asm` program in the lab's archive allocates and initialize - a string "Bob has corn". 1. Replace each `push` instruction with an equivalent sequence of instructions. -1. Print the addresses and values on the stack in the interval `[esp, ebp]` (from high addresses to low addresses) dword by dword. +1. Print the addresses and values on the stack in the interval `[rsp, rbp]` (from high addresses to low addresses) qword by qword. 1. Print the string allocated on the stack byte by byte and explain how it looks in memory. Think about where you should start displaying and when you should stop. 1. Print the vector allocated on the stack element by element. @@ -20,18 +20,18 @@ Think about where you should start displaying and what size each element has. After a successful implementation, the program should display something similar to the following output (it won't be exactly the same, stack memory addresses may differ): > >```c ->Bob has corn ->0xffe804cc: 0xf7d91519 ->0xffe804c8: 0x5 ->0xffe804c4: 0x4 ->0xffe804c0: 0x3 ->0xffe804bc: 0x2 ->0xffe804b8: 0x1 ->0xffe804b4: 0x0 ->0xffe804b0: 0x6e726f63 ->0xffe804ac: 0x20736168 ->0xffe804a8: 0x20626f42 ->Bob has corn +>Anthony is very handsome +>0x10dcdff8: 0x6182a1ca +>0x10dcdff0: 0x5 +>0x10dcdfe8: 0x4 +>0x10dcdfe0: 0x3 +>0x10dcdfd8: 0x2 +>0x10dcdfd0: 0x1 +>0x10dcdfc8: 0x0 +>0x10dcdfc0: 0x646e6168 +>0x10dcdfb8: 0x76207369 +>0x10dcdfb0: 0x68746e41 +>Anthony is very handsome >1 2 3 4 5 >``` > diff --git a/labs/lab-07/tasks/stack-addressing/solution/stack-addressing.asm b/labs/lab-07/tasks/stack-addressing/solution/stack-addressing.asm index 7c175b746..b9d10a249 100644 --- a/labs/lab-07/tasks/stack-addressing/solution/stack-addressing.asm +++ b/labs/lab-07/tasks/stack-addressing/solution/stack-addressing.asm @@ -1,4 +1,4 @@ -%include "../utils/printf32.asm" +%include "printf64.asm" %define NUM 5 section .text @@ -6,54 +6,65 @@ section .text extern printf global main main: - mov ebp, esp - mov ecx, NUM + sub rsp, 8 + mov qword [rsp], rbp + mov rbp, rsp + + mov rcx, NUM push_nums: - sub esp, 4 - mov dword [esp], ecx + sub rsp, 8 + mov qword [rsp], rcx loop push_nums - sub esp, 4 - mov dword [esp], 0 + and rsp, -16 ;; Align the stack to 16 bytes + + sub rsp, 8 + mov qword [rsp], 0 - sub esp, 4 - mov dword [esp], "corn" + sub rsp, 8 + mov rax, "handsome" + mov qword [rsp], rax - sub esp, 4 - mov dword [esp], "has " + sub rsp, 8 + mov rax, "is very " + mov qword [rsp], rax - sub esp, 4 - mov dword [esp], "Bob " - lea esi, [esp] - PRINTF32 `%s\n\x0`, esi + sub rsp, 8 + mov rax, "Anthony " + mov qword [rsp], rax + lea rsi, [rsp] + PRINTF64 `%s\n\x0`, rsi ; Print the stack in "address: value" format. - mov eax, ebp + mov rax, rbp print_stack: - PRINTF32 `0x%x: 0x%x\n\x0`, eax, [eax] + PRINTF64 `0x%x: 0x%x\n\x0`, rax, qword [rax] - sub eax, 4 - cmp eax, esp + sub rax, 8 + cmp rax, rsp jge print_stack ; Print the string. - lea esi, [esp] - PRINTF32 `%s\n\x0`, esi + lea rsi, [rsp] + PRINTF64 `%s\n\x0`, rsi ; Print the array. - add esp, 16 - mov eax, esp + lea rax, [rsp + 40] + print_array: - PRINTF32 `%d \x0`, [eax] + PRINTF64 `%d \x0`, qword [rax] - add eax, 4 - cmp eax, ebp + add rax, 8 + cmp rax, rbp jl print_array - PRINTF32 `\n\x0` + PRINTF64 `\n\x0` - ; Restore the previous value of the EBP (Base Pointer). - mov esp, ebp + ; Restore the previous value of the RBP (Base Pointer). + mov rsp, rbp + mov rbp, qword [rsp] + add rsp, 8 ; Exit without errors. - xor eax, eax + xor rax, rax + ret diff --git a/labs/lab-07/tasks/stack-addressing/support/Makefile b/labs/lab-07/tasks/stack-addressing/support/Makefile index a78256d7b..b25a20e81 100644 --- a/labs/lab-07/tasks/stack-addressing/support/Makefile +++ b/labs/lab-07/tasks/stack-addressing/support/Makefile @@ -7,9 +7,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -m64 -no-pie TARGET_EXEC = stack-addressing diff --git a/labs/lab-07/tasks/stack-addressing/support/stack-addressing.asm b/labs/lab-07/tasks/stack-addressing/support/stack-addressing.asm index 2f146d7cb..6cf0ec784 100644 --- a/labs/lab-07/tasks/stack-addressing/support/stack-addressing.asm +++ b/labs/lab-07/tasks/stack-addressing/support/stack-addressing.asm @@ -1,4 +1,4 @@ -%include "printf32.asm" +%include "printf64.asm" %define NUM 5 @@ -7,32 +7,35 @@ section .text extern printf global main main: - mov ebp, esp + mov rbp, rsp - ; TODO 1: replace every "push" instruction by an equivalent sequence of commands (use direct addressing of memory. Hint: esp) - mov ecx, NUM + ; TODO 1: replace every "push" instruction by an equivalent sequence of commands (use direct addressing of memory. Hint: rsp) + mov rcx, NUM push_nums: - push ecx + push rcx loop push_nums push 0 - push "corn" - push "has " - push "Bob " + mov rax, "handsome" + push rax + mov rax, "is very " + push rax + mov rax, "Anthony " + push rax - lea esi, [esp] - PRINTF32 `%s\n\x0`, esi + lea rsi, [rsp] + PRINTF64 `%s\n\x0`, rsi - ; TODO 2: print the stack in "address: value" format in the range of [ESP:EBP] - ; use PRINTF32 macro - see format above + ; TODO 2: print the stack in "address: value" format in the range of [RSP:RBP] + ; use PRINTF64 macro - see format above ; TODO 3: print the string ; TODO 4: print the array on the stack, element by element. - ; restore the previous value of the EBP (Base Pointer) - mov esp, ebp + ; restore the previous value of the rbp (Base Pointer) + mov rsp, rbp ; exit without errors - xor eax, eax + xor rax, rax ret diff --git a/labs/lab-07/tasks/stack-addressing/tests/test.sh b/labs/lab-07/tasks/stack-addressing/tests/test.sh index 3e14df43f..820844911 100755 --- a/labs/lab-07/tasks/stack-addressing/tests/test.sh +++ b/labs/lab-07/tasks/stack-addressing/tests/test.sh @@ -23,7 +23,7 @@ test_stack_addressing() awk ' BEGIN { expected_addr = "" } - /^Bob has corn$/ { + /^Anthony is very handsome$/ { if (NR != 1 && NR != 12) exit 1 next } @@ -31,7 +31,7 @@ test_stack_addressing() NR == 2 { if (!match($0, /^0x[0-9a-fA-F]+: 0x[0-9a-fA-F]+$/)) exit 1 split($0, parts, ": ") - expected_addr = strtonum(parts[1]) - 4 + expected_addr = strtonum(parts[1]) - 8 next } @@ -42,7 +42,7 @@ test_stack_addressing() if (current_addr != expected_addr) exit 1 - expected_addr -= 4 + expected_addr -= 8 next } diff --git a/labs/lab-07/tasks/stack-addressing/utils/printf32.asm b/labs/lab-07/tasks/stack-addressing/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-07/tasks/stack-addressing/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-07/tasks/stack-addressing/utils/printf64.asm b/labs/lab-07/tasks/stack-addressing/utils/printf64.asm new file mode 100644 index 000000000..2bcf2712b --- /dev/null +++ b/labs/lab-07/tasks/stack-addressing/utils/printf64.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-08/guides/disassembling-c/README.md b/labs/lab-08/guides/disassembling-c/README.md index 1c8863a85..ec0f718ca 100644 --- a/labs/lab-08/guides/disassembling-c/README.md +++ b/labs/lab-08/guides/disassembling-c/README.md @@ -23,7 +23,7 @@ We'll use the `test.c` program from the lab archive. 1. Use the command: ```Bash -gcc -m32 -o +gcc -fno-PIC -o ``` where `` is the name of the source file (`test.c`) and `` is the name of the result executable. @@ -31,7 +31,7 @@ where `` is the name of the source file (`test.c`) and `` is t If you **only** want to compile (**without** linking it), use: ```Bash -gcc -m32 -c -o +gcc -fno-PIC -c -o ``` where `` is the name of the source file and `` is the name of the desired output object file. @@ -39,7 +39,7 @@ where `` is the name of the source file and `` is the name Since we want to transform `test.c` into an object file, we'll run: ```Bash -gcc -m32 -c -o test.o test.c +gcc -fno-PIC -c -o test.o test.c ``` After running the above command, we should see a file named `test.o`. @@ -47,7 +47,7 @@ After running the above command, we should see a file named `test.o`. Furthermore, we can use `gcc` to transform the `C` code in `Assembly` code: ```Bash -gcc -m32 -masm=intel -S -o test.asm test.c +gcc -fno-PIC -masm=intel -S -o test.asm test.c ``` After running the above command we'll have a file called `test.asm`, which we can inspect using any text editor/reader, such as cat: @@ -69,80 +69,64 @@ Afterwards, you'll see an output similar to the following: ```console $ objdump -M intel -d test.o -test.o: file format elf32-i386 +test.o: file format elf64-x86-64 Disassembly of section .text: -00000000 : - 0: 55 push ebp - 1: 89 e5 mov ebp,esp - 3: e8 fc ff ff ff call 4 - 8: 05 01 00 00 00 add eax,0x1 - d: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] - 10: 8b 10 mov edx,DWORD PTR [eax] - 12: 8b 45 0c mov eax,DWORD PTR [ebp+0xc] - 15: 01 c2 add edx,eax - 17: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] - 1a: 89 10 mov DWORD PTR [eax],edx - 1c: 90 nop - 1d: 5d pop ebp - 1e: c3 ret - -0000001f : - 1f: 55 push ebp - 20: 89 e5 mov ebp,esp - 22: 53 push ebx - 23: 83 ec 14 sub esp,0x14 - 26: e8 fc ff ff ff call 27 - 2b: 05 01 00 00 00 add eax,0x1 - 30: c7 45 f4 03 00 00 00 mov DWORD PTR [ebp-0xc],0x3 - 37: 83 ec 0c sub esp,0xc - 3a: 8d 90 00 00 00 00 lea edx,[eax+0x0] - 40: 52 push edx - 41: 89 c3 mov ebx,eax - 43: e8 fc ff ff ff call 44 - 48: 83 c4 10 add esp,0x10 - 4b: 83 ec 08 sub esp,0x8 - 4e: ff 75 f4 push DWORD PTR [ebp-0xc] - 51: 8d 45 08 lea eax,[ebp+0x8] - 54: 50 push eax - 55: e8 a6 ff ff ff call 0 - 5a: 83 c4 10 add esp,0x10 - 5d: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] - 60: 8b 5d fc mov ebx,DWORD PTR [ebp-0x4] - 63: c9 leave - 64: c3 ret - -00000065
: - 65: 8d 4c 24 04 lea ecx,[esp+0x4] - 69: 83 e4 f0 and esp,0xfffffff0 - 6c: ff 71 fc push DWORD PTR [ecx-0x4] - 6f: 55 push ebp - 70: 89 e5 mov ebp,esp - 72: 53 push ebx - 73: 51 push ecx - 74: e8 fc ff ff ff call 75 - 79: 81 c3 02 00 00 00 add ebx,0x2 - 7f: 83 ec 0c sub esp,0xc - 82: 6a 0f push 0xf - 84: e8 96 ff ff ff call 1f - 89: 83 c4 10 add esp,0x10 - 8c: 83 ec 08 sub esp,0x8 - 8f: 50 push eax - 90: 8d 83 0e 00 00 00 lea eax,[ebx+0xe] - 96: 50 push eax - 97: e8 fc ff ff ff call 98 - 9c: 83 c4 10 add esp,0x10 - 9f: b8 00 00 00 00 mov eax,0x0 - a4: 8d 65 f8 lea esp,[ebp-0x8] - a7: 59 pop ecx - a8: 5b pop ebx - a9: 5d pop ebp - aa: 8d 61 fc lea esp,[ecx-0x4] - ad: c3 ret +0000000000000000 : + 0: f3 0f 1e fa endbr64 + 4: 55 push rbp + 5: 48 89 e5 mov rbp,rsp + 8: 48 89 7d f8 mov QWORD PTR [rbp-0x8],rdi + c: 89 75 f4 mov DWORD PTR [rbp-0xc],esi + f: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8] + 13: 8b 10 mov edx,DWORD PTR [rax] + 15: 8b 45 f4 mov eax,DWORD PTR [rbp-0xc] + 18: 01 c2 add edx,eax + 1a: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8] + 1e: 89 10 mov DWORD PTR [rax],edx + 20: 90 nop + 21: 5d pop rbp + 22: c3 ret + +0000000000000023 : + 23: f3 0f 1e fa endbr64 + 27: 55 push rbp + 28: 48 89 e5 mov rbp,rsp + 2b: 48 83 ec 20 sub rsp,0x20 + 2f: 89 7d ec mov DWORD PTR [rbp-0x14],edi + 32: c7 45 fc 03 00 00 00 mov DWORD PTR [rbp-0x4],0x3 + 39: bf 00 00 00 00 mov edi,0x0 + 3e: e8 00 00 00 00 call 43 + 43: 8b 55 fc mov edx,DWORD PTR [rbp-0x4] + 46: 48 8d 45 ec lea rax,[rbp-0x14] + 4a: 89 d6 mov esi,edx + 4c: 48 89 c7 mov rdi,rax + 4f: e8 ac ff ff ff call 0 + 54: 8b 45 ec mov eax,DWORD PTR [rbp-0x14] + 57: c9 leave + 58: c3 ret + +0000000000000059
: + 59: f3 0f 1e fa endbr64 + 5d: 55 push rbp + 5e: 48 89 e5 mov rbp,rsp + 61: bf 0f 00 00 00 mov edi,0xf + 66: e8 b8 ff ff ff call 23 + 6b: 89 c6 mov esi,eax + 6d: bf 00 00 00 00 mov edi,0x0 + 72: b8 00 00 00 00 mov eax,0x0 + 77: e8 00 00 00 00 call 7c + 7c: b8 00 00 00 00 mov eax,0x0 + 81: 5d pop rbp + 82: c3 ret ``` +You may notice the repeated occurrences of the `endbr64` instruction. +It is part of `Intel's Control-Flow Enforcement Technology(CET)` and its purpose is to prevent malicious function executions (such as corrupting buffers and trying to alter the normal execution flow of the program). +Detailed explanations about this instruction can be found in the [Buffer Management](https://cs-pub-ro.github.io/hardware-software-interface/labs/lab-10/README.html) lab. + There are many other utilities that allow disassembly of object modules, most of them with a graphical interface and offering debugging support. `objdump` is a simple utility that can be quickly used from the command-line. diff --git a/labs/lab-08/guides/disassembling-c/support/Makefile b/labs/lab-08/guides/disassembling-c/support/Makefile index da01c2a93..cf8d1b06e 100644 --- a/labs/lab-08/guides/disassembling-c/support/Makefile +++ b/labs/lab-08/guides/disassembling-c/support/Makefile @@ -1,7 +1,7 @@ CC = gcc -CFLAGS ?= -m32 -g -Wall -Wextra -Werror -fno-pic -masm=intel -LDFLAGS ?= -m32 -no-pie +CFLAGS ?= -g -Wall -Wextra -Werror -fno-pic -masm=intel +LDFLAGS ?= -fno-PIC -no-pie all: test diff --git a/labs/lab-08/guides/hello-world/support/Makefile b/labs/lab-08/guides/hello-world/support/Makefile index 55f0f26d0..d03974a7f 100644 --- a/labs/lab-08/guides/hello-world/support/Makefile +++ b/labs/lab-08/guides/hello-world/support/Makefile @@ -6,9 +6,9 @@ OBJS := $(SRCS:.asm=.o) UTILSDIR := ..utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -fno-PIC -no-pie all: hello_world diff --git a/labs/lab-08/guides/hello-world/support/hello_world.asm b/labs/lab-08/guides/hello-world/support/hello_world.asm index 4fd172d3f..34c86aca5 100644 --- a/labs/lab-08/guides/hello-world/support/hello_world.asm +++ b/labs/lab-08/guides/hello-world/support/hello_world.asm @@ -1,4 +1,4 @@ -%include "../utils/printf32.asm" +%include "../utils/printf64.asm" section .data msg db 'Hello, world!', 0 @@ -12,12 +12,11 @@ extern puts global main main: - push ebp ; Since main is a function, it has to adhere to the same convention - mov ebp, esp + push rbp ; Since main is a function, it has to adhere to the same convention + mov rbp, rsp - push msg + mov rdi, msg ; store the address of the string in rdi call puts - add esp, 4 ; The pushed msg argument takes up 4 bytes on the stack leave ret diff --git a/labs/lab-08/guides/hello-world/utils/printf32.asm b/labs/lab-08/guides/hello-world/utils/printf32.asm deleted file mode 100644 index 61a930ed7..000000000 --- a/labs/lab-08/guides/hello-world/utils/printf32.asm +++ /dev/null @@ -1,22 +0,0 @@ -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-08/guides/hello-world/utils/printf64.asm b/labs/lab-08/guides/hello-world/utils/printf64.asm new file mode 100644 index 000000000..e799a46e0 --- /dev/null +++ b/labs/lab-08/guides/hello-world/utils/printf64.asm @@ -0,0 +1,73 @@ +; SPDX-License-Identifier: BSD-3-Clause + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-08/guides/registers-terminator/README.md b/labs/lab-08/guides/registers-terminator/README.md new file mode 100644 index 000000000..31d7c79e4 --- /dev/null +++ b/labs/lab-08/guides/registers-terminator/README.md @@ -0,0 +1,49 @@ +--- +nav_order: 9 +parent: Lab 8 - Functions +--- + +# Guide: Registers Terminator + +Navigate to `guides/registers-terminator/support/`. + +Open the `registers_terminator.asm` file, assemble it, and run it. + +The purpose of this guide is to really make you aware how caller-saved registers **need to** be saved by the `caller`. +It is also important to notice that the `rbx` and `r12-r15` registers are preserved(`callee saved`). + +In the presented assembly file, we set some values to the `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8` and `r9` simulating a normal program flow. +We print their values and then call the libc `printf` function and then print their values again. +The output can look similar to this one: + +```console +rax=1 +rcx=2 +rdx=3 +rsi=4 +rdi=404018 +r8=6 +r9=7 +rbx=8 +r12=9 +r13=a +r14=b +r15=c +Hello, world! +rax=e +rcx=0 +rdx=0 +rsi=13e5b2a0 +rdi=1463ae00 +r8=6 +r9=7 +rbx=8 +r12=9 +r13=a +r14=b +r15=c +``` + +We can easily notice that the values of the `rax`, `rcx`, `rdx`, `rsi` and `rdi` registers have been completely altered. +If those were real values and we would have needed them after the `printf` call, the results would be unpredictable but, certainly, wrong. +However, we can observe that the `callee-saved` registers remained untouched. diff --git a/labs/lab-08/guides/registers-terminator/support/.gitignore b/labs/lab-08/guides/registers-terminator/support/.gitignore new file mode 100644 index 000000000..0900dce2e --- /dev/null +++ b/labs/lab-08/guides/registers-terminator/support/.gitignore @@ -0,0 +1 @@ +registers_terminator diff --git a/labs/lab-08/guides/registers-terminator/support/Makefile b/labs/lab-08/guides/registers-terminator/support/Makefile new file mode 100644 index 000000000..0811ea119 --- /dev/null +++ b/labs/lab-08/guides/registers-terminator/support/Makefile @@ -0,0 +1,23 @@ +AS = nasm +CC = gcc + +SRCS := $(shell find . -name "*.asm") +OBJS := $(SRCS:.asm=.o) + +UTILSDIR := ..utils/ + +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" +CFLAGS ?= -Wall +LDFLAGS ?= -fno-PIC -no-pie + +all: registers_terminator + +registers_terminator: registers_terminator.o + +%.o: %.asm + $(AS) $(ASFLAGS) $< -o $@ + +.PHONY: clean + +clean: + -rm -f *.o registers_terminator diff --git a/labs/lab-08/guides/registers-terminator/support/registers_terminator.asm b/labs/lab-08/guides/registers-terminator/support/registers_terminator.asm new file mode 100644 index 000000000..caf8ba984 --- /dev/null +++ b/labs/lab-08/guides/registers-terminator/support/registers_terminator.asm @@ -0,0 +1,53 @@ +%include "../utils/printf64.asm" + +section .data + msg db 10, 'printf called now', 10, 10, 0 + +section .text + +; We need to mark "printf()" as an external function. This means it is not implemented in this file. +; The linker will resolve this symbol by looking for it in other object files. +extern printf + +global main + +main: + push rbp ; Since main is a function, it has to adhere to the same convention + mov rbp, rsp + + ; store information in registers that should be caller saved, + ; rax, rcx, rdx, rsi, rdi, r8, r9 + mov rax, 0x1 + mov rcx, 0x2 + mov rdx, 0x3 + mov rsi, 0x4 + mov rdi, msg + mov r8, 0x6 + mov r9, 0x7 + + ; store information in registers that should be callee saved, + ; rbx, r12, r13, r14, r15 + mov rbx, 0x8 + mov r12, 0x9 + mov r13, 0xa + mov r14, 0xb + mov r15, 0xc + + ; print the caller-saved registers before the call + PRINTF64 `rax=%x\nrcx=%x\nrdx=%x\nrsi=%x\nrdi=%x\n`, rax, rcx, rdx, rsi, rdi + PRINTF64 `r8=%x\nr9=%x\n`, r8, r9 + + ; print the callee-saved registers before the call + PRINTF64 `rbx=%x\nr12=%x\nr13=%x\nr14=%x\nr15=%x\n`, rbx, r12, r13, r14, r15 + + call printf + + ; print the registers after the call + PRINTF64 `rax=%x\nrcx=%x\nrdx=%x\nrsi=%x\nrdi=%x\n`, rax, rcx, rdx, rsi, rdi + PRINTF64 `r8=%x\nr9=%x\n`, r8, r9 + + ; print the callee-saved registers after the call + PRINTF64 `rbx=%x\nr12=%x\nr13=%x\nr14=%x\nr15=%x\n`, rbx, r12, r13, r14, r15 + + leave + ret diff --git a/labs/lab-08/guides/registers-terminator/utils/printf64.asm b/labs/lab-08/guides/registers-terminator/utils/printf64.asm new file mode 100644 index 000000000..e799a46e0 --- /dev/null +++ b/labs/lab-08/guides/registers-terminator/utils/printf64.asm @@ -0,0 +1,73 @@ +; SPDX-License-Identifier: BSD-3-Clause + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-08/media/stack.svg b/labs/lab-08/media/stack.svg index 0a4d6aaa8..288a44f37 100644 --- a/labs/lab-08/media/stack.svg +++ b/labs/lab-08/media/stack.svg @@ -1,4 +1,4 @@ -
Local variable 1
Local variable 2

argn-1

argn-2
...
arg1
Return Address
arg0
Caller
Caller Base Pointer
Local variable 3
Local variable 1
Local variable 2
Callee
EBP + 4
EBP
EBP - 4
ESP
Higher address (eg. 0xf8)
Lower address (eg. 0xa8)
Caller's Caller BP
Old EBP
\ No newline at end of file +
Caller's Caller BP
Local variable 1
Local variable 2
argn - 1
argn - 2
...
arg7
arg6
Return Address
Caller's Base Pointer
Local Variable 1
Local Variable 2
Local Variable 3
Old RBP
RBP
RSP
RBP - 8
RBP + 8
Callee
Caller
Higher Address (eg.0xfff8)
Lower Address (eg. 0xfa08)
arg0
arg1
arg2
arg3
arg4
arg5
RDI
RSI
RDX
RCX
R8
R9
\ No newline at end of file diff --git a/labs/lab-08/reading/functions.md b/labs/lab-08/reading/functions.md index ab0430c3d..f48975f3f 100644 --- a/labs/lab-08/reading/functions.md +++ b/labs/lab-08/reading/functions.md @@ -38,13 +38,14 @@ When it comes to calling a function with parameters, there are two major options > **NOTE:** For **32-bit** architectures, the stack passing method is used, while for **64-bit** architectures, the register passing method is used for the first 6 arguments. > Starting from the 7th, the stack has to be used. -> We will use the convention for 32-bit architecture. +> We will use the convention for 64-bit architecture. ## Function Call When we call a function, the steps are as follows: -- We put the arguments on the stack, pushing them in the reverse order in which they are sent as function arguments. +- We put the first 6 arguments(if there are at least 6 arguments) in the following registers in left-to-right order: `rdi`, `rsi`, `rdx`, `rcx`, `r8` and `r9`. +- We put the remaining arguments on the stack, pushing them in the reverse order in which they are sent as function arguments. - We call `call`. - We restore the stack at the end of the call. @@ -56,32 +57,36 @@ As we know, stack operations fall into two types: - `pop reg/mem` where what is on the top of the stack is placed into a register or memory area When we `push`, we say that the stack **grows** (elements are added). -For reasons that will be better explained later, the stack pointer (indicated by the `esp` register in 32-bit mode) decreases in value when the stack grows (on `push`). +For reasons that will be better explained later, the stack pointer (indicated by the `rsp` register in 64-bit mode) decreases in value when the stack grows (on `push`). However, this contradiction in naming comes from the fact that the stack is typically represented vertically, with smaller values at the top and larger values at the bottom. Similarly, when we `pop`, we say that the stack **shrinks** (elements are removed). -Now the stack pointer (indicated by the `esp` register in 32-bit mode) increases in value. +Now the stack pointer (indicated by the `rsp` register in 64-bit mode) increases in value. -A summary of this is explained very well [here](https://en.wikibooks.org/wiki/X86_Disassembly/The_Stack). +A summary of this is explained very well [here](https://en.wikibooks.org/wiki/X86_Disassembly/The_Stack) and [here](https://medium.com/@_neerajpal/explained-difference-between-x86-x64-disassembly-49e9678e1ae2). For example, if we have the function `foo` with the following signature (in C language): ```C -int foo(int a, int b, int c); +long foo(long a, long b, long c, long d, + long e, long f, long g, long h); ``` The call to this function will look like this: ```Assembly -mov ecx, [c] ; take the value of parameter c from a memory location -mov ebx, [b] -mov eax, [a] +mov rdi, [a] ; store the first 6 arguments in the assigned registers +mov rsi, [b] +mov rdx, [c] +mov rcx, [d] +mov r8, [e] +mov r9, [f] + +push qword [h] ; put the last 2 arguments onto the stack in reverse order, first h +push qworg [g] ; then g -push ecx ; put parameters in reverse order, starting with c -push ebx ; then b -push eax ; then a call foo ; call the function -add esp, 12 ; restore the stack +add rsp, 16 ; restore the stack ``` ## Caller and Callee @@ -94,22 +99,24 @@ Until the `call` instruction, the stack contains the function's parameters. The `call` can be roughly equated to the following sequence: ```Assembly -push eip +push rip jmp function_name ``` That is, even the `call` uses the stack and saves the address of the next instruction, the one after the `call`, also known as the **return address**. This is necessary for the callee to know where to return to in the caller. -In the callee, at its beginning (called preamble), the frame pointer is saved (in the i386 architecture, this is the `ebp` register), with the frame pointer then referring to the current function stack frame. +In the callee, at its beginning (called preamble), the frame pointer is saved (in the X86_64 architecture, this is the `rbp` register), with the frame pointer then referring to the current function stack frame. This is crucial for accessing parameters and local variables via an offset from the frame pointer. Although not mandatory, saving the frame pointer helps in debugging and is used in most cases. +Also, in order to avoid any issues at runtime, it is imperative to keep the stack `16-byte aligned` by saving the `rbp` register in the preamble of the function. +Not having the stack `16-byte aligned` may cause some `libc` functions (such as `printf`) to end up causing a `Segmentation Fault` for a program that, besides that misalignment, would be correct. For these reasons, any function call will generally have a preamble: ```Assembly -push ebp -mov ebp, esp +push rbp +mov rbp, rsp ``` These modifications take place in the callee. @@ -124,12 +131,12 @@ After this instruction, the stack is as it was at the beginning of the function It is equivalent to the following code, which undoes the functions's preamble: ```Assembly -mov esp, ebp -pop ebp +mov rsp, rbp +pop rbp ``` To conclude the function, it is necessary for the execution to return and continue from the instruction following the `call` that started the function. -This involves influencing the `eip` register and putting back the value that was saved on the stack initially by the `call` instruction. +This involves influencing the `rip` register and putting back the value that was saved on the stack initially by the `call` instruction. This is achieved using the instruction: ```Assembly @@ -139,23 +146,27 @@ ret which is roughly equivalent to the instruction: ```Assembly -pop eip +pop rip ``` -For example, the definition and body of the function foo, which calculates the sum of 3 numbers, would look like this: +For example, the definition and body of the function foo, which calculates the sum of 8 numbers, would look like this: ```Assembly foo: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - mov eax, [ebp + 8] - mov ebx, [ebp + 12] - mov ecx, [ebp + 16] + mov rax, [rbp + 16] ; get the two arguments pushed onto the stack + mov rbx, [rbp + 24] - add eax, ebx - add eax, ecx + add rax, rdi + add rax, rsi + add rax, rdx + add rax, rcx + add rax, r8 + add rax, r9 + add rax, rbx ; the result is stored in rax leave ret @@ -172,19 +183,23 @@ foo: 1. Note that during the execution of the function, what does not change is the position of the frame pointer. This is the reason for its name: it points to the current function's frame. Therefore, it is common to access a function's parameters through the frame pointer. -Assuming a 32-bit system and processor word-sized parameters (32 bits, 4 bytes), we will have: - - - the first argument is found at address `ebp+8` - - the second argument is found at address `ebp+12` - - the third argument is found at address `ebp+16` +Assuming a 64-bit system and processor word-sized parameters (64 bits, 8 bytes), we will have: + + - the first argument is found in the `rdi` register + - the second argument is found in the `rsi` register + - the third argument is found in the `rdx` register + - the fourth argument is found in the `rcx` register + - the fifth argument is found in the `r8` register + - the sixth argument is found in the `r9` register + - the seventh argument is found at address `rbp + 16` + - the eighth argument is found at address `rbp + 24` - etc. - This is why, to get the parameters of the foo function in the eax, ebx, ecx registers, we use the constructions: + This is why, to get the parameters of the foo function in the rax and rbx registers, we use the constructions: ```Assembly - mov eax, dword [ebp+8] ; first argument in eax - mov ebx, dword [ebp+12] ; second argument in ebx - mov ecx, dword [ebp+16] ; third argument in ecx + mov rax, qword [rbp + 16] ; seventh argument in rax + mov rbx, qword [rbp + 24] ; eighth argument in rbx ``` 1. The return value of a function is placed in registers (generally in eax). @@ -192,21 +207,25 @@ Assuming a 32-bit system and processor word-sized parameters (32 bits, 4 bytes), - If the return value is **8 bits**, the function's result is placed in `al`. - If the return value is **16 bits**, the function's result is placed in `ax`. - If the return value is **32 bits**, the function's result is placed in `eax`. - - If the return value is **64 bits**, the result is placed in the `edx` and `eax` registers. - The most significant 32 bits are placed in `edx`, and the rest in the `eax` register. + - If the return value is **64 bits**, the function's result is placed in `rax`. + - If the return value is **>= 128 bits**, the result is placed in the `rdx` and `rax` registers. + The most significant bits are placed in `rdx` and the rest in `rax`. _Additionally, in some cases, a memory address can be returned to the stack/heap (e.g. `malloc()`), or other memory areas, which refer to the desired object after the function call._ 1. A function uses the same hardware registers; therefore, when exiting the function, the values of the registers are no longer the same. To avoid this situation, some/all registers can be saved on the stack. -You can push all registers to the stack using the [`pusha` instruction - "push all"](https://c9x.me/x86/html/file_module_x86_id_270.html). -And you can pop them all in the same order using [`popa`](https://c9x.me/x86/html/file_module_x86_id_249.html). +If we consider a 32-bit architecture, one can push all registers to the stack using the [`pusha` instruction - "push all"](https://c9x.me/x86/html/file_module_x86_id_270.html). +And one can pop them all in the same order using [`popa`](https://c9x.me/x86/html/file_module_x86_id_249.html). The disadvantage of doing so is that writing all registers to the stack is going to be slower than only explicitly saving the registers used by the function. -For this reason, the `cdecl` calling convention specifies that functions are allowed to change the values of the `eax`, `ecx` and `edx` registers. +However, on a 64-bit architecture, those two instructions are not available and the preserving must be done by hand. +More details on the behaviour of these functions on a 64-bit architecture can be found [here](https://www.felixcloutier.com/x86/pusha:pushad). +For this reason, the `System V AMD64 ABI` calling convention specifies that functions are allowed to change the values of the `rax`, `rcx`, `rdx`, `rsi`, `rdi` and `r8-r11` registers. > **NOTE:** Since assembly languages offer more opportunities, there is a need for calling conventions in x86. > The difference between them may consist of the parameter order, how the parameters are passed to the function, which registers need to be preserved by the callee or whether the caller or callee handles stack preparation. -> More details can be found [here](https://en.wikipedia.org/wiki/X86_calling_conventions) or [here](https://levelup.gitconnected.com/x86-calling-conventions-a34812afe097) if Wikipedia is too mainstream for you. -> For us, the registers `eax`, `ecx`, `edx` are considered **clobbered** (or volatile), and the callee can do whatever it wants to them. -> On the other hand, the callee has to ensure that `ebx` exits the function with the same value it has entered with. +> More details can be found [here](https://en.wikipedia.org/wiki/X86_calling_conventions) or [here](https://aaronbloomfield.github.io/pdr/book/x86-64bit-ccc-chapter.pdf) and [here](https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170) if Wikipedia is too mainstream for you. +> For us, the registers `rax`, `rcx`, `rdx`, `rsi`, `rdi` and `r8-r11` are considered **clobbered** (or volatile), and the callee can do whatever it wants to them. +> On the other hand, the callee has to ensure that `rbx` and `r12-r15` exit the function with the same value they have entered with. +> If you want to read more about why the `stack alignment` is needed, you can check out [this](https://stackoverflow.com/questions/49391001/why-does-the-x86-64-amd64-system-v-abi-mandate-a-16-byte-stack-alignment). diff --git a/labs/lab-08/tasks/print-rev-string/README.md b/labs/lab-08/tasks/print-rev-string/README.md index cb0ca9712..ee635a74d 100644 --- a/labs/lab-08/tasks/print-rev-string/README.md +++ b/labs/lab-08/tasks/print-rev-string/README.md @@ -1,5 +1,5 @@ --- -nav_order: 3 +nav_order: 4 parent: Lab 8 - Functions --- @@ -17,38 +17,41 @@ extern puts global print_reverse_string reverse_string: - push ebp - mov ebp, esp - push ebx ; preserve ebx as required by cdecl - - mov eax, [ebp + 8] - mov ecx, [ebp + 12] - add eax, ecx - dec eax - mov edx, [ebp + 16] - -copy_one_byte: - mov bl, [eax] - mov [edx], bl - dec eax - inc edx - loopnz copy_one_byte - - inc edx - mov byte [edx], 0 - - pop ebx ; restore ebx + push rbp + mov rbp, rsp + + mov rax, rdi ; get the address of the string + mov rcx, rsi ; get the length of the string + ; the address of the buffer to store the reversed string is already in rdx + + test rcx, rcx ; check if length is zero + jz done ; if zero, skip to null termination + + add rax, rcx ; point to one past the last character + dec rax ; point to the last character + +copy_loop: + mov bl, [rax] ; get a byte from the source + mov [rdx], bl ; store it in the destination + dec rax ; move to previous character in source + inc rdx ; move to next character in destination + dec rcx ; decrease counter + jnz copy_loop ; if counter not zero, continue loop + +done: + mov byte [rdx], 0 ; null-terminate the destination string + leave ret print_reverse_string: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp [...] ``` > **IMPORTANT:** When copying the `reverse_string()` function into your program, remember that the function starts at the `reverse_string()` label and ends at the `print_reverse_string` label. -> The `copy_one_byte` label is part of the `reverse_string()` function. +> The `copy_loop` and `done` labels are part of the `reverse_string()` function. The `reverse_string()` function reverses a string and has the following signature: `void reverse_string(const char *src, size_t len, char *dst);`. This means that the first `len` characters of the `src` string are reversed into the `dst` string. @@ -62,19 +65,32 @@ Reverse the `mystring` string into a new string and display that new string. > ``` > > This creates a string of 64 zero bytes, enough to store the reverse of the string. -> The equivalent C function call is `reverse_string(mystring, ecx, store_string);`. -> We assume that the length of the string is calculated and stored in the `ecx` register. +> The equivalent C function call is `reverse_string(mystring, rcx, store_string);`. +> We assume that the length of the string is calculated and stored in the `rcx` register. > -> You cannot directly use the value of `ecx` in its current form. -> After the `printf()` function call for displaying the length, the value of `ecx` is not preserved. +> You cannot directly use the value of `rcx` in its current form. +> After the `printf()` function call for displaying the length, the value of `rcx` is not preserved. > To retain it, you have two options: > -> 1. Store the value of the `ecx` register on the stack beforehand (using `push ecx` before the `printf` call) and then restore it after the `printf` call (using `pop ecx`). -> 1. Store the value of the `ecx` register in a global variable, which you define in the `.data` section. +> 1. Store the value of the `rcx` register on the stack beforehand (using `push rcx` before the `printf` call) and decrease the value of `rsp` by `8` in order to align the stack and then increase the value of `rsp` by `8` and restore it after the `printf` call (using `pop rcx`). +> 1. Store the value of the `rcx` register in a global variable, which you define in the `.data` section. > > You cannot use another register because there is a high chance that even that register will be modified by the `printf` call to display the length of the string. -To test the implementation, enter the `tests/` directory and run: +After you consider your implementation complete, it is recommended to first run it `manually` in order to assess its correctness. +In order to do so, enter the `support/` directory and run: + +```console +make +``` + +If your code successfully compiled, you can then run the binary like so: + +```console +./print_reverse_string +``` + +To fully test the implementation, enter the `tests/` directory and run: ```console make check diff --git a/labs/lab-08/tasks/print-rev-string/solution/main.asm b/labs/lab-08/tasks/print-rev-string/solution/main.asm index bba49131d..8f0af9e57 100644 --- a/labs/lab-08/tasks/print-rev-string/solution/main.asm +++ b/labs/lab-08/tasks/print-rev-string/solution/main.asm @@ -1,6 +1,6 @@ section .data mystring db "This is my string", 0 - print_format db "String length is %d", 10, 0 + fmt_str db "[before]: %s", 10, "[after]: ", 0 section .text @@ -9,34 +9,37 @@ extern print_reverse_string global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp + + mov rax, mystring + xor rcx, rcx - mov eax, mystring - xor ecx, ecx test_one_byte: - mov bl, [eax] + mov bl, [rax] test bl, bl je out - inc eax - inc ecx + inc rax + inc rcx jmp test_one_byte out: - ; save ecx's value since it can be changed by printf - push ecx + ; save rcx's value since it can be changed by printf + push rcx + ; align the stack + sub rsp, 8 - push ecx - push print_format + mov rdi, fmt_str + mov rsi, mystring call printf - add esp, 8 - pop ecx + add rsp, 8 + ; restore rcx's value + pop rcx - push ecx - push mystring + mov rdi, mystring + mov rsi, rcx call print_reverse_string - add esp, 8 leave ret diff --git a/labs/lab-08/tasks/print-rev-string/solution/print_reverse_string.asm b/labs/lab-08/tasks/print-rev-string/solution/print_reverse_string.asm index a85784e6c..99031e3f1 100644 --- a/labs/lab-08/tasks/print-rev-string/solution/print_reverse_string.asm +++ b/labs/lab-08/tasks/print-rev-string/solution/print_reverse_string.asm @@ -7,53 +7,53 @@ extern puts global print_reverse_string reverse_string: - push ebp - mov ebp, esp - push ebx ; preserve ebx as required by cdecl + push rbp + mov rbp, rsp - mov eax, [ebp + 8] ; get the address of the string - mov ecx, [ebp + 12] ; get the length of the string - mov edx, [ebp + 16] ; get the address of the buffer + sub rsp, 8 ; leave room in order to align the stack + push rbx ; preserve rbx as required by the System V AMD64 ABI - test ecx, ecx ; check if length is zero + mov rax, rdi ; get the address of the string + mov rcx, rsi ; get the length of the string + ; the address of the buffer to store the reversed string is already in rdx + + test rcx, rcx ; check if length is zero jz done ; if zero, skip to null termination - add eax, ecx ; point to one past the last character - dec eax ; point to the last character + add rax, rcx ; point to one past the last character + dec rax ; point to the last character copy_loop: - mov bl, [eax] ; get a byte from the source - mov [edx], bl ; store it in the destination - dec eax ; move to previous character in source - inc edx ; move to next character in destination - dec ecx ; decrease counter + mov bl, [rax] ; get a byte from the source + mov [rdx], bl ; store it in the destination + dec rax ; move to previous character in source + inc rdx ; move to next character in destination + dec rcx ; decrease counter jnz copy_loop ; if counter not zero, continue loop done: - mov byte [edx], 0 ; null-terminate the destination string + mov byte [rdx], 0 ; null-terminate the destination string + + pop rbx ; restore rbx + add rsp, 8 - pop ebx ; restore ebx leave ret print_reverse_string: - push ebp - mov ebp, esp - push ebx ; preserve ebx as required by cdecl + push rbp + mov rbp, rsp - mov eax, [ebp + 8] ; get the address of the string - mov ecx, [ebp + 12] ; get the length of the string + mov rax, rdi ; get the address of the string + mov rcx, rsi ; get the length of the string - push store_string - push ecx - push eax + mov rdi, rax + mov rsi, rcx + mov rdx, store_string call reverse_string - add esp, 12 - push store_string + mov rdi, store_string call puts - add esp, 4 - pop ebx leave ret diff --git a/labs/lab-08/tasks/print-rev-string/support/Makefile b/labs/lab-08/tasks/print-rev-string/support/Makefile index 3e10d1fe8..ba85d96c0 100644 --- a/labs/lab-08/tasks/print-rev-string/support/Makefile +++ b/labs/lab-08/tasks/print-rev-string/support/Makefile @@ -3,9 +3,9 @@ CC = gcc UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -fno-PIC -no-pie TARGET := print_reverse_string OBJS := main.o print_reverse_string.o diff --git a/labs/lab-08/tasks/print-rev-string/support/main.asm b/labs/lab-08/tasks/print-rev-string/support/main.asm index 3d2dc036b..8f0af9e57 100644 --- a/labs/lab-08/tasks/print-rev-string/support/main.asm +++ b/labs/lab-08/tasks/print-rev-string/support/main.asm @@ -1,6 +1,6 @@ section .data mystring db "This is my string", 0 - fmt_str db "[before]: %s\n[after]: ", 0 + fmt_str db "[before]: %s", 10, "[after]: ", 0 section .text @@ -9,29 +9,37 @@ extern print_reverse_string global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp + + mov rax, mystring + xor rcx, rcx - mov eax, mystring - xor ecx, ecx test_one_byte: - mov bl, [eax] + mov bl, [rax] test bl, bl je out - inc eax - inc ecx + inc rax + inc rcx jmp test_one_byte out: - push mystring - push fmt_str + ; save rcx's value since it can be changed by printf + push rcx + ; align the stack + sub rsp, 8 + + mov rdi, fmt_str + mov rsi, mystring call printf - add esp, 8 - push ecx - push mystring + add rsp, 8 + ; restore rcx's value + pop rcx + + mov rdi, mystring + mov rsi, rcx call print_reverse_string - add esp, 8 leave ret diff --git a/labs/lab-08/tasks/print-rev-string/support/print_reverse_string.asm b/labs/lab-08/tasks/print-rev-string/support/print_reverse_string.asm index c9a149044..49bd687c5 100644 --- a/labs/lab-08/tasks/print-rev-string/support/print_reverse_string.asm +++ b/labs/lab-08/tasks/print-rev-string/support/print_reverse_string.asm @@ -6,12 +6,14 @@ global print_reverse_string ; TODO: add the reverse_string() function print_reverse_string: - push ebp - mov ebp, esp - push ebx ; preserve ebx as required by cdecl + push rbp + mov rbp, rsp + + ; TODO: save the used registers and align the stack, if needed ; TODO: call the reverse_string() function and print the reversed string - pop ebx + ; TODO: restore the used registers and the stack pointer, if altered + leave ret diff --git a/labs/lab-08/tasks/print-rev-string/tests/.gitignore b/labs/lab-08/tasks/print-rev-string/tests/.gitignore new file mode 100644 index 000000000..3f1376bb1 --- /dev/null +++ b/labs/lab-08/tasks/print-rev-string/tests/.gitignore @@ -0,0 +1,3 @@ +graded_test.o +test_print_reverse_string.o +test_print_reverse_string diff --git a/labs/lab-08/tasks/print-rev-string/tests/Makefile b/labs/lab-08/tasks/print-rev-string/tests/Makefile index af5297b1e..ba8c96746 100644 --- a/labs/lab-08/tasks/print-rev-string/tests/Makefile +++ b/labs/lab-08/tasks/print-rev-string/tests/Makefile @@ -1,8 +1,8 @@ SRC_PATH ?= ../support FULL_SRC_PATH = "$(realpath $(SRC_PATH))" CPPFLAGS = -I. -I$(FULL_SRC_PATH) -I../utils -CFLAGS = -Wall -Wextra -m32 -LDFLAGS = -m32 +CFLAGS = -Wall -Wextra -m64 +LDFLAGS = -m64 -no-pie # Remove the line below to disable debugging support. CFLAGS += -g -O0 diff --git a/labs/lab-08/tasks/print-rev-string/utils/printf32.asm b/labs/lab-08/tasks/print-rev-string/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-08/tasks/print-rev-string/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-08/tasks/print-rev-string/utils/printf64.asm b/labs/lab-08/tasks/print-rev-string/utils/printf64.asm new file mode 100644 index 000000000..e799a46e0 --- /dev/null +++ b/labs/lab-08/tasks/print-rev-string/utils/printf64.asm @@ -0,0 +1,73 @@ +; SPDX-License-Identifier: BSD-3-Clause + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-08/tasks/rot13/README.md b/labs/lab-08/tasks/rot13/README.md index ff0ee8278..c8ebf6ef4 100644 --- a/labs/lab-08/tasks/rot13/README.md +++ b/labs/lab-08/tasks/rot13/README.md @@ -1,5 +1,5 @@ --- -nav_order: 5 +nav_order: 6 parent: Lab 8 - Functions --- @@ -31,7 +31,20 @@ Thus, the initial string `lorem\0ipsum\0dolor\0` will translate to `yberz vcfhz > > where you either store the total length of the string (from the beginning to the last `NULL` byte) or the number of strings in the array. -To test the implementation, enter the `tests/` directory and run: +After you consider your implementation complete, it is recommended to first run it `manually` in order to assess its correctness. +In order to do so, enter the `support/` directory and run: + +```console +make +``` + +If your code successfully compiled, you can then run the binary like so: + +```console +./rot13 +``` + +To fully test the implementation, enter the `tests/` directory and run: ```console make check diff --git a/labs/lab-08/tasks/rot13/solution/Makefile b/labs/lab-08/tasks/rot13/solution/Makefile deleted file mode 100644 index 2bb7f2e86..000000000 --- a/labs/lab-08/tasks/rot13/solution/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -AS = nasm -CC = gcc - -UTILSDIR := ../utils/ - -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" -CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie - -TARGET := rot13 -OBJS := main.o rot13.o - -all: $(TARGET) - -$(TARGET): $(OBJS) - $(CC) $(LDFLAGS) -o $@ $^ - -%.o: %.asm - $(AS) $(ASFLAGS) $< -o $@ - -.PHONY: clean - -clean: - -rm -f *.o $(TARGET) diff --git a/labs/lab-08/tasks/rot13/solution/Makefile b/labs/lab-08/tasks/rot13/solution/Makefile new file mode 120000 index 000000000..a39be4f20 --- /dev/null +++ b/labs/lab-08/tasks/rot13/solution/Makefile @@ -0,0 +1 @@ +../support/Makefile \ No newline at end of file diff --git a/labs/lab-08/tasks/rot13/solution/main.asm b/labs/lab-08/tasks/rot13/solution/main.asm index 850b121ec..b8d42e282 100644 --- a/labs/lab-08/tasks/rot13/solution/main.asm +++ b/labs/lab-08/tasks/rot13/solution/main.asm @@ -10,19 +10,21 @@ extern rot13 global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - mov eax, [length] - push eax + mov rax, [length] + + sub rsp, 8 ; align the stack push mystring + mov rdi, mystring + mov rsi, rax call rot13 - add esp, 8 + add rsp, 8 - push mystring - push after_fmt + mov rdi, after_fmt + mov rsi, mystring call printf - add esp, 8 leave ret diff --git a/labs/lab-08/tasks/rot13/solution/rot13.asm b/labs/lab-08/tasks/rot13/solution/rot13.asm index 831c19269..77a56de39 100644 --- a/labs/lab-08/tasks/rot13/solution/rot13.asm +++ b/labs/lab-08/tasks/rot13/solution/rot13.asm @@ -2,18 +2,17 @@ section .text global rot13 rot13: - push ebp - mov ebp, esp - push ebx ; preserve ebx as required by cdecl + push rbp + mov rbp, rsp - mov esi, [ebp + 8] ; pointer to input string - mov ecx, [ebp + 12] ; length of the buffer + mov rcx, rsi ; length of the buffer + mov rsi, rdi ; pointer to the input string loop: - cmp ecx, 0 ; Check if there are more characters to process + cmp rcx, 0 ; Check if there are more characters to process je done ; If no, we are done - mov al, [esi] ; Load the current byte into AL register + mov al, [rsi] ; Load the current byte into AL register cmp al, 0 ; Check if it's the null terminator je handle_null ; If it's a null terminator, handle it @@ -30,7 +29,7 @@ loop: lower_store: add al, 'a' ; Convert back to ASCII - mov [esi], al ; Store the transformed character back + mov [rsi], al ; Store the transformed character back jmp next check_upper: @@ -47,25 +46,25 @@ check_upper: upper_store: add al, 'A' ; Convert back to ASCII - mov [esi], al ; Store the transformed character back + mov [rsi], al ; Store the transformed character back next: - inc esi ; Move to the next byte in the string - dec ecx ; Decrement the length counter + inc rsi ; Move to the next byte in the string + dec rcx ; Decrement the length counter jmp loop ; Repeat the loop handle_null: ; We are at a null terminator, check if it's the last null terminator ; If so, just leave it and terminate the string - cmp byte [esi + 1], 0 ; Check if the next byte is also a null terminator + cmp byte [rsi + 1], 0 ; Check if the next byte is also a null terminator je done ; If yes, don't replace with a space ; Otherwise, replace null terminator with space - mov byte [esi], ' ' - inc esi ; Move to the next byte + mov byte [rsi], ' ' + inc rsi ; Move to the next byte jmp loop done: - pop ebx ; Restore the preserved register + leave ; Clean up the stack frame ret ; Return from the function diff --git a/labs/lab-08/tasks/rot13/support/Makefile b/labs/lab-08/tasks/rot13/support/Makefile index 2bb7f2e86..668cefd87 100644 --- a/labs/lab-08/tasks/rot13/support/Makefile +++ b/labs/lab-08/tasks/rot13/support/Makefile @@ -3,9 +3,9 @@ CC = gcc UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -fno-PIC -no-pie TARGET := rot13 OBJS := main.o rot13.o diff --git a/labs/lab-08/tasks/rot13/support/main.asm b/labs/lab-08/tasks/rot13/support/main.asm index 850b121ec..b8d42e282 100644 --- a/labs/lab-08/tasks/rot13/support/main.asm +++ b/labs/lab-08/tasks/rot13/support/main.asm @@ -10,19 +10,21 @@ extern rot13 global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - mov eax, [length] - push eax + mov rax, [length] + + sub rsp, 8 ; align the stack push mystring + mov rdi, mystring + mov rsi, rax call rot13 - add esp, 8 + add rsp, 8 - push mystring - push after_fmt + mov rdi, after_fmt + mov rsi, mystring call printf - add esp, 8 leave ret diff --git a/labs/lab-08/tasks/rot13/support/rot13.asm b/labs/lab-08/tasks/rot13/support/rot13.asm index 59fcbb946..f9c9da8a3 100644 --- a/labs/lab-08/tasks/rot13/support/rot13.asm +++ b/labs/lab-08/tasks/rot13/support/rot13.asm @@ -2,12 +2,14 @@ section .text global rot13 rot13: - push ebp - mov ebp, esp - push ebx ; preserve ebx as required by cdecl + push rbp + mov rbp, rsp + + ; TODO: save the used registers and align the stack, if needed ; TODO - pop ebx + ; TODO: restore the used registers and the stack pointer, if altered + leave ret diff --git a/labs/lab-08/tasks/rot13/tests/.gitignore b/labs/lab-08/tasks/rot13/tests/.gitignore new file mode 100644 index 000000000..34b8ce54b --- /dev/null +++ b/labs/lab-08/tasks/rot13/tests/.gitignore @@ -0,0 +1,3 @@ +graded_test.o +test_rot13.o +test_rot13 diff --git a/labs/lab-08/tasks/rot13/tests/Makefile b/labs/lab-08/tasks/rot13/tests/Makefile index 2cc5a4afb..a6d183d72 100644 --- a/labs/lab-08/tasks/rot13/tests/Makefile +++ b/labs/lab-08/tasks/rot13/tests/Makefile @@ -1,8 +1,8 @@ SRC_PATH ?= ../support FULL_SRC_PATH = "$(realpath $(SRC_PATH))" CPPFLAGS = -I. -I$(FULL_SRC_PATH) -I../utils -CFLAGS = -Wall -Wextra -m32 -LDFLAGS = -m32 +CFLAGS = -Wall -Wextra -m64 +LDFLAGS = -m64 -no-pie # Remove the line below to disable debugging support. CFLAGS += -g -O0 diff --git a/labs/lab-08/tasks/string-print-len/.gitignore b/labs/lab-08/tasks/string-print-len/.gitignore index 268b98689..a50b891a1 100644 --- a/labs/lab-08/tasks/string-print-len/.gitignore +++ b/labs/lab-08/tasks/string-print-len/.gitignore @@ -1 +1 @@ -print_string_len +print_string_length diff --git a/labs/lab-08/tasks/string-print-len/README.md b/labs/lab-08/tasks/string-print-len/README.md index 681d6059e..e0f8708a3 100644 --- a/labs/lab-08/tasks/string-print-len/README.md +++ b/labs/lab-08/tasks/string-print-len/README.md @@ -1,5 +1,5 @@ --- -nav_order: 2 +nav_order: 3 parent: Lab 8 - Functions --- @@ -7,12 +7,12 @@ parent: Lab 8 - Functions Navigate to `tasks/string-print-len/support/`. -The program `print_string_len.asm` displays the length of a string using the `PRINTF32` macro. +The program `print_string_len.asm` displays the length of a string using the `PRINTF64` macro. The calculation of the length of the `mystring` string occurs within the program (it is already implemented). Implement the program to display the length of the string using the `printf` function. -At the end, you will have the length of the string displayed twice: initially with the `PRINTF32` macro and then with the external function call `printf`. +At the end, you will have the length of the string displayed twice: initially with the `PRINTF64` macro and then with the external function call `printf`. > **NOTE:** Consider that the `printf` call is of the form `printf("String length is %u\n", len);`. > You need to construct the stack for this call. @@ -22,13 +22,25 @@ At the end, you will have the length of the string displayed twice: initially wi > 1. Mark the symbol `printf` as external. > 1. Define the format string `"String length is %u", 10, 0`. > 1. Make the function call to `printf`, i.e.: -> 1. Put the two arguments on the stack: the format string and the length. +> 1. Put the two arguments into the corresponding registers (`rdi` and `rsi`, in this order) > 1. Call `printf` using `call`. -> 1. Restore the stack. > -> The length of the string is found in the `ecx` register. +> The length of the string is found in the `rcx` register. -To test the implementation, enter the `tests/` directory and run: +After you consider your implementation complete, it is recommended to first run it `manually` in order to assess its correctness. +In order to do so, enter the `support/` directory and run: + +```console +make +``` + +If your code successfully compiled, you can then run the binary like so: + +```console +./print_string_length +``` + +To fully test the implementation, enter the `tests/` directory and run: ```console make check diff --git a/labs/lab-08/tasks/string-print-len/solution/main.asm b/labs/lab-08/tasks/string-print-len/solution/main.asm index ca31680db..219a3b59a 100644 --- a/labs/lab-08/tasks/string-print-len/solution/main.asm +++ b/labs/lab-08/tasks/string-print-len/solution/main.asm @@ -1,4 +1,4 @@ -%include "../utils/printf32.asm" +%include "../utils/printf64.asm" section .data mystring db "This is my string", 0 @@ -10,25 +10,24 @@ extern print_string_length global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - mov eax, mystring - xor ecx, ecx + mov rax, mystring + xor rcx, rcx test_one_byte: - mov bl, [eax] + mov bl, [rax] test bl, bl je out - inc eax - inc ecx + inc rax + inc rcx jmp test_one_byte out: - PRINTF32 `[PRINTF32]: %d\n[printf]: \x0`, ecx + PRINTF64 `[PRINTF64]: %d\n[printf]: \x0`, rcx - push ecx + mov rdi, rcx call print_string_length - add esp, 4 leave ret diff --git a/labs/lab-08/tasks/string-print-len/solution/print_string_length.asm b/labs/lab-08/tasks/string-print-len/solution/print_string_length.asm index 3cb6b60fd..99920e6ee 100644 --- a/labs/lab-08/tasks/string-print-len/solution/print_string_length.asm +++ b/labs/lab-08/tasks/string-print-len/solution/print_string_length.asm @@ -7,17 +7,20 @@ extern printf global print_string_length print_string_length: - push ebp - mov ebp, esp - push ebx ; preserve ebx as required by cdecl + push rbp + mov rbp, rsp - mov ecx, [ebp + 8] ; get the string length from the stack + sub rsp, 8 ; align the stack + push rcx ; save the string length - push ecx - push print_format + mov rcx, rdi ; get the string length from the stack + + mov rdi, print_format + mov rsi, rcx call printf - add esp, 8 - pop ebx + pop rcx ; restore the string length + add rsp, 8 ; restore the stack pointer + leave ret diff --git a/labs/lab-08/tasks/string-print-len/support/Makefile b/labs/lab-08/tasks/string-print-len/support/Makefile index 1aee33b81..65fb97a78 100644 --- a/labs/lab-08/tasks/string-print-len/support/Makefile +++ b/labs/lab-08/tasks/string-print-len/support/Makefile @@ -3,9 +3,9 @@ CC = gcc UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -fno-PIC -no-pie TARGET := print_string_length OBJS := main.o print_string_length.o diff --git a/labs/lab-08/tasks/string-print-len/support/main.asm b/labs/lab-08/tasks/string-print-len/support/main.asm index ca31680db..219a3b59a 100644 --- a/labs/lab-08/tasks/string-print-len/support/main.asm +++ b/labs/lab-08/tasks/string-print-len/support/main.asm @@ -1,4 +1,4 @@ -%include "../utils/printf32.asm" +%include "../utils/printf64.asm" section .data mystring db "This is my string", 0 @@ -10,25 +10,24 @@ extern print_string_length global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - mov eax, mystring - xor ecx, ecx + mov rax, mystring + xor rcx, rcx test_one_byte: - mov bl, [eax] + mov bl, [rax] test bl, bl je out - inc eax - inc ecx + inc rax + inc rcx jmp test_one_byte out: - PRINTF32 `[PRINTF32]: %d\n[printf]: \x0`, ecx + PRINTF64 `[PRINTF64]: %d\n[printf]: \x0`, rcx - push ecx + mov rdi, rcx call print_string_length - add esp, 4 leave ret diff --git a/labs/lab-08/tasks/string-print-len/support/print_string_length.asm b/labs/lab-08/tasks/string-print-len/support/print_string_length.asm index dc666e6f2..9de1b3a21 100644 --- a/labs/lab-08/tasks/string-print-len/support/print_string_length.asm +++ b/labs/lab-08/tasks/string-print-len/support/print_string_length.asm @@ -7,12 +7,14 @@ extern printf global print_string_length print_string_length: - push ebp - mov ebp, esp - push ebx ; preserve ebx as required by cdecl + push rbp + mov rbp, rsp + + ; TODO: save the used registers and align the stack, if needed ; TODO: print the string length - pop ebx + ; TODO: restore the used registers and the stack pointer, if altered + leave ret diff --git a/labs/lab-08/tasks/string-print-len/tests/.gitignore b/labs/lab-08/tasks/string-print-len/tests/.gitignore new file mode 100644 index 000000000..fcf2f0f99 --- /dev/null +++ b/labs/lab-08/tasks/string-print-len/tests/.gitignore @@ -0,0 +1,3 @@ +graded_test.o +test_print_string_length.o +test_print_string_length diff --git a/labs/lab-08/tasks/string-print-len/tests/Makefile b/labs/lab-08/tasks/string-print-len/tests/Makefile index b0ceb6c8a..6f7c9973d 100644 --- a/labs/lab-08/tasks/string-print-len/tests/Makefile +++ b/labs/lab-08/tasks/string-print-len/tests/Makefile @@ -1,8 +1,8 @@ SRC_PATH ?= ../support FULL_SRC_PATH = "$(realpath $(SRC_PATH))" CPPFLAGS = -I. -I$(FULL_SRC_PATH) -I../utils -CFLAGS = -Wall -Wextra -m32 -LDFLAGS = -m32 +CFLAGS = -Wall -Wextra -m64 +LDFLAGS = -m64 -no-pie # Remove the line below to disable debugging support. CFLAGS += -g -O0 diff --git a/labs/lab-08/tasks/string-print-len/utils/printf32.asm b/labs/lab-08/tasks/string-print-len/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-08/tasks/string-print-len/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-08/tasks/string-print-len/utils/printf64.asm b/labs/lab-08/tasks/string-print-len/utils/printf64.asm new file mode 100644 index 000000000..e799a46e0 --- /dev/null +++ b/labs/lab-08/tasks/string-print-len/utils/printf64.asm @@ -0,0 +1,73 @@ +; SPDX-License-Identifier: BSD-3-Clause + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-08/tasks/string-print/README.md b/labs/lab-08/tasks/string-print/README.md index 9423cc04b..3d7e18082 100644 --- a/labs/lab-08/tasks/string-print/README.md +++ b/labs/lab-08/tasks/string-print/README.md @@ -1,5 +1,5 @@ --- -nav_order: 1 +nav_order: 2 parent: Lab 8 - Functions --- @@ -7,15 +7,28 @@ parent: Lab 8 - Functions Navigate to `tasks/string-print/support/`. -To display a string, we can use the internal macro `PRINTF32`. +To display a string, we can use the internal macro `PRINTF64`. Alternatively, we can use a function such as `puts()`. -In the file `print_string.asm`, displaying a string using the `PRINTF32` macro is implemented. +In the file `print_string.asm`, displaying a string using the `PRINTF64` macro is implemented. Following the example of the `hello_world.asm` file, implement string display using `puts` as well. If you're having difficulties solving this exercise, take a peek at [hello_world.asm](../../guides/hello_world/). -To test the implementation, enter the `tests/` directory and run: +After you consider your implementation complete, it is recommended to first run it `manually` in order to assess its correctness. +In order to do so, enter the `support/` directory and run: + +```console +make +``` + +If your code successfully compiled, you can then run the binary like so: + +```console +./print_string +``` + +To fully test the implementation, enter the `tests/` directory and run: ```console make check diff --git a/labs/lab-08/tasks/string-print/solution/main.asm b/labs/lab-08/tasks/string-print/solution/main.asm index 584b41af8..d104eb1f1 100644 --- a/labs/lab-08/tasks/string-print/solution/main.asm +++ b/labs/lab-08/tasks/string-print/solution/main.asm @@ -1,4 +1,4 @@ -%include "../utils/printf32.asm" +%include "../utils/printf64.asm" section .data mystring db "This is my string", 0 @@ -9,14 +9,13 @@ extern printf extern print_string global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - PRINTF32 `[PRINTF32]: %s\n[PUTS]: \x0`, mystring + PRINTF64 `[PRINTF64]: %s\n[PUTS]: \x0`, mystring - push mystring + mov rdi, mystring call print_string - add esp, 4 leave ret diff --git a/labs/lab-08/tasks/string-print/solution/print_string.asm b/labs/lab-08/tasks/string-print/solution/print_string.asm index 7dca8572a..b1e39d7b6 100644 --- a/labs/lab-08/tasks/string-print/solution/print_string.asm +++ b/labs/lab-08/tasks/string-print/solution/print_string.asm @@ -4,16 +4,12 @@ extern puts global print_string print_string: - push ebp - mov ebp, esp - push ebx ; preserve ebx as required by cdecl + push rbp + mov rbp, rsp - mov eax, [ebp + 8] ; get the address of the string - - push eax + ; call the puts function + ; as the argument is already placed in rdi by the caller call puts - add esp, 4 - pop ebx leave ret diff --git a/labs/lab-08/tasks/string-print/support/Makefile b/labs/lab-08/tasks/string-print/support/Makefile index 0b644bd20..0fffef7e9 100644 --- a/labs/lab-08/tasks/string-print/support/Makefile +++ b/labs/lab-08/tasks/string-print/support/Makefile @@ -3,9 +3,9 @@ CC = gcc UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -fno-PIC -no-pie TARGET := print_string OBJS := main.o print_string.o diff --git a/labs/lab-08/tasks/string-print/support/main.asm b/labs/lab-08/tasks/string-print/support/main.asm index 584b41af8..d104eb1f1 100644 --- a/labs/lab-08/tasks/string-print/support/main.asm +++ b/labs/lab-08/tasks/string-print/support/main.asm @@ -1,4 +1,4 @@ -%include "../utils/printf32.asm" +%include "../utils/printf64.asm" section .data mystring db "This is my string", 0 @@ -9,14 +9,13 @@ extern printf extern print_string global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - PRINTF32 `[PRINTF32]: %s\n[PUTS]: \x0`, mystring + PRINTF64 `[PRINTF64]: %s\n[PUTS]: \x0`, mystring - push mystring + mov rdi, mystring call print_string - add esp, 4 leave ret diff --git a/labs/lab-08/tasks/string-print/support/print_string.asm b/labs/lab-08/tasks/string-print/support/print_string.asm index 232f98c51..6fcd3a322 100644 --- a/labs/lab-08/tasks/string-print/support/print_string.asm +++ b/labs/lab-08/tasks/string-print/support/print_string.asm @@ -4,12 +4,14 @@ extern puts global print_string print_string: - push ebp - mov ebp, esp - push ebx ; preserve ebx as required by cdecl + push rbp + mov rbp, rsp + + ; TODO: save the used registers and align the stack, if needed ; TODO: print the string - pop ebx + ; TODO: restore the used registers and the stack pointer, if altered + leave ret diff --git a/labs/lab-08/tasks/string-print/tests/.gitignore b/labs/lab-08/tasks/string-print/tests/.gitignore new file mode 100644 index 000000000..926637ce9 --- /dev/null +++ b/labs/lab-08/tasks/string-print/tests/.gitignore @@ -0,0 +1,3 @@ +graded_test.o +test_print_string.o +test_print_string diff --git a/labs/lab-08/tasks/string-print/tests/Makefile b/labs/lab-08/tasks/string-print/tests/Makefile index c6ec40cf7..5645b2f0a 100644 --- a/labs/lab-08/tasks/string-print/tests/Makefile +++ b/labs/lab-08/tasks/string-print/tests/Makefile @@ -1,8 +1,8 @@ SRC_PATH ?= ../support FULL_SRC_PATH = "$(realpath $(SRC_PATH))" CPPFLAGS = -I. -I$(FULL_SRC_PATH) -I../utils -CFLAGS = -Wall -Wextra -m32 -LDFLAGS = -m32 +CFLAGS = -Wall -Wextra -m64 +LDFLAGS = -m64 -no-pie # Remove the line below to disable debugging support. CFLAGS += -g -O0 diff --git a/labs/lab-08/tasks/string-print/utils/printf32.asm b/labs/lab-08/tasks/string-print/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-08/tasks/string-print/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-08/tasks/string-print/utils/printf64.asm b/labs/lab-08/tasks/string-print/utils/printf64.asm new file mode 100644 index 000000000..e799a46e0 --- /dev/null +++ b/labs/lab-08/tasks/string-print/utils/printf64.asm @@ -0,0 +1,73 @@ +; SPDX-License-Identifier: BSD-3-Clause + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-08/tasks/sum7/.gitignore b/labs/lab-08/tasks/sum7/.gitignore new file mode 100644 index 000000000..b293f82d3 --- /dev/null +++ b/labs/lab-08/tasks/sum7/.gitignore @@ -0,0 +1 @@ +sum7 diff --git a/labs/lab-08/tasks/sum7/README.md b/labs/lab-08/tasks/sum7/README.md new file mode 100644 index 000000000..c217517f2 --- /dev/null +++ b/labs/lab-08/tasks/sum7/README.md @@ -0,0 +1,47 @@ +--- +nav_order: 1 +parent: Lab 8 - Functions +--- + +# Task: Computing the sum of 7 numbers + +Navigate to `tasks/sum7/support/`. + +Similarly to what was described in the reading section of this lab, for this task, you will have to compute the sum of 7 numbers, all received as arguments to the `sum7` function. + +Be careful as not all of the arguments are stored in registers. + +The function's signature is the following: `long sum7(long a, long b, long c, long d, long e, long f, long g)`. + +After you consider your implementation complete, it is recommended to first run it `manually` in order to assess its correctness. +In order to do so, enter the `support/` directory and run: + +```console +make +``` + +If your code successfully compiled, you can then run the binary like so: + +```console +./sum7 +``` + +To fully test the implementation, enter the `tests/` directory and run: + +```console +make check +``` + +In case of a correct solution, you will get an output such as: + +```text +./run_all_tests.sh +test_sum_byte ........................ passed ... 15 +test_sum_word ........................ passed ... 20 +test_sum_dword ........................ passed ... 30 +test_sum_qword ........................ passed ... 35 + +Total: 100/100 +``` + +If you're having trouble solving this exercise, go through [this](../../reading/functions.md) reading material. diff --git a/labs/lab-09/tasks/max-c-calls-x64/solution/Makefile b/labs/lab-08/tasks/sum7/solution/Makefile similarity index 100% rename from labs/lab-09/tasks/max-c-calls-x64/solution/Makefile rename to labs/lab-08/tasks/sum7/solution/Makefile diff --git a/labs/lab-08/tasks/sum7/solution/main.asm b/labs/lab-08/tasks/sum7/solution/main.asm new file mode 100644 index 000000000..556918542 --- /dev/null +++ b/labs/lab-08/tasks/sum7/solution/main.asm @@ -0,0 +1,33 @@ +%include "../utils/printf64.asm" + +section .data + fmt db "sum = %d", 10, 0 + +section .text + +extern printf +extern sum7 +global main +main: + push rbp + mov rbp, rsp + + sub rsp, 8 ; align stack + mov rdi, 1 + mov rsi, 2 + mov rdx, 3 + mov rcx, 4 + mov r8, 5 + mov r9, 6 + mov r10, 7 + push r10 + call sum7 + pop r10 + add rsp, 8 ; restore stack + + mov rdi, fmt + mov rsi, rax + call printf + + leave + ret diff --git a/labs/lab-08/tasks/sum7/solution/sum7.asm b/labs/lab-08/tasks/sum7/solution/sum7.asm new file mode 100644 index 000000000..a6ce2639c --- /dev/null +++ b/labs/lab-08/tasks/sum7/solution/sum7.asm @@ -0,0 +1,23 @@ +section .text + +global sum7 + +sum7: + push rbp + mov rbp, rsp + + ; clear rax + xor rax, rax + + ; rdi, rsi, rdx, rcx, r8, r9 are the first 6 arguments + ; [rbp + 16] is the 7th argument + mov rax, [rbp + 16] + add rax, rdi + add rax, rsi + add rax, rdx + add rax, rcx + add rax, r8 + add rax, r9 + + leave + ret diff --git a/labs/lab-08/tasks/sum7/support/Makefile b/labs/lab-08/tasks/sum7/support/Makefile new file mode 100644 index 000000000..04829dcbf --- /dev/null +++ b/labs/lab-08/tasks/sum7/support/Makefile @@ -0,0 +1,24 @@ +AS = nasm +CC = gcc + +UTILSDIR := ../utils/ + +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" +CFLAGS ?= -Wall +LDFLAGS ?= -fno-PIC -no-pie + +TARGET := sum7 +OBJS := main.o sum7.o + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) $(LDFLAGS) -o $@ $^ + +%.o: %.asm + $(AS) $(ASFLAGS) $< -o $@ + +.PHONY: clean + +clean: + -rm -f *.o $(TARGET) diff --git a/labs/lab-08/tasks/sum7/support/main.asm b/labs/lab-08/tasks/sum7/support/main.asm new file mode 100644 index 000000000..556918542 --- /dev/null +++ b/labs/lab-08/tasks/sum7/support/main.asm @@ -0,0 +1,33 @@ +%include "../utils/printf64.asm" + +section .data + fmt db "sum = %d", 10, 0 + +section .text + +extern printf +extern sum7 +global main +main: + push rbp + mov rbp, rsp + + sub rsp, 8 ; align stack + mov rdi, 1 + mov rsi, 2 + mov rdx, 3 + mov rcx, 4 + mov r8, 5 + mov r9, 6 + mov r10, 7 + push r10 + call sum7 + pop r10 + add rsp, 8 ; restore stack + + mov rdi, fmt + mov rsi, rax + call printf + + leave + ret diff --git a/labs/lab-08/tasks/sum7/support/sum7.asm b/labs/lab-08/tasks/sum7/support/sum7.asm new file mode 100644 index 000000000..a6ce2639c --- /dev/null +++ b/labs/lab-08/tasks/sum7/support/sum7.asm @@ -0,0 +1,23 @@ +section .text + +global sum7 + +sum7: + push rbp + mov rbp, rsp + + ; clear rax + xor rax, rax + + ; rdi, rsi, rdx, rcx, r8, r9 are the first 6 arguments + ; [rbp + 16] is the 7th argument + mov rax, [rbp + 16] + add rax, rdi + add rax, rsi + add rax, rdx + add rax, rcx + add rax, r8 + add rax, r9 + + leave + ret diff --git a/labs/lab-08/tasks/sum7/tests/.gitignore b/labs/lab-08/tasks/sum7/tests/.gitignore new file mode 100644 index 000000000..a2bcd9334 --- /dev/null +++ b/labs/lab-08/tasks/sum7/tests/.gitignore @@ -0,0 +1,3 @@ +graded_test.o +test_sum7.o +test_sum7 diff --git a/labs/lab-08/tasks/sum7/tests/Makefile b/labs/lab-08/tasks/sum7/tests/Makefile new file mode 100644 index 000000000..376f5bd8a --- /dev/null +++ b/labs/lab-08/tasks/sum7/tests/Makefile @@ -0,0 +1,43 @@ +SRC_PATH ?= ../support +FULL_SRC_PATH = "$(realpath $(SRC_PATH))" +CPPFLAGS = -I. -I$(FULL_SRC_PATH) -I../utils +CFLAGS = -Wall -Wextra -m64 +LDFLAGS = -m64 -no-pie +# Remove the line below to disable debugging support. +CFLAGS += -g -O0 + +SRCS = $(wildcard test*.c) +OBJS = $(patsubst %.c,%.o,$(SRCS)) +EXECS = $(patsubst %.c,%,$(SRCS)) + + +.PHONY: all src check lint clean + +all: src $(EXECS) + +$(EXECS): %:%.o graded_test.o $(SRC_PATH)/sum7.o | src + $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +$(OBJS): %.o:%.c graded_test.h + +graded_test.o: graded_test.c graded_test.h + +src: + make -C $(FULL_SRC_PATH) + +check: + make -C $(FULL_SRC_PATH) clean + make clean + make -i SRC_PATH=$(FULL_SRC_PATH) + ./run_all_tests.sh + +lint: + -cd .. && checkpatch.pl -f src/*.c tests/*.c tests/*/*.c + -cd .. && checkpatch.pl -f checker/*.sh tests/*.sh + -cd .. && cpplint --recursive src/ tests/ checker/ + -cd .. && shellcheck checker/*.sh tests/*.sh + +clean: + -rm -f *~ + -rm -f graded_test.o $(OBJS) + -rm -f $(EXECS) diff --git a/labs/lab-08/tasks/sum7/tests/graded_test.c b/labs/lab-08/tasks/sum7/tests/graded_test.c new file mode 100644 index 000000000..1eb03d100 --- /dev/null +++ b/labs/lab-08/tasks/sum7/tests/graded_test.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include +#include +#include +#include + +#include "./graded_test.h" + +static void my_itoa(size_t num, char *a) +{ + unsigned char digit3; + unsigned char digit2; + unsigned char digit1; + + /* Print at most 3 decimal digits. */ + if (num > 999) + num = 999; + + digit1 = num % 10; + num /= 10; + digit2 = num % 10; + num /= 10; + digit3 = num % 10; + + if (digit3 == 0) + a[0] = ' '; + else + a[0] = '0' + digit3; + + if (digit2 == 0) + a[1] = ' '; + else + a[1] = '0' + digit2; + + a[2] = '0' + digit1; +} + +/* + * Print test result. Printed message should fit in 72 characters. + * + * Print format is: + * + * description ...................... passed ... NNN + * description ...................... failed ... NNN + * 32 chars 24 chars 6 3 3 + */ + +static void print_test(const char *description, int result, size_t points) +{ + /* Make these global linkage, so it's only allocated once. */ + static char print_buffer[74]; + static const char failed[] = "failed"; + static const char passed[] = "passed"; + size_t i; + size_t len; + + /* Collect description in print_buffer. */ + len = MIN(strlen(description), 32); + for (i = 0; i < len; i++) + print_buffer[i] = description[i]; + for (i = len; i < 32; i++) + print_buffer[i] = ' '; + print_buffer[32] = ' '; + + /* Collect dots in print_buffer. */ + for (i = 0; i < 24; i++) + print_buffer[33+i] = '.'; + print_buffer[57] = ' '; + + /* Collect passed / failed. */ + for (i = 0; i < 6; i++) { + if (result == 1) + print_buffer[58+i] = passed[i]; + else + print_buffer[58+i] = failed[i]; + } + print_buffer[64] = ' '; + + /* Collect dots in print_buffer. */ + for (i = 0; i < 3; i++) + print_buffer[65+i] = '.'; + print_buffer[68] = ' '; + + /* Collect number. */ + if (result == 1) { + my_itoa(points, &print_buffer[69]); + } else { + print_buffer[69] = ' '; + print_buffer[70] = ' '; + print_buffer[71] = '0'; + } + + /* Collect newline. */ + print_buffer[72] = '\n'; + + write(1, print_buffer, 73); +} + +void run_test(struct graded_test *test) +{ + int res; + + res = test->function(); + print_test(test->description, res, test->points); +#ifdef EXIT_IF_FAIL + exit(EXIT_FAILURE); +#endif +} + +void run_tests(struct graded_test *tests, size_t count) +{ + size_t i; + + for (i = 0; i < count; i++) + run_test(&tests[i]); +} diff --git a/labs/lab-08/tasks/sum7/tests/graded_test.h b/labs/lab-08/tasks/sum7/tests/graded_test.h new file mode 100644 index 000000000..f56f2e07c --- /dev/null +++ b/labs/lab-08/tasks/sum7/tests/graded_test.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef GRADED_TEST_H_ +#define GRADED_TEST_H_ 1 + +/* test function prototype */ +typedef int (*test_f)(void); + +struct graded_test { + test_f function; /* test/evaluation function */ + char *description; /* test description */ + size_t points; /* points for each test */ +}; + +void run_test(struct graded_test *test); +void run_tests(struct graded_test *tests, size_t count); + +#endif /* GRADED_TEST_H_ */ diff --git a/labs/lab-08/tasks/sum7/tests/run_all_tests.sh b/labs/lab-08/tasks/sum7/tests/run_all_tests.sh new file mode 100755 index 000000000..79cb0daab --- /dev/null +++ b/labs/lab-08/tasks/sum7/tests/run_all_tests.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# SPDX-License-Identifier: BSD-3-Clause + +./test_sum7 | tee results.txt + +total=$(grep '\( passed \| failed \)' results.txt | rev | cut -d ' ' -f 1 | rev | paste -s -d'+' | bc) +echo "" +echo -n "Total: " +echo -n " " +LC_ALL=C printf "%3d/100\n" "$total" + +rm results.txt diff --git a/labs/lab-08/tasks/sum7/tests/test_sum7.c b/labs/lab-08/tasks/sum7/tests/test_sum7.c new file mode 100644 index 000000000..2a14e51f5 --- /dev/null +++ b/labs/lab-08/tasks/sum7/tests/test_sum7.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include +#include +#include +#include "graded_test.h" + +extern long sum7(long a, long b, long c, long d, long e, long f, long g); + +#define OUTPUT_BUFFER_SIZE 1024 + +/* reverse: reverse string s in place */ +void reverse(char s[]) +{ + int i, j; + char c; + + for (i = 0, j = strlen(s)-1; i < j; i++, j--) { + c = s[i]; + s[i] = s[j]; + s[j] = c; + } +} + +void itoa(long n, char s[]) +{ + long i, sign; + + sign = n; + + if (n < 0) + n = -n; + i = 0; + do { + s[i++] = n % 10 + '0'; + } while ((n /= 10) > 0); + if (sign < 0) + s[i++] = '-'; + s[i] = '\0'; + reverse(s); +} + +static int check_print_output(long a, long b, long c, long d, long e, long f, long g, char *res) +{ + char buffer[OUTPUT_BUFFER_SIZE] = {0}; + + long output = sum7(a, b, c, d, e, f, g); + + // get the string representation of the output + itoa(output, buffer); + + return strcmp(buffer, res) == 0; +} + +static int test_sum_byte(void) +{ + return check_print_output(1, 2, 3, 4, 5, 6, 7, "28"); +} + +static int test_sum_word(void) +{ + return check_print_output(195, 196, 187, 198, 199, 200, 201, "1376"); +} + +static int test_sum_dword(void) +{ + return check_print_output(65536, 65537, 65538, 65539, 65540, 65541, 65542, "458773"); +} + +static int test_sum_qword(void) +{ + return check_print_output(4294967296, 4294967297, 4294967298, 4294967299, 4294967300, 4294967301, 4294967302, + "30064771093"); +} + +static struct graded_test all_tests[] = { + { test_sum_byte, "test_sum_byte", 15 }, + { test_sum_word, "test_sum_word", 20 }, + { test_sum_dword, "test_sum_dword", 30 }, + { test_sum_qword, "test_sum_qword", 35 } +}; + +int main(void) +{ + run_tests(all_tests, sizeof(all_tests) / sizeof(all_tests[0])); + return 0; +} diff --git a/labs/lab-08/tasks/sum7/utils/printf64.asm b/labs/lab-08/tasks/sum7/utils/printf64.asm new file mode 100644 index 000000000..e799a46e0 --- /dev/null +++ b/labs/lab-08/tasks/sum7/utils/printf64.asm @@ -0,0 +1,73 @@ +; SPDX-License-Identifier: BSD-3-Clause + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-08/tasks/to-upper/.gitignore b/labs/lab-08/tasks/to-upper/.gitignore index 91d366d5d..6ce843e64 100644 --- a/labs/lab-08/tasks/to-upper/.gitignore +++ b/labs/lab-08/tasks/to-upper/.gitignore @@ -1 +1 @@ -toupper +to_upper diff --git a/labs/lab-08/tasks/to-upper/README.md b/labs/lab-08/tasks/to-upper/README.md index 987224b75..69fb6d0df 100644 --- a/labs/lab-08/tasks/to-upper/README.md +++ b/labs/lab-08/tasks/to-upper/README.md @@ -1,5 +1,5 @@ --- -nav_order: 4 +nav_order: 5 parent: Lab 8 - Functions --- @@ -30,7 +30,20 @@ there is no need for another string. Implement the `toupper()` function so that the transformation occurs only for lowercase characters, not uppercase letters or other types of characters. -To test the implementation, enter the `tests/` directory and run: +After you consider your implementation complete, it is recommended to first run it `manually` in order to assess its correctness. +In order to do so, enter the `support/` directory and run: + +```console +make +``` + +If your code successfully compiled, you can then run the binary like so: + +```console +./to_upper +``` + +To fully test the implementation, enter the `tests/` directory and run: ```console make check diff --git a/labs/lab-08/tasks/to-upper/solution/main.asm b/labs/lab-08/tasks/to-upper/solution/main.asm index 101cd2b05..68ccdf4c5 100644 --- a/labs/lab-08/tasks/to-upper/solution/main.asm +++ b/labs/lab-08/tasks/to-upper/solution/main.asm @@ -10,22 +10,19 @@ extern to_upper global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - push mystring - push before_format + mov rdi, before_format + mov rsi, mystring call printf - add esp, 8 - push mystring + mov rdi, mystring call to_upper - add esp, 4 - push mystring - push after_format + mov rdi, after_format + mov rsi, mystring call printf - add esp, 8 leave ret diff --git a/labs/lab-08/tasks/to-upper/solution/to_upper.asm b/labs/lab-08/tasks/to-upper/solution/to_upper.asm index afef0f4d2..fdade5819 100644 --- a/labs/lab-08/tasks/to-upper/solution/to_upper.asm +++ b/labs/lab-08/tasks/to-upper/solution/to_upper.asm @@ -3,21 +3,25 @@ section .text global to_upper to_upper: - push ebp - mov ebp, esp - push ebx ; preserve ebx as required by cdecl + push rbp + mov rbp, rsp - mov eax, [ebp + 8] + sub rsp, 8 ; leave room in order to align the stack + push rbx ; preserve rbx as required by the System V AMD64 ABI + + mov rax, rdi check_one_byte: - mov bl, [eax] + mov bl, [rax] test bl, bl je out sub bl, 0x20 - mov [eax], bl - inc eax + mov [rax], bl + inc rax jmp check_one_byte out: - pop ebx + pop rbx ; restore rbx + add rsp, 8 + leave ret diff --git a/labs/lab-08/tasks/to-upper/support/Makefile b/labs/lab-08/tasks/to-upper/support/Makefile index 8b23c8bc7..78337ff3f 100644 --- a/labs/lab-08/tasks/to-upper/support/Makefile +++ b/labs/lab-08/tasks/to-upper/support/Makefile @@ -3,9 +3,9 @@ CC = gcc UTILSDIR := ../utils/ -ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +ASFLAGS ?= -f elf64 -F dwarf -I "$(UTILSDIR)" CFLAGS ?= -Wall -LDFLAGS ?= -m32 -no-pie +LDFLAGS ?= -fno-PIC -no-pie TARGET := to_upper OBJS := main.o to_upper.o diff --git a/labs/lab-08/tasks/to-upper/support/main.asm b/labs/lab-08/tasks/to-upper/support/main.asm index 101cd2b05..68ccdf4c5 100644 --- a/labs/lab-08/tasks/to-upper/support/main.asm +++ b/labs/lab-08/tasks/to-upper/support/main.asm @@ -10,22 +10,19 @@ extern to_upper global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - push mystring - push before_format + mov rdi, before_format + mov rsi, mystring call printf - add esp, 8 - push mystring + mov rdi, mystring call to_upper - add esp, 4 - push mystring - push after_format + mov rdi, after_format + mov rsi, mystring call printf - add esp, 8 leave ret diff --git a/labs/lab-08/tasks/to-upper/support/to_upper.asm b/labs/lab-08/tasks/to-upper/support/to_upper.asm index be2328552..c2c62e111 100644 --- a/labs/lab-08/tasks/to-upper/support/to_upper.asm +++ b/labs/lab-08/tasks/to-upper/support/to_upper.asm @@ -3,12 +3,14 @@ section .text global to_upper to_upper: - push ebp - mov ebp, esp - push ebx ; preserve ebx as required by cdecl + push rbp + mov rbp, rsp + + ; TODO: save the used registers and align the stack, if needed ; TODO - pop ebx + ; TODO: restore the used registers and the stack pointer, if altered + leave ret diff --git a/labs/lab-08/tasks/to-upper/tests/.gitignore b/labs/lab-08/tasks/to-upper/tests/.gitignore new file mode 100644 index 000000000..9a3294024 --- /dev/null +++ b/labs/lab-08/tasks/to-upper/tests/.gitignore @@ -0,0 +1,3 @@ +graded_test.o +test_to_upper.o +test_to_upper diff --git a/labs/lab-08/tasks/to-upper/tests/Makefile b/labs/lab-08/tasks/to-upper/tests/Makefile index e56989788..ff7a403d5 100644 --- a/labs/lab-08/tasks/to-upper/tests/Makefile +++ b/labs/lab-08/tasks/to-upper/tests/Makefile @@ -1,8 +1,8 @@ SRC_PATH ?= ../support FULL_SRC_PATH = "$(realpath $(SRC_PATH))" CPPFLAGS = -I. -I$(FULL_SRC_PATH) -I../utils -CFLAGS = -Wall -Wextra -m32 -LDFLAGS = -m32 +CFLAGS = -Wall -Wextra -m64 +LDFLAGS = -m64 -no-pie # Remove the line below to disable debugging support. CFLAGS += -g -O0 diff --git a/labs/lab-08/tasks/to-upper/utils/printf32.asm b/labs/lab-08/tasks/to-upper/utils/printf32.asm deleted file mode 100644 index 0617f3d8d..000000000 --- a/labs/lab-08/tasks/to-upper/utils/printf32.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -;;; macro to use printf with 32bit parameters: -;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` -;;; escape \n and \x0 only work with backquotes -;;; - rest of parameters MUST be 32bit -;;; - gen purpose and flags are preserved -;;; - stack is cleaned -%macro PRINTF32 1-* - pushf - pushad - jmp %%endstr -%%str: db %1 -%%endstr: -%rep %0 - 1 -%rotate -1 - push dword %1 -%endrep - push %%str - call printf - add esp, 4*%0 - popad - popf -%endmacro diff --git a/labs/lab-08/tasks/to-upper/utils/printf64.asm b/labs/lab-08/tasks/to-upper/utils/printf64.asm new file mode 100644 index 000000000..e799a46e0 --- /dev/null +++ b/labs/lab-08/tasks/to-upper/utils/printf64.asm @@ -0,0 +1,73 @@ +; SPDX-License-Identifier: BSD-3-Clause + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; no floating point support +;; all parameters need to be 64bit wide +;; format string int8_t=%hhx int16_t=%hx int32_t=%x int64_t=%lx +;; +%macro PRINTF64 1-* +jmp %%endstr +%%str db %1, 0 +%%endstr: + pushfq + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + push %%str +%if %0 >= 2 + push %2 +%endif +%if %0 >= 3 + push %3 +%endif +%if %0 >= 4 + push %4 +%endif +%if %0 >= 5 + push %5 +%endif +%if %0 == 6 + push %6 +%endif +%if %0 > 6 + %error "PRINTF64 accepts at most 6 arguments" +%endif +%if %0 == 6 + pop r9 +%endif +%if %0 >= 5 + pop r8 +%endif +%if %0 >= 4 + pop rcx +%endif +%if %0 >= 3 + pop rdx +%endif +%if %0 >= 2 + pop rsi +%endif + pop rdi + xor eax, eax + + call printf + + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + popfq +%endmacro diff --git a/labs/lab-09/README.md b/labs/lab-09/README.md index e2756e391..6e57c7515 100644 --- a/labs/lab-09/README.md +++ b/labs/lab-09/README.md @@ -1,5 +1,5 @@ --- -nav_order: 9 +nav_order: 11 parent: Labs has_children: true --- diff --git a/labs/lab-09/reading/calling-convention.md b/labs/lab-09/reading/calling-convention.md new file mode 100644 index 000000000..fafa9bab7 --- /dev/null +++ b/labs/lab-09/reading/calling-convention.md @@ -0,0 +1,69 @@ +--- +nav_order: 8 +parent: Lab 9 - The C - Assembly Interaction +--- + +# Reading: C - Assembly Interaction + +The assembly language poses challenges both in terms of readability and development. +Developers that are required to work on an existing codebase written entirely in assembly will most likely have trouble understanding it. +Meanwhile, requesting new features that were not specified during the initial design of the project may necessitate a full rewrite of the existing implementation. +If you want an example for why this is always a bad idea, look no further than [The Story of Mel](https://www.catb.org/jargon/html/story-of-mel.html). + +For these reasons, the general trend for the past few dozen years has been to adopt higher-level languages such as C/C++, Java, etc. +These are more human-readable, easier to manage, but unfortunately depend on compilers for generating machine code. +Although compilers can perform some ingenious optimizations, they also utilize only a small fraction of the total number of instructions available in the target ISA. +This leaves room for developers to manually optimize critical sections (i.e., areas that are executed often, mostly in tight loops). + +In this laboratory, we will explore how assembly modules can be integrated into C programs and vice versa. + +## Using Assembly Procedures in C Functions + +### Declaration of the Procedure + +In order to ensure the compatibility of the assembly procedure with the compiled C code, the following requirements must be met: + +- Declare the procedure label as global, using the `global` directive. + In addition to this, any data that will be used by the procedure must be declared as global. + +- Use the `extern` directive to declare procedures and global data as external. + +## Calling C Functions from Assembly Procedures + +In most cases, calling routines or functions from the standard C library in an assembly language program is a much more complex operation than vice versa. +Take the example of calling the `printf()` function from an assembly language program: + +```asm +global main +extern printf + +section .data + + text db "291 is the best!", 0 + fmt db "%s", 10, 0 + +section .text + +main: + push rbp + + mov rdi, fmt + mov rsi, text + call printf + + pop rbp + xor rax, rax + ret +``` + +To compile and run the program (stored in `main.asm`): + +```bash +nasm -f elf64 main.asm +gcc -no-pie main.o +$ ./a.out +``` + +If on 32-bit x86 systems all parameters are pushed onto the stack, on x86-64 we use up to six registers to pass the first arguments (left to right). +If we have more than six arguments, these will follow the 32-bit method and will be pushed to stack from right to left. +Also, if one of the first six arguments is larger than the size of a 64-bit register (e.g., passing a `struct` by value), on the stack it goes! diff --git a/labs/lab-09/reading/inline-assembly.md b/labs/lab-09/reading/inline-assembly.md new file mode 100644 index 000000000..31bb94b0d --- /dev/null +++ b/labs/lab-09/reading/inline-assembly.md @@ -0,0 +1,111 @@ +--- +nav_order: 9 +parent: Lab 9 - The C - Assembly Interaction +--- + +# Reading: Inline Assembly in C + +The situations where you absolutely *need* to implement something in Assembly instead of C are few and far between. +In most cases, you just want to manually optimize tight loops (i.e., blocks of code that execute often and repeatedly). +Or implement a wrapper over a *very* specific assembly instruction that the compiler does not know how to generate. + +For this purpose, **gcc** has support for what it calls [Extended Asm](https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html). +The structure of an extended assembly statement looks something like this: + +```C +asm asm-qualifiers ( AssemblerTemplate + : OutputOperands + [ : InputOperands + [ : Clobbers ] ]) +``` + +Where: + +- **asm:** Is just the keyword `asm` (like `for`, or `if`). +- **asm-qualifiers:** Are modifiers in the same vein as `const` is a modifier to variable declarations. +The only one you will ever be interested in is `volatile`. + This keyword tells **gcc** to avoid modifying your assembly code snippet, even for the purpose of what it calls "optimization". +- **AssemblerTemplate:** Is a string literal containing one or more assembly statements. +These statements can be separated through either a `;` character or a `\n`. +- **OutputOperands:** Binds register values at the end of the assembly code snippet to C variables where the values should be stored. +- **InputOperands:** Specifies what values (immediate or located in C variables) should be used to initialize specific registers when starting to execute the assembly code snippet. +- **Clobbers:** A list of registers (or memory) changed by the AssemblerTemplate that were not explicitly stated as OutputOperands. + +## A practical example: `rdtsc` + +Let's say you want to find the elapsed time between two points in your code. +For example, how long does it take for a `for` loop to execute. +Normally, you would use [clock_gettime()](https://www.man7.org/linux/man-pages/man3/clock_gettime.3.html) to retrieve the value of a certain clock (check the description of the `clockid` argument). +However, performing this measurement continuously may be very expensive. +The reason is more nuanced and outside the scope of this example, but it has to do with `clock_gettime()` having to acquire a lock, to perform a read from memory. + +As an alternative, we can use the [RDTSC](https://www.felixcloutier.com/x86/rdtsc) instruction that Reads the CPU's Timestamp Counter. +This counter is a register that is initialized with 0 when the CPU powers up and is incremented at a fixed frequency. +The benefit of `RDTSC` is that it's very precise and there is essentially no cost to reading it: only a few clock cycles on a multi-GHz CPU. +The downside is that it's accurate only when running Linux natively, not inside a Virtual Machine. +That's something `clock_gettime()` accounts for, and the reason why we don't always just use `RDTSC`. + +So, `RDTSC` will store the 64-bit counter in `EDX:EAX` on both i386 and x64. +In the following code snippet, we declare a C macro that wraps the extended asm expression. +The `eax` and `edx` are macro argument names, but are representative of the `EAX` and `EDX` registers when we'll specify the `OutputOperands`. + +```C +#define rdtsc(eax, edx) \ + asm volatile ( \ + "rdtsc" \ + : "=a"(eax), \ + "=d"(edx)) +``` + +In order to more easily interpret the result, we define the following structure (well... union). + +```C +typedef union { + uint64_t raw; + struct { + uint32_t low; + uint32_t high; + }; +} tscval_t; +``` + +The 64-bit `raw` field will share the same space in memory as the two 32-bit `low` and `high` fields. +Before we use it, we need to find out what's the frequency at which the Timestamp Counter is being incremented. +This value is exposed by the kernel through `sysfs` and is expressed in kHz: + +```bash +$ cat /sys/devices/system/cpu/cpu0/cpufreq/base_frequency +2600000 +``` + +So the Timestamp Counter is incremented 2.6 billion times per second. +Let's see how we can use our macro to perform a measurement: + +```C +#include /* printf */ +#include /* [u]int*_t */ +#include /* sleep */ + +#define TSC_FREQ 2.6e9 + +int32_t main(void) +{ + tscval_t start, end; + + /* perform initial timestamp measurement * + * least significant 32b -> EAX * + * most significat 32b -> EDX */ + rdtsc(start.low, start.high); + + /* do some "work" */ + sleep(3); + + /* perform final timestamp measurement */ + rdtsc(end.low, end.high); + + /* print elapsed time (in seconds, not clock ticks!) */ + printf("%.3f\n", (end.raw - start.raw) / TSC_FREQ); + + return 0; +} +``` diff --git a/labs/lab-09/reading/memory-layout-c-asm.md b/labs/lab-09/reading/memory-layout-c-asm.md index 6c91b7c6b..82f45645c 100644 --- a/labs/lab-09/reading/memory-layout-c-asm.md +++ b/labs/lab-09/reading/memory-layout-c-asm.md @@ -1,5 +1,5 @@ --- -nav_order: 9 +nav_order: 10 parent: Lab 9 - The C - Assembly Interaction --- diff --git a/labs/lab-09/reading/stack-c-asm.md b/labs/lab-09/reading/stack-c-asm.md deleted file mode 100644 index d53379afc..000000000 --- a/labs/lab-09/reading/stack-c-asm.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -nav_order: 10 -parent: Lab 9 - The C - Assembly Interaction ---- - -# Reading: C - Assembly Interaction: Stack - -## Setting the Stack - -When entering a procedure, it is necessary to set a stack frame to which to send parameters. -Of course, if the procedure does not receive parameters, this step is not necessary. So to set the stack, the following code must be included: - -```Assembly -push ebp -mov ebp, esp -``` - -The EBP gives us the ability to use it as an index within the stack and should not be altered during the procedure. - -## Passing Parameters from C to the Assembly Procedure - -C programs send parameters to assembly procedures using the stack. -Consider the following C program sequence: - -```C -#include - -extern int sum(int a, int b); // declare the assembly procedure as external - -int main() { - int a = 5, b = 7; - int res = sum(a, b); // call the assembly procedure - - return 0; -} -``` - -When C executes the call to `sum()`, it first pushes arguments on the stack in reverse order, then actually calls the procedure. -Thus, upon entering the procedure body, the stack will be intact. - -Since the variables `a` and `b` are declared as `int` values, they will each use one word on the stack. -This method of passing parameters is called value passing. -The code of the Sum procedure might look like this: - -```Assembly -section .text - global sum ; declare the procedure label as global - -sum: - push ebp - mov ebp, esp - - mov eax, [ebp+8] ; retrieve the first argument - mov ecx, [ebp+12] ; retrieve the second argument - add eax, ecx ; calculate the sum - - pop ebp - ret -``` - -It is interesting to note several things. -First, the assembly code defaults the return value of the procedure to the `eax` register. -Second, the `ret` command is sufficient to exit the procedure, due to the fact that the C compiler takes care of the rest of the stuff, such as removing parameters from the stack. diff --git a/labs/lab-09/tasks/export-fix/README.md b/labs/lab-09/tasks/export-fix/README.md index ffab90bef..ba6c59878 100644 --- a/labs/lab-09/tasks/export-fix/README.md +++ b/labs/lab-09/tasks/export-fix/README.md @@ -1,5 +1,5 @@ --- -nav_order: 6 +nav_order: 7 parent: Lab 9 - The C - Assembly Interaction --- @@ -7,23 +7,8 @@ parent: Lab 9 - The C - Assembly Interaction Navigate to `tasks/export-fix/support/`. -Each subdirectory (`support/a-func/`, `suppoer/b-var/`, `support/c-var-2/`) contains a problem related to exporting some symbols (functions or variables). -In each subdirectory, run the `make` command, identify the problem and edit the files needed to fix the problem. - -After finishing the exercise and testing it manually, run the checker script in `export-fix/tests/` to validate the result: - -```console -make check -``` +Each subdirectory (i.e., `support/a-func/`, `suppoer/b-var/`, `support/c-var-2/`) contains a problem related to exporting some symbols (functions or variables). -In case of a correct solution, you will get an output such as: - -```text -test_a_func ........................ passed ... 33 -test_b_var ........................ passed ... 33 -test_c_var_2 ........................ passed ... 34 - -Total: 100/100 -``` +In each subdirectory, run the `make` command, identify the problem and edit the files needed to fix the problem. -If you're having difficulties solving this exercise, go through [this relevant section](../../reading/memory-layout-c-asm.md) reading material. +If you're having difficulties solving this exercise, go through [this relevant section](../../reading/calling-convention.md) of the reading material. diff --git a/labs/lab-09/tasks/export-fix/solution/a-func/ops.h b/labs/lab-09/tasks/export-fix/solution/a-func/ops.h index 22d3d3ac0..1171fabb5 100644 --- a/labs/lab-09/tasks/export-fix/solution/a-func/ops.h +++ b/labs/lab-09/tasks/export-fix/solution/a-func/ops.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause */ -#ifndef OPS_H_ -#define OPS_H_ 1 +#ifndef _OPS_H +#define _OPS_H void init(void); void set(int value); @@ -10,4 +10,4 @@ int get(void); extern int age; void print_age(void); -#endif +#endif /* _OPS_H */ diff --git a/labs/lab-09/tasks/export-fix/solution/b-var/ops.h b/labs/lab-09/tasks/export-fix/solution/b-var/ops.h index 22d3d3ac0..1171fabb5 100644 --- a/labs/lab-09/tasks/export-fix/solution/b-var/ops.h +++ b/labs/lab-09/tasks/export-fix/solution/b-var/ops.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause */ -#ifndef OPS_H_ -#define OPS_H_ 1 +#ifndef _OPS_H +#define _OPS_H void init(void); void set(int value); @@ -10,4 +10,4 @@ int get(void); extern int age; void print_age(void); -#endif +#endif /* _OPS_H */ diff --git a/labs/lab-09/tasks/export-fix/solution/c-var-2/ops.h b/labs/lab-09/tasks/export-fix/solution/c-var-2/ops.h index 9be0fbb21..848fd7ab9 100644 --- a/labs/lab-09/tasks/export-fix/solution/c-var-2/ops.h +++ b/labs/lab-09/tasks/export-fix/solution/c-var-2/ops.h @@ -1,12 +1,13 @@ /* SPDX-License-Identifier: BSD-3-Clause */ -#ifndef OPS_H_ -#define OPS_H_ 1 +#ifndef _OPS_H +#define _OPS_H void init(void); void set(int value); int get(void); -extern int age; void print_age(void); -#endif +extern int age; + +#endif /* _OPS_H */ diff --git a/labs/lab-09/tasks/export-fix/support/a-func/ops.h b/labs/lab-09/tasks/export-fix/support/a-func/ops.h index 22d3d3ac0..1171fabb5 100644 --- a/labs/lab-09/tasks/export-fix/support/a-func/ops.h +++ b/labs/lab-09/tasks/export-fix/support/a-func/ops.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause */ -#ifndef OPS_H_ -#define OPS_H_ 1 +#ifndef _OPS_H +#define _OPS_H void init(void); void set(int value); @@ -10,4 +10,4 @@ int get(void); extern int age; void print_age(void); -#endif +#endif /* _OPS_H */ diff --git a/labs/lab-09/tasks/export-fix/support/b-var/ops.h b/labs/lab-09/tasks/export-fix/support/b-var/ops.h index 22d3d3ac0..1171fabb5 100644 --- a/labs/lab-09/tasks/export-fix/support/b-var/ops.h +++ b/labs/lab-09/tasks/export-fix/support/b-var/ops.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause */ -#ifndef OPS_H_ -#define OPS_H_ 1 +#ifndef _OPS_H +#define _OPS_H void init(void); void set(int value); @@ -10,4 +10,4 @@ int get(void); extern int age; void print_age(void); -#endif +#endif /* _OPS_H */ diff --git a/labs/lab-09/tasks/export-fix/support/c-var-2/ops.h b/labs/lab-09/tasks/export-fix/support/c-var-2/ops.h index e484a8b44..530075bf3 100644 --- a/labs/lab-09/tasks/export-fix/support/c-var-2/ops.h +++ b/labs/lab-09/tasks/export-fix/support/c-var-2/ops.h @@ -1,11 +1,11 @@ /* SPDX-License-Identifier: BSD-3-Clause */ -#ifndef OPS_H_ -#define OPS_H_ 1 +#ifndef _OPS_H +#define _OPS_H void init(void); void set(int value); int get(void); void print_age(void); -#endif +#endif /* _OPS_H */ diff --git a/labs/lab-09/tasks/include-fix/README.md b/labs/lab-09/tasks/include-fix/README.md index 9529ecf09..a23087094 100644 --- a/labs/lab-09/tasks/include-fix/README.md +++ b/labs/lab-09/tasks/include-fix/README.md @@ -1,5 +1,5 @@ --- -nav_order: 5 +nav_order: 6 parent: Lab 9 - The C - Assembly Interaction --- @@ -7,11 +7,10 @@ parent: Lab 9 - The C - Assembly Interaction Navigate to `tasks/include-fix/support`. -Run the `make` command. -You will get a warning. -Is it from compiling or linking? -Resolve this warning by editing the `support/hello.c` file. +Run the `make` command and you will get a warning. +Is this related to the compiler, or the linker? -Resolve the warning without using the `#include` directive. +Solve this warning by editing the `support/hello.c` file. +Do so without using the `#include` directive. -If you're having difficulties solving this exercise, go through [this relevant section](../../reading/memoy-layout-c-asm.md) reading material. +If you're having difficulties solving this exercise, go through [this relevant section](../../reading/calling-convention.md) of the reading material. diff --git a/labs/lab-09/tasks/include-fix/solution/.gitignore b/labs/lab-09/tasks/include-fix/solution/.gitignore new file mode 100644 index 000000000..e92569d01 --- /dev/null +++ b/labs/lab-09/tasks/include-fix/solution/.gitignore @@ -0,0 +1 @@ +/hello diff --git a/labs/lab-09/tasks/include-fix/solution/Makefile b/labs/lab-09/tasks/include-fix/solution/Makefile index bba58ea15..a1e2347b5 100644 --- a/labs/lab-09/tasks/include-fix/solution/Makefile +++ b/labs/lab-09/tasks/include-fix/solution/Makefile @@ -1,5 +1,5 @@ CFLAGS = -Wall -g -CXXFLAGS = -Wall -g +CXXFLAGS = -Wall -Werror -g .DEFAULT_GOAL: all @@ -13,5 +13,4 @@ hello: hello.o hello.o: hello.c clean: - -rm -f hello - -rm -f *.o + -rm -f hello *.o diff --git a/labs/lab-09/tasks/include-fix/support/Makefile b/labs/lab-09/tasks/include-fix/support/Makefile index bba58ea15..a1e2347b5 100644 --- a/labs/lab-09/tasks/include-fix/support/Makefile +++ b/labs/lab-09/tasks/include-fix/support/Makefile @@ -1,5 +1,5 @@ CFLAGS = -Wall -g -CXXFLAGS = -Wall -g +CXXFLAGS = -Wall -Werror -g .DEFAULT_GOAL: all @@ -13,5 +13,4 @@ hello: hello.o hello.o: hello.c clean: - -rm -f hello - -rm -f *.o + -rm -f hello *.o diff --git a/labs/lab-09/tasks/max-assembly-calls-x64/README.md b/labs/lab-09/tasks/max-assembly-calls-x64/README.md deleted file mode 100644 index 5d2686691..000000000 --- a/labs/lab-09/tasks/max-assembly-calls-x64/README.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -nav_order: 8 -parent: Lab 9 - The C - Assembly Interaction ---- - -# Task: Maximum Calculation in C with Call from Assembly - 64 Bits - -Enter the directory `tasks/max-assembly-calls-x64/support` and implement the maximum calculation in C with a call from Assembly language on a 64-bit system. -Start from the program used in `tasks/max-assembly-calls`, ensuring it runs on a 64-bit system. -Follow the instructions from the previous exercise and pay attention to the order of parameters. - -After finishing the exercise and testing it manually, run the checker script to validate the result: - -```console -make check -``` - -The format of your output should be `"max: on position: \n"` -In case of a correct solution, you will get an output such as: - -```text -test_max_asm_calls_x86_64 ........................ passed ... 100 - -Total: 100/100 -``` diff --git a/labs/lab-09/tasks/max-assembly-calls-x64/solution/main.asm b/labs/lab-09/tasks/max-assembly-calls-x64/solution/main.asm deleted file mode 100644 index d6e56e067..000000000 --- a/labs/lab-09/tasks/max-assembly-calls-x64/solution/main.asm +++ /dev/null @@ -1,43 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -BITS 64 - -section .data - arr: dd 19, 7, 129, 87, 54, 218, 67, 12, 19, 99 - len: equ $-arr - pos: dd 0 - - print_format: db "max: %u on position: %u", 10, 0 - -section .text - -global main -extern printf -extern get_max - -main: - push rbp - mov rbp, rsp - - ; Compute length in eax. - ; Divide by 4 (we are using integer data type of 4 bytes) by - ; using shr 2 (shift right with 2 bits). - xor rax, rax - mov eax, len - shr eax, 2 - - mov rdi, arr - xor rsi, rsi - mov esi, eax - mov rdx, pos - call get_max - - ; Print max. - mov rdi, print_format - xor rsi, rsi - mov esi, eax - mov rdx, [pos] - call printf - - leave - ret diff --git a/labs/lab-09/tasks/max-assembly-calls-x64/support/main.asm b/labs/lab-09/tasks/max-assembly-calls-x64/support/main.asm deleted file mode 100644 index bfac805af..000000000 --- a/labs/lab-09/tasks/max-assembly-calls-x64/support/main.asm +++ /dev/null @@ -1,37 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -BITS 64 - -section .data - arr: dd 19, 7, 129, 87, 54, 218, 67, 12, 19, 99 - len: equ $-arr - - print_format: db "max: %u", 10, 0 - -section .text - -global main -extern printf -extern get_max - -main: - push rbp - mov rbp, rsp - - ; Compute length in eax. - ; Divide by 4 (we are using integer data type of 4 bytes) by - ; using shr 2 (shift right with 2 bits). - mov eax, len - shr eax, 2 - - mov rdi, arr - mov rsi, rax - call get_max - - ; Print max. - mov rdi, print_format - mov rsi, rax - call printf - - leave - ret diff --git a/labs/lab-09/tasks/max-assembly-calls-x86/README.md b/labs/lab-09/tasks/max-assembly-calls-x86/README.md new file mode 100644 index 000000000..b20a13c72 --- /dev/null +++ b/labs/lab-09/tasks/max-assembly-calls-x86/README.md @@ -0,0 +1,12 @@ +--- +nav_order: 3 +parent: Lab 9 - The C - Assembly Interaction +--- + +# Task: Maximum Calculation in C with Call from Assembly (32-bit version) + +This is the exact same task as the last, with one major difference. +We are going to solve this using 32-bit assembly instead of 64-bit. +Take note of the distinctions. + +If you're having difficulties solving this exercise, go through [this relevant section](../../reading/calling-convention.md) of the reading material. diff --git a/labs/lab-09/tasks/max-assembly-calls-x64/solution/.gitignore b/labs/lab-09/tasks/max-assembly-calls-x86/solution/.gitignore similarity index 100% rename from labs/lab-09/tasks/max-assembly-calls-x64/solution/.gitignore rename to labs/lab-09/tasks/max-assembly-calls-x86/solution/.gitignore diff --git a/labs/lab-09/tasks/max-assembly-calls-x64/support/Makefile b/labs/lab-09/tasks/max-assembly-calls-x86/solution/Makefile similarity index 61% rename from labs/lab-09/tasks/max-assembly-calls-x64/support/Makefile rename to labs/lab-09/tasks/max-assembly-calls-x86/solution/Makefile index b8862033c..82e659f7d 100644 --- a/labs/lab-09/tasks/max-assembly-calls-x64/support/Makefile +++ b/labs/lab-09/tasks/max-assembly-calls-x86/solution/Makefile @@ -1,7 +1,7 @@ -ASMFLAGS := -f elf64 -LDFLAGS := -no-pie -CFLAGS := -g -Wall -Wextra -Werror -fno-pic -masm=intel +CFLAGS = -Wall -g -m32 ASM = nasm +ASMFLAGS = -f elf32 +LDFLAGS = -no-pie .DEFAULT_GOAL: all @@ -10,7 +10,7 @@ ASM = nasm all: main main: main.o max.o - $(CC) $(LDFLAGS) -o $@ $^ + $(CC) -m32 $(LDFLAGS) -o $@ $^ main.o: main.asm $(ASM) $(ASMFLAGS) -o $@ $< diff --git a/labs/lab-09/tasks/max-assembly-calls-x86/solution/main.asm b/labs/lab-09/tasks/max-assembly-calls-x86/solution/main.asm new file mode 100644 index 000000000..72262f246 --- /dev/null +++ b/labs/lab-09/tasks/max-assembly-calls-x86/solution/main.asm @@ -0,0 +1,58 @@ +; SPDX-License-Identifier: BSD-3-Clause + +extern printf +extern get_max + +section .data + arr: dd 19, 7, 129, 87, 54, 218, 67, 12, 19, 99 + len: equ $-arr + + fmt: db "max: %u on position: %u", 10, 0 + +section .bss + ; we are _reserving_ space for a double word (4 bytes) + ; but we are not initializing it; so it can't reside in .data + pos: resd 1 + +section .text + +global main + +main: + push ebp + mov ebp, esp + + ; push 3rd argument on the stack + mov eax, pos + push eax + + ; push 2nd argument on the stack + ; NOTE: len is the total array size; we want the number of elements + mov eax, len + shr eax, 2 + push eax + + ; push 1st argument on the stack + mov eax, arr + push arr + + call get_max + + ; print maximum value and its position + ; NOTE: EAX holds the return value of get_max() + ; NOTE: pos written by get_max() at given memory address + mov ecx, dword [pos] + push ecx + + push eax + + mov eax, fmt + push fmt + + call printf + + ; set exit code 0 (in main) + xor eax, eax + + leave + ret diff --git a/labs/lab-09/tasks/max-assembly-calls-x64/solution/max.c b/labs/lab-09/tasks/max-assembly-calls-x86/solution/max.c similarity index 88% rename from labs/lab-09/tasks/max-assembly-calls-x64/solution/max.c rename to labs/lab-09/tasks/max-assembly-calls-x86/solution/max.c index 094ce7868..222739c11 100644 --- a/labs/lab-09/tasks/max-assembly-calls-x64/solution/max.c +++ b/labs/lab-09/tasks/max-assembly-calls-x86/solution/max.c @@ -7,12 +7,11 @@ unsigned int get_max(unsigned int *arr, unsigned int len, unsigned int *pos) unsigned int max = 0; size_t i; - for (i = 0; i < len; i++) { + for (i = 0; i < len; i++) if (max < arr[i]) { max = arr[i]; *pos = i; } - } return max; } diff --git a/labs/lab-09/tasks/max-assembly-calls-x64/support/.gitignore b/labs/lab-09/tasks/max-assembly-calls-x86/support/.gitignore similarity index 100% rename from labs/lab-09/tasks/max-assembly-calls-x64/support/.gitignore rename to labs/lab-09/tasks/max-assembly-calls-x86/support/.gitignore diff --git a/labs/lab-09/tasks/max-assembly-calls-x64/solution/Makefile b/labs/lab-09/tasks/max-assembly-calls-x86/support/Makefile similarity index 60% rename from labs/lab-09/tasks/max-assembly-calls-x64/solution/Makefile rename to labs/lab-09/tasks/max-assembly-calls-x86/support/Makefile index 044aacbf9..82e659f7d 100644 --- a/labs/lab-09/tasks/max-assembly-calls-x64/solution/Makefile +++ b/labs/lab-09/tasks/max-assembly-calls-x86/support/Makefile @@ -1,7 +1,7 @@ -ASMFLAGS := -f elf64 -LDFLAGS := -no-pie -CFLAGS := -g -Wall -Wextra -Werror -fno-pic -masm=intel +CFLAGS = -Wall -g -m32 ASM = nasm +ASMFLAGS = -f elf32 +LDFLAGS = -no-pie .DEFAULT_GOAL: all @@ -10,7 +10,7 @@ ASM = nasm all: main main: main.o max.o - $(CC) $(LDFLAGS) -o $@ $^ + $(CC) -m32 $(LDFLAGS) -o $@ $^ main.o: main.asm $(ASM) $(ASMFLAGS) -o $@ $< diff --git a/labs/lab-09/tasks/max-assembly-calls-x86/support/main.asm b/labs/lab-09/tasks/max-assembly-calls-x86/support/main.asm new file mode 100644 index 000000000..b34f960a9 --- /dev/null +++ b/labs/lab-09/tasks/max-assembly-calls-x86/support/main.asm @@ -0,0 +1,50 @@ +; SPDX-License-Identifier: BSD-3-Clause + +extern printf +extern get_max + +section .data + arr: dd 19, 7, 129, 87, 54, 218, 67, 12, 19, 99 + len: equ $-arr + + fmt: db "max: %u", 10, 0 + +section .bss + ; we are _reserving_ space for a double word (4 bytes) + ; but we are not initializing it; so it can't reside in .data + pos: resd 1 + +section .text + +global main + +main: + push ebp + mov ebp, esp + + ; push 2nd argument on the stack + ; NOTE: len is the total array size; we want the number of elements + mov eax, len + shr eax, 2 + push eax + + ; push 1st argument on the stack + mov eax, arr + push arr + + call get_max + + ; print maximum value and its position + ; NOTE: EAX holds the return value of get_max() + push eax + + mov eax, fmt + push fmt + + call printf + + ; set exit code 0 (in main) + xor eax, eax + + leave + ret diff --git a/labs/lab-09/tasks/max-assembly-calls-x64/support/max.c b/labs/lab-09/tasks/max-assembly-calls-x86/support/max.c similarity index 100% rename from labs/lab-09/tasks/max-assembly-calls-x64/support/max.c rename to labs/lab-09/tasks/max-assembly-calls-x86/support/max.c diff --git a/labs/lab-09/tasks/max-assembly-calls-x64/tests/Makefile b/labs/lab-09/tasks/max-assembly-calls-x86/tests/Makefile similarity index 100% rename from labs/lab-09/tasks/max-assembly-calls-x64/tests/Makefile rename to labs/lab-09/tasks/max-assembly-calls-x86/tests/Makefile diff --git a/labs/lab-09/tasks/max-assembly-calls-x64/tests/graded_test.inc.sh b/labs/lab-09/tasks/max-assembly-calls-x86/tests/graded_test.inc.sh similarity index 100% rename from labs/lab-09/tasks/max-assembly-calls-x64/tests/graded_test.inc.sh rename to labs/lab-09/tasks/max-assembly-calls-x86/tests/graded_test.inc.sh diff --git a/labs/lab-09/tasks/max-assembly-calls-x64/tests/run_all_tests.sh b/labs/lab-09/tasks/max-assembly-calls-x86/tests/run_all_tests.sh similarity index 100% rename from labs/lab-09/tasks/max-assembly-calls-x64/tests/run_all_tests.sh rename to labs/lab-09/tasks/max-assembly-calls-x86/tests/run_all_tests.sh diff --git a/labs/lab-09/tasks/max-c-calls-x64/tests/tests.sh b/labs/lab-09/tasks/max-assembly-calls-x86/tests/tests.sh similarity index 90% rename from labs/lab-09/tasks/max-c-calls-x64/tests/tests.sh rename to labs/lab-09/tasks/max-assembly-calls-x86/tests/tests.sh index 456e9451e..097b6a284 100755 --- a/labs/lab-09/tasks/max-c-calls-x64/tests/tests.sh +++ b/labs/lab-09/tasks/max-assembly-calls-x86/tests/tests.sh @@ -10,9 +10,10 @@ fi binary=$SRC_PATH/main -test_max_c_calls_x64() +test_max_asm_calls() { make -s -C "$SRC_PATH" || return 1 + # Execute the program and capture the output OUTPUT=$($binary) @@ -29,4 +30,4 @@ test_max_c_calls_x64() return $OUT } -run_test "test_max_c_calls_x64" 100 +run_test "test_max_asm_calls" 100 diff --git a/labs/lab-09/tasks/max-assembly-calls/README.md b/labs/lab-09/tasks/max-assembly-calls/README.md index 0c1d38c03..6d97f358e 100644 --- a/labs/lab-09/tasks/max-assembly-calls/README.md +++ b/labs/lab-09/tasks/max-assembly-calls/README.md @@ -1,53 +1,17 @@ --- -nav_order: 3 +nav_order: 2 parent: Lab 9 - The C - Assembly Interaction --- # Task: Maximum Calculation in C with Call from Assembly -Navigate to `tasks/max-assembly-calls/support/` and open `main.asm` +Enter the directory `tasks/max-assembly-calls/support`. +Here, you have the `get_max()` function implemented in `max.c` that returns the maximum value inside an array of `uint32_t` elements. +This function is invoked in `main.asm` and the result is printed to the standard output. -In this directory you can find an implementation of calculating the maximum of a number where the `main()` function is defined in assembly language from which the `get_max()` function defined in C is called. +Modify the `get_max()` in such way that it also stores the index at which the element was found. +Make the necessary changes in `main.asm` and display both the maximum value *and* its position. -Trace the code in the two files and how the function arguments and return value are passed. +> **NOTE:** Yes, this is the previous exercise in reverse. -Compile and run the program. - -> **IMPORTANT:** -> Pay attention to understanding the code before proceeding to the next exercise. - -## Extending Maximum Computation in C with Call from Assembly - -Extend the program from the previous exercise (in assembly language and C) so that the `get_max()` function now has the signature `unsigned int get_max(unsigned int *arr, unsigned int len, unsigned int *pos)`. -The third argument to the function is the address where the position in the vector on which the maximum is found will be held. - -The position in the vector on which the maximum is found will also be displayed on display. - -> **TIP:** -> To hold the position, it is best to define a global variable in the assembly file (`main.asm`) in the `.data` section, of the form -> -> ```Assembly -> pos: dd 0 -> ``` -> -> This variable you will pass (by address) to the `get_max()` call and by value to the `printf()` call for display. -> -> For display modify the `print_format` string and the `printf()` call in the assembly file (`main.asm`) to allow two values to be displayed: maximum and position. -> - -After finishing the exercise and testing it manually, run the checker script to validate the result: - -```console -make check -``` - -The format of your output should be `"max: on position: \n"` -In case of a correct solution, you will get an output such as: - -```text -test_max_asm_calls ........................ passed ... 100 - -Total: 100/100 -``` - -If you're having difficulties solving this exercise, go through [this relevant section](../../reading/stack-c-asm.md) reading material. +If you're having difficulties solving this exercise, go through [this relevant section](../../reading/calling-convention.md) of the reading material. diff --git a/labs/lab-09/tasks/max-assembly-calls/solution/Makefile b/labs/lab-09/tasks/max-assembly-calls/solution/Makefile index 68799e871..3c317c8d4 100644 --- a/labs/lab-09/tasks/max-assembly-calls/solution/Makefile +++ b/labs/lab-09/tasks/max-assembly-calls/solution/Makefile @@ -1,6 +1,7 @@ -CFLAGS = -Wall -g -m32 +CFLAGS = -Wall -g ASM = nasm -ASMFLAGS = -f elf32 +ASMFLAGS = -f elf64 +LDFLAGS = -no-pie .DEFAULT_GOAL: all @@ -9,7 +10,7 @@ ASMFLAGS = -f elf32 all: main main: main.o max.o - $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $^ + $(CC) $(LDFLAGS) -o $@ $^ main.o: main.asm $(ASM) $(ASMFLAGS) -o $@ $< diff --git a/labs/lab-09/tasks/max-assembly-calls/solution/main.asm b/labs/lab-09/tasks/max-assembly-calls/solution/main.asm index 316d0967d..ea28cd6b2 100644 --- a/labs/lab-09/tasks/max-assembly-calls/solution/main.asm +++ b/labs/lab-09/tasks/max-assembly-calls/solution/main.asm @@ -6,36 +6,42 @@ extern get_max section .data arr: dd 19, 7, 129, 87, 54, 218, 67, 12, 19, 99 len: equ $-arr - pos: dd 0 - print_format: db "max: %u on position: %u", 10, 0 + fmt: db "max: %u on position: %u", 10, 0 + +section .bss + ; we are _reserving_ space for a double word (4 bytes) + ; but we are not initializing it; so it can't reside in .data + pos: resd 1 section .text global main main: - push ebp - mov ebp, esp - - ; Compute length in eax. - ; Divide by 4 (we are using integer data type of 4 bytes) by - ; using shr 2 (shift right with 2 bits). - mov eax, len - shr eax, 2 - - push dword pos ; pointer to position - push eax ; length of the array - push arr ; pointer to the array + push rbp + mov rbp, rsp + + ; compute the array length in RSI + ; NOTE: len is the total array size; we want the number of elements + mov rsi, len + shr rsi, 2 + + mov rdi, arr + mov rdx, pos call get_max - add esp, 12 - ; Print max. - push dword [pos] - push eax - push print_format + ; print maximum value and its position + ; NOTE: RAX holds the return value of get_max() + ; NOTE: pos written by get_max() at given memory address + mov rdi, fmt + mov rsi, rax + mov edx, dword [pos] call printf - add esp, 12 + + ; set exit code 0 (in main) + xor rax, rax leave ret + diff --git a/labs/lab-09/tasks/max-assembly-calls/solution/max.c b/labs/lab-09/tasks/max-assembly-calls/solution/max.c index 9b113f866..222739c11 100644 --- a/labs/lab-09/tasks/max-assembly-calls/solution/max.c +++ b/labs/lab-09/tasks/max-assembly-calls/solution/max.c @@ -12,5 +12,6 @@ unsigned int get_max(unsigned int *arr, unsigned int len, unsigned int *pos) max = arr[i]; *pos = i; } + return max; } diff --git a/labs/lab-09/tasks/max-assembly-calls/support/.gitignore b/labs/lab-09/tasks/max-assembly-calls/support/.gitignore index 95811e001..ba2906d06 100644 --- a/labs/lab-09/tasks/max-assembly-calls/support/.gitignore +++ b/labs/lab-09/tasks/max-assembly-calls/support/.gitignore @@ -1 +1 @@ -/main +main diff --git a/labs/lab-09/tasks/max-assembly-calls/support/Makefile b/labs/lab-09/tasks/max-assembly-calls/support/Makefile index 68799e871..3c317c8d4 100644 --- a/labs/lab-09/tasks/max-assembly-calls/support/Makefile +++ b/labs/lab-09/tasks/max-assembly-calls/support/Makefile @@ -1,6 +1,7 @@ -CFLAGS = -Wall -g -m32 +CFLAGS = -Wall -g ASM = nasm -ASMFLAGS = -f elf32 +ASMFLAGS = -f elf64 +LDFLAGS = -no-pie .DEFAULT_GOAL: all @@ -9,7 +10,7 @@ ASMFLAGS = -f elf32 all: main main: main.o max.o - $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $^ + $(CC) $(LDFLAGS) -o $@ $^ main.o: main.asm $(ASM) $(ASMFLAGS) -o $@ $< diff --git a/labs/lab-09/tasks/max-assembly-calls/support/main.asm b/labs/lab-09/tasks/max-assembly-calls/support/main.asm index da9940ae4..d5228f675 100644 --- a/labs/lab-09/tasks/max-assembly-calls/support/main.asm +++ b/labs/lab-09/tasks/max-assembly-calls/support/main.asm @@ -6,34 +6,34 @@ extern get_max section .data arr: dd 19, 7, 129, 87, 54, 218, 67, 12, 19, 99 len: equ $-arr - max: dd 0 - print_format: db "max: %u", 10, 0 + fmt: db "max: %u", 10, 0 section .text global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - ; Compute length in eax. - ; Divide by 4 (we are using integer data type of 4 bytes) by - ; using shr 2 (shift right with 2 bits). - mov eax, len - shr eax, 2 + ; compute the array length in RSI + ; NOTE: len is the total array size; we want the number of elements + mov rsi, len + shr rsi, 2 - push eax ; length of the array - push arr ; pointer to the array + mov rdi, arr call get_max - add esp, 8 ; clean up the stack - ; Print max. - push eax - push print_format + ; print maximum value + ; NOTE: RAX holds the return value of get_max() + mov rdi, fmt + mov rsi, rax call printf - add esp, 8 + + ; set exit code 0 (in main) + xor rax, rax leave ret + diff --git a/labs/lab-09/tasks/max-c-calls-x64/README.md b/labs/lab-09/tasks/max-c-calls-x64/README.md deleted file mode 100644 index a80c22a8e..000000000 --- a/labs/lab-09/tasks/max-c-calls-x64/README.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -nav_order: 7 -parent: Lab 9 - The C - Assembly Interaction ---- - -# Task: Maximum Computation in Assembly with 64-bit C Call - -Navigate to `tasks/max-c-calls-x64/support` and open `main.c` - -In this subdirectory you should have implemented the maximum assembly language calculation on a 64-bit system. -Start the program from exercises 4 and 5 in such a way that you run it using a 64-bit system. - -After finishing the exercise and testing it manually, run the checker script in the `max-c-calls-x64/tests/` folder to validate the result: - -```console -make check -``` - -The format of your output should be `"max: on position: \n"` -In case of a correct solution, you will get an output such as: - -```text -test_max_c_calls_x64 ........................ passed ... 100 - -Total: 100/100 -``` - -> **TIP:** -> . -> -> The first thing to note is that on the x64 architecture the registers are 8 bytes in size and have different names than the 32-bit ones (in addition to extending the traditional ones: `eax` register becomes `rax`, `ebx` register becomes `rbx`, etc., there are new ones: R10-R15: for more information see [here]()). -> -> Also, on x64 architecture parameters are no longer sent to the stack, but put in registers. -> The first 3 parameters are put in: `rdi`, `rsi` and `rdx` registers. -> This is not a uniformly adopted convention. -> This convention is only valid on Linux, on Windows there are other registers which are used to pass the parameters of a function. -> The calling convention requires that, for functions with a variable number of arguments, `rax` register be set to the number of vector registers used to pass arguments. -> `printf()` is a variable argument count function, and unless you use registers other than those mentioned in the previous paragraph for passing arguments, you must set `rax = 0` before calling. -> Read more [here](). diff --git a/labs/lab-09/tasks/max-c-calls-x64/solution/.gitignore b/labs/lab-09/tasks/max-c-calls-x64/solution/.gitignore deleted file mode 100644 index 35911abab..000000000 --- a/labs/lab-09/tasks/max-c-calls-x64/solution/.gitignore +++ /dev/null @@ -1 +0,0 @@ -mainmax diff --git a/labs/lab-09/tasks/max-c-calls-x64/solution/main.c b/labs/lab-09/tasks/max-c-calls-x64/solution/main.c deleted file mode 100644 index a2c624019..000000000 --- a/labs/lab-09/tasks/max-c-calls-x64/solution/main.c +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause - -#include - -int get_max(unsigned int *arr, unsigned int size, unsigned int *pos); - -int main(void) -{ - unsigned int arr[] = { 19, 7, 129, 87, 54, 218, 67, 12, 19, 99 }; - unsigned int max, pos; - - max = get_max(arr, sizeof(arr) / sizeof(arr[0]), &pos); - - printf("max: %d on position: %d\n", max, pos); - - return 0; -} diff --git a/labs/lab-09/tasks/max-c-calls-x64/solution/max.asm b/labs/lab-09/tasks/max-c-calls-x64/solution/max.asm deleted file mode 100644 index 1197eac03..000000000 --- a/labs/lab-09/tasks/max-c-calls-x64/solution/max.asm +++ /dev/null @@ -1,32 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -BITS 64 -section .text - -global get_max - -get_max: - push rbp - mov rbp, rsp - - push rbx ; the callee should save ebx if it uses it - - mov rbx, rdi ; first argument - mov rcx, rsi ; second argument - ; third argument is in rdx - - xor rax, rax - -compare: - cmp eax, [rbx+rcx*4-4] - jge check_end - mov r12, rcx ; array starts at zero: use fancy 64-bit arch register - dec r12 - mov [rdx], r12 ; save position - mov eax, [rbx+rcx*4-4] -check_end: - loopnz compare - - pop rbx ; the callee should restore ebx before returning - leave - ret diff --git a/labs/lab-09/tasks/max-c-calls-x64/support/.gitignore b/labs/lab-09/tasks/max-c-calls-x64/support/.gitignore deleted file mode 100644 index 35911abab..000000000 --- a/labs/lab-09/tasks/max-c-calls-x64/support/.gitignore +++ /dev/null @@ -1 +0,0 @@ -mainmax diff --git a/labs/lab-09/tasks/max-c-calls-x64/support/Makefile b/labs/lab-09/tasks/max-c-calls-x64/support/Makefile deleted file mode 100644 index b86fc2632..000000000 --- a/labs/lab-09/tasks/max-c-calls-x64/support/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -CC = gcc -CFLAGS = -g -Wall -Wextra -Werror -fno-pic -masm=intel -ASFLAGS = -f elf64 -LDFLAGS = -no-pie -PROGNAME = main - -ASM = nasm - -.DEFAULT_GOAL: all - -.PHONY: all clean - -all: $(PROGNAME) - -$(PROGNAME): main.o max.o - $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $^ - -max.o: max.asm - $(ASM) $(ASFLAGS) -o $@ $< - -main.o: main.c - $(CC) $(CFLAGS) -c -o $@ $< - -clean: - -rm -f $(PROGNAME) *.o diff --git a/labs/lab-09/tasks/max-c-calls-x64/support/main.c b/labs/lab-09/tasks/max-c-calls-x64/support/main.c deleted file mode 100644 index 07ecc399d..000000000 --- a/labs/lab-09/tasks/max-c-calls-x64/support/main.c +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause - -#include - -int get_max(unsigned int *arr, unsigned int size, unsigned int *pos); - -int main(void) -{ - unsigned int arr[] = { 19, 7, 129, 87, 54, 218, 67, 12, 19, 99 }; - unsigned int max, pos = sizeof(arr) / sizeof(arr[0]); - - max = get_max(arr, sizeof(arr) / sizeof(arr[0]), &pos); - - printf("max: %d on position: %d\n", max, pos); - - return 0; -} diff --git a/labs/lab-09/tasks/max-c-calls-x64/support/max.asm b/labs/lab-09/tasks/max-c-calls-x64/support/max.asm deleted file mode 100644 index 2eedc4081..000000000 --- a/labs/lab-09/tasks/max-c-calls-x64/support/max.asm +++ /dev/null @@ -1,24 +0,0 @@ -; SPDX-License-Identifier: BSD-3-Clause - -section .text - -global get_max - -get_max: - push rbp - mov rbp, rsp - - mov rbx, rdi ; first argument - mov rcx, rsi ; second argument - - xor rax, rax - -compare: - cmp eax, [rbx+rcx*4-4] - jge check_end - mov rax, [rbx+rcx*4-4] -check_end: - loopnz compare - - leave - ret diff --git a/labs/lab-09/tasks/max-assembly-calls/.gitignore b/labs/lab-09/tasks/max-c-calls-x86/.gitignore similarity index 83% rename from labs/lab-09/tasks/max-assembly-calls/.gitignore rename to labs/lab-09/tasks/max-c-calls-x86/.gitignore index 1a94556d8..ba2906d06 100644 --- a/labs/lab-09/tasks/max-assembly-calls/.gitignore +++ b/labs/lab-09/tasks/max-c-calls-x86/.gitignore @@ -1,2 +1 @@ main - diff --git a/labs/lab-09/tasks/max-c-calls-x86/README.md b/labs/lab-09/tasks/max-c-calls-x86/README.md new file mode 100644 index 000000000..46569a71d --- /dev/null +++ b/labs/lab-09/tasks/max-c-calls-x86/README.md @@ -0,0 +1,12 @@ +--- +nav_order: 4 +parent: Lab 9 - The C - Assembly Interaction +--- + +# Task: Maximum Calculation in Assembly with Call from C (32-bit version) + +This is the exact same task as the last, with one major difference. +We are going to solve this using 32-bit assembly instead of 64-bit. +Take note of the distinctions. + +If you're having difficulties solving this exercise, go through [this relevant section](../../reading/calling-convention.md) of the reading material. diff --git a/labs/lab-09/tasks/max-c-calls-x86/solution/Makefile b/labs/lab-09/tasks/max-c-calls-x86/solution/Makefile new file mode 100644 index 000000000..c96576c80 --- /dev/null +++ b/labs/lab-09/tasks/max-c-calls-x86/solution/Makefile @@ -0,0 +1,20 @@ +CFLAGS = -Wall -g +ASM = nasm +ASMFLAGS = -f elf64 -F dwarf + +.DEFAULT_GOAL: all + +.PHONY: all clean + +all: main + +main: main.o max.o + $(CC) $(LDFLAGS) -o $@ $^ + +max.o: max.asm + $(ASM) $(ASMFLAGS) -o $@ $< + +main.o: main.c + +clean: + -rm -f main *.o diff --git a/labs/lab-09/tasks/max-c-calls-x86/solution/main.c b/labs/lab-09/tasks/max-c-calls-x86/solution/main.c new file mode 100644 index 000000000..d48806493 --- /dev/null +++ b/labs/lab-09/tasks/max-c-calls-x86/solution/main.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include + +unsigned int get_max(unsigned int *arr, unsigned int len, unsigned int *pos); + +int main(void) +{ + unsigned int arr[] = { 19, 7, 129, 87, 54, 218, 67, 12, 19, 99 }; + unsigned int max; + unsigned int pos = -1; + + max = get_max(arr, 10, &pos); + + printf("max: %u on position: %u\n", max, pos); + + return 0; +} diff --git a/labs/lab-09/tasks/max-c-calls-x86/solution/max.asm b/labs/lab-09/tasks/max-c-calls-x86/solution/max.asm new file mode 100644 index 000000000..b0a4fce24 --- /dev/null +++ b/labs/lab-09/tasks/max-c-calls-x86/solution/max.asm @@ -0,0 +1,38 @@ +; SPDX-License-Identifier: BSD-3-Clause + +section .text + +global get_max + + +; RDI = array pointer +; RSI = array length +; RDX = pos pointer +get_max: + push rbp + mov rbp, rsp + + ; initialize EAX with the first value as currently known maximum + mov eax, [rdi] + mov [rdx], eax + + ; initialize RCX as loop counter for remaining elements + mov rcx, rsi + dec rcx + + ; loop over remaining array elements +compare: + cmp eax, [rdi + 4*rcx] + jge check_end + + ; update maximum and its position + mov eax, [rdi + 4*rcx] + mov [rdx], ecx +check_end: + loop compare + + ; result stored in RAX + + leave + ret + diff --git a/labs/lab-09/tasks/max-c-calls-x86/support/Makefile b/labs/lab-09/tasks/max-c-calls-x86/support/Makefile new file mode 100644 index 000000000..c96576c80 --- /dev/null +++ b/labs/lab-09/tasks/max-c-calls-x86/support/Makefile @@ -0,0 +1,20 @@ +CFLAGS = -Wall -g +ASM = nasm +ASMFLAGS = -f elf64 -F dwarf + +.DEFAULT_GOAL: all + +.PHONY: all clean + +all: main + +main: main.o max.o + $(CC) $(LDFLAGS) -o $@ $^ + +max.o: max.asm + $(ASM) $(ASMFLAGS) -o $@ $< + +main.o: main.c + +clean: + -rm -f main *.o diff --git a/labs/lab-09/tasks/max-c-calls-x86/support/main.c b/labs/lab-09/tasks/max-c-calls-x86/support/main.c new file mode 100644 index 000000000..789368c4d --- /dev/null +++ b/labs/lab-09/tasks/max-c-calls-x86/support/main.c @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include + +unsigned int get_max(unsigned int *arr, unsigned int len); + +int main(void) +{ + unsigned int arr[] = { 19, 7, 129, 87, 54, 218, 67, 12, 19, 99 }; + unsigned int max; + + max = get_max(arr, 10); + + printf("max: %u\n", max); + + return 0; +} diff --git a/labs/lab-09/tasks/max-c-calls-x86/support/max.asm b/labs/lab-09/tasks/max-c-calls-x86/support/max.asm new file mode 100644 index 000000000..1794c71ba --- /dev/null +++ b/labs/lab-09/tasks/max-c-calls-x86/support/max.asm @@ -0,0 +1,33 @@ +; SPDX-License-Identifier: BSD-3-Clause + +section .text + +global get_max + + +; RDI = array pointer +; RSI = array length +get_max: + push rbp + mov rbp, rsp + + ; initialize EAX with the first value as currently known maximum + mov eax, [rdi] + + ; initialize RCX as loop counter for remaining elements + mov rcx, rsi + dec rcx + + ; loop over remaining array elements +compare: + cmp eax, [rdi + 4*rcx] + jge check_end + mov eax, [rdi + 4*rcx] +check_end: + loop compare + + ; result stored in RAX + + leave + ret + diff --git a/labs/lab-09/tasks/max-c-calls-x64/tests/Makefile b/labs/lab-09/tasks/max-c-calls-x86/tests/Makefile similarity index 89% rename from labs/lab-09/tasks/max-c-calls-x64/tests/Makefile rename to labs/lab-09/tasks/max-c-calls-x86/tests/Makefile index e144ff43f..436c8dce5 100644 --- a/labs/lab-09/tasks/max-c-calls-x64/tests/Makefile +++ b/labs/lab-09/tasks/max-c-calls-x86/tests/Makefile @@ -1,4 +1,4 @@ -SRC_PATH ?= ../solution +SRC_PATH ?= ../support FULL_SRC_PATH = $(realpath $(SRC_PATH)) .PHONY: check clean build diff --git a/labs/lab-09/tasks/max-c-calls-x64/tests/graded_test.inc.sh b/labs/lab-09/tasks/max-c-calls-x86/tests/graded_test.inc.sh similarity index 100% rename from labs/lab-09/tasks/max-c-calls-x64/tests/graded_test.inc.sh rename to labs/lab-09/tasks/max-c-calls-x86/tests/graded_test.inc.sh diff --git a/labs/lab-09/tasks/max-c-calls-x64/tests/run_all_tests.sh b/labs/lab-09/tasks/max-c-calls-x86/tests/run_all_tests.sh similarity index 100% rename from labs/lab-09/tasks/max-c-calls-x64/tests/run_all_tests.sh rename to labs/lab-09/tasks/max-c-calls-x86/tests/run_all_tests.sh diff --git a/labs/lab-09/tasks/max-assembly-calls-x64/tests/tests.sh b/labs/lab-09/tasks/max-c-calls-x86/tests/tests.sh similarity index 79% rename from labs/lab-09/tasks/max-assembly-calls-x64/tests/tests.sh rename to labs/lab-09/tasks/max-c-calls-x86/tests/tests.sh index cf9a0d07e..aeb0dec3b 100755 --- a/labs/lab-09/tasks/max-assembly-calls-x64/tests/tests.sh +++ b/labs/lab-09/tasks/max-c-calls-x86/tests/tests.sh @@ -10,15 +10,14 @@ fi binary=$SRC_PATH/main -test_max_asm_calls_x86_64() +test_max_c_calls() { make -s -C "$SRC_PATH" || return 1 - # Execute the program and capture the output OUTPUT=$($binary) # Calculate the expected output - EXPECTED_OUTPUT=$(printf "max: %d on position: %d\n" 218 5) + EXPECTED_OUTPUT=$(printf "max: %u on position: %u\n" 218 5) # Check if the output matches the expected output if [[ "$OUTPUT" == "$EXPECTED_OUTPUT" ]]; then @@ -30,4 +29,4 @@ test_max_asm_calls_x86_64() return $OUT } -run_test "test_max_asm_calls_x86_64" 100 +run_test "test_max_c_calls" 100 diff --git a/labs/lab-09/tasks/max-c-calls/README.md b/labs/lab-09/tasks/max-c-calls/README.md index d17408fe2..50bbfc4da 100644 --- a/labs/lab-09/tasks/max-c-calls/README.md +++ b/labs/lab-09/tasks/max-c-calls/README.md @@ -5,64 +5,29 @@ parent: Lab 9 - The C - Assembly Interaction # Task: Maximum Calculation in Assembly with Call from C -Navigate to `tasks/max-c-calls/support` and open `main.c` +Navigate to `tasks/max-c-calls/support` and open `main.c`. +The `get_max()` function that it invokes in order to find the maximum value in +an array is implemented in `max.asm`. -In this subdirectory you can find an implementation of calculating the maximum of a number where the `main()` function is defined in C from which the `get_max()` function defined in assembly language is called. +Trace the code in both files and notice how the generated assembly code from +`main()` performs the function call. +Specifically, look at how the arguments are passed and how the return value is +interpreted. -Trace the code in the two files and how the function arguments and return value are passed. - -Compile and run the program. -To compile it run the command: - -```bash -make -``` - -Then run the resulting executable: - -```bash -./main -``` - -> **IMPORTANT:** -> Pay attention to understanding the code before proceeding to the next exercise. -> -> **IMPORTANT:** -> The return value of a function is placed in the `eax` register. +> **IMPORTANT:** Pay attention and understand the code before proceeding to the + next exercise. ## Maximum Computation Extension in Assembly with Call from C -Extend the program from the previous exercise (in assembly language and C) so that the `get_max()` function now has the signature `unsigned int get_max(unsigned int *arr, unsigned int len, unsigned int *pos)`. -The third argument to the function is the address where the position in the vector on which the maximum is found will be held. - -The position in the vector on which the maximum is found will also be displayed on display. - -> **TIP:** -> To hold the position, it is best to define a local variable `pos` in the `main()` function in the C file (`main.c`) in the form -> -> ```C -> unsigned int pos; -> ``` -> -> and call the `get_max()` function in the form: -> -> ```C -> max = get_max(arr, 10, &pos); -> ``` - -After finishing the exercise and testing it manually, run the checker script in the `max-c-calls/tests/` folder to validate the result: - -```console -make check -``` - -The format of your output should be `"max: on position: \n"` -In case of a correct solution, you will get an output such as: - -```text -test_max_c_calls ........................ passed ... 100 +Extend the program from the previous exercise (in assembly language and C) so +that the `get_max()` function now has the signature +`unsigned int get_max(unsigned int *arr, unsigned int len, unsigned int *pos)`. +The third argument to the function is a reference to a variable where the +maximum's position should be stored. -Total: 100/100 -``` +The position in the vector on which the maximum is found should also be +displayed to `stdout`. -If you're having difficulties solving this exercise, go through [this relevant section](../../readiing/stack-c-asm.md) reading material. +If you're having difficulties solving this exercise, go through +[this relevant section](../../reading/calling-convention.md) of the reading +material. diff --git a/labs/lab-09/tasks/max-c-calls/solution/Makefile b/labs/lab-09/tasks/max-c-calls/solution/Makefile index bc08eb299..c96576c80 100644 --- a/labs/lab-09/tasks/max-c-calls/solution/Makefile +++ b/labs/lab-09/tasks/max-c-calls/solution/Makefile @@ -1,6 +1,6 @@ -CFLAGS = -Wall -g -m32 +CFLAGS = -Wall -g ASM = nasm -ASMFLAGS = -f elf32 +ASMFLAGS = -f elf64 -F dwarf .DEFAULT_GOAL: all @@ -9,7 +9,7 @@ ASMFLAGS = -f elf32 all: main main: main.o max.o - $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $^ + $(CC) $(LDFLAGS) -o $@ $^ max.o: max.asm $(ASM) $(ASMFLAGS) -o $@ $< diff --git a/labs/lab-09/tasks/max-c-calls/solution/main.c b/labs/lab-09/tasks/max-c-calls/solution/main.c index ab7186d53..d48806493 100644 --- a/labs/lab-09/tasks/max-c-calls/solution/main.c +++ b/labs/lab-09/tasks/max-c-calls/solution/main.c @@ -2,14 +2,15 @@ #include -int get_max(unsigned int *arr, unsigned int size, unsigned int *max); +unsigned int get_max(unsigned int *arr, unsigned int len, unsigned int *pos); int main(void) { unsigned int arr[] = { 19, 7, 129, 87, 54, 218, 67, 12, 19, 99 }; - unsigned int max, pos; + unsigned int max; + unsigned int pos = -1; - max = get_max(arr, sizeof(arr) / sizeof(arr[0]), &pos); + max = get_max(arr, 10, &pos); printf("max: %u on position: %u\n", max, pos); diff --git a/labs/lab-09/tasks/max-c-calls/solution/max.asm b/labs/lab-09/tasks/max-c-calls/solution/max.asm index 101ab166f..b0a4fce24 100644 --- a/labs/lab-09/tasks/max-c-calls/solution/max.asm +++ b/labs/lab-09/tasks/max-c-calls/solution/max.asm @@ -4,33 +4,35 @@ section .text global get_max -get_max: - push ebp - mov ebp, esp - ; save ebx in callee - push ebx +; RDI = array pointer +; RSI = array length +; RDX = pos pointer +get_max: + push rbp + mov rbp, rsp - ; [ebp+8] is array pointer - ; [ebp+12] is array length + ; initialize EAX with the first value as currently known maximum + mov eax, [rdi] + mov [rdx], eax - mov ebx, [ebp+8] - mov ecx, [ebp+12] - xor eax, eax + ; initialize RCX as loop counter for remaining elements + mov rcx, rsi + dec rcx + ; loop over remaining array elements compare: - cmp eax, [ebx+ecx*4-4] - jge check_end - mov eax, [ebx+ecx*4-4] - mov edx, ecx + cmp eax, [rdi + 4*rcx] + jge check_end + + ; update maximum and its position + mov eax, [rdi + 4*rcx] + mov [rdx], ecx check_end: - loopnz compare + loop compare - dec edx - mov ebx, [ebp+16] - mov [ebx], edx + ; result stored in RAX - pop ebx + leave + ret - leave - ret diff --git a/labs/lab-09/tasks/max-c-calls/support/Makefile b/labs/lab-09/tasks/max-c-calls/support/Makefile index c3660e453..c96576c80 100644 --- a/labs/lab-09/tasks/max-c-calls/support/Makefile +++ b/labs/lab-09/tasks/max-c-calls/support/Makefile @@ -1,6 +1,6 @@ -CFLAGS = -Wall -g -m32 +CFLAGS = -Wall -g ASM = nasm -ASMFLAGS = -f elf32 +ASMFLAGS = -f elf64 -F dwarf .DEFAULT_GOAL: all @@ -9,7 +9,7 @@ ASMFLAGS = -f elf32 all: main main: main.o max.o - $(CC) $(LDFLAGS) -m32 -o $@ $^ + $(CC) $(LDFLAGS) -o $@ $^ max.o: max.asm $(ASM) $(ASMFLAGS) -o $@ $< diff --git a/labs/lab-09/tasks/max-c-calls/support/main.c b/labs/lab-09/tasks/max-c-calls/support/main.c index 71dba4abc..789368c4d 100644 --- a/labs/lab-09/tasks/max-c-calls/support/main.c +++ b/labs/lab-09/tasks/max-c-calls/support/main.c @@ -2,14 +2,14 @@ #include -int get_max(unsigned int *arr, unsigned int size); +unsigned int get_max(unsigned int *arr, unsigned int len); int main(void) { unsigned int arr[] = { 19, 7, 129, 87, 54, 218, 67, 12, 19, 99 }; unsigned int max; - max = get_max(arr, sizeof(arr) / sizeof(arr[0])); + max = get_max(arr, 10); printf("max: %u\n", max); diff --git a/labs/lab-09/tasks/max-c-calls/support/max.asm b/labs/lab-09/tasks/max-c-calls/support/max.asm index e44e9fc95..1794c71ba 100644 --- a/labs/lab-09/tasks/max-c-calls/support/max.asm +++ b/labs/lab-09/tasks/max-c-calls/support/max.asm @@ -4,28 +4,30 @@ section .text global get_max -get_max: - push ebp - mov ebp, esp - ; save ebx in callee - push ebx +; RDI = array pointer +; RSI = array length +get_max: + push rbp + mov rbp, rsp - ; [ebp+8] is array pointer - ; [ebp+12] is array length + ; initialize EAX with the first value as currently known maximum + mov eax, [rdi] - mov ebx, [ebp+8] - mov ecx, [ebp+12] - xor eax, eax + ; initialize RCX as loop counter for remaining elements + mov rcx, rsi + dec rcx + ; loop over remaining array elements compare: - cmp eax, [ebx+ecx*4-4] - jge check_end - mov eax, [ebx+ecx*4-4] + cmp eax, [rdi + 4*rcx] + jge check_end + mov eax, [rdi + 4*rcx] check_end: - loopnz compare + loop compare + + ; result stored in RAX - pop ebx + leave + ret - leave - ret diff --git a/labs/lab-09/tasks/regs-preserve/README.md b/labs/lab-09/tasks/regs-preserve/README.md index bb3bc83f8..1ad1966c1 100644 --- a/labs/lab-09/tasks/regs-preserve/README.md +++ b/labs/lab-09/tasks/regs-preserve/README.md @@ -1,44 +1,35 @@ --- -nav_order: 4 +nav_order: 5 parent: Lab 9 - The C - Assembly Interaction --- # Task: Keeping Records -Navigate to `tasks/regs-preserve/support` and open `main.asm` +Navigate to `tasks/regs-preserve/support` and open `main.asm`. +Here, you will find the `print_reverse_array()` function that loops over the elements of an array and calls `printf()` for each one. -In this subdirectory of the lab's task repository you will find the `print_reverse_array()` function implemented by a simple loop that makes repeated calls of the `printf()` function. +Analyze the code in `main.asm`, then compile and run the program. -Follow the code in the `main.asm` file, compile and run the program. What happened? The program runs indefinitely. -This is because the `printf()` function does not preserve the value in the `ecx` register, used here as a counter. +That's because the `printf()` function does not preserve the value in the `RCX` register, used here as a counter. Uncomment the lines marked `TODO1` and rerun the program. -## Troubleshooting SEGFAULT +## Troubleshooting -Decompose the lines marked `TODO2` in the assembly file from the previous exercise. +Uncomment the lines marked `TODO2` in the assembly file from the previous exercise. The code sequence makes a call to the `double_array()` function, implemented in C, just before displaying the vector using the function seen earlier. +The `double_array()` function should double each element of the original array. Compile and run the program. -To debug the segfault you can use the `objdump` utility to trace the assembly language code corresponding to the `double_array()` function. -Notice which of the registers used before and after the call are modified by this function. +Is the output as expected? +Do you get a SEGFAULT? +Investigate with GDB and take note of the Parameter Registers at the time of each function call. +One you find the problem, fix it. -Add the instructions for preserving and restoring the required registers to the assembly file. +> **NOTE:** We compiled `double_array.c` with -O2 optimization level to increase the probability of RDI and RSI registers being used in calculations. +> With `-O0`, the generated assembly code is much simpler and the parameter registers are most likely never used for anything other than holding the function argument values. +> In this case, the bug in `main.asm` could pass unnoticed, but the implementation would still be incorrect. -After finishing the exercise and testing it manually, run the checker script in the `regs-preserve/tests/` folder to validate the result: - -```console -make check -``` - -In case of a correct solution, you will get an output such as: - -```text -test_regs_preserve ........................ passed ... 100 - -Total: 100/100 -``` - -If you're having difficulties solving this exercise, go through [this relevant section](../../reading/memory-layout-c-asm.md) reading material. +If you're having difficulties solving this exercise, go through [this relevant section](../../reading/calling-convention.md) of the reading material. diff --git a/labs/lab-09/tasks/regs-preserve/solution/.gitignore b/labs/lab-09/tasks/regs-preserve/solution/.gitignore new file mode 100644 index 000000000..95811e001 --- /dev/null +++ b/labs/lab-09/tasks/regs-preserve/solution/.gitignore @@ -0,0 +1 @@ +/main diff --git a/labs/lab-09/tasks/regs-preserve/solution/Makefile b/labs/lab-09/tasks/regs-preserve/solution/Makefile index 52431e1df..a8a45d18a 100644 --- a/labs/lab-09/tasks/regs-preserve/solution/Makefile +++ b/labs/lab-09/tasks/regs-preserve/solution/Makefile @@ -1,6 +1,7 @@ -CFLAGS = -Wall -g -m32 +CFLAGS = -Wall -g -O2 ASM = nasm -ASMFLAGS = -f elf32 +ASMFLAGS = -f elf64 -F dwarf +LDFLAGS = -no-pie .DEFAULT_GOAL: all @@ -9,7 +10,7 @@ ASMFLAGS = -f elf32 all: main main: main.o double_array.o - $(CC) $(LDFLAGS) -m32 -o $@ $^ + $(CC) $(LDFLAGS) -o $@ $^ main.o: main.asm $(ASM) $(ASMFLAGS) -o $@ $< diff --git a/labs/lab-09/tasks/regs-preserve/solution/main.asm b/labs/lab-09/tasks/regs-preserve/solution/main.asm index aca1596a5..be5df9353 100644 --- a/labs/lab-09/tasks/regs-preserve/solution/main.asm +++ b/labs/lab-09/tasks/regs-preserve/solution/main.asm @@ -1,75 +1,75 @@ ; SPDX-License-Identifier: BSD-3-Clause +global main extern printf extern double_array section .data - myarray dd 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 - myarray_len dd 10 - - format_string db "%d ", 0 - newline db 13, 10, 0 + myarray dd 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + myarray_len equ 10 + format_string db "%d ", 0 + newline db 13, 10, 0 section .text - +; RDI = array pointer +; RSI = array length print_reverse_array: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - ; save ebx in callee - push ebx + ; save RBX in callee + push rbx - ; [ebp+8] is array pointer - ; [ebp+12] is array length - mov ebx, [ebp+8] - mov ecx, [ebp+12] - xor eax, eax + ; store copy of RDI; initialize counter register + mov rbx, rdi + mov rcx, rsi next: + push rcx -; TODO1: Decomentați următoarele două linii comentate - push ecx - push dword [ebx+ecx*4-4] - push format_string - call printf - add esp, 8 - pop ecx - loop next + xor rax, rax + mov esi, [rbx + 4*rcx - 4] + mov rdi, format_string + call printf - push newline - call printf - add esp, 4 + pop rcx + loop next - pop ebx + ; align the stack to 16 bytes before calling printf + sub rsp, 8 - leave - ret + xor rax, rax + mov rdi, newline + call printf + ; restore preserved register + pop rbx + + ; restore the stack after calling printf + add rsp, 8 + + leave + ret -global main main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - mov edx, [myarray_len] - lea eax, [myarray] + mov rdi, myarray + mov rsi, myarray_len -; TODO2: Decomentați această secvență de cod - push edx - push eax + ; TODO2: Uncomment this function call call double_array - add esp, 8 - - mov edx, [myarray_len] - lea eax, [myarray] - push edx - push eax - call print_reverse_array - add esp, 8 + ; RDI and RSI are scratch registers + ; compiling double_array() with -O2 overwrites these registers + ; reload the argument data before calling print_reverse_array() + mov rdi, myarray + mov rsi, myarray_len + call print_reverse_array - leave - ret + leave + ret diff --git a/labs/lab-09/tasks/regs-preserve/support/Makefile b/labs/lab-09/tasks/regs-preserve/support/Makefile index 7caf95b45..87c0ca162 100644 --- a/labs/lab-09/tasks/regs-preserve/support/Makefile +++ b/labs/lab-09/tasks/regs-preserve/support/Makefile @@ -1,6 +1,7 @@ -CFLAGS = -Wall -g -m32 +CFLAGS = -Wall -g -O2 ASM = nasm -ASMFLAGS = -f elf32 +ASMFLAGS = -f elf64 -F dwarf +LDFLAGS = -no-pie .DEFAULT_GOAL: all @@ -9,7 +10,7 @@ ASMFLAGS = -f elf32 all: main main: main.o double_array.o - $(CC) $(LDFLAGS) -m32 -o $@ $^ + $(CC) $(LDFLAGS) -o $@ $^ main.o: main.asm $(ASM) $(ASMFLAGS) -o $@ $< diff --git a/labs/lab-09/tasks/regs-preserve/support/main.asm b/labs/lab-09/tasks/regs-preserve/support/main.asm index 2dc7c3420..1f5b8cfe8 100644 --- a/labs/lab-09/tasks/regs-preserve/support/main.asm +++ b/labs/lab-09/tasks/regs-preserve/support/main.asm @@ -1,73 +1,63 @@ ; SPDX-License-Identifier: BSD-3-Clause +global main extern printf extern double_array section .data - myarray dd 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 - myarray_len dd 10 - - format_string db "%d ", 0 - newline db 13, 10, 0 + myarray dd 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + myarray_len equ 10 + format_string db "%d ", 0 + newline db 13, 10, 0 section .text - +; RDI = array pointer +; RSI = array length print_reverse_array: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - ; save ebx in callee - push ebx + ; save RBX in callee + push rbx - ; [ebp+8] is array pointer - ; [ebp+12] is array length - mov ebx, [ebp+8] - mov ecx, [ebp+12] - xor eax, eax + ; store copy of RDI; initialize counter register + mov rbx, rdi + mov rcx, rsi next: + ; TODO1: uncomment the following two lines + ; push rcx + xor rax, rax + mov esi, [rbx + 4*rcx - 4] + mov rdi, format_string + call printf + ; pop rcx + loop next -; TODO1: Decomentați următoarele două linii comentate -; push ecx - push dword [ebx+ecx*4-4] - push format_string - call printf - add esp, 8 -; pop ecx - loop next - - push newline - call printf - add esp, 4 + xor rax, rax + mov rdi, newline + call printf - pop ebx + ; restore preserved register + pop rbx - leave - ret + leave + ret -global main - main: - push ebp - mov ebp, esp - - mov edx, [myarray_len] - lea eax, [myarray] + push rbp + mov rbp, rsp -; TODO2: Decomentați această secvență de cod -; push edx -; push eax -; call double_array -; add esp, 8 + mov rdi, myarray + mov rsi, myarray_len - push edx - push eax - call print_reverse_array - add esp, 8 + ; TODO2: Uncomment this function call + ; call double_array + call print_reverse_array - leave - ret + leave + ret diff --git a/labs/lab-09/tasks/stack-frame/README.md b/labs/lab-09/tasks/stack-frame/README.md index 9ad218518..bb980b176 100644 --- a/labs/lab-09/tasks/stack-frame/README.md +++ b/labs/lab-09/tasks/stack-frame/README.md @@ -1,40 +1,35 @@ --- -nav_order: 2 +nav_order: 4 parent: Lab 9 - The C - Assembly Interaction --- # Task: Corrupt Stack Frame Debugging -Navigate to `tasks/stack-frame/support` and open `main.c` - -In current subdirectory of the lab's task archive you can find a C program that implements the display of the string `Hello world!` by a call to the `print_hello()` function defined in the assembly for the first part of the message, followed by two calls to the `printf()` function directly from the C code. +Navigate to `tasks/stack-frame/support` and open `main.c`. +There you will find a program that displays the string *"Hello world!"*. +The first part of the message is printed by calling the `print_hello()` function +(implemented in assembly). Compile and run the program. What do you notice? -The printed message is not as expected because the assembly code is missing an instruction. +The printed message is not as expected because the assembly code is missing an +instruction. -Use GDB to inspect the address at the top of the stack before executing the `ret` statement in the `print_hello()` function. +Use GDB to inspect the address at the top of the stack before executing the +`ret` statement in the `print_hello()` function. What does it point to? -Track the values of the `ebp` and `esp` registers during the execution of this function. -What should be at the top of the stack after execution of the `leave` statement? - -Find the missing instruction and rerun the executable. - -> **TIP:** -> In order to restore the stack to its state at the start of the current function, the `leave` statement relies on the function's pointer frame having been set. -After finishing the exercise and testing it manually, run the checker script in the `stack-frame/tests/` folder to validate the result: - -```console -make check -``` - -In case of a correct solution, you will get an output such as: +Track the values of the `RBP` and `RSP` registers during the execution of this +function. +What should be at the top of the stack after execution of the `leave` statement? -```text -test_stack_frame ........................ passed ... 100 +Find the missing instruction and rerun the program. +The output should be *"Hello world!"* instead of simply *"Hello!"*. -Total: 100/100 -``` +> **TIP:** In order to restore the stack to its state at the start of the + current function, the `leave` statement relies on the function's + pointer frame having been set. -If you're having difficulties solving this exercise, go through [this relevant section](../../reading/stack-c-asm.md) reading material. +If you're having difficulties solving this exercise, go through +[this relevant section](../../reading/calling-convention.md) of the reading +material. diff --git a/labs/lab-09/tasks/stack-frame/solution/Makefile b/labs/lab-09/tasks/stack-frame/solution/Makefile deleted file mode 120000 index a39be4f20..000000000 --- a/labs/lab-09/tasks/stack-frame/solution/Makefile +++ /dev/null @@ -1 +0,0 @@ -../support/Makefile \ No newline at end of file diff --git a/labs/lab-09/tasks/stack-frame/solution/Makefile b/labs/lab-09/tasks/stack-frame/solution/Makefile new file mode 100644 index 000000000..aac90542f --- /dev/null +++ b/labs/lab-09/tasks/stack-frame/solution/Makefile @@ -0,0 +1,22 @@ +CFLAGS = -Wall -g +ASM = nasm +ASMFLAGS = -f elf64 -F dwarf +LDFLAGS = -no-pie + +.DEFAULT_GOAL: all + +.PHONY: all clean + +all: main + +main: main.o print_hello.o + $(CC) $(LDFLAGS) -o $@ $^ + +print_hello.o: print_hello.asm + $(ASM) $(ASMFLAGS) -o $@ $< + +main.o: main.c + $(CC) $(CFLAGS) -c -o $@ $< + +clean: + -rm -f main *.o diff --git a/labs/lab-09/tasks/stack-frame/solution/main.c b/labs/lab-09/tasks/stack-frame/solution/main.c index 8ef7f5671..063c1d3a8 100644 --- a/labs/lab-09/tasks/stack-frame/solution/main.c +++ b/labs/lab-09/tasks/stack-frame/solution/main.c @@ -10,7 +10,6 @@ void asm_call_wrapper(void) printf(" world"); } - int main(void) { asm_call_wrapper(); diff --git a/labs/lab-09/tasks/stack-frame/solution/print_hello.asm b/labs/lab-09/tasks/stack-frame/solution/print_hello.asm index ecd3ebfa8..33ef1740f 100644 --- a/labs/lab-09/tasks/stack-frame/solution/print_hello.asm +++ b/labs/lab-09/tasks/stack-frame/solution/print_hello.asm @@ -1,33 +1,32 @@ ; SPDX-License-Identifier: BSD-3-Clause +global print_hello extern printf section .data - message db "Hello", 0 + message db "Hello", 0 section .text -global print_hello - +; TODO: Add the missing instruction print_hello: - push ebp - - ; leave is equivalent to the instruction set: - ; mov esp, ebp - ; pop ebp - ; - ; In the absence of this instruction (saving the current pointer frame), - ; leave would restore the stack tip (esp) to the beginning of the previous frame, - ; above which, on the stack, is an ebp, followed by the return address - ; of the function from which print_hello() was called; thus, when executing - ; the ret statement at the end of print_hello(), it will jump immediately after - ; call of the asm_call_wrapper() function; - - mov ebp, esp - - push message - call printf - add esp, 4 - - leave - ret + push rbp + + ; leave is equivalent to the instruction set: + ; mov rsp, rbp + ; pop rbp + ; + ; In the absence of this instruction (saving the current pointer frame), + ; leave would restore the stack tip (RSP) to the beginning of the previous frame, + ; above which, on the stack, is a RBP, followed by the return address + ; of the function from which print_hello() was called; thus, when executing + ; the ret statement at the end of print_hello(), it will jump immediately after + ; call of the asm_call_wrapper() function; + mov rbp, rsp + + xor rax, rax + mov rdi, message + call printf + + leave + ret diff --git a/labs/lab-09/tasks/stack-frame/support/Makefile b/labs/lab-09/tasks/stack-frame/support/Makefile index 0a5810455..aac90542f 100644 --- a/labs/lab-09/tasks/stack-frame/support/Makefile +++ b/labs/lab-09/tasks/stack-frame/support/Makefile @@ -1,6 +1,7 @@ -CFLAGS = -Wall -g -m32 -ASM = nasm -ASMFLAGS = -f elf32 +CFLAGS = -Wall -g +ASM = nasm +ASMFLAGS = -f elf64 -F dwarf +LDFLAGS = -no-pie .DEFAULT_GOAL: all @@ -9,7 +10,7 @@ ASMFLAGS = -f elf32 all: main main: main.o print_hello.o - $(CC) $(LDFLAGS) -m32 -o $@ $^ + $(CC) $(LDFLAGS) -o $@ $^ print_hello.o: print_hello.asm $(ASM) $(ASMFLAGS) -o $@ $< diff --git a/labs/lab-09/tasks/stack-frame/support/main.c b/labs/lab-09/tasks/stack-frame/support/main.c index 8ef7f5671..063c1d3a8 100644 --- a/labs/lab-09/tasks/stack-frame/support/main.c +++ b/labs/lab-09/tasks/stack-frame/support/main.c @@ -10,7 +10,6 @@ void asm_call_wrapper(void) printf(" world"); } - int main(void) { asm_call_wrapper(); diff --git a/labs/lab-09/tasks/stack-frame/support/print_hello.asm b/labs/lab-09/tasks/stack-frame/support/print_hello.asm index 32e18ff46..ecc3d902b 100644 --- a/labs/lab-09/tasks/stack-frame/support/print_hello.asm +++ b/labs/lab-09/tasks/stack-frame/support/print_hello.asm @@ -1,21 +1,20 @@ ; SPDX-License-Identifier: BSD-3-Clause +global print_hello extern printf section .data - message db "Hello", 0 + message db "Hello", 0 section .text -global print_hello - ; TODO: Add the missing instruction print_hello: - push ebp + push rbp - push message - call printf - add esp, 4 + xor rax, rax + mov rdi, message + call printf - leave - ret + leave + ret diff --git a/labs/lab-11/guides/cpp-obs/README.md b/labs/lab-11/guides/cpp-obs/README.md index 998ce60f8..8d9ad7668 100644 --- a/labs/lab-11/guides/cpp-obs/README.md +++ b/labs/lab-11/guides/cpp-obs/README.md @@ -5,7 +5,7 @@ parent: Lab 11 - Linking # Guide: Linking C and C++ -Access the directory `tasks/cpp-obs/support/`. +Access the directory `guides/cpp-obs/support/`. We want to observe how linking is performed with mixed sources: C and C++. In the `bad/` subdirectory, we have two directories, `c-calls-cpp/` and `cpp-calls-c/`, where we combine C and C++ sources. diff --git a/labs/lab-11/reading/linking.md b/labs/lab-11/reading/linking.md index a000fcb54..95ab73ee7 100644 --- a/labs/lab-11/reading/linking.md +++ b/labs/lab-11/reading/linking.md @@ -68,34 +68,22 @@ The Linux linking utility, `ld`, is invoked transparently by `gcc` or `g++`. To see how the linker is invoked, we use the `-v` option of the `gcc` utility, with the following output: ```console -/usr/lib/gcc/x86_64-linux-gnu/7/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/7/liblto_plugin.so --plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper -plugin-opt=-fresolution=/tmp/ccwnf5NM.res --plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc --plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_i386 --hash-style=gnu ---as-needed -dynamic-linker /lib/ld-linux.so.2 -z relro -o hello -/usr/lib/gcc/x86_64-linux-gnu/7/../../../i386-linux-gnu/crt1.o -/usr/lib/gcc/x86_64-linux-gnu/7/../../../i386-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/7/32/crtbegin.o --L/usr/lib/gcc/x86_64-linux-gnu/7/32 -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../i386-linux-gnu --L/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib32 -L/lib/i386-linux-gnu -L/lib/../lib32 -L/usr/lib/i386-linux-gnu --L/usr/lib/../lib32 -L/usr/lib/gcc/x86_64-linux-gnu/7 -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../i386-linux-gnu --L/usr/lib/gcc/x86_64-linux-gnu/7/../../.. -L/lib/i386-linux-gnu -L/usr/lib/i386-linux-gnu hello.o -lgcc --push-state ---as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state -/usr/lib/gcc/x86_64-linux-gnu/7/32/crtend.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../i386-linux-gnu/crtn.o -COLLECT_GCC_OPTIONS='-no-pie' '-m32' '-v' '-o' 'hello' '-mtune=generic' '-march=i686' + /usr/lib/gcc/x86_64-linux-gnu/11/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper -plugin-opt=-fresolution=/tmp/ccxcxtmC.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o app /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/11/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/11 -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/11/../../.. app.o helpers.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/11/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crtn.o +COLLECT_GCC_OPTIONS='-v' '-o' 'app' '-mtune=generic' '-march=x86-64' '-dumpdir' 'app.' ``` The `collect2` utility is, in fact, a wrapper around the `ld` utility. The result of running the command is complex. A "manual" invocation of the `ld` command would look like this: ```console -ld -dynamic-linker /lib/ld-linux.so.2 -m elf_i386 -o app /usr/lib32/crt1.o /usr/lib32/crti.o app.o helpers.o -lc /usr/lib32/crtn.o +ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -m elf_x86_64 -o app /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o app.o helpers.o -lc /usr/lib/x86_64-linux-gnu/crtn.o ``` The arguments of the above command have the following meanings: -- `-dynamic-linker /lib/ld-linux.so.2`: specifies the dynamic loader/linker used for loading the dynamic executable -- `-m elf_i386`: links files for the x86 architecture (32-bit, i386) -- `/usr/lib32/crt1.o`, `/usr/lib32/crti.o`, `/usr/lib32/crtn.o`: represent the C runtime library (`crt` - *C runtime*) that provides the necessary support for loading the executable +- `-dynamic-linker /lib64/ld-linux-x86-64.so.2`: specifies the dynamic loader/linker used for loading the dynamic executable +- `-m elf_x86_64`: links files for the x86_64 architecture +- `/usr/lib/x86_64-linux-gnu/crt1.o`, `/usr/lib/x86_64-linux-gnu/crti.o`, `/usr/lib/x86_64-linux-gnu/rtn.o`: represent the C runtime library (`crt` - *C runtime*) that provides the necessary support for loading the executable - `-lc`: links against standard C library (libc) ## File Inspection @@ -105,44 +93,35 @@ To track the linking process, we use static analysis utilities such as `nm`, `ob We use the `nm` utility to display symbols from an object file or an executable file: ```console -$ nm hello.o -00000000 T main - U puts - -$ nm hello -0804a01c B __bss_start -0804a01c b completed.7283 -0804a014 D __data_start -0804a014 W data_start -08048370 t deregister_tm_clones -08048350 T _dl_relocate_static_pie -080483f0 t __do_global_dtors_aux -08049f10 t __do_global_dtors_aux_fini_array_entry -0804a018 D __dso_handle -08049f14 d _DYNAMIC -0804a01c D _edata -0804a020 B _end -080484c4 T _fini -080484d8 R _fp_hw -08048420 t frame_dummy -08049f0c t __frame_dummy_init_array_entry -0804861c r __FRAME_END__ -0804a000 d _GLOBAL_OFFSET_TABLE_ - w __gmon_start__ -080484f0 r __GNU_EH_FRAME_HDR -080482a8 T _init -08049f10 t __init_array_end -08049f0c t __init_array_start -080484dc R _IO_stdin_used -080484c0 T __libc_csu_fini -08048460 T __libc_csu_init - U __libc_start_main@@GLIBC_2.0 -08048426 T main - U puts@@GLIBC_2.0 -080483b0 t register_tm_clones -08048310 T _start -0804a01c D __TMC_END__ -08048360 T __x86.get_pc_thunk.bx +$ nm app.o + U add + U _GLOBAL_OFFSET_TABLE_ +0000000000000000 T main + U printf + + +$ nm app +00000000004010be T add +0000000000404024 D __bss_start +0000000000404020 D __data_start +0000000000404020 W data_start +0000000000401080 T _dl_relocate_static_pie +0000000000403e50 d _DYNAMIC +0000000000404024 D _edata +0000000000404028 D _end +0000000000401168 T _fini +0000000000404000 d _GLOBAL_OFFSET_TABLE_ + w __gmon_start__ +0000000000401000 T _init +0000000000403e50 d __init_array_end +0000000000403e50 d __init_array_start +0000000000402000 R _IO_stdin_used +0000000000401160 T __libc_csu_fini +00000000004010f0 T __libc_csu_init + U __libc_start_main@@GLIBC_2.2.5 +0000000000401085 T main + U printf@@GLIBC_2.2.5 +0000000000401050 T _start ``` The `nm` command displays three columns: @@ -182,49 +161,66 @@ With the `readelf` command, we can view the headers of files. An important piece of information in the header of executable files is the entry point, the address of the first instruction executed: ```console -$ readelf -h hello +$ readelf -h app ELF Header: - Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 - Class: ELF32 + Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 + Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) - Machine: Intel 80386 + Machine: Advanced Micro Devices X86-64 Version: 0x1 - Entry point address: 0x8048310 - Start of program headers: 52 (bytes into file) - Start of section headers: 8076 (bytes into file) + Entry point address: 0x401050 + Start of program headers: 64 (bytes into file) + Start of section headers: 14008 (bytes into file) Flags: 0x0 - Size of this header: 52 (bytes) - Size of program headers: 32 (bytes) - Number of program headers: 9 - Size of section headers: 40 (bytes) - Number of section headers: 35 - Section header string table index: 34 + Size of this header: 64 (bytes) + Size of program headers: 56 (bytes) + Number of program headers: 12 + Size of section headers: 64 (bytes) + Number of section headers: 27 + Section header string table index: 26[] ``` Using the `readelf` command, we can see the sections of an executable/object file: ```console -$ readelf -S hello -There are 35 section headers, starting at offset 0x1f8c: -Section Headers: - [Nr] Name Type Addr Off Size ES Flg Lk Inf Al - [ 0] NULL 00000000 000000 000000 00 0 0 0 - [ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1 - [ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4 - [ 3] .note.gnu.build-i NOTE 08048188 000188 000024 00 A 0 0 4 +$ readelf -S app +There are 27 section headers, starting at offset 0x36b8: + +Section +Headers: + + [Nr] Name Type Address + Offset + Size EntSize Flags Link Info + Align + + [ 0] NULL 0000000000000000 + 00000000 + 0000000000000000 0000000000000000 0 0 + 0 + [ 1] .interp PROGBITS 00000000004002e0 + 000002e0 + 000000000000001c 0000000000000000 A 0 0 + 1 + [ 2] .note.gnu.propert NOTE 0000000000400300 + 00000300 + 0000000000000020 0000000000000000 A 0 0 + 8 + [ 3] .note.ABI-tag NOTE 0000000000400320 + 00000320 + 0000000000000020 0000000000000000 A 0 0 4 [...] ``` Also, with the `readelf` command, we can list (dump) the contents of a specific section: ```console -$ readelf -x .rodata hello +$ readelf -x .rodata app Hex dump of section '.rodata': - 0x080484d8 03000000 01000200 48656c6c 6f2c2057 ........Hello, W - 0x080484e8 6f726c64 2100 orld!. + 0x00402000 01000200 61202b20 623d2564 0a00 ....a + b=%d.. ``` diff --git a/labs/lab-11/tasks/entry-fix-1/solution/b-asm/hello.asm b/labs/lab-11/tasks/entry-fix-1/solution/b-asm/hello.asm index 43bf35cac..62975c7ae 100644 --- a/labs/lab-11/tasks/entry-fix-1/solution/b-asm/hello.asm +++ b/labs/lab-11/tasks/entry-fix-1/solution/b-asm/hello.asm @@ -1,7 +1,7 @@ ; SPDX-License-Identifier: BSD-3-Clause extern puts -extern main +global main section .rodata @@ -11,28 +11,28 @@ section .rodata section .text hi: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - push hi_str + mov rdi, hi_str call puts leave ret bye: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - push bye_str + mov rdi, bye_str call puts leave ret main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp call hi call bye diff --git a/labs/lab-11/tasks/entry-fix-1/solution/c-extra-nolibc/Makefile b/labs/lab-11/tasks/entry-fix-1/solution/c-extra-nolibc/Makefile index 29d5206ea..454cf8215 100644 --- a/labs/lab-11/tasks/entry-fix-1/solution/c-extra-nolibc/Makefile +++ b/labs/lab-11/tasks/entry-fix-1/solution/c-extra-nolibc/Makefile @@ -1,7 +1,7 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -nostdlib -nostdinc -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -nostdlib -nostdinc -no-pie AS = nasm -ASFLAGS = -f elf32 +ASFLAGS = -f elf64 .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/entry-fix-1/solution/c-extra-nolibc/puts.asm b/labs/lab-11/tasks/entry-fix-1/solution/c-extra-nolibc/puts.asm index 57a09dedf..11c5327d6 100644 --- a/labs/lab-11/tasks/entry-fix-1/solution/c-extra-nolibc/puts.asm +++ b/labs/lab-11/tasks/entry-fix-1/solution/c-extra-nolibc/puts.asm @@ -4,48 +4,49 @@ section .text global puts -; /usr/include/x86_64-linux-gnu/asm/unistd_32.h -__NR_write equ 4 +; /usr/include/x86_64-linux-gnu/asm/unistd_64.h +__NR_write equ 1 ; Argument is message to be printed to standard output. puts: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp ; Call __NR_write(1, message, message_len) (system call). ; - ; Use x86 Linux system call convention. - ; https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#Making_a_system_call + ; Use x86_64 Linux system call convention. + ; https://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-and-user-space-f ; - ; eax stores the system call id. - ; ebx stores the first system call argument: 1 (standard output). - ; ecx stores the second system call argument: message. - ; edc stores the third system call argument: message length. + ; rax stores the system call id. + ; rdi stores the first system call argument: 1 (standard output). + ; rsi stores the second system call argument: message. + ; rdx stores the third system call argument: message length. - ; Store the write system call id in eax. - mov eax, __NR_write + ; Store the write system call id in rax. + mov rax, __NR_write - ; Store standard output file descriptor (1) in ebx. - mov ebx, 1 - ; Store function argument (message) in ecx. - mov ecx, [ebp + 8] + ; Store function argument (message) in rsi. + mov rsi, rdi - ; Compute message length in edx. - ; Find NUL byte address of message using edi. Start from message address (ecx). - ; Then edx <- edi - ecx. - mov edi, ecx - dec edi + ; Store standard output file descriptor (1) in rdi. + mov rdi, 1 + + ; Compute message length in rdx. + ; Find NUL byte address of message using rcx. Start from message address rsi. + ; Then rdx <- rcx - rsi. + mov rcx, rsi + dec rcx again: - inc edi - cmp byte [edi], 0 + inc rcx + cmp byte [rcx], 0 jne again - mov edx, edi - sub edx, ecx + mov rdx, rcx + sub rdx, rsi ; Do system call. - int 0x80 + syscall leave ret diff --git a/labs/lab-11/tasks/entry-fix-1/solution/c-extra-nolibc/start.asm b/labs/lab-11/tasks/entry-fix-1/solution/c-extra-nolibc/start.asm index 51a69879e..36482656c 100644 --- a/labs/lab-11/tasks/entry-fix-1/solution/c-extra-nolibc/start.asm +++ b/labs/lab-11/tasks/entry-fix-1/solution/c-extra-nolibc/start.asm @@ -4,8 +4,8 @@ extern my_main section .text -; /usr/include/x86_64-linux-gnu/asm/unistd_32.h -__NR_exit equ 1 +; /usr/include/x86_64-linux-gnu/asm/unistd_64.h +__NR_exit equ 60 global _start @@ -14,17 +14,17 @@ _start: ; Call __NR_exit(main_return_value) (system call). ; - ; Use x86 Linux system call convention. - ; https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#Making_a_system_call + ; Use x86_64 Linux system call convention. + ; https://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-and-user-space-f ; - ; ebx stores the first system call argument. - ; eax stores the system call id. + ; rdi stores the first system call argument. + ; rax stores the system call id. - ; eax is main return value. Store it in ebx. - mov ebx, eax + ; eax is main return value. Store it in rdi. + mov rdi, rax ; Store the exit system call id in rax. - mov eax, __NR_exit + mov rax, __NR_exit ; Do system call. - int 0x80 + syscall diff --git a/labs/lab-11/tasks/entry-fix-1/solution/d-extra-libc/Makefile b/labs/lab-11/tasks/entry-fix-1/solution/d-extra-libc/Makefile index 13508c1fc..f31b11056 100644 --- a/labs/lab-11/tasks/entry-fix-1/solution/d-extra-libc/Makefile +++ b/labs/lab-11/tasks/entry-fix-1/solution/d-extra-libc/Makefile @@ -1,7 +1,7 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie AS = nasm -ASFLAGS = -f elf32 +ASFLAGS = -f elf64 .DEFAULT_GOAL: all @@ -10,7 +10,7 @@ ASFLAGS = -f elf32 all: hello hello: hello.o start.o - ld -static -m elf_i386 -L/usr/lib/gcc/x86_64-linux-gnu/13/32/ -o $@ $^ /usr/lib32/crti.o --start-group -lc -lgcc -lgcc_eh --end-group /usr/lib32/crtn.o + ld -static -m elf_x86_64 -L/usr/lib/gcc/x86_64-linux-gnu/13 -o $@ $^ /usr/lib/x86_64-linux-gnu/crti.o --start-group -lc -lgcc -lgcc_eh --end-group /usr/lib/x86_64-linux-gnu/crtn.o hello.o: hello.c diff --git a/labs/lab-11/tasks/entry-fix-1/solution/d-extra-libc/start.asm b/labs/lab-11/tasks/entry-fix-1/solution/d-extra-libc/start.asm index 72f8ada73..e81c5152f 100644 --- a/labs/lab-11/tasks/entry-fix-1/solution/d-extra-libc/start.asm +++ b/labs/lab-11/tasks/entry-fix-1/solution/d-extra-libc/start.asm @@ -8,22 +8,19 @@ section .text global _start global _dl_relocate_static_pie -; Adapted from GNU Libc i386 start.S -; https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/i386/start.S;hb=HEAD +; Adapted from GNU Libc x86_64 start.S +; https://github.com/lattera/glibc/blob/master/sysdeps/x86_64/start.S _start: ; Align stack. - xor ebp, ebp - pop esi - mov ecx, esp - and esp, 0xfffffff0 - - push eax - push esp - push edx - - push ecx - push esi - push my_main + xor rbp, rbp + pop rsi + mov rdx, rsp + and rsp, 0xfffffffffffffff0 + + push rax + push rsp + + mov rdi, my_main call __libc_start_main hlt diff --git a/labs/lab-11/tasks/entry-fix-1/support/a-c/Makefile b/labs/lab-11/tasks/entry-fix-1/support/a-c/Makefile index 6f7d34fd5..eb43972f6 100644 --- a/labs/lab-11/tasks/entry-fix-1/support/a-c/Makefile +++ b/labs/lab-11/tasks/entry-fix-1/support/a-c/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/entry-fix-1/support/b-asm/Makefile b/labs/lab-11/tasks/entry-fix-1/support/b-asm/Makefile index 3e0c09646..86a82c90d 100644 --- a/labs/lab-11/tasks/entry-fix-1/support/b-asm/Makefile +++ b/labs/lab-11/tasks/entry-fix-1/support/b-asm/Makefile @@ -1,7 +1,7 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie AS = nasm -ASFLAGS = -f elf32 +ASFLAGS = -f elf64 .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/entry-fix-1/support/b-asm/hello.asm b/labs/lab-11/tasks/entry-fix-1/support/b-asm/hello.asm index aef141c3f..0426dc615 100644 --- a/labs/lab-11/tasks/entry-fix-1/support/b-asm/hello.asm +++ b/labs/lab-11/tasks/entry-fix-1/support/b-asm/hello.asm @@ -10,28 +10,28 @@ section .rodata section .text hi: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - push hi_str + mov rdi, hi_str call puts leave ret bye: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp - push bye_str + mov rdi, bye_str call puts leave ret main: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp call hi call bye diff --git a/labs/lab-11/tasks/entry-fix-1/support/c-extra-nolibc/Makefile b/labs/lab-11/tasks/entry-fix-1/support/c-extra-nolibc/Makefile index 29d5206ea..454cf8215 100644 --- a/labs/lab-11/tasks/entry-fix-1/support/c-extra-nolibc/Makefile +++ b/labs/lab-11/tasks/entry-fix-1/support/c-extra-nolibc/Makefile @@ -1,7 +1,7 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -nostdlib -nostdinc -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -nostdlib -nostdinc -no-pie AS = nasm -ASFLAGS = -f elf32 +ASFLAGS = -f elf64 .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/entry-fix-1/support/c-extra-nolibc/puts.asm b/labs/lab-11/tasks/entry-fix-1/support/c-extra-nolibc/puts.asm index 57a09dedf..11c5327d6 100644 --- a/labs/lab-11/tasks/entry-fix-1/support/c-extra-nolibc/puts.asm +++ b/labs/lab-11/tasks/entry-fix-1/support/c-extra-nolibc/puts.asm @@ -4,48 +4,49 @@ section .text global puts -; /usr/include/x86_64-linux-gnu/asm/unistd_32.h -__NR_write equ 4 +; /usr/include/x86_64-linux-gnu/asm/unistd_64.h +__NR_write equ 1 ; Argument is message to be printed to standard output. puts: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp ; Call __NR_write(1, message, message_len) (system call). ; - ; Use x86 Linux system call convention. - ; https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#Making_a_system_call + ; Use x86_64 Linux system call convention. + ; https://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-and-user-space-f ; - ; eax stores the system call id. - ; ebx stores the first system call argument: 1 (standard output). - ; ecx stores the second system call argument: message. - ; edc stores the third system call argument: message length. + ; rax stores the system call id. + ; rdi stores the first system call argument: 1 (standard output). + ; rsi stores the second system call argument: message. + ; rdx stores the third system call argument: message length. - ; Store the write system call id in eax. - mov eax, __NR_write + ; Store the write system call id in rax. + mov rax, __NR_write - ; Store standard output file descriptor (1) in ebx. - mov ebx, 1 - ; Store function argument (message) in ecx. - mov ecx, [ebp + 8] + ; Store function argument (message) in rsi. + mov rsi, rdi - ; Compute message length in edx. - ; Find NUL byte address of message using edi. Start from message address (ecx). - ; Then edx <- edi - ecx. - mov edi, ecx - dec edi + ; Store standard output file descriptor (1) in rdi. + mov rdi, 1 + + ; Compute message length in rdx. + ; Find NUL byte address of message using rcx. Start from message address rsi. + ; Then rdx <- rcx - rsi. + mov rcx, rsi + dec rcx again: - inc edi - cmp byte [edi], 0 + inc rcx + cmp byte [rcx], 0 jne again - mov edx, edi - sub edx, ecx + mov rdx, rcx + sub rdx, rsi ; Do system call. - int 0x80 + syscall leave ret diff --git a/labs/lab-11/tasks/entry-fix-1/support/c-extra-nolibc/start.asm b/labs/lab-11/tasks/entry-fix-1/support/c-extra-nolibc/start.asm index 51a69879e..3fecb86b0 100644 --- a/labs/lab-11/tasks/entry-fix-1/support/c-extra-nolibc/start.asm +++ b/labs/lab-11/tasks/entry-fix-1/support/c-extra-nolibc/start.asm @@ -1,30 +1,30 @@ ; SPDX-License-Identifier: BSD-3-Clause -extern my_main +extern main section .text -; /usr/include/x86_64-linux-gnu/asm/unistd_32.h -__NR_exit equ 1 +; /usr/include/x86_64-linux-gnu/asm/unistd_64.h +__NR_exit equ 60 global _start _start: - call my_main + call main ; Call __NR_exit(main_return_value) (system call). ; - ; Use x86 Linux system call convention. - ; https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#Making_a_system_call + ; Use x86_64 Linux system call convention. + ; https://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-and-user-space-f ; - ; ebx stores the first system call argument. - ; eax stores the system call id. + ; rdi stores the first system call argument. + ; rax stores the system call id. - ; eax is main return value. Store it in ebx. - mov ebx, eax + ; eax is main return value. Store it in rdi. + mov rdi, rax ; Store the exit system call id in rax. - mov eax, __NR_exit + mov rax, __NR_exit ; Do system call. - int 0x80 + syscall diff --git a/labs/lab-11/tasks/entry-fix-1/support/d-extra-libc/Makefile b/labs/lab-11/tasks/entry-fix-1/support/d-extra-libc/Makefile index 13508c1fc..f31b11056 100644 --- a/labs/lab-11/tasks/entry-fix-1/support/d-extra-libc/Makefile +++ b/labs/lab-11/tasks/entry-fix-1/support/d-extra-libc/Makefile @@ -1,7 +1,7 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie AS = nasm -ASFLAGS = -f elf32 +ASFLAGS = -f elf64 .DEFAULT_GOAL: all @@ -10,7 +10,7 @@ ASFLAGS = -f elf32 all: hello hello: hello.o start.o - ld -static -m elf_i386 -L/usr/lib/gcc/x86_64-linux-gnu/13/32/ -o $@ $^ /usr/lib32/crti.o --start-group -lc -lgcc -lgcc_eh --end-group /usr/lib32/crtn.o + ld -static -m elf_x86_64 -L/usr/lib/gcc/x86_64-linux-gnu/13 -o $@ $^ /usr/lib/x86_64-linux-gnu/crti.o --start-group -lc -lgcc -lgcc_eh --end-group /usr/lib/x86_64-linux-gnu/crtn.o hello.o: hello.c diff --git a/labs/lab-11/tasks/entry-fix-1/support/d-extra-libc/start.asm b/labs/lab-11/tasks/entry-fix-1/support/d-extra-libc/start.asm index d654f7e45..96a6431ad 100644 --- a/labs/lab-11/tasks/entry-fix-1/support/d-extra-libc/start.asm +++ b/labs/lab-11/tasks/entry-fix-1/support/d-extra-libc/start.asm @@ -1,29 +1,26 @@ ; SPDX-License-Identifier: BSD-3-Clause extern __libc_start_main -extern my_main +extern main section .text global _start global _dl_relocate_static_pie -; Adapted from GNU Libc i386 start.S -; https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/i386/start.S;hb=HEAD +; Adapted from GNU Libc x86_64 start.S +; https://github.com/lattera/glibc/blob/master/sysdeps/x86_64/start.S _start: ; Align stack. - xor ebp, ebp - pop esi - mov ecx, esp - and esp, 0xfffffff0 - - push eax - push esp - push edx - - push ecx - push esi - push main + xor rbp, rbp + pop rsi + mov rdx, rsp + and rsp, 0xfffffffffffffff0 + + push rax + push rsp + + mov rdi, main call __libc_start_main hlt diff --git a/labs/lab-11/tasks/entry-fix-2/support/Makefile b/labs/lab-11/tasks/entry-fix-2/support/Makefile index 6f7d34fd5..eb43972f6 100644 --- a/labs/lab-11/tasks/entry-fix-2/support/Makefile +++ b/labs/lab-11/tasks/entry-fix-2/support/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/export-fix/support/a-func/Makefile b/labs/lab-11/tasks/export-fix/support/a-func/Makefile index 2d77fae47..dd4be2036 100644 --- a/labs/lab-11/tasks/export-fix/support/a-func/Makefile +++ b/labs/lab-11/tasks/export-fix/support/a-func/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/export-fix/support/b-var/Makefile b/labs/lab-11/tasks/export-fix/support/b-var/Makefile index 2d77fae47..dd4be2036 100644 --- a/labs/lab-11/tasks/export-fix/support/b-var/Makefile +++ b/labs/lab-11/tasks/export-fix/support/b-var/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/export-fix/support/c-var-2/Makefile b/labs/lab-11/tasks/export-fix/support/c-var-2/Makefile index 2d77fae47..dd4be2036 100644 --- a/labs/lab-11/tasks/export-fix/support/c-var-2/Makefile +++ b/labs/lab-11/tasks/export-fix/support/c-var-2/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/include-fix/support/Makefile b/labs/lab-11/tasks/include-fix/support/Makefile index 6f7d34fd5..eb43972f6 100644 --- a/labs/lab-11/tasks/include-fix/support/Makefile +++ b/labs/lab-11/tasks/include-fix/support/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/lib-fix/solution/Makefile b/labs/lab-11/tasks/lib-fix/solution/Makefile index f77a5c8bf..5a4316f94 100644 --- a/labs/lab-11/tasks/lib-fix/solution/Makefile +++ b/labs/lab-11/tasks/lib-fix/solution/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/lib-fix/support/Makefile b/labs/lab-11/tasks/lib-fix/support/Makefile index 9117ea755..36201b05a 100644 --- a/labs/lab-11/tasks/lib-fix/support/Makefile +++ b/labs/lab-11/tasks/lib-fix/support/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/multiple-link/solution/a-no-header/Makefile b/labs/lab-11/tasks/multiple-link/solution/a-no-header/Makefile index 23b4fe288..598ebd8ad 100644 --- a/labs/lab-11/tasks/multiple-link/solution/a-no-header/Makefile +++ b/labs/lab-11/tasks/multiple-link/solution/a-no-header/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/multiple-link/solution/b-header/Makefile b/labs/lab-11/tasks/multiple-link/solution/b-header/Makefile index 1bad54704..a8435541b 100644 --- a/labs/lab-11/tasks/multiple-link/solution/b-header/Makefile +++ b/labs/lab-11/tasks/multiple-link/solution/b-header/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/multiple-link/solution/c-lib/Makefile b/labs/lab-11/tasks/multiple-link/solution/c-lib/Makefile index 5a8cce37d..dcd33448f 100644 --- a/labs/lab-11/tasks/multiple-link/solution/c-lib/Makefile +++ b/labs/lab-11/tasks/multiple-link/solution/c-lib/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/multiple-link/support/example/a-no-header/Makefile b/labs/lab-11/tasks/multiple-link/support/example/a-no-header/Makefile index bc1a97682..da4ce1e97 100644 --- a/labs/lab-11/tasks/multiple-link/support/example/a-no-header/Makefile +++ b/labs/lab-11/tasks/multiple-link/support/example/a-no-header/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/multiple-link/support/example/b-header/Makefile b/labs/lab-11/tasks/multiple-link/support/example/b-header/Makefile index 661113e66..c70276755 100644 --- a/labs/lab-11/tasks/multiple-link/support/example/b-header/Makefile +++ b/labs/lab-11/tasks/multiple-link/support/example/b-header/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/multiple-link/support/example/c-lib/Makefile b/labs/lab-11/tasks/multiple-link/support/example/c-lib/Makefile index 18b882f71..fbd540c71 100644 --- a/labs/lab-11/tasks/multiple-link/support/example/c-lib/Makefile +++ b/labs/lab-11/tasks/multiple-link/support/example/c-lib/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/single-link/solution/a-dynamic/Makefile b/labs/lab-11/tasks/single-link/solution/a-dynamic/Makefile index 86c98f9b7..48ffb153e 100644 --- a/labs/lab-11/tasks/single-link/solution/a-dynamic/Makefile +++ b/labs/lab-11/tasks/single-link/solution/a-dynamic/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all @@ -8,8 +8,8 @@ LDFLAGS = -no-pie -m32 all: name name: name.o - $(CC) $(LDFLAGS) -o $@ $^ - #ld -dynamic-linker /lib/ld-linux.so.2 -m elf_i386 -o $@ /usr/lib32/crt1.o /usr/lib32/crti.o $^ -lc /usr/lib32/crtn.o + #$(CC) $(LDFLAGS) -o $@ $^ + ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -m elf_x86_64 -o $@ /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o $^ -lc /usr/lib/x86_64-linux-gnu/crtn.o name.o: name.c diff --git a/labs/lab-11/tasks/single-link/solution/b-static/Makefile b/labs/lab-11/tasks/single-link/solution/b-static/Makefile index c1888930e..ba1f20d5c 100644 --- a/labs/lab-11/tasks/single-link/solution/b-static/Makefile +++ b/labs/lab-11/tasks/single-link/solution/b-static/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -static -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -static -no-pie .DEFAULT_GOAL: all @@ -8,8 +8,8 @@ LDFLAGS = -static -no-pie -m32 all: name name: name.o - $(CC) $(LDFLAGS) -o $@ $^ - #ld -static -m elf_i386 -L/usr/lib/gcc/x86_64-linux-gnu/13/32/ -o $@ /usr/lib32/crt1.o /usr/lib32/crti.o $^ --start-group -lc -lgcc -lgcc_eh --end-group /usr/lib32/crtn.o + #$(CC) $(LDFLAGS) -o $@ $^ + ld -static -m elf_x86_64 -L/usr/lib/gcc/x86_64-linux-gnu/13 -o $@ /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o $^ --start-group -lc -lgcc -lgcc_eh --end-group /usr/lib/x86_64-linux-gnu/crtn.o name.o: name.c diff --git a/labs/lab-11/tasks/single-link/solution/c-standalone/Makefile b/labs/lab-11/tasks/single-link/solution/c-standalone/Makefile index 2082e473f..85565604f 100644 --- a/labs/lab-11/tasks/single-link/solution/c-standalone/Makefile +++ b/labs/lab-11/tasks/single-link/solution/c-standalone/Makefile @@ -1,7 +1,7 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -nostdinc -nostdlib -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -nostdinc -nostdlib -no-pie AS = nasm -ASFLAGS = -f elf32 +ASFLAGS = -f elf64 .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/single-link/solution/c-standalone/puts.asm b/labs/lab-11/tasks/single-link/solution/c-standalone/puts.asm index 57a09dedf..11c5327d6 100644 --- a/labs/lab-11/tasks/single-link/solution/c-standalone/puts.asm +++ b/labs/lab-11/tasks/single-link/solution/c-standalone/puts.asm @@ -4,48 +4,49 @@ section .text global puts -; /usr/include/x86_64-linux-gnu/asm/unistd_32.h -__NR_write equ 4 +; /usr/include/x86_64-linux-gnu/asm/unistd_64.h +__NR_write equ 1 ; Argument is message to be printed to standard output. puts: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp ; Call __NR_write(1, message, message_len) (system call). ; - ; Use x86 Linux system call convention. - ; https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#Making_a_system_call + ; Use x86_64 Linux system call convention. + ; https://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-and-user-space-f ; - ; eax stores the system call id. - ; ebx stores the first system call argument: 1 (standard output). - ; ecx stores the second system call argument: message. - ; edc stores the third system call argument: message length. + ; rax stores the system call id. + ; rdi stores the first system call argument: 1 (standard output). + ; rsi stores the second system call argument: message. + ; rdx stores the third system call argument: message length. - ; Store the write system call id in eax. - mov eax, __NR_write + ; Store the write system call id in rax. + mov rax, __NR_write - ; Store standard output file descriptor (1) in ebx. - mov ebx, 1 - ; Store function argument (message) in ecx. - mov ecx, [ebp + 8] + ; Store function argument (message) in rsi. + mov rsi, rdi - ; Compute message length in edx. - ; Find NUL byte address of message using edi. Start from message address (ecx). - ; Then edx <- edi - ecx. - mov edi, ecx - dec edi + ; Store standard output file descriptor (1) in rdi. + mov rdi, 1 + + ; Compute message length in rdx. + ; Find NUL byte address of message using rcx. Start from message address rsi. + ; Then rdx <- rcx - rsi. + mov rcx, rsi + dec rcx again: - inc edi - cmp byte [edi], 0 + inc rcx + cmp byte [rcx], 0 jne again - mov edx, edi - sub edx, ecx + mov rdx, rcx + sub rdx, rsi ; Do system call. - int 0x80 + syscall leave ret diff --git a/labs/lab-11/tasks/single-link/solution/c-standalone/start.asm b/labs/lab-11/tasks/single-link/solution/c-standalone/start.asm index c384e42b4..3fecb86b0 100644 --- a/labs/lab-11/tasks/single-link/solution/c-standalone/start.asm +++ b/labs/lab-11/tasks/single-link/solution/c-standalone/start.asm @@ -4,8 +4,8 @@ extern main section .text -; /usr/include/x86_64-linux-gnu/asm/unistd_32.h -__NR_exit equ 1 +; /usr/include/x86_64-linux-gnu/asm/unistd_64.h +__NR_exit equ 60 global _start @@ -14,17 +14,17 @@ _start: ; Call __NR_exit(main_return_value) (system call). ; - ; Use x86 Linux system call convention. - ; https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#Making_a_system_call + ; Use x86_64 Linux system call convention. + ; https://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-and-user-space-f ; - ; ebx stores the first system call argument. - ; eax stores the system call id. + ; rdi stores the first system call argument. + ; rax stores the system call id. - ; eax is main return value. Store it in ebx. - mov ebx, eax + ; eax is main return value. Store it in rdi. + mov rdi, rax ; Store the exit system call id in rax. - mov eax, __NR_exit + mov rax, __NR_exit ; Do system call. - int 0x80 + syscall diff --git a/labs/lab-11/tasks/single-link/support/diy/c-standalone/puts.asm b/labs/lab-11/tasks/single-link/support/diy/c-standalone/puts.asm index 57a09dedf..11c5327d6 100644 --- a/labs/lab-11/tasks/single-link/support/diy/c-standalone/puts.asm +++ b/labs/lab-11/tasks/single-link/support/diy/c-standalone/puts.asm @@ -4,48 +4,49 @@ section .text global puts -; /usr/include/x86_64-linux-gnu/asm/unistd_32.h -__NR_write equ 4 +; /usr/include/x86_64-linux-gnu/asm/unistd_64.h +__NR_write equ 1 ; Argument is message to be printed to standard output. puts: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp ; Call __NR_write(1, message, message_len) (system call). ; - ; Use x86 Linux system call convention. - ; https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#Making_a_system_call + ; Use x86_64 Linux system call convention. + ; https://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-and-user-space-f ; - ; eax stores the system call id. - ; ebx stores the first system call argument: 1 (standard output). - ; ecx stores the second system call argument: message. - ; edc stores the third system call argument: message length. + ; rax stores the system call id. + ; rdi stores the first system call argument: 1 (standard output). + ; rsi stores the second system call argument: message. + ; rdx stores the third system call argument: message length. - ; Store the write system call id in eax. - mov eax, __NR_write + ; Store the write system call id in rax. + mov rax, __NR_write - ; Store standard output file descriptor (1) in ebx. - mov ebx, 1 - ; Store function argument (message) in ecx. - mov ecx, [ebp + 8] + ; Store function argument (message) in rsi. + mov rsi, rdi - ; Compute message length in edx. - ; Find NUL byte address of message using edi. Start from message address (ecx). - ; Then edx <- edi - ecx. - mov edi, ecx - dec edi + ; Store standard output file descriptor (1) in rdi. + mov rdi, 1 + + ; Compute message length in rdx. + ; Find NUL byte address of message using rcx. Start from message address rsi. + ; Then rdx <- rcx - rsi. + mov rcx, rsi + dec rcx again: - inc edi - cmp byte [edi], 0 + inc rcx + cmp byte [rcx], 0 jne again - mov edx, edi - sub edx, ecx + mov rdx, rcx + sub rdx, rsi ; Do system call. - int 0x80 + syscall leave ret diff --git a/labs/lab-11/tasks/single-link/support/diy/c-standalone/start.asm b/labs/lab-11/tasks/single-link/support/diy/c-standalone/start.asm index c384e42b4..3fecb86b0 100644 --- a/labs/lab-11/tasks/single-link/support/diy/c-standalone/start.asm +++ b/labs/lab-11/tasks/single-link/support/diy/c-standalone/start.asm @@ -4,8 +4,8 @@ extern main section .text -; /usr/include/x86_64-linux-gnu/asm/unistd_32.h -__NR_exit equ 1 +; /usr/include/x86_64-linux-gnu/asm/unistd_64.h +__NR_exit equ 60 global _start @@ -14,17 +14,17 @@ _start: ; Call __NR_exit(main_return_value) (system call). ; - ; Use x86 Linux system call convention. - ; https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#Making_a_system_call + ; Use x86_64 Linux system call convention. + ; https://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-and-user-space-f ; - ; ebx stores the first system call argument. - ; eax stores the system call id. + ; rdi stores the first system call argument. + ; rax stores the system call id. - ; eax is main return value. Store it in ebx. - mov ebx, eax + ; eax is main return value. Store it in rdi. + mov rdi, rax ; Store the exit system call id in rax. - mov eax, __NR_exit + mov rax, __NR_exit ; Do system call. - int 0x80 + syscall diff --git a/labs/lab-11/tasks/single-link/support/example/a-dynamic/Makefile b/labs/lab-11/tasks/single-link/support/example/a-dynamic/Makefile index 8285dc71d..c650d0584 100644 --- a/labs/lab-11/tasks/single-link/support/example/a-dynamic/Makefile +++ b/labs/lab-11/tasks/single-link/support/example/a-dynamic/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all @@ -9,7 +9,7 @@ all: hello hello: hello.o $(CC) $(LDFLAGS) -o $@ $^ - #ld -dynamic-linker /lib/ld-linux.so.2 -m elf_i386 -o $@ /usr/lib32/crt1.o /usr/lib32/crti.o $^ -lc /usr/lib32/crtn.o + # ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -m elf_x86_64 -o $@ /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o $^ -lc /usr/lib/x86_64-linux-gnu/crtn.o hello.o: hello.c diff --git a/labs/lab-11/tasks/single-link/support/example/b-static/Makefile b/labs/lab-11/tasks/single-link/support/example/b-static/Makefile index 0877f1ec1..9d9f5b4e9 100644 --- a/labs/lab-11/tasks/single-link/support/example/b-static/Makefile +++ b/labs/lab-11/tasks/single-link/support/example/b-static/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -static -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -static -no-pie .DEFAULT_GOAL: all @@ -8,8 +8,8 @@ LDFLAGS = -static -no-pie -m32 all: hello hello: hello.o - $(CC) $(LDFLAGS) -o $@ $^ - #ld -static -m elf_i386 -L/usr/lib/gcc/x86_64-linux-gnu/13/32/ -o $@ /usr/lib32/crt1.o /usr/lib32/crti.o $^ --start-group -lc -lgcc -lgcc_eh --end-group /usr/lib32/crtn.o + #$(CC) $(LDFLAGS) -o $@ $^ + ld -static -m elf_x86_64 -L/usr/lib/gcc/x86_64-linux-gnu/13 -o $@ /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o $^ --start-group -lc -lgcc -lgcc_eh --end-group /usr/lib/x86_64-linux-gnu/crtn.o hello.o: hello.c diff --git a/labs/lab-11/tasks/single-link/support/example/c-standalone/Makefile b/labs/lab-11/tasks/single-link/support/example/c-standalone/Makefile index f1c81d8d0..134f46325 100644 --- a/labs/lab-11/tasks/single-link/support/example/c-standalone/Makefile +++ b/labs/lab-11/tasks/single-link/support/example/c-standalone/Makefile @@ -1,7 +1,7 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -nostdinc -nostdlib -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -nostdinc -nostdlib -no-pie AS = nasm -ASFLAGS = -f elf32 +ASFLAGS = -f elf64 .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/single-link/support/example/c-standalone/puts.asm b/labs/lab-11/tasks/single-link/support/example/c-standalone/puts.asm index 57a09dedf..11c5327d6 100644 --- a/labs/lab-11/tasks/single-link/support/example/c-standalone/puts.asm +++ b/labs/lab-11/tasks/single-link/support/example/c-standalone/puts.asm @@ -4,48 +4,49 @@ section .text global puts -; /usr/include/x86_64-linux-gnu/asm/unistd_32.h -__NR_write equ 4 +; /usr/include/x86_64-linux-gnu/asm/unistd_64.h +__NR_write equ 1 ; Argument is message to be printed to standard output. puts: - push ebp - mov ebp, esp + push rbp + mov rbp, rsp ; Call __NR_write(1, message, message_len) (system call). ; - ; Use x86 Linux system call convention. - ; https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#Making_a_system_call + ; Use x86_64 Linux system call convention. + ; https://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-and-user-space-f ; - ; eax stores the system call id. - ; ebx stores the first system call argument: 1 (standard output). - ; ecx stores the second system call argument: message. - ; edc stores the third system call argument: message length. + ; rax stores the system call id. + ; rdi stores the first system call argument: 1 (standard output). + ; rsi stores the second system call argument: message. + ; rdx stores the third system call argument: message length. - ; Store the write system call id in eax. - mov eax, __NR_write + ; Store the write system call id in rax. + mov rax, __NR_write - ; Store standard output file descriptor (1) in ebx. - mov ebx, 1 - ; Store function argument (message) in ecx. - mov ecx, [ebp + 8] + ; Store function argument (message) in rsi. + mov rsi, rdi - ; Compute message length in edx. - ; Find NUL byte address of message using edi. Start from message address (ecx). - ; Then edx <- edi - ecx. - mov edi, ecx - dec edi + ; Store standard output file descriptor (1) in rdi. + mov rdi, 1 + + ; Compute message length in rdx. + ; Find NUL byte address of message using rcx. Start from message address rsi. + ; Then rdx <- rcx - rsi. + mov rcx, rsi + dec rcx again: - inc edi - cmp byte [edi], 0 + inc rcx + cmp byte [rcx], 0 jne again - mov edx, edi - sub edx, ecx + mov rdx, rcx + sub rdx, rsi ; Do system call. - int 0x80 + syscall leave ret diff --git a/labs/lab-11/tasks/single-link/support/example/c-standalone/start.asm b/labs/lab-11/tasks/single-link/support/example/c-standalone/start.asm index c384e42b4..3fecb86b0 100644 --- a/labs/lab-11/tasks/single-link/support/example/c-standalone/start.asm +++ b/labs/lab-11/tasks/single-link/support/example/c-standalone/start.asm @@ -4,8 +4,8 @@ extern main section .text -; /usr/include/x86_64-linux-gnu/asm/unistd_32.h -__NR_exit equ 1 +; /usr/include/x86_64-linux-gnu/asm/unistd_64.h +__NR_exit equ 60 global _start @@ -14,17 +14,17 @@ _start: ; Call __NR_exit(main_return_value) (system call). ; - ; Use x86 Linux system call convention. - ; https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#Making_a_system_call + ; Use x86_64 Linux system call convention. + ; https://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-and-user-space-f ; - ; ebx stores the first system call argument. - ; eax stores the system call id. + ; rdi stores the first system call argument. + ; rax stores the system call id. - ; eax is main return value. Store it in ebx. - mov ebx, eax + ; eax is main return value. Store it in rdi. + mov rdi, rax ; Store the exit system call id in rax. - mov eax, __NR_exit + mov rax, __NR_exit ; Do system call. - int 0x80 + syscall diff --git a/labs/lab-11/tasks/var-func-fix/support/Makefile b/labs/lab-11/tasks/var-func-fix/support/Makefile index c54ce55d0..1281e13f1 100644 --- a/labs/lab-11/tasks/var-func-fix/support/Makefile +++ b/labs/lab-11/tasks/var-func-fix/support/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/vars-obs/.vscode/c_cpp_properties.json b/labs/lab-11/tasks/vars-obs/.vscode/c_cpp_properties.json deleted file mode 100644 index c2098a2d0..000000000 --- a/labs/lab-11/tasks/vars-obs/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "configurations": [ - { - "name": "linux-gcc-x64", - "includePath": [ - "${workspaceFolder}/**" - ], - "compilerPath": "/usr/bin/gcc", - "cStandard": "${default}", - "cppStandard": "${default}", - "intelliSenseMode": "linux-gcc-x64", - "compilerArgs": [ - "" - ] - } - ], - "version": 4 -} \ No newline at end of file diff --git a/labs/lab-11/tasks/vars-obs/.vscode/launch.json b/labs/lab-11/tasks/vars-obs/.vscode/launch.json deleted file mode 100644 index d81881d4c..000000000 --- a/labs/lab-11/tasks/vars-obs/.vscode/launch.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "C/C++ Runner: Debug Session", - "type": "cppdbg", - "request": "launch", - "args": [], - "stopAtEntry": false, - "externalConsole": false, - "cwd": "/home/matei/IOCLA/hardware-software-interface/labs/lab-11/tasks/vars-obs/support", - "program": "/home/matei/IOCLA/hardware-software-interface/labs/lab-11/tasks/vars-obs/support/build/Debug/outDebug", - "MIMode": "gdb", - "miDebuggerPath": "gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - } - ] -} \ No newline at end of file diff --git a/labs/lab-11/tasks/vars-obs/.vscode/settings.json b/labs/lab-11/tasks/vars-obs/.vscode/settings.json deleted file mode 100644 index 3e5eb956e..000000000 --- a/labs/lab-11/tasks/vars-obs/.vscode/settings.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "C_Cpp_Runner.cCompilerPath": "gcc", - "C_Cpp_Runner.cppCompilerPath": "g++", - "C_Cpp_Runner.debuggerPath": "gdb", - "C_Cpp_Runner.cStandard": "", - "C_Cpp_Runner.cppStandard": "", - "C_Cpp_Runner.msvcBatchPath": "", - "C_Cpp_Runner.useMsvc": false, - "C_Cpp_Runner.warnings": [ - "-Wall", - "-Wextra", - "-Wpedantic", - "-Wshadow", - "-Wformat=2", - "-Wcast-align", - "-Wconversion", - "-Wsign-conversion", - "-Wnull-dereference" - ], - "C_Cpp_Runner.msvcWarnings": [ - "/W4", - "/permissive-", - "/w14242", - "/w14287", - "/w14296", - "/w14311", - "/w14826", - "/w44062", - "/w44242", - "/w14905", - "/w14906", - "/w14263", - "/w44265", - "/w14928" - ], - "C_Cpp_Runner.enableWarnings": true, - "C_Cpp_Runner.warningsAsError": false, - "C_Cpp_Runner.compilerArgs": [], - "C_Cpp_Runner.linkerArgs": [], - "C_Cpp_Runner.includePaths": [], - "C_Cpp_Runner.includeSearch": [ - "*", - "**/*" - ], - "C_Cpp_Runner.excludeSearch": [ - "**/build", - "**/build/**", - "**/.*", - "**/.*/**", - "**/.vscode", - "**/.vscode/**" - ], - "C_Cpp_Runner.useAddressSanitizer": false, - "C_Cpp_Runner.useUndefinedSanitizer": false, - "C_Cpp_Runner.useLeakSanitizer": false, - "C_Cpp_Runner.showCompilationTime": false, - "C_Cpp_Runner.useLinkTimeOptimization": false, - "C_Cpp_Runner.msvcSecureNoWarnings": false -} \ No newline at end of file diff --git a/labs/lab-11/tasks/vars-obs/solution/checker.o b/labs/lab-11/tasks/vars-obs/solution/checker.o index 7dc7fe4c6..de68869c5 100644 Binary files a/labs/lab-11/tasks/vars-obs/solution/checker.o and b/labs/lab-11/tasks/vars-obs/solution/checker.o differ diff --git a/labs/lab-11/tasks/vars-obs/support/Makefile b/labs/lab-11/tasks/vars-obs/support/Makefile index afc59aa46..5ff0db13d 100644 --- a/labs/lab-11/tasks/vars-obs/support/Makefile +++ b/labs/lab-11/tasks/vars-obs/support/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -fno-PIC -m32 -g -LDFLAGS = -no-pie -m32 +CFLAGS = -fno-PIC -g +LDFLAGS = -no-pie .DEFAULT_GOAL: all diff --git a/labs/lab-11/tasks/vars-obs/support/checker.o b/labs/lab-11/tasks/vars-obs/support/checker.o index 7dc7fe4c6..de68869c5 100644 Binary files a/labs/lab-11/tasks/vars-obs/support/checker.o and b/labs/lab-11/tasks/vars-obs/support/checker.o differ