Hi folks,
I spent (far, far too much) time trying to isolate what I reluctantly suspected was a bug in avr-objdump or avr-gcc. The reluctance was warranted, because of course I was wrong.
I noticed there were extra RJMP instructions appended to the end of the interrupt vector table when looking at the output of avr-objdump -S. The extra RJMP instructions appeared in groups of at least 8, and appeared to point to random parts of the main code.
I whittled my 3800 line program down to 33 lines of code that reproduced the problem. Along the way I had thought to myself "Jump tables? Nah...!".
Turns out they are. The test code:
#include#define ONE_MORE int main(void) { uint8_t mode; switch (mode) { case 0: TCNT0 = 0; break; case 1: TCNT0 = 0; break; case 2: TCNT0 = 0; break; case 3: TCNT0 = 0; break; case 4: TCNT0 = 0; break; case 5: TCNT0 = 0; break; case 6: // EDIT: OP was lacking this case, so TCNT0 = 0; // attempts by others to reproduce the break; // results below failed. Apologies. #ifdef ONE_MORE case 7: TCNT0 = 0; break; #endif } }
On an ATtiny85, compiling with #define ONE_MORE selects a switch statement with 8 cases, and generates a jump table:
00000000 <__vectors>: 0: 16 c0 rjmp .+44 ; 0x2e <__ctors_end> 2: 1d c0 rjmp .+58 ; 0x3e <__bad_interrupt> 4: 1c c0 rjmp .+56 ; 0x3e <__bad_interrupt> 6: 1b c0 rjmp .+54 ; 0x3e <__bad_interrupt> 8: 1a c0 rjmp .+52 ; 0x3e <__bad_interrupt> a: 19 c0 rjmp .+50 ; 0x3e <__bad_interrupt> c: 18 c0 rjmp .+48 ; 0x3e <__bad_interrupt> e: 17 c0 rjmp .+46 ; 0x3e <__bad_interrupt> 10: 16 c0 rjmp .+44 ; 0x3e <__bad_interrupt> 12: 15 c0 rjmp .+42 ; 0x3e <__bad_interrupt> 14: 14 c0 rjmp .+40 ; 0x3e <__bad_interrupt> 16: 13 c0 rjmp .+38 ; 0x3e <__bad_interrupt> 18: 12 c0 rjmp .+36 ; 0x3e <__bad_interrupt> 1a: 11 c0 rjmp .+34 ; 0x3e <__bad_interrupt> 1c: 10 c0 rjmp .+32 ; 0x3e <__bad_interrupt> 1e: 18 c0 rjmp .+48 ; 0x5020: 18 c0 rjmp .+48 ; 0x52 22: 18 c0 rjmp .+48 ; 0x54 24: 18 c0 rjmp .+48 ; 0x56 26: 18 c0 rjmp .+48 ; 0x58 28: 18 c0 rjmp .+48 ; 0x5a 2a: 10 c0 rjmp .+32 ; 0x4c 2c: 10 c0 rjmp .+32 ; 0x4e 0000002e <__ctors_end>: 2e: 11 24 eor r1, r1 30: 1f be out 0x3f, r1 ; 63 32: cf e5 ldi r28, 0x5F ; 95 34: d2 e0 ldi r29, 0x02 ; 2 36: de bf out 0x3e, r29 ; 62 38: cd bf out 0x3d, r28 ; 61 3a: 02 d0 rcall .+4 ; 0x40 3c: 0f c0 rjmp .+30 ; 0x5c <_exit> 0000003e <__bad_interrupt>: 3e: e0 cf rjmp .-64 ; 0x0 <__vectors> 00000040 : 40: e0 e0 ldi r30, 0x00 ; 0 42: f0 e0 ldi r31, 0x00 ; 0 44: e1 5f subi r30, 0xF1 ; 241 46: ff 4f sbci r31, 0xFF ; 255 48: 12 be out 0x32, r1 ; 50 4a: 09 94 ijmp 4c: 08 95 ret 4e: 08 95 ret 50: 08 95 ret 52: 08 95 ret 54: 08 95 ret 56: 08 95 ret 58: 08 95 ret 5a: 08 95 ret 0000005c <_exit>: 5c: f8 94 cli 0000005e <__stop_program>: 5e: ff cf rjmp .-2 ; 0x5e <__stop_program>
Less than 7 cases, no jump table.
Interestingly, the threshold seems to be different when compiling for the ATmega328P. Instead of prefering jump tables at 8 cases, the threshold is 17 cases. Additionally, these jump tables are easier to spot:
00000000 <__vectors>: 0: 0c 94 45 00 jmp 0x8a ; 0x8a <__ctors_end> 4: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 8: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> c: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 10: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 14: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 18: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 1c: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 20: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 24: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 28: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 2c: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 30: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 34: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 38: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 3c: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 40: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 44: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 48: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 4c: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 50: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 54: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 58: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 5c: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 60: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 64: 0c 94 4f 00 jmp 0x9e ; 0x9e <__bad_interrupt> 68: 5a 00 .word 0x005a ; ???? 6a: 5b 00 .word 0x005b ; ???? 6c: 5c 00 .word 0x005c ; ???? 6e: 5d 00 .word 0x005d ; ???? 70: 5e 00 .word 0x005e ; ???? 72: 5f 00 .word 0x005f ; ???? 74: 60 00 .word 0x0060 ; ???? 76: 61 00 .word 0x0061 ; ???? 78: 62 00 .word 0x0062 ; ???? 7a: 63 00 .word 0x0063 ; ???? 7c: 64 00 .word 0x0064 ; ???? 7e: 65 00 .word 0x0065 ; ???? 80: 66 00 .word 0x0066 ; ???? 82: 67 00 .word 0x0067 ; ???? 84: 68 00 .word 0x0068 ; ???? 86: 58 00 .word 0x0058 ; ???? 88: 59 00 .word 0x0059 ; ???? 0000008a <__ctors_end>: 8a: 11 24 eor r1, r1 8c: 1f be out 0x3f, r1 ; 63 8e: cf ef ldi r28, 0xFF ; 255 90: d8 e0 ldi r29, 0x08 ; 8 92: de bf out 0x3e, r29 ; 62 94: cd bf out 0x3d, r28 ; 61 96: 0e 94 51 00 call 0xa2 ; 0xa29a: 0c 94 6f 00 jmp 0xde ; 0xde <_exit> 0000009e <__bad_interrupt>: 9e: 0c 94 00 00 jmp 0 ; 0x0 <__vectors> 000000a2 : a2: e0 e0 ldi r30, 0x00 ; 0 a4: f0 e0 ldi r31, 0x00 ; 0 a6: ec 5c subi r30, 0xCC ; 204 a8: ff 4f sbci r31, 0xFF ; 255 aa: 16 bc out 0x26, r1 ; 38 ac: 0c 94 69 00 jmp 0xd2 ; 0xd2 <__tablejump2__> b0: 08 95 ret b2: 08 95 ret b4: 08 95 ret b6: 08 95 ret b8: 08 95 ret ba: 08 95 ret bc: 08 95 ret be: 08 95 ret c0: 08 95 ret c2: 08 95 ret c4: 08 95 ret c6: 08 95 ret c8: 08 95 ret ca: 08 95 ret cc: 08 95 ret ce: 08 95 ret d0: 08 95 ret 000000d2 <__tablejump2__>: d2: ee 0f add r30, r30 d4: ff 1f adc r31, r31 000000d6 <__tablejump__>: d6: 05 90 lpm r0, Z+ d8: f4 91 lpm r31, Z da: e0 2d mov r30, r0 dc: 09 94 ijmp 000000de <_exit>: de: f8 94 cli 000000e0 <__stop_program>: e0: ff cf rjmp .-2 ; 0xe0 <__stop_program>
... due to helpful label names like <__tablejump__>, and the fact that the tables are collections of addresses instead of RJMP instructions. I understand the reasons for the differences.
[edit]
- I'd say the mechanism used here (jump to 'jump handler', address fetch, indirect jump) more closely resembles a trampoline, albeit not one used to cross 16-bit address boundaries.
I'm left with one question, though. Is there any way to get avr-objdump to recognise these jump tables for what they are, and label them as such, instead of blindly disassembling them? It seems to me it might involve sections.
Any thoughts?
JJ