Files
talks/x86-assembly/slides.org
T
2026-10-27 12:41:06 +00:00

9.7 KiB
Raw Blame History

1. x86 Assembly

  • David Wen Riccardi-Zhu
  • Senior Software Engineer @ Good Uncle
  • dwrz@dwrz.net

Slides and code: https://github.com/dwrz/talks/x86-assembly.

2. Overview

  1. Follow a trail of questions into the depths of the machine.
  2. Peel away some abstractions on the way.
  3. Gloss over a bunch of stuff (we have an hour, not a semester).
  4. Surface (hopefully) with a better understanding of how computers work.

3. What does this program do?

node -e "console.log('Hello, World');"

4. How does this program work?

node -e "console.log('Hello, World');"

How does "Hello World" get onto the terminal?

5. What does this program do?

node -e ""

Does it do anything?

6. It does something.

We can tell, because it takes a few milliseconds to execute.

time node -e ""
time node -e "console.log('Hello, World');"

7. It definitely does something.

We can tell, because it's making system calls.

strace node -e ""

Lot of noise for a program that does "nothing"…

strace node -e "console.log('Hello, World');"

8. JavaScript

We've actually started with something difficult.

JavaScript is an interpreted language.

const myProgram = '0 + 1'.replace('+', '-');

const result = eval(myProgram);

console.log(result); // -1

9. C

Let's use a simpler example.

C is compiled.

We create our own program, rather than use a program that interprets strings.

#include <stdio.h>

int main(void) {
	printf("Hello, World\n");
}

10. Compile and Execute

gcc hello-world.c -o hello-world
./hello-world
strace ./hello-world

11. write

This seems familiar:

write(1, "Hello, World\n", 13Hello, World
)          = 13

It's in our node program, too:

strace node -e "console.log('Hello, World');" 2>&1 | grep "Hello"

What is this? What is write? What is 1? What is 13?

12. System Calls

strace shows us system calls. write is a system call.

What is a system call?

man syscalls

The system call is the fundamental interface between an application and the Linux kernel.

13. write

man 2 write
write(int fd, const void *buf, size_t count);

write() writes up to count bytes from the buffer starting at buf to the file referred to by the file descriptor fd.

14. write

write(1, "Hello, World\n", 13Hello, World
)          = 13

File Descriptor 1 = Standard Out (inherited from terminal process) Hello World = Buffer Count = 13 bytes

H e l l o , W o r l d \n
1 2 3 4 5 6 7 8 9 0 1 2 3

15. Back to Nothing

int main(void) {}
gcc exit.c -o exit
strace ./exit

16. Exit Code

In C, the return type prefixes the function.

main returns an int; the default is zero (indicating no error).

int main(void) {
	return 1;
}
exit_group(1)

17. exit_group

man 2 exit_group
void exit_group(int status);

18. Exit

19. Assemble, Link, Execute, Trace

as exit.s -o exit.o

ld exit.o -o exit

./exit

strace ./exit

20. x86 Assembly

  • Human readable form of machine code.
  • 1-to-1 mapping between one assembly instruction and one CPU instruction.
  • Hardware specific: e.g., x86 Assembly differs from ARM Assembly.
  • Often OS specific –> Linux System Calls != BSD, Mac, Windows system calls.
  • Different syntax formats: ATT, Intel.

    • Examples use ATT syntax.
  • What instructions? Need to consult hardware manual.

lscpu

21. Use Cases

  • Low-level programming (micro-controllers, operating systems)
  • Resource Constrained Hardware

  • Performance

    • Less runtime overhead (system calls, etc)
    • Better code than compiler (harder to do these days)
  • Control

    • Instructions not available in higher level language
  • Reverse Engineering
hexdump -C exit
objdump -D exit

22. Instructions

  • Represented by numbers (opcodes).
  • Describe an operation the CPU should perform, e.g.:

    • Move data in and out of registers
    • Modify register contents
    • Modify stack
    • Control program flow

23. Instruction Cycle

  • On every tick of its internal clock, the CPU:

    • Fetches the next instruction.
    • Decodes it (what operation, on what operands).
    • Executes the instruction.
    • Increments the instruction pointer.

24. Registers

  • Storage on the CPU (fastest storage).
  • Act as a scratchpad – temporary variables.
  • General Purpose Registers

    • RAX, RBX, RCX
    • RSP, RBP (stack pointer, stack frame pointer)
  • Special Purpose Registers

    • RIP (Instruction Pointer)
    • RFLAGS (negative, zero, etc.)
  • It's possible to use just a portion of the register.
|__64__|__56__|__48__|__40__|__32__|__24__|__16__|__8___|
|__________________________RAX__________________________|
|xxxxxxxxxxxxxxxxxxxxxxxxxxx|____________EAX____________|
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|_____AX______|
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|__AH__|__AL__|

25. Exit++

26. Sections

What happens when we run a program? A few things…

One of them: the kernel loads the executable into memory.

Assembly sections refer to executable's memory layout:

TEXT –> Code (instructions)
RODATA –> const str = "Hello, World";
DATA –> var str = "Hello, World";
BSS –> var str;
↓ HEAP ↓ –> (for traditional C)
↑ STACK ↑

28. Control Flow

Programs are either sequential, looping, or branching.

  • CPU sets FLAGS register after instruction: e.g., result is zero, negative.
  • Jump to code based on the state of FLAGS.
  • Jump changes instruction pointer.

src/control-flow/control-flow.s

29. Stack

RSP register points to the top of the stack. RBP register (typically) points to the (current) base of the stack. Together, they form a stack frame.

Instructions:

push
decrements RSP, moves bytes onto stack.
pop
increments RSP, moves stack bytes into register.

src/stack/stack.s

30. Functions

Why do we use functions? Same reasons apply in Assembly:

  • Reuse
  • Organization
  • Abstraction
  • Splitting work

Problems:

  • How to pass arguments?

    • Registers – which ones?
    • Stack – what order?
  • Whose job is it to preserve or clean up registers? Caller? Callee?

    • E.g., caller saves a value in %rbx to use after function returns.
    • Callee uses %rbx and overwrites that value.
  • How to pass return value(s)?

31. Convention

Which side of the street should we drive on? Either way works, both are used in practice. What matters is agreement on an approach.

static/convention.jpg

System V AMD64 ABI is calling convention for Unix x64 systems:

  • Some registers must be saved by the caller, so callee can use them.
  • Some registers must be saved by callee, if the plan to use them later.
  • Some registers used to pass arguments.
  • Stack used to pass extra or large arguments.
  • RAX and RDX are used for return values.

32. Stack Arguments

src/func/func.s

Each row is 8 bytes (64 bits).

Address Data Stack Pointers
0x7fffffffe8f8
0x7fffffffe900 0x0 (rbp)
0x7fffffffe908 0x401002
0x7fffffffe910 3 ←rsp

←rbp

33. Safety and Security

34. Review

Where we started:

node -e ""
  • CPU processes instructions
  • Uses registers and memory (stack)
  • Control flow with jump instruction and flags register
  • Functions
  • System Calls
  • Comparison with Compiled and Interpreted Languages
  • Tradeoffs

35. Conclusion

  • Insight into how computers work.
  • Appreciation for higher level, and work done to get us here.
  • A platform to better understand things like functions, closures, APIs, pass by reference and pass by value, performance.