https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103975
Bug ID: 103975 Summary: DWARF .debug_frame incorrect for ISRs on AVR; pushing SREG creates off-by-one error Product: gcc Version: 7.3.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: debug Assignee: unassigned at gcc dot gnu.org Reporter: kimballa at apache dot org Target Milestone: --- On the AVR platform, when generating ISRs/interrupts/signal handlers (without using the `naked` attribute), the ISR prologue correctly emits standard code to read the value of `SREG` from its register port 0x3f and save it to the stack. However, this is ignored when generating the FDE for stack frame unwind information in .debug_frame; the result is that for $PC values after that point, the calculations for the CFA as well as the locations of all subsequently-pushed registers are off-by-one on the stack. The test case (attached) is a minimal ISR: ``` volatile unsigned char x = 0; ISR(TIMER1_COMPA_vect) { x++; // may set carry/overflow flags and affect SREG, forcing it to be pushed. } ``` which generates the following (correct) AVR assembly: ``` ISR(TIMER1_COMPA_vect) { ce: 1f 92 push r1 d0: 0f 92 push r0 d2: 0f b6 in r0, 0x3f ; 63 # read SREG to r0 d4: 0f 92 push r0 # push SREG on stack d6: 11 24 eor r1, r1 d8: 8f 93 push r24 x++; // may set carry/overflow flags and affect SREG, forcing it to be pushed. da: 80 91 00 01 lds r24, 0x0100 ; 0x800100 <_edata> de: 8f 5f subi r24, 0xFF ; 255 # (ISR continues; remainder elided) ``` A separate bug in binutils/objdump makes `objdump -Wframe` fail to decode the unwind instructions, but pyelftools can parse the info correctly and shows as follows: ``` * FDE for __vector_17() starting at $PC=0x00ce: PC: 00ce {'cfa': CFARule(reg=32, offset=2, expr=None), 36: RegisterRule(OFFSET, -1)} PC: 00d0 {'cfa': CFARule(reg=32, offset=3, expr=None), 36: RegisterRule(OFFSET, -1), 1: RegisterRule(OFFSET, -2)} PC: 00d2 {'cfa': CFARule(reg=32, offset=4, expr=None), 36: RegisterRule(OFFSET, -1), 1: RegisterRule(OFFSET, -2), 0: RegisterRule(OFFSET, -3)} <-- we *should* see a RegisterRule for SREG here valid after $PC=00d4h. PC: 00da {'cfa': CFARule(reg=32, offset=5, expr=None), 36: RegisterRule(OFFSET, -1), 1: RegisterRule(OFFSET, -2), 0: RegisterRule(OFFSET, -3), 24: RegisterRule(OFFSET, -4)} ^-- Even that notwithstanding, the 'push' at 0xd4 means the CFARule offset at 0xda is now 1 too few, and the subsequent RegisterRule offset for r24 makes it look snug against r0, ignoring the intervening push; CFARule offset should = 6 and r24's rule should have OFFSET=-5. ``` (n.b., gcc refers to $SP as register 32 in the stack frame info, and the return addr / virtual link register as register 36.) I am by no means a gcc internals expert but I tried to satisfy myself that it was a legitimate bug (and not my own goof) by reading the gcc source code. I believe the bug is in `gcc/config/avr/avr.c` at lines 1946--47: ``` 1946 /* ??? There's no dwarf2 column reserved for SREG. */ 1947 emit_push_sfr (sreg_rtx, false, false /* clr */, AVR_TMP_REGNO); ``` The author's confused comment about lacking an unwind-tracking register for SREG shows that something might be going wrong here. I think the 2nd argument to emit_push_sfr() may need to be `true` to set `frame_related_p=true` (which marks the instruction as `RTX_FRAME_RELATED_P(insn)=1`). But I don't know what other knock-on effects frame_related_p=true might have, so it's hard for me to say it's definitely just a one-flag fix. Whether or not unwind info for SREG can be tracked, at minimum the remaining unwind info needs to account for the extra push to the stack mid-prologue. I have replicated this with avr-gcc 7.3.0-atmel3.6.1-arduino7 (Arduino's official package) as well as gcc version 5.4.0 with target=avr, which is what Ubuntu 20.04.3 installs as `apt install gcc-avr`. Although I couldn't bootstrap a functioning cross-compiler with gcc 11.2 myself, the source code snippet above is from the main branch of gcc.git, which makes me believe it's likely still an issue. The attached file was created with the 7.3.0 edition of gcc referenced above: ``` $ ./avr-g++ -v Using built-in specs. Reading specs from /home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/device-specs/specs-avr2 COLLECT_GCC=./avr-g++ COLLECT_LTO_WRAPPER=/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../libexec/gcc/avr/7.3.0/lto-wrapper Target: avr Configured with: ../gcc/configure --enable-fixed-point --enable-languages=c,c++ --prefix=/home/jenkins/workspace/avr-gcc-staging/label/debian7-x86_64/objdir --disable-nls --disable-libssp --disable-libada --disable-shared --with-avrlibc=yes --with-dwarf2 --disable-doc --target=avr Thread model: single gcc version 7.3.0 (GCC) ``` I'm compiling on Ubuntu 20.04.3 in a VM: ``` $ uname -a Linux ubuntu 5.4.0-91-generic #102-Ubuntu SMP Fri Nov 5 16:31:28 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux ``` My build target is an Atmel atmega32u4 (`-mmcu=atmega32u4`). The command line for my original test-case .c file is: ``` $ avr-gcc -Os -g -Wl,--relax,--gc-section -DARCH_AVR -mmcu=atmega32u4 \ -DF_CPU=16000000 -fno-exceptions isr.c -o isr.elf ``` If compiled with the avr-gcc 5.4 from Ubuntu, you need to replace `-g` with `-gdwarf-2` to force it to generate the dwarf sections. Thanks - Aaron PS Running the above command line with `avr-gcc -v -save-temps -Os -g...` gives the following log; isr.i is attached: ``` Using built-in specs. Reading specs from /home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/device-specs/specs-atmega32u4 COLLECT_GCC=/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-gcc COLLECT_LTO_WRAPPER=/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../libexec/gcc/avr/7.3.0/lto-wrapper Target: avr Configured with: ../gcc/configure --enable-fixed-point --enable-languages=c,c++ --prefix=/home/jenkins/workspace/avr-gcc-staging/label/debian7-x86_64/objdir --disable-nls --disable-libssp --disable-libada --disable-shared --with-avrlibc=yes --with-dwarf2 --disable-doc --target=avr Thread model: single gcc version 7.3.0 (GCC) COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Os' '-g' '-D' 'ARCH_AVR' '-D' 'F_CPU=16000000' '-fno-exceptions' '-specs=device-specs/specs-atmega32u4' '-mmcu=avr5' /home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../libexec/gcc/avr/7.3.0/cc1 -E -quiet -v -imultilib avr5 -iprefix /home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/ -D__AVR_ATmega32U4__ -D__AVR_DEVICE_NAME__=atmega32u4 -D ARCH_AVR -D F_CPU=16000000 isr.c -mn-flash=1 -mno-skip-bug -mmcu=avr5 -fno-exceptions -g -fworking-directory -Os -fpch-preprocess -o isr.i ignoring nonexistent directory "/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/../../../../avr/sys-include" ignoring duplicate directory "/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/include" ignoring duplicate directory "/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/include-fixed" ignoring nonexistent directory "/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/../../../../avr/sys-include" ignoring duplicate directory "/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/../../../../avr/include" #include "..." search starts here: #include <...> search starts here: /home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/include /home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/include-fixed /home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/../../../../avr/include End of search list. COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Os' '-g' '-D' 'ARCH_AVR' '-D' 'F_CPU=16000000' '-fno-exceptions' '-specs=device-specs/specs-atmega32u4' '-mmcu=avr5' /home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../libexec/gcc/avr/7.3.0/cc1 -fpreprocessed isr.i -mn-flash=1 -mno-skip-bug -quiet -dumpbase isr.c -mmcu=avr5 -auxbase isr -g -Os -version -fno-exceptions -o isr.s GNU C11 (GCC) version 7.3.0 (avr) compiled by GNU C version 4.7.2, GMP version 5.0.2, MPFR version 3.1.0, MPC version 0.9, isl version none GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 GNU C11 (GCC) version 7.3.0 (avr) compiled by GNU C version 4.7.2, GMP version 5.0.2, MPFR version 3.1.0, MPC version 0.9, isl version none GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 Compiler executable checksum: f1a5be5edc2698bbea3030b94defb7b4 COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Os' '-g' '-D' 'ARCH_AVR' '-D' 'F_CPU=16000000' '-fno-exceptions' '-specs=device-specs/specs-atmega32u4' '-mmcu=avr5' /home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/../../../../avr/bin/as -mmcu=avr5 -mno-skip-bug -o isr.o isr.s COMPILER_PATH=/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../libexec/gcc/avr/7.3.0/:/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../libexec/gcc/:/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/../../../../avr/bin/ LIBRARY_PATH=/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/avr5/:/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/:/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/:/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/:/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/ COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Os' '-g' '-D' 'ARCH_AVR' '-D' 'F_CPU=16000000' '-fno-exceptions' '-specs=device-specs/specs-atmega32u4' '-mmcu=avr5' /home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../libexec/gcc/avr/7.3.0/collect2 -plugin /home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../libexec/gcc/avr/7.3.0/liblto_plugin.so -plugin-opt=/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../libexec/gcc/avr/7.3.0/lto-wrapper -plugin-opt=-fresolution=isr.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lm -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-latmega32u4 -mavr5 -Tdata 0x800100 /home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5/crtatmega32u4.o -L/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/avr5 -L/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/avr5 -L/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0 -L/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc -L/home/aaron/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib --relax --gc-section isr.o --start-group -lgcc -lm -lc -latmega32u4 --end-group COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Os' '-g' '-D' 'ARCH_AVR' '-D' 'F_CPU=16000000' '-fno-exceptions' '-specs=device-specs/specs-atmega32u4' '-mmcu=avr5' ```