Skip to content

VTables which end up in the data segment are miscompiled on AVR. #79889

Closed
@branan

Description

@branan

The following code snippet should light the pin 13 LED when compiled for the atmega328 and run on an arduino. It does not. This is possibly related to #74743, and potentially also avr-rust#47. I initially found this while trying to implement a RawWakerVTable, so it is not restricted to compiler-generated vtables - any function pointers which are stored in the data segment are affected.

Attempting to simplify further (such as using a single function pointer) tends to end up with pointers which are passed entirely in code and never loaded from the data segment. These appear to be compiled correctly.

#![no_std]
#![no_main]
#![feature(llvm_asm, test)]

#[panic_handler]
fn panic(_panic: &core::panic::PanicInfo) -> ! {
    loop {}
}

trait Foo {
    unsafe fn foo(&self);
}

impl Foo for () {
    unsafe fn foo(&self) {
        llvm_asm!(
            "SBI 0x04, 5
         SBI 0x05, 5"
        );
    }
}

#[inline(never)]
unsafe fn invoke(foo: &dyn Foo) {
    foo.foo()
}

#[no_mangle]
pub unsafe extern "C" fn main() {
    invoke(core::hint::black_box(&()));
    loop {}
}

Built + Flashed with:

% cargo +nightly build -Z build-std=core --target=avr-unknown-gnu-atmega328 --release
% avrdude -p atmega328p -c arduino -P /dev/tty.usbmodem142401 -Uflash:w:./target/avr-unknown-gnu-atmega328/release/avr-test.elf

Stepping through the execution in simavr/gdb makes the failure obvious - the vtable contains the byte-address of the implementation function, but the icall instruction (and indeed the instruction pointer of the AVR in general) uses word-addresses.

(gdb) disas
Dump of assembler code for function _ZN8avr_test6invoke17h7f830b8dbe21df91E:
   0x000000ae <+0>:	movw	r30, r22
   0x000000b0 <+2>:	ldd	r18, Z+6	; 0x06
   0x000000b2 <+4>:	ldd	r19, Z+7	; 0x07
   0x000000b4 <+6>:	movw	r30, r18
=> 0x000000b6 <+8>:	icall
   0x000000b8 <+10>:	ret
End of assembler dump.
(gdb) info r
<snip>
r30            0xa8                168
r31            0x0                 0
SREG           0x0                 0
SP             0x8f5               0x8008f5
PC2            0xb6                182
pc             0x5b                0xb6 <avr_test::invoke+8>
(gdb) si
0x00000150 in ?? ()

As can be seen from the GDB output, after loading the VTable entry the Z register (the combination of r30 and r31) contains "0x00A8", and execution jumps to "0x0150". The vtable should instead include "0x0054", which when treated as a word-address will jump to the expected "0x00A8".

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessO-AVRTarget: AVR processors (ATtiny, ATmega, etc.)requires-nightlyThis issue requires a nightly compiler in some way.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions