Don't like this style? Click here to change it! blue.css
So based on the green check to red X ratio I'm going to mix mastery task A and B into one brand new lecture.
I'd like to talk about C addresses, pointers, and the all-important stack.
The strategy is to compile some simple programs and reverse engineer them.
Now, I'd like to give you various methods to start to reverse engineer and debug our code:
Let's look at the steps from your conceptualization of a desire to creating something that your silly computer can manage:
Run `gcc -save-temps main.c` The code gets translated into the precompiled `main.i` and the assembly file `main.s` and then the "object file" `main.o` and finally `a.out` the final executable.
There are technical specifics that go on in each of those stages all worthy of contemplation.
Any functions that are external to your code have to be "linked" to your code, and your code itself needs to be translated into specific machine code using a pleasant "tokenization" and substitution process.
The compiler makes optimizations in the code when it can, and you can control the level of that optimization too.
We will talk much more about the object files when we get to the GOT/PLT mastery tasks.
Here is the same program in an interactive online compiler so we can move in real time:
Let's code something, then look at the ASM then change it, etc.
Use Godbolt to inspect the output of this from gcc
Investigate how a loop looks.
Try this again with -O2 optimizations.
OK so we don't really know what these commands are, but we do know what the code was supposed to do.
So there's a bit of a Rosetta stone hidden in that process.
Just under the hood of all Comp. Sci. thinkers there is always the following simple concept of a CPU:
I'm going to try to capture this in parallel to learning C so that as we dive into x86 you can feel the model as your guide.
On some level all computer scientists share the following visual for what a computer really is:
That is, we are given "memory" as a long strip of "boxes" and a "head" that can go to a box and either read the value or write a value.
Everything else is frosting.
The programming language that most exposes this model is Brainf*ck:
Keep this model in the back of your head while we explore pointers in C.
Run this, what is happening to the addresses?
Is we use `-save-temps` or reverse the binary, what do the addresses look like in assembly?
I wrote this a while ago to play with how a stack works for storing variables.
See the Pen Simple x86 CPU by Andy Novocin (@AndyNovo) on CodePen.
OK, so every program that runs is blocks of memory by the operating system.
I like to imagine the stack as a long strip of graph paper.
During a function's execution one arrow "register" the "base pointer" is used like an arrow. It is fixed to an address (on the right side of the graph paper).
The "stack pointer" is another arrow that marks the left side of the stack. The stack pointer is always to the left of the base pointer (a lower value).
This piece of paper is used to store variables, and the addresses are relative to the "base pointer".
Let's take a look at today's problem together in this context.