I've been able to build and run binaries successfully when I compile and link in one step. However if I try compiling but not linking with the -c option to avr-gcc, and then subsequently linking the resulting object files into a binary, the result is garbage, apparently due to a nonexistent interrupt table.
Is there something I must do at the link stage if I want to build from multiple intermediate object files?
Details: Say I have a simple program for an ATMega48P, like this one, which we can call ledblink.c:
#define F_CPU 16000000UL
#define F_ADC 48000
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
int main(void)
{
DDRD = (1<<PD6);
DDRB = (1<<PB0);
TCCR0A = (1<<WGM01); // CTC mode
TCCR0B = (1<<CS01); // divide by 8 prescaler
TIMSK0 = (1 << OCIE0A);
OCR0A = F_CPU / 8 / F_ADC;
sei();
while(1)
{
PORTB |= (1<<PB0);
_delay_ms(100);
PORTB &= ~(1<<PB0);
_delay_ms(100);
}
return 0;
}
ISR(TIMER0_COMPA_vect) {
PORTD |= (1<<PD6);
_delay_us(5);
PORTD &= ~(1<<PD6);
}
It should:
- blink an LED on PB0 in the main loop, and simultaneously
- output an (approximately) 48 kHz square wave on PD6, using timer0 and the ISR
If I build it like this:
avr-gcc -mmcu=atmega48p -Os -o ledblink ledblink.c
avr-objcopy -j .text -j .data -O ihex ledblink ledblink.hex
avrdude -c avrispmkII -p m48p -U flash:w:ledblink.hex
it works great. But if I build it like this:
avr-gcc -mmcu=atmega48p -Os -c -o ledblink.o ledblink.c
avr-gcc ledblink.o -o ledblink
avr-objcopy -j .text -j .data -O ihex ledblink ledblink.hex
avrdude -c avrispmkII -p m48p -U flash:w:ledblink.hex
the LED on PB0 does not blink, nor does PD6 change.
The reason, I believe, is in the 2nd case the __vectors table, and some other things doesn't end up in the binary. I can see this with avr-objdump -d ledblink. When I compile and link in one step, as in the first case:
ledblink: file format elf32-avr
Disassembly of section .text:
00000000 <__vectors>:
0: 19 c0 rjmp .+50 ; 0x34 <__ctors_end>
2: 20 c0 rjmp .+64 ; 0x44 <__bad_interrupt>
4: 1f c0 rjmp .+62 ; 0x44 <__bad_interrupt>
6: 1e c0 rjmp .+60 ; 0x44 <__bad_interrupt>
8: 1d c0 rjmp .+58 ; 0x44 <__bad_interrupt>
a: 1c c0 rjmp .+56 ; 0x44 <__bad_interrupt>
c: 1b c0 rjmp .+54 ; 0x44 <__bad_interrupt>
e: 1a c0 rjmp .+52 ; 0x44 <__bad_interrupt>
10: 19 c0 rjmp .+50 ; 0x44 <__bad_interrupt>
12: 18 c0 rjmp .+48 ; 0x44 <__bad_interrupt>
14: 17 c0 rjmp .+46 ; 0x44 <__bad_interrupt>
16: 16 c0 rjmp .+44 ; 0x44 <__bad_interrupt>
18: 15 c0 rjmp .+42 ; 0x44 <__bad_interrupt>
1a: 14 c0 rjmp .+40 ; 0x44 <__bad_interrupt>
1c: 14 c0 rjmp .+40 ; 0x46 <__vector_14>
1e: 12 c0 rjmp .+36 ; 0x44 <__bad_interrupt>
20: 11 c0 rjmp .+34 ; 0x44 <__bad_interrupt>
22: 10 c0 rjmp .+32 ; 0x44 <__bad_interrupt>
24: 0f c0 rjmp .+30 ; 0x44 <__bad_interrupt>
26: 0e c0 rjmp .+28 ; 0x44 <__bad_interrupt>
28: 0d c0 rjmp .+26 ; 0x44 <__bad_interrupt>
2a: 0c c0 rjmp .+24 ; 0x44 <__bad_interrupt>
2c: 0b c0 rjmp .+22 ; 0x44 <__bad_interrupt>
2e: 0a c0 rjmp .+20 ; 0x44 <__bad_interrupt>
30: 09 c0 rjmp .+18 ; 0x44 <__bad_interrupt>
32: 08 c0 rjmp .+16 ; 0x44 <__bad_interrupt>
00000034 <__ctors_end>:
34: 11 24 eor r1, r1
36: 1f be out 0x3f, r1 ; 63
38: cf ef ldi r28, 0xFF ; 255
3a: d2 e0 ldi r29, 0x02 ; 2
3c: de bf out 0x3e, r29 ; 62
3e: cd bf out 0x3d, r28 ; 61
40: 14 d0 rcall .+40 ; 0x6a <main>
42: 34 c0 rjmp .+104 ; 0xac <_exit>
00000044 <__bad_interrupt>:
44: dd cf rjmp .-70 ; 0x0 <__vectors>
00000046 <__vector_14>:
46: 1f 92 push r1
48: 0f 92 push r0
4a: 0f b6 in r0, 0x3f ; 63
4c: 0f 92 push r0
4e: 11 24 eor r1, r1
50: 8f 93 push r24
52: 5e 9a sbi 0x0b, 6 ; 11
54: 8a e1 ldi r24, 0x1A ; 26
56: 8a 95 dec r24
58: f1 f7 brne .-4 ; 0x56 <__vector_14+0x10>
5a: 00 c0 rjmp .+0 ; 0x5c <__vector_14+0x16>
5c: 5e 98 cbi 0x0b, 6 ; 11
5e: 8f 91 pop r24
60: 0f 90 pop r0
62: 0f be out 0x3f, r0 ; 63
64: 0f 90 pop r0
66: 1f 90 pop r1
68: 18 95 reti
0000006a <main>:
6a: 80 e4 ldi r24, 0x40 ; 64
6c: 8a b9 out 0x0a, r24 ; 10
6e: 81 e0 ldi r24, 0x01 ; 1
70: 84 b9 out 0x04, r24 ; 4
72: 82 e0 ldi r24, 0x02 ; 2
74: 84 bd out 0x24, r24 ; 36
76: 85 bd out 0x25, r24 ; 37
78: 80 93 6e 00 sts 0x006E, r24 ; 0x80006e <__EEPROM_REGION_LENGTH__+0x7f006e>
7c: 89 e2 ldi r24, 0x29 ; 41
7e: 87 bd out 0x27, r24 ; 39
80: 78 94 sei
82: 28 9a sbi 0x05, 0 ; 5
84: 2f ef ldi r18, 0xFF ; 255
86: 81 ee ldi r24, 0xE1 ; 225
88: 94 e0 ldi r25, 0x04 ; 4
8a: 21 50 subi r18, 0x01 ; 1
8c: 80 40 sbci r24, 0x00 ; 0
8e: 90 40 sbci r25, 0x00 ; 0
90: e1 f7 brne .-8 ; 0x8a <main+0x20>
92: 00 c0 rjmp .+0 ; 0x94 <main+0x2a>
94: 00 00 nop
96: 28 98 cbi 0x05, 0 ; 5
98: 2f ef ldi r18, 0xFF ; 255
9a: 81 ee ldi r24, 0xE1 ; 225
9c: 94 e0 ldi r25, 0x04 ; 4
9e: 21 50 subi r18, 0x01 ; 1
a0: 80 40 sbci r24, 0x00 ; 0
a2: 90 40 sbci r25, 0x00 ; 0
a4: e1 f7 brne .-8 ; 0x9e <main+0x34>
a6: 00 c0 rjmp .+0 ; 0xa8 <main+0x3e>
a8: 00 00 nop
aa: eb cf rjmp .-42 ; 0x82 <main+0x18>
000000ac <_exit>:
ac: f8 94 cli
000000ae <__stop_program>:
ae: ff cf rjmp .-2 ; 0xae <__stop_program>
But compiling to an object file, then linking in a second step I get:
ledblink: file format elf32-avr
Disassembly of section .text:
00000000 <__vector_14>:
0: 1f 92 push r1
2: 0f 92 push r0
4: 0f b6 in r0, 0x3f ; 63
6: 0f 92 push r0
8: 11 24 eor r1, r1
a: 8f 93 push r24
c: 5e 9a sbi 0x0b, 6 ; 11
e: 8a e1 ldi r24, 0x1A ; 26
10: 8a 95 dec r24
12: f1 f7 brne .-4 ; 0x10 <__zero_reg__+0xf>
14: 00 c0 rjmp .+0 ; 0x16 <__zero_reg__+0x15>
16: 5e 98 cbi 0x0b, 6 ; 11
18: 8f 91 pop r24
1a: 0f 90 pop r0
1c: 0f be out 0x3f, r0 ; 63
1e: 0f 90 pop r0
20: 1f 90 pop r1
22: 18 95 reti
00000024 <main>:
24: 80 e4 ldi r24, 0x40 ; 64
26: 8a b9 out 0x0a, r24 ; 10
28: 81 e0 ldi r24, 0x01 ; 1
2a: 84 b9 out 0x04, r24 ; 4
2c: 82 e0 ldi r24, 0x02 ; 2
2e: 84 bd out 0x24, r24 ; 36
30: 85 bd out 0x25, r24 ; 37
32: 80 93 6e 00 sts 0x006E, r24 ; 0x80006e <_edata+0xe>
36: 89 e2 ldi r24, 0x29 ; 41
38: 87 bd out 0x27, r24 ; 39
3a: 78 94 sei
3c: 28 9a sbi 0x05, 0 ; 5
3e: 2f ef ldi r18, 0xFF ; 255
40: 81 ee ldi r24, 0xE1 ; 225
42: 94 e0 ldi r25, 0x04 ; 4
44: 21 50 subi r18, 0x01 ; 1
46: 80 40 sbci r24, 0x00 ; 0
48: 90 40 sbci r25, 0x00 ; 0
4a: e1 f7 brne .-8 ; 0x44 <__SREG__+0x5>
4c: 00 c0 rjmp .+0 ; 0x4e <__SREG__+0xf>
4e: 00 00 nop
50: 28 98 cbi 0x05, 0 ; 5
52: 2f ef ldi r18, 0xFF ; 255
54: 81 ee ldi r24, 0xE1 ; 225
56: 94 e0 ldi r25, 0x04 ; 4
58: 21 50 subi r18, 0x01 ; 1
5a: 80 40 sbci r24, 0x00 ; 0
5c: 90 40 sbci r25, 0x00 ; 0
5e: e1 f7 brne .-8 ; 0x58 <__SREG__+0x19>
60: 00 c0 rjmp .+0 ; 0x62 <__SREG__+0x23>
62: 00 00 nop
64: eb cf rjmp .-42 ; 0x3c <main+0x18>
Note how __vectors is missing, and at address 0 there is only the ISR. I've never actually read the datasheet in enough detail to confirm, but I guess this means the MCU starts execution at some garbage address, so I get garbage behavior.
How can I build a working hex file from a set of intermediate object files?