29

Interesting bug hunt!

Got called in because a co-team had a strange bug and couldn't make sense of it. After a compiler update, things had stopped working.

They had already hunted down the bug to something equivalent to the screenshot and put a breakpoint on the if-statement. The memory window showed the memory content, and it was indeed 42. However, the debugger would still jump over do_stuff(), both in single step and when setting a breakpoint on the function call. Very unusual, but the rest worked.

Looking closer, I noticed that the pointer's content was an odd number, but was supposed to be of type uint32_t *. So I dug out the controller's manual and looked up the instruction set what it would do with a 32 bit load from an unaligned address: the most braindead thing possible, it would just ignore the lowest two address bits. So the actual load happened from a different address, that's why the comparison failed.

I think the debugger fetched the memory content bytewise because that would work for any kind of data structure with only one code path, that's how it bypassed the alignment issue. Nice pitfall!

Investigating further why the pointer was off, it turned out that it pointed into an underlying array of type char. The offset into the array was correctly divisible by 4, but the beginning had no alignment, and a char array doesn't need one. I checked the mapfiles and indeed, the old compiler had put the array to a 4 byte boundary and the new one didn't.

Sure enough, after giving the array a 4 byte alignment directive, the code worked as intended.

Comments
  • 1
    @irene because it is 42.
  • 3
    @irene Oh Irene, silly question 😄
  • 2
    That is really interesting. Thanks for sharing.
  • 0
    @irene OP said something about opcodes, so i'm assuming it's C boiling down to Assembly. On most architectures, declaring and using variables slow things down, ESPECIALLY when using C instead of Assembly. It's better to use constants and hard memory pointers.

    (This is all true of the Z80/GBz80 and of what I've seen with x86.)
  • 0
    @irene i've seen some compilers that have to look up every variable from a table... AFTER assembly has been reached.

    compilers can be wack yo
  • 1
  • 0
    @irene actually, the reason is "something equivalent" to the screenshot. I left out "looking up the define in the source text and comparing it to the memory content" because it wasn't a relevant step. And 42 is the answer anyway.
  • 0
    @hash-table yep, that attribute-aligned way like in the SO question, but using 4 of course.
Add Comment