So far, you've been reading from memory addresses that we set up for you.
But your program already has a region of memory ready to go: the stack.
The stack is pointed to by the rsp register, and it contains useful data about how your program was launched and accumulates other data as the program executes.
Let's explore it!
So far, we've been loading data from memory at addresses that we gave you: either hardcoded (like 133700) or stored in a register (like rax).
But there's one region of memory that your program already has access to without any setup from us: the stack.
The stack is a region of allocated memory used as scratch space for your program, and the register rsp (the Stack Pointer) points to the top of it.
We'll explore the stack further later, but for now, the relevant detail is this: when a program starts, rsp points to data that represents the number of command-line arguments passed to the program (including the program name itself).
So if you run:
hacker@dojo:~$ /tmp/your-program hello world
Then the situation looks like this (the actual addresses are an example):
rsp points to the stack, and the value there is 3: one for the program name, one for hello, and one for world.
The stack also has other data, as shown, but we won't worry about that for now!
In this challenge, read the argument count from [rsp] and use it as the exit code of your program.
We'll run your program a few times with different arguments to make sure you're reading it correctly!
In the previous challenge, you read the value at [rsp]: the very top of the stack.
But the stack has lots of data on it, and you can access any of it by adding an offset to rsp.
For example, [rsp+8] reads the 8-byte value right after[rsp], [rsp+16] reads the next one after that, and so on.
In general, [rsp+N] reads memory at the address rsp+N:
You'll notice these offsets go in multiples of 8.
That's because many values on the stack, such as numbers or memory addresses, tend to be 8 bytes (64 bits) wide, so consecutive values are 8 bytes apart.
But this is mostly convention: in reality, the stack, like any other region of memory, is a contiguous region of individual bytes, though for now we'll treat the stack as a bunch of 8-byte/64-bit values.
In this challenge, we've stashed a secret value on the stack at an offset of 128 bytes from rsp.
Read the value at [rsp+128] and use it as the exit code!
You've now read [rsp] to get the argument count, and [rsp+128] to get data at an offset.
Let's look at what else is on the stack!
Right after the argument count, the stack stores pointers to each program argument.
These are addresses stored in memory: [rsp+16] doesn't contain the argument text directly --- it contains the address where that text lives.
For example, if your program is run as /tmp/your-program Hi:
To get the actual argument data, you need to dereference twice: once to get the pointer from the stack, and once to follow it to the data.
mov rdi, [rsp+16] # load the first argument pointer (e.g., 1234000) from the stack
mov rdi, [rdi] # follow the pointer to read the actual data (e.g., "Hi")
In this challenge, your program will be invoked with an argument.
Read the value of the first argument and exit with it!
Why is the stack called a stack?
So far, we've just used it as a region of memory that we read from with mov, like any other memory dereference.
But the stack is meant to be used as, well, a stack of data: you pop values off the top!
The pop instruction is purpose-built for this.
pop rdi does two things:
Reads the value at [rsp] into rdi (just like mov rdi, [rsp]).
Adds 8 to rsp, advancing the stack pointer to the next value.
The value 3 was popped off the top of the stack into rdi, and rsp advanced by 8 bytes to point to the next value.
The data at 1337000 is still there in memory, but as far as the stack is concerned, it's been removed: rsp has moved past it.
In this challenge, use pop to read the argument count and exit with it!