> Abstractions. They don’t exist in assembler. Memory is read from registers and the stack and written to registers and the stack.
[...] But my application-coded debugging brain kept looking at abstractions like they would provide all the answers. I rationally knew that the abstractions wouldn’t help, but my instincts hadn’t gotten the message.
That feels like the wrong takeaway for me. Assembly still runs on abstractions: You're ignoring the CPU microcode, the physical interaction with memory modules, etc. If the CPU communicates with other devices, this has more similarities with network calls and calling the "high level APIs" of those devices. For user space assembly, the entire kernel is abstracted away and system calls are essentially "stdlib functions".
So I think it has a different execution model, something like "everything is addressable byte strings and operates on addressable byte strings". But you can get that execution model occasionally in high-level languages as well, e.g. in file handling or networking code. (Or in entire languages built around it like brainfuck)
So I think assembly is just located a few levels lower in the abstraction pile, but it's still abstractions all the way down...
I think lots of commenters are being unintentionally pedantic. It’s clear that there are different types of abstractions one is concerned with when programming at the application level. Yes, it’s all abstractions on top of subatomic probability fields, but no one is thinking at even the atomic level when they step through the machine code execution with a debugger.
Asm is simple enough that "mental execution" is far easier, if more tedious, than in HLLs, especially those with lots of hidden side-effects. The concept of a function doesn't really exist (and this is even more true when working with RISCs that don't have implicit stack management instructions), and although there are instructions that make it more convenient to do HLL-style call and return, it's just as easy to write a "function" that returns to its caller's caller (or further), switches to a different task or thread, etc. If you're going to learn Asm, then IMHO you should try to exploit this freedom in control flow and leverage the rest of the machine's ability, since merely being a human compiler is not particularly enlightening nor useful.
I agree entirely, great insight! I'd like to add that assembly is best enjoyed in a suitable environment for it, where "APIs" are just memory writes and interrupts. Game programming for the C64 is way more fun than dealing with linux syscalls, for example. A lower level interface enables all the fun assembler tricks, and limited resources require you to be clever.
Then you goto hell…
> Asm is simple enough that "mental execution" is far easier, if more tedious, than in HLLs
Ya totally I can also keep 32 registers, a memory file, and stack pointer all in my head at once ...fellow human... (In 2026 I might actually be an LLM in which I really can keep all that context in my "head"!)
8 registers are sufficient; if you forget what one holds, looking up at the previous write to it is enough.
Contrast this with trying to figure out all the nested implicit actions that a single line of some HLL like C++ will do.
there's an interesting new API skill for the human cortex v1.0, that allows for a much larger context window, it's called pen and paper.
For real! I occasionally write assembly because, for some reason, I kind of enjoy it, and also to keep my brain sharp. But yes, there is no way I could do it without pencil and paper (unless I’m on a site like CPUlator that visually shows everything that’s happening).
Not sure what to take away from this. __abstract works because GCC allows it as an alias to __abstract__, not because parsing the syntax is forgiving.
Abstractions do exist (disagreeing with the single other post in here) and they also exist in most flavours of assembly, because assembly itself is still an abstraction for machine code. A very thin one, sure, but assemblers will generally provide a fair amount of syntactic sugar on top, if you want to make use of it.
Protip: your functions should be padded with instructions that'll trap if you miss a return.
>Protip: your functions should be padded with instructions that'll trap if you miss a return.
Galaxy brained protip: instead of a trap, use return instructions as padding, that way it will just work correctly!
Some compilers insert trap instructions when aligning the start of functions, mainly because the empty space has to be filled with something, and it's better to use a trapping instruction if for some reason this unreachable code is ever jumped to. But if you have to do it manually, it doesn't really help, since it's easier to forget than the return.
Neat. The author is about to stumble onto a secret.
> In Sum#
> Abstractions. They don’t exist in assembler. Memory is read from registers and the stack and written to registers and the stack.
Abstractions do not exist periodi. They are patterns, but these patterns aren’t isolated from each other. This is how a hacker is born, through this deconstruction.
It’s just like the fact that electrons and protons don’t really exist. but the patterns in energy gradients are consistent enough to give them names and model their relationship. There are still points where these models fail (QM and GR at plank scale, or just the classical-quantum boundaries). It’s gradients all the way down, and even that is an abstraction layer.
Equipped with this understanding you can make an exploit like Rowhammer.
Abstractions pretty much exist and in assembler they matter even more because the code is so terse.
Now, there are abstractions (which exist in your brain, whatever the language) and tools to represent abstractions (in ASM you've got macros and JSR/RET; both pretty leaky).
That wasn’t my point. You almost got there when you wrote “there are abstractions (which exist in you brain, whatever the language)”. And your point on leaky abstractions is exactly the indication that they exist in your mind, not out there.
My point is that we settle with what we see for convenience/utility and base our models on that. We build real things on top of these models. Then the result meets reality. If only that transition were so simple.
When an effect jumps unexpectedly between layers of abstraction we call it an abstraction leak. As you mentioned. The correct response is to re-examine these leaks and make other frameworks to cover the edge cases, not to blame the world.
Hackers actively seek these “leaks” by suspending assumptions that arise out of the abstractions that humans tend to rely on.
I’m not surprised that my OP got downvoted. It’s can be very upsetting when one’s conceptual frameworks are challenged. Well, if they can’t parse it, they don’t deserve it. Keeps me in the market.
> Abstractions. They don’t exist in assembler. Memory is read from registers and the stack and written to registers and the stack.
[...] But my application-coded debugging brain kept looking at abstractions like they would provide all the answers. I rationally knew that the abstractions wouldn’t help, but my instincts hadn’t gotten the message.
That feels like the wrong takeaway for me. Assembly still runs on abstractions: You're ignoring the CPU microcode, the physical interaction with memory modules, etc. If the CPU communicates with other devices, this has more similarities with network calls and calling the "high level APIs" of those devices. For user space assembly, the entire kernel is abstracted away and system calls are essentially "stdlib functions".
So I think it has a different execution model, something like "everything is addressable byte strings and operates on addressable byte strings". But you can get that execution model occasionally in high-level languages as well, e.g. in file handling or networking code. (Or in entire languages built around it like brainfuck)
So I think assembly is just located a few levels lower in the abstraction pile, but it's still abstractions all the way down...
I think lots of commenters are being unintentionally pedantic. It’s clear that there are different types of abstractions one is concerned with when programming at the application level. Yes, it’s all abstractions on top of subatomic probability fields, but no one is thinking at even the atomic level when they step through the machine code execution with a debugger.
Asm is simple enough that "mental execution" is far easier, if more tedious, than in HLLs, especially those with lots of hidden side-effects. The concept of a function doesn't really exist (and this is even more true when working with RISCs that don't have implicit stack management instructions), and although there are instructions that make it more convenient to do HLL-style call and return, it's just as easy to write a "function" that returns to its caller's caller (or further), switches to a different task or thread, etc. If you're going to learn Asm, then IMHO you should try to exploit this freedom in control flow and leverage the rest of the machine's ability, since merely being a human compiler is not particularly enlightening nor useful.
I agree entirely, great insight! I'd like to add that assembly is best enjoyed in a suitable environment for it, where "APIs" are just memory writes and interrupts. Game programming for the C64 is way more fun than dealing with linux syscalls, for example. A lower level interface enables all the fun assembler tricks, and limited resources require you to be clever.
Then you goto hell…
> Asm is simple enough that "mental execution" is far easier, if more tedious, than in HLLs
Ya totally I can also keep 32 registers, a memory file, and stack pointer all in my head at once ...fellow human... (In 2026 I might actually be an LLM in which I really can keep all that context in my "head"!)
8 registers are sufficient; if you forget what one holds, looking up at the previous write to it is enough.
Contrast this with trying to figure out all the nested implicit actions that a single line of some HLL like C++ will do.
there's an interesting new API skill for the human cortex v1.0, that allows for a much larger context window, it's called pen and paper.
For real! I occasionally write assembly because, for some reason, I kind of enjoy it, and also to keep my brain sharp. But yes, there is no way I could do it without pencil and paper (unless I’m on a site like CPUlator that visually shows everything that’s happening).
Not sure what to take away from this. __abstract works because GCC allows it as an alias to __abstract__, not because parsing the syntax is forgiving.
Abstractions do exist (disagreeing with the single other post in here) and they also exist in most flavours of assembly, because assembly itself is still an abstraction for machine code. A very thin one, sure, but assemblers will generally provide a fair amount of syntactic sugar on top, if you want to make use of it.
Protip: your functions should be padded with instructions that'll trap if you miss a return.
>Protip: your functions should be padded with instructions that'll trap if you miss a return.
Galaxy brained protip: instead of a trap, use return instructions as padding, that way it will just work correctly!
Some compilers insert trap instructions when aligning the start of functions, mainly because the empty space has to be filled with something, and it's better to use a trapping instruction if for some reason this unreachable code is ever jumped to. But if you have to do it manually, it doesn't really help, since it's easier to forget than the return.
Neat. The author is about to stumble onto a secret.
> In Sum# > Abstractions. They don’t exist in assembler. Memory is read from registers and the stack and written to registers and the stack.
Abstractions do not exist periodi. They are patterns, but these patterns aren’t isolated from each other. This is how a hacker is born, through this deconstruction.
It’s just like the fact that electrons and protons don’t really exist. but the patterns in energy gradients are consistent enough to give them names and model their relationship. There are still points where these models fail (QM and GR at plank scale, or just the classical-quantum boundaries). It’s gradients all the way down, and even that is an abstraction layer.
Equipped with this understanding you can make an exploit like Rowhammer.
https://en.wikipedia.org/wiki/Row_hammer
Abstractions pretty much exist and in assembler they matter even more because the code is so terse.
Now, there are abstractions (which exist in your brain, whatever the language) and tools to represent abstractions (in ASM you've got macros and JSR/RET; both pretty leaky).
That wasn’t my point. You almost got there when you wrote “there are abstractions (which exist in you brain, whatever the language)”. And your point on leaky abstractions is exactly the indication that they exist in your mind, not out there.
My point is that we settle with what we see for convenience/utility and base our models on that. We build real things on top of these models. Then the result meets reality. If only that transition were so simple.
When an effect jumps unexpectedly between layers of abstraction we call it an abstraction leak. As you mentioned. The correct response is to re-examine these leaks and make other frameworks to cover the edge cases, not to blame the world.
Hackers actively seek these “leaks” by suspending assumptions that arise out of the abstractions that humans tend to rely on.
I’m not surprised that my OP got downvoted. It’s can be very upsetting when one’s conceptual frameworks are challenged. Well, if they can’t parse it, they don’t deserve it. Keeps me in the market.