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?
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.