Posts Tagged ‘recover mcu program’
ATmega861PA: Why the Addresses Move When the Code Grows
An image is laid out in order, and there is no padding between sections by default. Insert an instruction anywhere before the end and everything after it moves by the size of the insertion. Nothing is wrong; the addresses are simply different.
That has a consequence for anyone comparing two builds or matching a disassembly against documentation: an address is not an identity. A function at a given address in one build may be something else entirely in the next.
Where the layout comes from
The linker places sections in a fixed order and, within each, in the order the objects were supplied. Four sections are typical:
| Section | Content | Moves when |
|---|---|---|
| Vectors | Jump table | Rarely; fixed size |
| Text | Code | Any code change before it |
| Data initialisers | Initial values for SRAM | Code size changes |
| Constants | Tables, strings | Code size changes |
The first row is the only stable one. Everything after the vector table is positioned relative to the code that precedes it, which is why an address found in one build rarely means anything in another.
What this does to a comparison
Two builds of the same product, one with a bug fix, produce images that differ from the point of the change onward. A byte-wise diff reports the whole tail as different, which is useless.
The way to compare meaningfully is to compare structure rather than addresses:
- Align on the vector table, which does not move.
- Align on string and constant blocks, which keep their content even when their address changes.
- Compare instruction sequences rather than byte offsets.
With those three anchors, a diff of two builds becomes a list of actual changes rather than a list of shifted bytes.
What it means for a reconstruction
A reconstruction is a description tied to addresses: “the routine at this address does that”. If the description is ever applied to a different build, the addresses are wrong and the description misleads.
The fix is to record structure alongside address. A function described as “the third handler in the table, calling the shift routine twice” survives a rebuild; one described as “the code at address 0x04A2” does not.
That is not pedantry. Products get reflashed, revisions get rebuilt, and a report that only works for one binary has a short useful life.
The array limit and what happens near it
As an image approaches the size of its array, two things occur:
Constants get pushed. Tables that were inline move to the end, because there is no room between functions. The image’s tail becomes a data region rather than code, which changes how it should be read.
Jump distances grow. Branches that were short become long, which changes instruction sizes and therefore shifts everything again. On a part where the boundary between relative and absolute jumps is near, a small addition can convert a whole region.
Both effects are visible if the image is examined near its limit, and both are invisible if it is not. Knowing which case applies sets the expectation for how much of the tail will disassemble as code.
Reading an image near its limit
- Read the array, verified page by page.
- Decode the vector table; confirm the reset target.
- Read forward from the target until instructions stop making sense.
- From that point, treat content as data and check for structure: fixed stride, printable text.
- Record the boundary and describe the function in structural terms.
Step 4 is the one that prevents the classic error of disassembling a constant table as code. Tables have regular stride and no valid opcodes; code has irregular structure and valid opcodes. The transition is usually obvious once it is being looked for.
Related notes
- ATmega861PV and version strings — an anchor that survives relocation.
- ATmega261A and small images — the case where the whole image fits without pressure.
- ATtiny461 and instruction width — how jump size changes shift a layout.
FAQ
Do addresses stay the same across builds? Only the vector table. Everything after it moves with code size.
Can a diff be done without alignment? It can be run. The output will be mostly false differences.
Why do constants end up at the end? Because the linker places them after code when there is no room inline.
Is a disassembly of the data region harmful? It produces plausible nonsense that can be mistaken for obfuscation.
Conclusion
Addresses are a property of a build, not of a program. Comparing builds needs structural anchors, and describing a program needs structural language, because the numbers change the moment anyone edits the source. Recording structure is what makes an analysis survive the next release.