slides.org (10006B)
1 * 1. x86 Assembly 2 3 - David Wen Riccardi-Zhu 4 - Senior Software Engineer @ Good Uncle 5 - dwrz@dwrz.net 6 7 Slides and code: https://github.com/dwrz/talks/x86-assembly. 8 9 * 2. Overview 10 11 1. Follow a trail of questions into the depths of the machine. 12 2. Peel away some abstractions on the way. 13 3. Gloss over a bunch of stuff (we have an hour, not a semester). 14 4. Surface (hopefully) with a better understanding of how computers work. 15 16 * 3. What does this program do? 17 18 #+begin_src bash :results raw 19 node -e "console.log('Hello, World');" 20 #+end_src 21 22 * 4. How does this program /work/? 23 24 #+begin_src bash :results raw 25 node -e "console.log('Hello, World');" 26 #+end_src 27 28 How does "Hello World" get onto the terminal? 29 30 * 5. What does this program do? 31 Let's try something simpler. 32 33 #+begin_src bash 34 node -e "" 35 #+end_src 36 37 Does this do /anything/? 38 39 * 6. It does /something/. 40 41 We can tell, because it takes a few milliseconds to execute. 42 43 #+begin_src bash :results raw 44 time node -e "" 45 #+end_src 46 47 #+begin_src bash :results raw 48 time node -e "console.log('Hello, World');" 49 #+end_src 50 51 * 7. It /definitely/ does /something/. 52 53 We can tell, because it's making system calls. 54 55 #+begin_src bash :results raw 56 strace node -e "" 57 #+end_src 58 59 Lot of noise for a program that does "nothing"... 60 61 #+begin_src bash :results raw 62 strace node -e "console.log('Hello, World');" 63 #+end_src 64 65 * 8. JavaScript 66 We've actually started with something difficult. 67 68 JavaScript is an interpreted language. 69 70 #+begin_src js 71 const myProgram = '0 + 1'.replace('+', '-'); 72 73 const result = eval(myProgram); 74 75 console.log(result); // -1 76 #+end_src 77 78 #+RESULTS: 79 80 * 9. C 81 Let's use a simpler example. 82 83 C is compiled. 84 85 We create our own program, rather than use a program that interprets strings. 86 87 #+begin_src C :results raw 88 #include <stdio.h> 89 90 int main(void) { 91 printf("Hello, World\n"); 92 } 93 #+end_src 94 95 * 10. Compile and Execute 96 97 #+begin_src bash 98 gcc hello-world.c -o hello-world 99 #+end_src 100 101 #+begin_src bash 102 ./hello-world 103 #+end_src 104 105 #+begin_src bash 106 strace ./hello-world 107 #+end_src 108 109 * 11. write 110 This seems familiar: 111 #+begin_src C 112 write(1, "Hello, World\n", 13Hello, World 113 ) = 13 114 #+end_src 115 116 It's in our node program, too: 117 #+begin_src bash 118 strace node -e "console.log('Hello, World');" 2>&1 | grep "Hello" 119 #+end_src 120 121 /What is this?/ 122 What is ~write~? 123 What is 1? 124 What is 13? 125 126 * 12. System Calls 127 ~strace~ shows us system calls. ~write~ is a system call. 128 129 What is a system call? 130 131 #+begin_src bash 132 man syscalls 133 #+end_src 134 135 #+begin_quote 136 The system call is the fundamental interface between an application and the Linux kernel. 137 #+end_quote 138 139 * 13. write 140 141 #+begin_src bash 142 man 2 write 143 #+end_src 144 145 #+begin_src C 146 write(int fd, const void *buf, size_t count); 147 #+end_src 148 149 #+begin_quote 150 write() writes up to count bytes from the buffer starting at buf to the file referred to by the file descriptor fd. 151 #+end_quote 152 153 * 14. write 154 155 #+begin_src text 156 write(1, "Hello, World\n", 13Hello, World 157 ) = 13 158 #+end_src 159 160 File Descriptor 1 = Standard Out (inherited from terminal process) 161 Hello World = Buffer 162 Count = 13 bytes 163 164 |---+---+---+---+---+---+---+---+---+---+---+---+----| 165 | H | e | l | l | o | , | | W | o | r | l | d | \n | 166 |---+---+---+---+---+---+---+---+---+---+---+---+----| 167 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | 168 |---+---+---+---+---+---+---+---+---+---+---+---+----| 169 170 * 15. Back to Nothing 171 #+begin_src C 172 int main(void) {} 173 #+end_src 174 175 #+begin_src bash 176 gcc exit.c -o exit 177 #+end_src 178 179 #+begin_src bash 180 strace ./exit 181 #+end_src 182 183 * 16. Exit Code 184 In C, the return type prefixes the function. 185 186 ~main~ returns an ~int~; the default is zero (indicating no error). 187 188 #+begin_src C 189 int main(void) { 190 return 1; 191 } 192 #+end_src 193 194 #+begin_src C 195 exit_group(1) 196 #+end_src 197 198 * 17. exit_group 199 #+begin_src bash 200 man 2 exit_group 201 #+end_src 202 203 #+begin_src C 204 void exit_group(int status); 205 #+end_src 206 207 * 18. Exit 208 209 [[file:src/exit/exit.s]] 210 211 * 19. Assemble, Link, Execute, Trace 212 #+begin_src bash 213 as exit.s -o exit.o 214 215 ld exit.o -o exit 216 217 ./exit 218 219 strace ./exit 220 #+end_src 221 222 * 20. x86 Assembly 223 - Human readable form of machine code. 224 - 1-to-1 mapping between one assembly instruction and one CPU instruction. 225 - Hardware specific: e.g., x86 Assembly differs from ARM Assembly. 226 - Often OS specific --> Linux System Calls != BSD, Mac, Windows system calls. 227 - Different syntax formats: ATT, Intel. 228 - Examples use ATT syntax. 229 - What instructions? Need to consult hardware manual. 230 - [[https://software.intel.com/content/www/us/en/develop/articles/intel-sdm.html][Intel x86 Developer Manual]] is ~5,000 pages long, plus errata. 231 232 #+begin_src bash 233 lscpu 234 #+end_src 235 236 * 21. Use Cases 237 - Low-level programming (micro-controllers, operating systems) 238 - Resource Constrained Hardware 239 - [[https://github.com/chrislgarry/Apollo-11][Apollo 11 Guidance Computer Assembly]] (1969) 240 - [[https://github.com/pret/pokered][Pokemon Red/Blue Assembly]] (1996, AA batteries) 241 - Performance 242 - Less runtime overhead (system calls, etc) 243 - Better code than compiler (harder to do these days) 244 - Control 245 - Instructions not available in higher level language 246 - Reverse Engineering 247 248 #+begin_src bash 249 hexdump -C exit 250 #+end_src 251 252 #+begin_src bash 253 objdump -D exit 254 #+end_src 255 256 * 22. Instructions 257 - Represented by numbers (opcodes). 258 - Describe an operation the CPU should perform, e.g.: 259 - Move data in and out of registers 260 - Modify register contents 261 - Modify stack 262 - Control program flow 263 264 * 23. Instruction Cycle 265 - On every tick of its internal clock, the CPU: 266 - *Fetches* the next instruction. 267 - *Decodes* it (what operation, on what operands). 268 - *Executes* the instruction. 269 - Increments the instruction pointer. 270 271 * 24. Registers 272 - Storage on the CPU (fastest storage). 273 - Act as a scratchpad -- temporary variables. 274 - General Purpose Registers 275 - RAX, RBX, RCX 276 - RSP, RBP (stack pointer, stack frame pointer) 277 - Special Purpose Registers 278 - RIP (Instruction Pointer) 279 - RFLAGS (negative, zero, etc.) 280 - It's possible to use just a portion of the register. 281 #+begin_src text 282 |__64__|__56__|__48__|__40__|__32__|__24__|__16__|__8___| 283 |__________________________RAX__________________________| 284 |xxxxxxxxxxxxxxxxxxxxxxxxxxx|____________EAX____________| 285 |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|_____AX______| 286 |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|__AH__|__AL__| 287 #+end_src 288 289 * 25. Exit++ 290 291 [[file:src/math/math.s]] 292 293 * 26. Sections 294 What happens when we run a program? A few things... 295 296 One of them: the kernel loads the executable into memory. 297 298 Assembly sections refer to executable's memory layout: 299 300 |-----------| 301 | TEXT | --> Code (instructions) 302 | RODATA | --> const str = "Hello, World"; 303 | DATA | --> var str = "Hello, World"; 304 | BSS | --> var str; 305 | ↓ HEAP ↓ | --> (for traditional C) 306 | | 307 | ↑ STACK ↑ | 308 |-----------| 309 310 * 27. Hello World 311 312 [[file:src/hello-world/hello-world.s]] 313 314 * 28. Control Flow 315 Programs are either sequential, looping, or branching. 316 317 - CPU sets FLAGS register after instruction: e.g., result is zero, negative. 318 - Jump to code based on the state of FLAGS. 319 - Jump changes instruction pointer. 320 321 [[file:src/control-flow/control-flow.s]] 322 323 * 29. Stack 324 RSP register points to the top of the stack. 325 RBP register (typically) points to the (current) base of the stack. 326 Together, they form a stack frame. 327 328 Instructions: 329 - ~push~ :: decrements RSP, moves bytes onto stack. 330 - ~pop~ :: increments RSP, moves stack bytes into register. 331 332 [[file:src/stack/stack.s]] 333 334 * 30. Functions 335 Why do we use functions? Same reasons apply in Assembly: 336 - Reuse 337 - Organization 338 - Abstraction 339 - Splitting work 340 341 Problems: 342 - How to pass arguments? 343 - Registers -- which ones? 344 - Stack -- what order? 345 - Whose job is it to preserve or clean up registers? Caller? Callee? 346 - E.g., caller saves a value in %rbx to use after function returns. 347 - Callee uses %rbx and overwrites that value. 348 - How to pass return value(s)? 349 350 * 31. Convention 351 Which side of the street should we drive on? 352 Either way works, both are used in practice. 353 What matters is agreement on an approach. 354 355 [[file:static/convention.jpg]] 356 357 System V AMD64 ABI is calling convention for Unix x64 systems: 358 - Some registers must be saved by the caller, so callee can use them. 359 - Some registers must be saved by callee, if the plan to use them later. 360 - Some registers used to pass arguments. 361 - Stack used to pass extra or large arguments. 362 - RAX and RDX are used for return values. 363 364 * 32. Stack Arguments 365 366 [[file:src/func/func.s]] 367 368 Each row is 8 bytes (64 bits). 369 |----------------+-----------+----------------| 370 | Address | Data | Stack Pointers | 371 |----------------+-----------+----------------| 372 | 0x7fffffffe8f8 | | | 373 | 0x7fffffffe900 | 0x0 (rbp) | | 374 | 0x7fffffffe908 | 0x401002 | | 375 | 0x7fffffffe910 | 3 | ←rsp | 376 |----------------+-----------+----------------| 377 ←rbp 378 379 * 33. Safety and Security 380 381 [[file:src/safety/safety.s]] 382 383 * 34. Review 384 Where we started: 385 386 #+begin_src bash 387 node -e "" 388 #+end_src 389 390 - CPU processes instructions 391 - Uses registers and memory (stack) 392 - Control flow with jump instruction and flags register 393 - Functions 394 - System Calls 395 - Comparison with Compiled and Interpreted Languages 396 - Tradeoffs 397 398 * 35. Conclusion 399 - Insight into how computers work. 400 - Appreciation for higher level, and work done to get us here. 401 - A platform to better understand things like functions, closures, APIs, pass by reference and pass by value, performance. 402 - A few mysteries to leave you curious... 403 404 * 36. References / Further Reading 405 406 - [[https://www.youtube.com/watch?v=tpIctyqH29Q&list=PL8dPuuaLjXtNlUrzyH5r6jN9ulIgZBpdo][Crash Course: Computer Science]] 407 - Davy Wybiral, [[https://www.youtube.com/playlist?list=PLmxT2pVYo5LB5EzTPZGfFN0c2GDiSXgQe][Intro to x86 Assembly Language]] 408 - [[https://www.gnu.org/software/gdb/][GDB]] 409 - Jennifer Rexford, [[https://www.cs.princeton.edu/courses/archive/fall05/cos217/][Princeton COS 217: Introduction to Programming Systems]] 410 - [[https://en.wikipedia.org/wiki/Structured_program_theorem][Structured Program Theorem]]