This project is a functional, albeit simplified, toolchain for assembling and linking x86-64 assembly code into a runnable 64-bit executable for both Linux (ELF) and Windows (PE). It was developed by incrementally refactoring a non-functional assembler into a working tool.
The core components are:
- A two-pass Assembler: Parses a subset of x86-64 assembly syntax and generates the corresponding machine code. It can translate Linux syscalls to Windows API calls when targeting the PE format.
- ELF and PE Generators: Takes the machine code and symbol information from the assembler and packages it into a valid, static executable for the target platform.
- Architecture: x86-64 (64-bit)
- Output Formats:
- Static ELF executable for Linux
- Static PE executable for Windows
- Relocatable object files (
.o)
- Assembly Process: Two-pass assembly to correctly handle forward-referenced labels.
- Addressing Modes:
- Register-to-register (
mov rax, rbx) - Immediate-to-register (
mov rax, 123) - RIP-relative memory addressing (
mov rax, [my_variable])
- Register-to-register (
- Control Flow:
- Unconditional jumps (
jmp) - Conditional jumps (
je,jne,jl,jle,jg,jge,jz,jnz) - Function calls (
call,ret)
- Unconditional jumps (
- Stack Operations:
pushandpopfor 64-bit registers. - Atomic Operations:
lockprefix supported for valid read-modify-write instructions.
The assembler currently supports the following x86-64 instructions:
- Data Transfer:
mov - Arithmetic:
add,sub - Comparison:
cmp - Stack:
push,pop - Control Flow:
jmp,call,ret - Conditional Jumps:
je,jne,jz,jnz,jl,jle,jg,jge - System Calls:
syscall(translated to Windows API calls for PE format)
-
Build the Assembler:
- On Linux:
./build.sh - On Windows:
build.batorbuild_cmake.bat
- On Linux:
-
To Generate Executables (Default): By default, the assembler generates executables for both ELF and PE formats.
# On Linux ./build/assembler <input_file.asm> -o <output_file> # On Windows build\assembler.exe <input_file.asm> -o <output_file>
This will create
<output_file>.elfand<output_file>.exe. -
To Generate a Specific Format: Use the
--formatflag to specify eitherelforpe.# Generate only an ELF file ./build/assembler <input_file.asm> -o <output_file> --format elf # Generate only a PE file ./build/assembler <input_file.asm> -o <output_file> --format pe
-
To Generate a Relocatable Object File: Use the
-cflag to generate a standard object file. The output format depends on the--formatflag.-
For ELF (Linux):
./build/assembler -c --format elf <input_file.asm> -o <output_file.o>
-
For COFF (Windows):
./build/assembler -c --format pe <input_file.asm> -o <output_file.obj>
-
-
Linking with GCC: You can link the generated object file with other object files or libraries using a standard linker like GCC.
-
For ELF (Linux):
gcc -no-pie -nostdlib <output_file.o> -o <final_executable>
-
For COFF (Windows, using MinGW):
x86_64-w64-mingw32-gcc <output_file.obj> -o <final_executable.exe>
-
The following sample program is included in test.asm. It performs simple arithmetic and uses the result as the program's exit code.
section .text
global _start
_start:
mov rax, 10 ; Start with 10
mov rbx, 5 ; Load 5 into another register
add rax, rbx ; Add them, rax = 15
add rax, 7 ; Add an immediate value, rax = 22
mov rdi, rax ; Move the result to rdi for the exit code
mov rax, 60 ; syscall number for exit
syscallWhen targeting Windows, the assembler will automatically translate the syscall instruction into a call to the ExitProcess function from kernel32.dll.