As many of you know, I maintain ATTinyCore.
It has bootloaders based on Optiboot. There has been a longstanding problem which I finally decided to come clean about on Github and opened an issue for. Basically, virtual boot (the trick used to get bootloader support on parts without hardware bootloader support) relies on rewriting the vector table as it programs the part. This works great - except for one situation: If the programming is interrupted right at the beginning, when it has erased the first page of flash, but not rewritten it. Then, the modified vector table will be gone, and neither the old sketch, nor the bootloader can run. While this situation sounds unlikely, it has occurred more than once for me.
Anyway - I had an idea to fix this: If the first instruction word of the second page of flash was a JMP (or RJMP, as appropriate for the part in question) pointed at the bootloader, when this happened, execution would just skid along the erased flash (0xFFFF is a no-op), hit the JMP/RJMP, and land in the bootloader so it could be reprogrammed, and all would be well again.
So my question to those who are experts with bullying the linker into doing weird things - how can I make the linker put a specific value into the flash at the start of the second page. This is an address typically a few bytes after the end of the vector table. Does anyone have any thoughts here? I haven't dug this deep into the compiler, so I'm not really sure where to start. Anything that leads me closer to a solution would be awesome.
Here's an assembly listing of one relevant part (a 1634, which has 32-byte pages, but does 4-page erase. Thus, this special JMP instruction needs to go at 0x80 (byte address). I am willing to sacrifice the space between the end of the vector table and 0x80.
Disassembly of section .text:
00000000 <__vectors>:
__vectors():
0: 0c 94 50 00 jmp 0xa0 ; 0xa0 <__ctors_end>
4: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
8: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
c: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
10: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
14: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
18: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
1c: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
20: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
24: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
28: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
2c: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
30: 0c 94 ee 00 jmp 0x1dc ; 0x1dc <__vector_12>
34: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
38: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
3c: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
40: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
44: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
48: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
4c: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
50: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
54: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
58: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
5c: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
60: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
64: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
68: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
6c: 0c 94 62 00 jmp 0xc4 ; 0xc4 <__bad_interrupt>
00000070 <__trampolines_end>:
__trampolines_start():
70: 00 30 cpi r16, 0x00 ; 0
72: 2c 28 or r2, r12
00000074 <port_to_pullup_PGM>:
74: 00 32 2e 2a .2.*
00000078 <port_to_output_PGM>:
78: 00 31 2d 29 .1-)
0000007c <digital_pin_to_port_PGM>:
7c: 02 01 01 01 01 01 01 01 01 03 03 03 03 03 02 02 ................
8c: 02 03 ..
(note that this does not contain the modified reset vector, since it's an assembly listing - the reset vector gets modified by the bootloader as it's uploaded)
ie, what I want to do is to tell the linker to put the stuff that starts at 0x70 20 bytes later, at 0x84, and put my 4-byte rescue-JMP at 0x80... If the vector table and the code weren't in the same section, I think this would be reasonably easy (a few section-start parameters, with the magic JMP in a section starting at 0x80 and the code starting at 0x84) - but I don't know how to get them into separate sections so I can do this...
On the upside - if I'm understanding all this correctly, it shouldn't even require bootloader changes, just some ugly changes to the core...