I'm programming an Atmel ATtiny13a microcontroller using avr-gcc 4.8.2.
This is my c code:
#include <avr/io.h>
#include <util/delay.h>
int main(void) {
DDRB = 1; // PB0 is output
for (uint8_t i = 0; i < 10; i++) {
PORTB = 1;
_delay_ms(500);
PORTB = 0;
_delay_ms(500);
}
while(1);
}
void test(void) {
DDRB = 1; // PB0 is output
for (uint8_t i = 0; i < 10; i++) {
PORTB = 1;
_delay_ms(100);
PORTB = 0;
_delay_ms(100);
}
}
The test function (fast blinking of an LED) is never called from the main function, so the controller should only enter the main function (slow blinking).
When I compile the code with -O1
, everything works fine:
avr-gcc -std=gnu99 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -mmcu=attiny13 -DF_CPU=1200000 -Wall -Wstrict-prototypes -Os -c test.c -o test.o
avr-gcc test.o -o test.elf
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature test.elf test.hex
But if I use -Os
(optimization for size) or -O2
, the microcontroller runs the test
function instead of the main
function: The LED blinks quickly and never stops.
Is the -Os
flag simply too dangerous to use, should it be avoided? Or is there something I can change in my code to avoid this kind of bug? The ATtiny13a only has 1K of flash, so size reduction is something important.
Edit: As suggested in the comments, here's the assembler diff with -O1
and -O2
: http://www.diffchecker.com/3l9cdln6
In there you can see that -O2
changes the first section from .text
to .text.startup
.
--- test.o1.txt 2013-12-03 19:10:43.874598682 +0100
+++ test.o2.txt 2013-12-03 19:10:50.574674155 +0100
@@ -3,7 +3,7 @@
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
- .text
+ .section .text.startup,"ax",@progbits
.global main
.type main, @function
main:
That's probably the main issue here. After some further testing I found that the culprit is the -freorder-functions
optimization. Is there a way to prevent this behavior?
I did some further debugging, and found that the "culprit" was the
-freorder-functions
optimization. It is documented in the manpage as follows:The last line in the documentation explains the problem I was having/causing. If we look at the compile commands from the original question again:
...we see that I was passing the optimization flags to the compiler, but not to the linker. I assumed that the
CFLAGS
only affect compilation and not linking, so I did not pass them to the linker, but in that case I was wrong.The result: The assembly code was reordered by the compiler (including appropriate labels), but the linker did not consider these labels. And because the
test
function was placed before themain
function by the compiler and not re-arranged by the linker, that was the code that was actually executed on the microcontroller.So the solution turned out to be: Compiler flags should also be passed to the linker!