x64 Assembly: Multithreading from Scratch Part 1: Hello World!
Tags: tutorial, multithreading, assembly
Published: 2023-03-15 14:21:13


## Content of The Series
In this series of tutorials we will look into how to implement multithreading techniques in x86_64 Assembly from scratch without using the standard library.

Part 1: Hello World! from Scratch
   Writing a simple program to print to the standard output from two different processes.

[Part 2: Threads](https://dev.to/ahmedwadod/x64-assembly-multithreading-from-scratch-part-2-threads-11g1)
   Implementing a functional way of creating and waiting for threads.

Part 3: Shared Memory
   Implementing memory managment functions and showing how we can share memory between threads.

Part 4: Mutexes
   Implementing a mutex to control memory access between threads.


## Prerequisites
To follow along with this tutorial you wil need a basic knowledge in assembly. You will also need a linux environment as we will implement everything from scratch using only the kernel system calls.

## Tools
In this tutorial I'm using the [GNU](https://gcc.gnu.org/) assembler `gas` with `intel syntax` along the the GNU linker.

Using other assemblers like `MASM` or `NASM` should be fine, just make sure to review the differences in syntax between the code shown in this series of tutorials with the code you intend to write, take a look at this [link](https://stackoverflow.com/tags/intel-syntax/info).

## Hello, World! from scratch
Since we won't be linking against the standard library (Linking with `-nostdlib`) we will not have access to commonly used functions like `printf` or `malloc` and will have to implement them from scratch using the [x86_64 linux system calls](https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md).

### Project structure
```
- Project Folder
  |
  |-- lib/   # The directory for our reusable code
  |   |
  |   |-- util.asm   # utilities (print, malloc, etc..)
  |   |....
  |
  |-- hello_world/
      |
      | -- main.asm # Our code for Part 1
    
```

### Print function
Now we will need to implement a simple function to print to the standard output `STDOUT` using the [write](https://man7.org/linux/man-pages/man2/write.2.html) system call.

Our `print` function will take 2 arguments: `ptr` and `len`

`ptr`: The address of the string (passed in register `rdi`)
`len`: The length of the string (passed in register `rsi`)

```
# lib/util.asm


.intel_syntax
.section .text

# print function that takes (ptr, len)
# as arguments (rdi, rsi)
.global print
print:
	mov %rdx, %rsi # move the length to rdx
	mov %rsi, %rdi # move the pointer (rdi) to rsi
	mov %rax, 0x01 # write syscall on x64 Linux
	mov %rdi, 0x01 # STDOUT file descriptor
	syscall
	ret


```

This function that we defined above takes 2 arguments using the `rdi` and `rsi` registers then rearrange the registers to call the `write` syscall on the `STDOUT` file descriptor.

Now lets assemble it with the following command:
```
$ as lib/util.asm -o lib/util.o
```

Now we should have an object file `lib/util.o` that we will link against to have access to our utility functions.

### `fork` System Call
Throughout this series we will be making heavy use of the [fork](https://man7.org/linux/man-pages/man2/fork.2.html) system call which basically tell the OS to create a child process of the current process, this child process is an exact copy of the parent and it will start executing from the next instruction of the parent.

### Writing the Code

```
# hello_world/main.asm


.intel_syntax
.global _start
.section .text

.extern print # Use our print function

_start:
	# Call the 'fork' syscall
	mov %rax, 0x39 # fork syscall on x64 Linux
	syscall
	cmp %rax, 0 # 'fork' will return 0 to the child process
	je _child

_parent:
	# Print 'Hello from parent!'
	lea %rdi, [%rip + msg1]
	mov %rsi, OFFSET msg1len
	call print
	jmp _exit

_child:
	# Print 'Hello from child!'
	lea %rdi, [%rip + msg2]
	mov %rsi, OFFSET msg2len
	call print

_exit:
	# Call the 'exit' syscall
	mov %rax, 0x3c # exit syscall on x64 Linux
	mov %rdi, 0x0 # Exit code
	syscall

.section .data
msg1:
	.ascii "Hello from parent!\n"
	msg1len = . - msg1
msg2:
	.ascii "Hello from child!\n"
	msg2len = . - msg2
	
```

In the code above we have 2 different functions: `_parent` which will print `Hello from parent!` and exit, and function `_child` which will print `Hello from child!` and exit. On the main function `_start` we will make a `fork` system call which will create a copy of the current process and store a value in the register `rax`, this value will be `0` on the child process and will be the PID of the child process on the parent, so we will make use of this information. We will compare the value of register `rax` to zero, if it is zero we will jump to the `_child` function, if not, we will continue to execute the `_parent` function.

### Assembling and Linking
We will assemble the code and link it with `util.o` object file containing the `print` function without linking the standard library using the following commads:
```
$ as hello_world/main.asm -o hello_world/hello_world.o
$ ld hello_world/hello_world.o lib/util.o -o hello_world/hello_world.elf -nostdlib
```

If everything goes well we should have an executable `hello_world/hello_world.elf` and if we execute it we should see the output:
```
$ ./hello_world/hello_world.elf
Hello from parent!
Hello from child!
$ 
```

Great! we have successfully written an x86_64 assembly program to execute 2 deifferent code pieces from two different threads. Next, we will look into how to implement a more functional solution to make it easy create and wait for threads.

The code for this tutorial is available in this [repository](https://github.com/ahmedwadod/x64_asm_multithreading_from_scratch). The code repository will be updated with every new part of the series.