Debugging Below the Abstraction Line (written by ChatGPT)
(Note: This post was written by ChatGPT, based on my initial rough notes. I, a human, wrote my own version of this post here: How my application programmer instincts failed when debugging assembler. I’m posting them both because it’s fun.)
This post is about two bugs that stopped me cold. They were not clever bugs. They were basic mistakes that took far too long to understand. Each one forced me to confront how much of my programming intuition depends on abstraction, and how little of that intuition survives when you step into kernel code and assembly.
The Missing sret#
The first bug was a naked assembly routine that did not end with an sret. Control fell through into the next function in memory. Those functions happened to be related, which made the behavior feel intentional even though it was wrong.
The symptom was bizarre process behavior. Every time the scheduler switched processes, a new process appeared to be created. I spent hours staring at the process creation code, convinced there was a logic error in the scheduler or fork path. I was debugging at the level of concepts rather than instructions.
What made this hard was that my mental model assumed structure. A function ends where it should end. Control returns because that is what functions do. In naked assembly, none of that is true unless you make it true. The processor does exactly what you told it to do, including continuing into the next block of code if you forget to stop it.
The lesson here was blunt. When working at this level, there is no implied structure. If you forget a return instruction, the machine does not complain. It just keeps going. Debugging means reading the assembly and following execution one instruction at a time, not reasoning about what the code is supposed to represent.
The Packed Struct With the Wrong Types#
The second bug involved a packed struct that was accessed in two different ways. C code accessed it by name. Assembly code accessed it by address and fixed offsets, with the sizes assumed by hand.
The types in the C struct were wrong. The sizes did not match what the assembly expected. Everything looked fine when printed from C. Values appeared correct because they were written and read consistently within the same incorrect layout. The moment assembly touched the data, it broke.
This was difficult because decades of application development teach you that types are descriptive, not structural. You think in terms of getters and setters. The exact width of a field rarely matters because the abstraction hides it.
In kernel work, there is no such abstraction. Memory is the interface. The stack is not a metaphor. If the struct layout in C does not match the offsets in assembly, you are corrupting data, even if all your printfs look reasonable.
The conclusion here is uncomfortable but clear. When C and assembly share data, types are not hints. They are contracts. If the contract is wrong, nothing above it matters.
The Attribute That Compiled Anyway#
There was also a smaller but revealing issue. I wrote an attribute with incorrect syntax. The compiler accepted it, built the code, and ran it. No warning. No error. The attribute simply did nothing.
This reinforced the same theme. Tooling is thinner at this level. You cannot rely on the compiler to save you from yourself. You have to assume that silence means nothing.
What This Taught Me#
I am not an operating systems programmer. I never was, and I likely never will be. My instincts are tuned for systems that reward clean abstractions and punish unnecessary attention to representation.
That said, working through this was valuable. Handling data directly on the stack and watching control flow at the instruction level is powerful. It strips away comforting stories and replaces them with reality.
Debugging a complex application framework often feels like ritual and experience. Debugging assembly is simpler in a harsh way. You read the code. You step through it. You accept that the machine is always right.
The tutorial did its job. It showed me exactly where my habits stop working, and why.