Now we have a binary file. Can we do anything useful with it (besides put it into the processor?) The GNU Binutils suite is made up of many useful tools for manipulating object files that get generated. One tool is avr-objdump, which takes information from the object file and displays it in many useful ways. Typing the command by itself will cause it to list out its options.

For instance, to get a feel of the application's size, the -h option can be used. The output of this option shows how much space is used in each of the sections (the .stab and .stabstr sections hold the debugging information and won't make it into the ROM file).

An even more useful option is -S. This option disassembles the binary file and intersperses the source code in the output! This method is much better, in my opinion, than using the -S with the compiler because this listing includes routines from the libraries and the vector table contents. Also, all the "fix-ups" have been satisfied. In other words, the listing generated by this option reflects the actual code that the processor will run.

$ avr-objdump -h -S demo.elf > demo.lst

Here's the output as saved in the demo.lst file:

demo.elf: file format elf32-avr Sections: Idx Name Size VMA LMA File off Algn 0 .text 000000d8 00000000 00000000 00000074 2**1 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .bss 00000003 00800060 00800060 0000014c 2**0 ALLOC 2 .comment 0000002c 00000000 00000000 0000014c 2**0 CONTENTS, READONLY 3 .debug_aranges 00000068 00000000 00000000 00000178 2**3 CONTENTS, READONLY, DEBUGGING 4 .debug_info 00000339 00000000 00000000 000001e0 2**0 CONTENTS, READONLY, DEBUGGING 5 .debug_abbrev 00000102 00000000 00000000 00000519 2**0 CONTENTS, READONLY, DEBUGGING 6 .debug_line 00000215 00000000 00000000 0000061b 2**0 CONTENTS, READONLY, DEBUGGING 7 .debug_frame 00000060 00000000 00000000 00000830 2**2 CONTENTS, READONLY, DEBUGGING 8 .debug_str 000000f9 00000000 00000000 00000890 2**0 CONTENTS, READONLY, DEBUGGING 9 .debug_loc 00000056 00000000 00000000 00000989 2**0 CONTENTS, READONLY, DEBUGGING 10 .debug_ranges 00000018 00000000 00000000 000009df 2**0 CONTENTS, READONLY, DEBUGGING Disassembly of section .text: 00000000 <__ctors_end>: /* __do_clear_bss is only necessary if there is anything in .bss section. */ #ifdef L_clear_bss .section .init4,"ax",@progbits DEFUN __do_clear_bss ldi r18, hi8(__bss_end) 0: 20 e0 ldi r18, 0x00 ; 0 ldi r26, lo8(__bss_start) 2: a0 e6 ldi r26, 0x60 ; 96 ldi r27, hi8(__bss_start) 4: b0 e0 ldi r27, 0x00 ; 0 rjmp .do_clear_bss_start 6: 01 c0 rjmp .+2 ; 0xa <.do_clear_bss_start> 00000008 <.do_clear_bss_loop>: .do_clear_bss_loop: st X+, __zero_reg__ 8: 1d 92 st X+, r1 0000000a <.do_clear_bss_start>: .do_clear_bss_start: cpi r26, lo8(__bss_end) a: a3 36 cpi r26, 0x63 ; 99 cpc r27, r18 c: b2 07 cpc r27, r18 brne .do_clear_bss_loop e: e1 f7 brne .-8 ; 0x8 <.do_clear_bss_loop> 00000010 <__vector_8>: #include "iocompat.h" /* Note [1] */ enum { UP, DOWN }; ISR (TIMER1_OVF_vect) /* Note [2] */ { 10: 1f 92 push r1 12: 0f 92 push r0 14: 0f b6 in r0, 0x3f ; 63 16: 0f 92 push r0 18: 11 24 eor r1, r1 1a: 2f 93 push r18 1c: 8f 93 push r24 1e: 9f 93 push r25 static uint16_t pwm; /* Note [3] */ static uint8_t direction; switch (direction) /* Note [4] */ 20: 80 91 62 00 lds r24, 0x0062 24: 88 23 and r24, r24 26: 01 f1 breq .+64 ; 0x68 <__SREG__+0x29> 28: 81 30 cpi r24, 0x01 ; 1 2a: 81 f4 brne .+32 ; 0x4c <__SREG__+0xd> if (++pwm == TIMER1_TOP) direction = DOWN; break; case DOWN: if (--pwm == 0) 2c: 80 91 60 00 lds r24, 0x0060 30: 90 91 61 00 lds r25, 0x0061 34: 01 97 sbiw r24, 0x01 ; 1 36: 90 93 61 00 sts 0x0061, r25 3a: 80 93 60 00 sts 0x0060, r24 3e: 00 97 sbiw r24, 0x00 ; 0 40: 49 f4 brne .+18 ; 0x54 <__SREG__+0x15> direction = UP; 42: 10 92 62 00 sts 0x0062, r1 46: 80 e0 ldi r24, 0x00 ; 0 48: 90 e0 ldi r25, 0x00 ; 0 4a: 04 c0 rjmp .+8 ; 0x54 <__SREG__+0x15> 4c: 80 91 60 00 lds r24, 0x0060 50: 90 91 61 00 lds r25, 0x0061 break; } OCR = pwm; /* Note [5] */ 54: 9b bd out 0x2b, r25 ; 43 56: 8a bd out 0x2a, r24 ; 42 } 58: 9f 91 pop r25 5a: 8f 91 pop r24 5c: 2f 91 pop r18 5e: 0f 90 pop r0 60: 0f be out 0x3f, r0 ; 63 62: 0f 90 pop r0 64: 1f 90 pop r1 66: 18 95 reti static uint8_t direction; switch (direction) /* Note [4] */ { case UP: if (++pwm == TIMER1_TOP) 68: 80 91 60 00 lds r24, 0x0060 6c: 90 91 61 00 lds r25, 0x0061 70: 01 96 adiw r24, 0x01 ; 1 72: 90 93 61 00 sts 0x0061, r25 76: 80 93 60 00 sts 0x0060, r24 7a: 8f 3f cpi r24, 0xFF ; 255 7c: 23 e0 ldi r18, 0x03 ; 3 7e: 92 07 cpc r25, r18 80: 49 f7 brne .-46 ; 0x54 <__SREG__+0x15> direction = DOWN; 82: 81 e0 ldi r24, 0x01 ; 1 84: 80 93 62 00 sts 0x0062, r24 88: 8f ef ldi r24, 0xFF ; 255 8a: 93 e0 ldi r25, 0x03 ; 3 8c: e3 cf rjmp .-58 ; 0x54 <__SREG__+0x15> 0000008e <ioinit>: void ioinit (void) /* Note [6] */ { /* Timer 1 is 10-bit PWM (8-bit PWM on some ATtinys). */ TCCR1A = TIMER1_PWM_INIT; 8e: 83 e8 ldi r24, 0x83 ; 131 90: 8f bd out 0x2f, r24 ; 47 * Start timer 1. * * NB: TCCR1A and TCCR1B could actually be the same register, so * take care to not clobber it. */ TCCR1B |= TIMER1_CLOCKSOURCE; 92: 8e b5 in r24, 0x2e ; 46 94: 81 60 ori r24, 0x01 ; 1 96: 8e bd out 0x2e, r24 ; 46 #if defined(TIMER1_SETUP_HOOK) TIMER1_SETUP_HOOK(); #endif /* Set PWM value to 0. */ OCR = 0; 98: 1b bc out 0x2b, r1 ; 43 9a: 1a bc out 0x2a, r1 ; 42 /* Enable OC1 as output. */ DDROC = _BV (OC1); 9c: 82 e0 ldi r24, 0x02 ; 2 9e: 87 bb out 0x17, r24 ; 23 /* Enable timer 1 overflow interrupt. */ TIMSK = _BV (TOIE1); a0: 84 e0 ldi r24, 0x04 ; 4 a2: 89 bf out 0x39, r24 ; 57 sei (); a4: 78 94 sei a6: 08 95 ret 000000a8 <main>: void ioinit (void) /* Note [6] */ { /* Timer 1 is 10-bit PWM (8-bit PWM on some ATtinys). */ TCCR1A = TIMER1_PWM_INIT; a8: 83 e8 ldi r24, 0x83 ; 131 aa: 8f bd out 0x2f, r24 ; 47 * Start timer 1. * * NB: TCCR1A and TCCR1B could actually be the same register, so * take care to not clobber it. */ TCCR1B |= TIMER1_CLOCKSOURCE; ac: 8e b5 in r24, 0x2e ; 46 ae: 81 60 ori r24, 0x01 ; 1 b0: 8e bd out 0x2e, r24 ; 46 #if defined(TIMER1_SETUP_HOOK) TIMER1_SETUP_HOOK(); #endif /* Set PWM value to 0. */ OCR = 0; b2: 1b bc out 0x2b, r1 ; 43 b4: 1a bc out 0x2a, r1 ; 42 /* Enable OC1 as output. */ DDROC = _BV (OC1); b6: 82 e0 ldi r24, 0x02 ; 2 b8: 87 bb out 0x17, r24 ; 23 /* Enable timer 1 overflow interrupt. */ TIMSK = _BV (TOIE1); ba: 84 e0 ldi r24, 0x04 ; 4 bc: 89 bf out 0x39, r24 ; 57 sei (); be: 78 94 sei ioinit (); /* loop forever, the interrupts are doing the rest */ for (;;) /* Note [7] */ sleep_mode(); c0: 85 b7 in r24, 0x35 ; 53 c2: 80 68 ori r24, 0x80 ; 128 c4: 85 bf out 0x35, r24 ; 53 c6: 88 95 sleep c8: 85 b7 in r24, 0x35 ; 53 ca: 8f 77 andi r24, 0x7F ; 127 cc: 85 bf out 0x35, r24 ; 53 ce: f8 cf rjmp .-16 ; 0xc0 <main+0x18> 000000d0 <exit>: d0: f8 94 cli d2: 00 c0 rjmp .+0 ; 0xd4 <_exit> 000000d4 <_exit>: ENDF _exit /* Code from .fini8 ... .fini1 sections inserted by ld script. */ .section .fini0,"ax",@progbits cli d4: f8 94 cli 000000d6 <__stop_program>: __stop_program: rjmp __stop_program d6: ff cf rjmp .-2 ; 0xd6 <__stop_program>