Compare commits

...
10 Commits
Author SHA1 Message Date
dwrz 4cd9ba10fd Add README 2026-10-27 14:05:49 +00:00
dwrz 31d9e78f3c Update Go module and repository URLs 2026-10-27 12:41:07 +00:00
dwrz cedd1d98ec Update x86 assembly slides 2026-10-27 12:41:07 +00:00
dwrz 06a0189e2e Update assembly x86 safety example 2026-10-27 12:41:07 +00:00
dwrz 9c947ed91e Add x86 assembly slide image 2026-10-27 12:41:06 +00:00
dwrz 695a79e106 Document x86 assembly examples 2026-10-27 12:41:06 +00:00
dwrz 724b6988db Update x86 assembly slides 2026-10-27 12:41:06 +00:00
dwrz b4196dcfaa Update x86 assembly slides 2026-10-27 12:41:06 +00:00
dwrz dcacadffc0 Add x86-assembly 2026-10-27 12:41:06 +00:00
dwrz 42c2105b16 Update concurrency slides 2026-10-27 12:41:06 +00:00
17 changed files with 823 additions and 87 deletions
+58
View File
@@ -0,0 +1,58 @@
* Talks
Slides and example code from two technical presentations given in 2020.
Slides use [[https://orgmode.org/][org-mode,]] and were presented in Emacs with [[https://github.com/rlister/org-present][org-present]].
** Concurrency (June 2020)
Introduction to concurrency.
*** Slides
- Sequential vs. concurrent execution, I/O latency, interrupts, and cooperative vs. preemptive scheduling
- Concurrency vs. parallelism, processes vs. threads, and language runtimes (C, JavaScript event loop, Go scheduler)
- Shared memory synchronization vs. message passing, backpressure, and distributed data races
*** Examples
- temperatures: concurrent HTTP requests fetching city temperatures from OpenWeatherMap.
- mutex: data race demonstration and mutual exclusion on a shared bank account balance (~go run -race ./main.go~).
- csp: pipeline of goroutines and channels reading a CSV of zip codes, querying weather data with a concurrency limiter, and displaying a live top-ten leaderboard.
To run the examples:
#+begin_src shell
# Mutex example (with and without race detector):
cd concurrency/cmd/mutex
go run ./main.go
go run -race ./main.go
# Weather examples (require an OpenWeatherMap API key):
export OPEN_WEATHER_MAP_API_KEY="..."
cd concurrency/cmd/temperatures && go run .
cd concurrency/cmd/csp && go run .
#+end_src
** x86-64 Assembly (November 2020)
A brief tour of x86-64 assembly on Linux.
** Slides
- Compilation pipeline, ISAs, CPU registers, and Linux system calls
- Instructions, addressing modes, control flow, and memory safety
- Virtual memory, the stack, function prologues/epilogues, and the System V AMD64 ABI calling convention
** Examples
- exit: minimal program invoking the ~exit~ system call.
- math: basic arithmetic instructions (~mov~, ~add~, ~sub~, ~mul~, ~div~), inspected via exit status.
- control-flow: loops, comparisons (~cmp~), and conditional/unconditional jumps (~je~, ~jmp~).
- hello-world: writing a string from the ~.data~ section to stdout with the ~write~ system call.
- safety: out-of-bounds memory overwrite across adjacent ~.data~ buffers.
- stack: pushing bytes onto the stack and printing from ~%rsp~.
- func: function call, stack frame setup (~%rbp~ / ~%rsp~), and return.
To run the examples:
#+begin_src shell
as exit.s -o exit.o
ld exit.o -o exit
./exit
echo $?
#+end_src
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"sync"
"time"
"github.com/dwrz/talks/concurrency/pkg/weather"
"code.chimeric.al/dwrz/talks/concurrency/pkg/weather"
)
func getZipTemp(
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"fmt"
"sync"
"github.com/dwrz/talks/concurrency/pkg/weather"
"code.chimeric.al/dwrz/talks/concurrency/pkg/weather"
)
const (
+2 -2
View File
@@ -1,3 +1,3 @@
module github.com/dwrz/talks/concurrency
module code.chimeric.al/dwrz/talks/concurrency
go 1.14
go 1.27.3
+96 -83
View File
@@ -4,7 +4,7 @@
- Senior Software Engineer @ Good Uncle
- dwrz@dwrz.net
Slides and code: https://github.com/dwrz/talks/concurrency.
Slides and code: https://code.chimeric.al/dwrz/talks.
* 2. Why
@@ -61,7 +61,7 @@ A second is a duration, not an instant.
Two things can happen in the same second and not be simultaneous.
In computer science, this nuance matters.
With computers, this nuance matters.
* 9. Time
@@ -98,10 +98,12 @@ Source: [[https://www.prowesscorp.com/computer-latency-at-a-human-scale/]]
* 12. Cooking Recipes
- Step-by-step
- Concurrent
- Pre-heat oven
- Boil water
- Timers
- Concurrent:
- Handle other tasks while:
- Preheating oven
- Boiling water
- Waiting for timers
- Switching back to a task when it is ready for more work.
* 13. Multitasking
@@ -117,24 +119,28 @@ Computers can "simultaneously" work on:
Combination of:
- Operating System *Scheduler*
- Hardware (/interrupts/)
- Keyboard
- Filesystem
- Network Card
- Timer
* 17. Cooperative Scheduling
* 16. Cooperative Scheduling
[[file:images/cooperative-scheduling.jpg]]
* 18. Preemptive Scheduling
* 17. Preemptive Scheduling
[[file:images/preemptive-scheduling.jpg]]
* 19. Preemptive Scheduling
* 18. Preemptive Scheduling
[[file:images/preemptive-scheduling-tree.jpg]]
* 20. Multi Core
* 19. Multi Core
[[file:images/preemptive-scheduling-multi-core.jpg]]
* 21. Concurrency versus Parallelism
* 20. Concurrency versus Parallelism
- Parallelism :: performing computations at the same time.
- Multiple cores.
- Multiples computers.
- Multiple computers.
- Kind of like having multiple cooks in a kitchen.
- Can do different things at the same time.
- Can do one job faster (peeling a lot of potatoes).
@@ -142,13 +148,14 @@ Combination of:
- Concurrency :: performing computations over inter-leaving, non-sequential time periods.
- Single or multi-core.
* 22. Processes and Threads
* 21. Processes and Threads
A little more nuance.
- Different /recipes/
- Different /parts/ of a recipe
- Process :: a program in execution.
- Unique resources (memory, code, files, etc.).
- Start off with one thread.
- Can /fork/.
- Communicate via inter-process communication (IPC).
@@ -157,17 +164,17 @@ A little more nuance.
- Share resources ∴ less resource intensive.
- Can communicate via shared resources.
* 23. Processes and Threads
* 22. Processes and Threads
[[file:images/process-vs-thread.jpg]]
Source: https://stackoverflow.com/questions/16354460/forking-vs-threading/16354658
* 24. htop
* 23. htop
Press ~H~ to show threads.
* 25. Languages and Runtimes
* 24. Languages and Runtimes
- ~C~, ~C++~, ~Rust~ allow for fine-grained control of processes and threads.
#+begin_src C :output raw
@@ -180,7 +187,7 @@ int main() {
}
#+end_src
* 26. JavaScript
* 25. JavaScript
- Single-threaded (for application code).
- Runtimes implement concurrency via an event loop.
@@ -188,42 +195,7 @@ int main() {
Source: [[https://medium.com/preezma/node-js-event-loop-architecture-go-deeper-node-core-c96b4cec7aa4]]
* 27. Go
Runtime multi-threading, multiplexes goroutines onto threads.
[[file:images/go-scheduler.png]]
Source: https://freethreads.net/2019/01/23/go-runtime-scheduler-design-internals/
* 28. Quick Review
- Sequential versus Concurrent Steps
- Compare with recipes.
- Concurrency versus Parallelism
- Parallelism: multiple cooks, simultaneously /doing/.
- Concurrency: one or more cooks, simultaneously /dealing/ with many tasks.
- Operating System Scheduler
- Processes and Threads
- Languages and Runtimes
* 29. Part 2: Using Concurrency
Typical use cases:
- Network I/O
- Requests between clients and servers.
- Requests to a database.
- Requests between microservices.
- Requests to external APIs.
- User interfaces
- Capturing user input
- Rendering output
- Similar, but independent computations.
- Solving problems that are best expressed with concurrency.
* 30. JavaScript
* 26. Aside: Event Loop
What is the output of the following? Why?
#+begin_src javascript
@@ -238,14 +210,49 @@ setTimeout(() => console.log('2'), 0);
console.log('end');
#+end_src
* 31. Go Examples
* 27. Go
*Runtime scheduler* manages multiple threads.
[[file:images/go-scheduler.png]]
Source: https://freethreads.net/2019/01/23/go-runtime-scheduler-design-internals/
* 28. Quick Review
- Sequential versus Concurrent Steps
- Compare with recipes.
- Concurrency versus Parallelism
- Parallelism: multiple cooks, simultaneously /doing/.
- Concurrency: one or more cooks, /dealing/ with many tasks.
- Operating System Scheduler
- Processes and Threads
- Languages and Runtimes
* 29. Part 2: Using Concurrency
Typical use cases:
- Network I/O between:
- Clients and servers.
- Services and database(s).
- Microservices.
- Service and external APIs.
- User interfaces
- Capturing user input
- Rendering output
- Similar, but independent computations.
- Solving problems that are best expressed with concurrency.
* 30. Go Examples
1. Shared Memory with Mutex (Mutual Exclusion)
- Pitfall: synchronization, debugging.
2. Message Passing (Channels and Goroutines)
- Pitfall: resources usage, complexity.
* 32. Mutex Example
2. Message Passing (Channels and Goroutines)
- Pitfall: resource usage, complexity.
* 31. Mutex Example
Concurrent updates to a bank account balance.
Code:
@@ -253,31 +260,37 @@ Code:
Run: [[file:cmd/mutex/run.sh]]
- Without mutex.
- With mutex.
* 33. Data Races
* 32. Data Races
[[file:images/data-race.jpg]]
* 34. Mutex
* 33. Mutex
/Mutual Exclusion/
- Purpose: protect a critical section of code.
- Effect: serialization.
* 35. Race Detector
Run: [[file:cmd/mutex/run.sh]]
- With mutex.
* 34. Race Detector
How can we be sure?
Race detector can give us /some/ confidence (but no guarantees).
- Without mutex.
- With mutex.
* 36. Tradeoffs: Validating Correctness, Testing, Debugging
* 35. Tradeoffs: Validating Correctness, Testing, Debugging
- Non-deterministic behavior
- Works fine on one run, fatal bug in another.
- Up to how the scheduler(s) order things at execution time.
- Concealed bugs
* 37. Message Passing
* 36. Message Passing
Proverbs:
#+begin_quote
@@ -286,7 +299,7 @@ Don't communicate by sharing memory, share memory by communicating.
Channels orchestrate, mutexes serialize.
#+end_quote
* 38. Requirements
* 37. Program Requirements
- Read CSV of zip codes
- 50 in our example
@@ -298,27 +311,27 @@ Channels orchestrate, mutexes serialize.
- Display running list of top ten temperatures.
* 39. Concurrency Hallmarks
* 38. Concurrency Hallmarks
- Repeatedly processing similar data (lines of CSV)
- Network I/O
- UI I/O
* 40. Message Passing Example
* 39. Message Passing Example
Run: [[file:cmd/csp/run.sh]]
* 41. Code Walkthrough
* 40. Code Walkthrough
[[file:cmd/csp/main.go]]
* 42. Channels and Goroutines
* 41. Channels and Goroutines
[[file:images/message-passing-example-diagram.jpg]]
* 43. Channels and Goroutines
* 42. Channels and Goroutines
[[file:images/go-scheduler.jpg]]
* 44. Scaling
* 43. Scaling
What if:
- We wanted to process the entire list of 42,000 zip codes?
@@ -326,7 +339,7 @@ What if:
Could we just get rid of the limiter?
* 45. Resources
* 44. Resources
Probably not, because:
@@ -339,12 +352,12 @@ Probably not, because:
- Better data structures and algorithms could help here.
- Goroutines are cheap, but not free.
* 46. Possible Solutions
* 45. Possible Solutions
- Use a semaphore to limit concurrency.
- Buffers
* 47. Designing and Managing Concurrency
* 46. Designing and Managing Concurrency
- Size of input matters.
- Resources
@@ -353,7 +366,7 @@ Probably not, because:
- Backpressure
- Complexity
* 48. Quick Review
* 47. Quick Review
Techniques for implementing and managing concurrency:
- Event loop
@@ -367,7 +380,7 @@ Concurrency Pitfalls:
- Complexity
- Need to think differently about concurrent code.
* 49. Part 3: Infrastructure Concurrency
* 48. Part 3: Infrastructure Concurrency
Concurrent applications running concurrently on distributed computers.
@@ -376,20 +389,20 @@ Concurrent applications running concurrently on distributed computers.
- Databases
- Host and Network Infrastructure
* 50. Example
* 49. Example
[[file:images/infrastructure-data-race.jpg]]
* 51. Review
* 50. Review
- What concurrency is
- Sequential versus concurrent
- Comparison with recipes
- Comparison with recipes and cooks
- Compared to parallelism
- How it is implemented
- Operating System Scheduler and Interrupts
- Languages and Runtimes
* 52. Review
* 51. Review
- When, why, and how to use it
- I/O + UI
@@ -398,16 +411,16 @@ Concurrent applications running concurrently on distributed computers.
- Pitfalls
- Synchronization
- Resources Usage
- Resource Usage
- Debugging
- Complexity
* 53. Review
* 52. Review
- Infrastructure
- Data Race
* 54. Conclusion
* 53. Conclusion
- Goals:
- Give you a rough sense of what concurrency is.
@@ -421,7 +434,7 @@ Concurrent applications running concurrently on distributed computers.
- Tradeoffs and economics (resources, costs, developer time).
- Learning is continuous...
* 55. Further Reading
* 54. Further Reading
- [[https://slikts.github.io/concurrency-glossary/][Concurrency Glossary]]
- [[https://blog.golang.org/waza-talk][Concurrency is not parallelism]]
+410
View File
@@ -0,0 +1,410 @@
* 1. x86 Assembly
- David Wen Riccardi-Zhu
- Senior Software Engineer @ Good Uncle
- dwrz@dwrz.net
Slides and code: https://code.chimeric.al/dwrz/talks.
* 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?
#+begin_src bash :results raw
node -e "console.log('Hello, World');"
#+end_src
* 4. How does this program /work/?
#+begin_src bash :results raw
node -e "console.log('Hello, World');"
#+end_src
How does "Hello World" get onto the terminal?
* 5. What does this program do?
Let's try something simpler.
#+begin_src bash
node -e ""
#+end_src
Does this do /anything/?
* 6. It does /something/.
We can tell, because it takes a few milliseconds to execute.
#+begin_src bash :results raw
time node -e ""
#+end_src
#+begin_src bash :results raw
time node -e "console.log('Hello, World');"
#+end_src
* 7. It /definitely/ does /something/.
We can tell, because it's making system calls.
#+begin_src bash :results raw
strace node -e ""
#+end_src
Lot of noise for a program that does "nothing"...
#+begin_src bash :results raw
strace node -e "console.log('Hello, World');"
#+end_src
* 8. JavaScript
We've actually started with something difficult.
JavaScript is an interpreted language.
#+begin_src js
const myProgram = '0 + 1'.replace('+', '-');
const result = eval(myProgram);
console.log(result); // -1
#+end_src
#+RESULTS:
* 9. C
Let's use a simpler example.
C is compiled.
We create our own program, rather than use a program that interprets strings.
#+begin_src C :results raw
#include <stdio.h>
int main(void) {
printf("Hello, World\n");
}
#+end_src
* 10. Compile and Execute
#+begin_src bash
gcc hello-world.c -o hello-world
#+end_src
#+begin_src bash
./hello-world
#+end_src
#+begin_src bash
strace ./hello-world
#+end_src
* 11. write
This seems familiar:
#+begin_src C
write(1, "Hello, World\n", 13Hello, World
) = 13
#+end_src
It's in our node program, too:
#+begin_src bash
strace node -e "console.log('Hello, World');" 2>&1 | grep "Hello"
#+end_src
/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?
#+begin_src bash
man syscalls
#+end_src
#+begin_quote
The system call is the fundamental interface between an application and the Linux kernel.
#+end_quote
* 13. write
#+begin_src bash
man 2 write
#+end_src
#+begin_src C
write(int fd, const void *buf, size_t count);
#+end_src
#+begin_quote
write() writes up to count bytes from the buffer starting at buf to the file referred to by the file descriptor fd.
#+end_quote
* 14. write
#+begin_src text
write(1, "Hello, World\n", 13Hello, World
) = 13
#+end_src
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
#+begin_src C
int main(void) {}
#+end_src
#+begin_src bash
gcc exit.c -o exit
#+end_src
#+begin_src bash
strace ./exit
#+end_src
* 16. Exit Code
In C, the return type prefixes the function.
~main~ returns an ~int~; the default is zero (indicating no error).
#+begin_src C
int main(void) {
return 1;
}
#+end_src
#+begin_src C
exit_group(1)
#+end_src
* 17. exit_group
#+begin_src bash
man 2 exit_group
#+end_src
#+begin_src C
void exit_group(int status);
#+end_src
* 18. Exit
[[file:src/exit/exit.s]]
* 19. Assemble, Link, Execute, Trace
#+begin_src bash
as exit.s -o exit.o
ld exit.o -o exit
./exit
strace ./exit
#+end_src
* 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.
- [[https://software.intel.com/content/www/us/en/develop/articles/intel-sdm.html][Intel x86 Developer Manual]] is ~5,000 pages long, plus errata.
#+begin_src bash
lscpu
#+end_src
* 21. Use Cases
- Low-level programming (micro-controllers, operating systems)
- Resource Constrained Hardware
- [[https://github.com/chrislgarry/Apollo-11][Apollo 11 Guidance Computer Assembly]] (1969)
- [[https://github.com/pret/pokered][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
#+begin_src bash
hexdump -C exit
#+end_src
#+begin_src bash
objdump -D exit
#+end_src
* 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.
#+begin_src text
|__64__|__56__|__48__|__40__|__32__|__24__|__16__|__8___|
|__________________________RAX__________________________|
|xxxxxxxxxxxxxxxxxxxxxxxxxxx|____________EAX____________|
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|_____AX______|
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|__AH__|__AL__|
#+end_src
* 25. Exit++
[[file:src/math/math.s]]
* 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
[[file:src/hello-world/hello-world.s]]
* 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.
[[file: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.
[[file: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.
[[file: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 they 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
[[file: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
[[file:src/safety/safety.s]]
* 34. Review
Where we started:
#+begin_src bash
node -e ""
#+end_src
- 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.
- A few mysteries to leave you curious...
* 36. References / Further Reading
- [[https://www.youtube.com/watch?v=tpIctyqH29Q&list=PL8dPuuaLjXtNlUrzyH5r6jN9ulIgZBpdo][Crash Course: Computer Science]]
- Davy Wybiral, [[https://www.youtube.com/playlist?list=PLmxT2pVYo5LB5EzTPZGfFN0c2GDiSXgQe][Intro to x86 Assembly Language]]
- [[https://www.gnu.org/software/gdb/][GDB]]
- Jennifer Rexford, [[https://www.cs.princeton.edu/courses/archive/fall05/cos217/][Princeton COS 217: Introduction to Programming Systems]]
- [[https://en.wikipedia.org/wiki/Structured_program_theorem][Structured Program Theorem]]
@@ -0,0 +1,23 @@
.data
stop:
.int 3
.text
.global _start
_start:
mov $0, %rcx
loop:
cmp stop, %rcx
# If equal, change RIP to exit.
je exit
inc %rcx
# Unconditionally change RIP to loop.
jmp loop
exit:
# Try this: comment out the following three instructions.
# What happens if you assemble and link? Why?
# Think about RIP and the Fetch-Decode-Execute cycle.
mov $231, %rax
mov %rcx, %rdi # Exit code -- let's see the value of %rcx.
syscall
+3
View File
@@ -0,0 +1,3 @@
int main(void) {
// return 1;
}
+16
View File
@@ -0,0 +1,16 @@
.text
.global _start
_start:
# Store system call 1 in the EAX register.
# mov = instruction
# $1, %eax = operands
# %eax = register
mov $1, %eax
# Store the exit code 0 in the EBX register.
mov $0, %ebx
# Call interrupt handler 0x80 (128) -- on Linux, this is the kernel.
# Kernel will take over and see 1 in %eax, 0 in %ebx.
int $0x80
+41
View File
@@ -0,0 +1,41 @@
.text
.global _start
_start:
# Store 3 on the stack.
push $3
# call stores return address on the stack.
call square
# Use the return value, in %rax, as our exit status number.
mov %rax, %rdi
mov $231, %rax
syscall
# square expects an integer on the stack, 16 bytes down from RBP.
# The squared value is returned on %rax.
square:
# Function Prologue --> {
push %rbp
mov %rsp, %rbp
# Retrieve the parameter from the stack.
# 8 bytes down from RSP is the return address from square.
# 8 bytes down from that is the parameter to multiply.
mov 16(%rbp), %rax
mul %rax
# Function Epilogue --> }
mov %rbp, %rsp
pop %rbp
# ret pops and returns to address on stack.
ret
# |----------------+-----------+----------------|
# | Address | Data | Stack Pointers |
# |----------------+-----------+----------------|
# | 0x7fffffffe8f8 | | |
# | 0x7fffffffe900 | 0x0 (rbp) | |
# | 0x7fffffffe908 | 0x401002 | |
# | 0x7fffffffe910 | 3 | ←rsp |
# |----------------+-----------+----------------|
# ←rbp
@@ -0,0 +1,5 @@
#include <stdio.h>
int main(void) {
printf("Hello, World\n");
}
@@ -0,0 +1,17 @@
.data
str:
.ascii "Hello, World\n"
.text
.global _start
_start:
mov $1, %rax # 64-bit system call number for sys_write.
mov $1, %rdi # stdout.
mov $str, %rsi # Address of the buffer.
mov $13, %rdx # Number of bytes to write.
syscall
mov $231, %rax # 64-bit system call number for exit_group.
mov $0, %rdi # Exit code.
syscall
+23
View File
@@ -0,0 +1,23 @@
package main
import (
"fmt"
"strings"
"time"
)
func main() {
now := time.Now()
for i := 0; i < 1000; i++ {
fmt.Println(i)
}
fmt.Printf("took %v\n", time.Since(now))
now = time.Now()
var str strings.Builder
for i := 0; i < 1000; i++ {
str.WriteString(fmt.Sprintf("%d\n", i))
}
fmt.Println(str.String())
fmt.Printf("took %v\n", time.Since(now))
}
+32
View File
@@ -0,0 +1,32 @@
.text
.global _start
_start:
mov $0, %rax # rax = 0
inc %rax # rax++
dec %rax # rax--
add $7, %rax # rax += 7
sub $5, %rax # rax -= 5
# At this point, rax = 2.
# Multiplication cannot use immediate addressing.
# So we store the multiplier in %rbx.
# mul assumes the multiplicand is in %rax.
# The product is also stored in %rax.
mov $12, %rbx
mul %rbx # rax *= 12
# rax = 24.
# Division also cannot use immediate addressing.
# Store the divisor in %rbx.
# div assumes the dividend is in %rax.
# The quotient is also stored in %rax.
mov $3, %rbx
div %rbx
# rax = 8
# Let's use this as our exit code.
# We're using 64-bits now -- the exit code should be in %rdi.
mov %rax, %rdi
mov $231, %rax
syscall
+47
View File
@@ -0,0 +1,47 @@
.data
romeo:
.ascii "What's in a name? That which we call a rose,\n"
juliet:
.ascii "By any other name would smell as sweet.\n"
.ascii "\n"
stop:
# Try changing this number. What happens? Why?
# Hint: try 0, 1, 11, 12, 21, 22.
.int 12
.text
.global _start
_start:
# Print the string before it's messed up.
mov $1, %rax # sys_write.
mov $1, %rdi # stdout
mov $romeo, %rsi # address
mov $86, %rdx # count bytes
syscall
# Setup.
lea romeo, %rax # Load the starting address of string into %rax.
mov $0xb98c9ff0, %ebx # 🌹 = b98c9ff0 (4 bytes)
mov $0, %rcx # Set the initial counter.
# Overwrite the strings with 🌹.
# Increment the address in %rax by four bytes on each iteration.
loop:
mov %ebx, (%rax) # Copy
inc %rcx # increment our counter
add $4, %rax # Try changing this to sub. What happens? Why?
cmp stop, %rcx
jne loop
# Print the string after it's been messed up.
# NB: nothing stopped us from overwriting past the "romeo" string.
mov $1, %rax # sys_write
mov $1, %rdi # stdout
mov $romeo, %rsi # address
mov $85, %rdx # count bytes
syscall
mov $231, %rax # 64-bit system call number for exit_group.
mov $0, %rdi # Exit code.
syscall # Call the kernel.
+48
View File
@@ -0,0 +1,48 @@
.text
.global _start
_start:
# Push some 64-bit integers onto the stack.
push $3
push $2
push $1
# Let's print "Hey\n", using the stack.
# man ascii
# |---------+----+-----+-----+----|
# | ASCII | H | e | y | \n |
# |---------+----+-----+-----+----|
# | Decimal | 72 | 101 | 121 | 10 |
# |---------+----+-----+-----+----|
# | Hex | 48 | 65 | 79 | 0A |
# |---------+----+-----+-----+----|
# x86 is little-endian: it stores the least significant byte
# of a multi-byte value at the lowest memory address.
# Since the write system call reads memory from low to high address,
# we put the first character ('H' = 0x48) in the least significant byte.
# <-- high low -->
# \n y e H
push $0x000000000A796548
# %rsp points to the top of the stack (the lowest address of our push,
# where 'H' is stored).
# We can pass this address to the kernel.
# write will start at 'H', and work up to '\n'.
mov $1, %rax # write
mov $1, %rdi # stdout
lea (%rsp), %rsi # address of buffer
# Try changing the number of bytes to 9, 17, 25.
# Then call the program with strace.
# You'll be able to see the previous numbers we pushed onto the stack.
mov $4, %rdx # number of bytes
syscall
# Remember, the stack is LIFO.
pop %rdi # pop the "Hey\n" string
pop %rdi # pop 1
pop %rdi # pop 2
# We'll use the last popped value in %rdi as our exit code.
mov $231, %rax
syscall
Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB