cgccembeddedfunction-pointersriscv

Function pointer is always zero, but works when dereferenced and called


Here is a minimal example of code I have (I've tried running the minimal example to make sure it reproduces the issue I'm seeing):

void testfn(void) {
    printf("Hello, world!\n");
}

int main(int argc, char *argv[]) {
    printf("fp: %p\n", &testfn);
    return 0;
}

The output I get is fp: 0x0000000000000000, indicating that the function pointer I'm trying to print is 0. I've tried using the GCC __attribute__((noinline)) attribute on testfn() and I get the same result.

I am compiling for a RISC-V system and running in QEMU. I thought that perhaps the printf() implementation was buggy (though it looks good to me and other pointers print just fine), so I tried casting the function pointer to an unsigned 64-bit int and then looping that many iterations (I recognize this is technically UB, but usually works in practice and this is a dumb test), but the loop performs precisely 0 iterations:

void __attribute__((noinline)) testfn(void) {
    printf("Hello, world!\n");
}

int main(int argc, char *argv[]) {
    void (*fn)(void) = &testfn;

    printf("Testing printf...\n");
    printf("fp addr: %p\n", &fn);
    fn();

    for (int i = 0; i < (unsigned long long) fn; ++i) {
        printf("Here!\n");
    }

    printf("fp: %p\n", fn);
    return 0;
}

This produces the following output:

Testing printf...
fp addr: 0x0000000000003FA8
Hello, world!
fp: 0x0000000000000000

Here is the command being used for compilation: riscv64-unknown-elf-gcc -Wall -O0 -fno-omit-frame-pointer -ggdb -gdwarf-2 -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o user/rkttest.o user/rkttest.c

Here is the command I'm using to run QEMU; qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 3 -nographic -global virtio-mmio.force-legacy=false -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0.

It seems to me that the function pointer is always zero, except when I try to call it as a function. This feels to me like a compiler bug or some weird thing happening in my environment, but I thought I'd throw it up here on Stack Overflow. Am I missing something?


Solution

  • It appears the system is literally placing the code section at address 0x0 and the compiler is putting the function at the beginning of the code section. This is a REALLY bad idea on part of the system. Nothing should be at address 0x0, ever.

    The following code:

    static void __attribute__((noinline)) testfn(void) {
        printf("Hello, world!\n");
    }
    
    static void __attribute__((noinline)) testfn2(void) {
        printf("Hello, world!\n");
    }
    
    int main(int argc, char *argv[]) {
        printf("fp: %p\n", (void *) &testfn);
        printf("fp: %p\n", (void *) &testfn2);
    
        return 0;
    }
    

    produces the following output:

    fp: 0x000000000000001A
    fp: 0x0000000000000000
    

    Oh boy. 🤦‍♂️

    Edit: Okay, maybe saying "nothing should be at address 0x0, ever" isn't necessarily true (counterpoint: systems without virtual addressing). But for systems with virtual addressing, I still believe it is a really bad idea to map that page, if only to avoid issues arising from bad assumptions.