9.7 KiB
- 1. x86 Assembly
- 2. Overview
- 3. What does this program do?
- 4. How does this program work?
- 5. What does this program do?
- 6. It does something.
- 7. It definitely does something.
- 8. JavaScript
- 9. C
- 10. Compile and Execute
- 11. write
- 12. System Calls
- 13. write
- 14. write
- 15. Back to Nothing
- 16. Exit Code
- 17. exit_group
- 18. Exit
- 19. Assemble, Link, Execute, Trace
- 20. x86 Assembly
- 21. Use Cases
- 22. Instructions
- 23. Instruction Cycle
- 24. Registers
- 25. Exit++
- 26. Sections
- 27. Hello World
- 28. Control Flow
- 29. Stack
- 30. Functions
- 31. Convention
- 32. Stack Arguments
- 33. Safety and Security
- 34. Review
- 35. Conclusion
- 36. References / Further Reading
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
- Follow a trail of questions into the depths of the machine.
- Peel away some abstractions on the way.
- Gloss over a bunch of stuff (we have an hour, not a semester).
- 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.
- Intel x86 Developer Manual is ~5,000 pages long, plus errata.
lscpu
21. Use Cases
- Low-level programming (micro-controllers, operating systems)
-
Resource Constrained Hardware
- Apollo 11 Guidance Computer Assembly (1969)
- Pokemon Red/Blue Assembly (1996, AA batteries)
-
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 ↑ |
27. Hello World
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.
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.
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.
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
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.
36. References / Further Reading
- Crash Course: Computer Science
- Davy Wybiral, Intro to x86 Assembly Language
- Jennifer Rexford, Princeton COS 217: Introduction to Programming Systems
- Structured Program Theorem
- GDB
