这是我拥有的最小代码示例(我尝试运行最小示例以确保它重现我所看到的问题):
void testfn(void) {
printf("Hello, world!\n");
}
int main(int argc, char *argv[]) {
printf("fp: %p\n", &testfn);
return 0;
}
我得到的输出是
fp: 0x0000000000000000
,表明我尝试打印的函数指针是 0。我尝试在 __attribute__((noinline))
上使用 GCC testfn()
属性,得到了相同的结果。
我正在为 RISC-V 系统进行编译并在 QEMU 中运行。我认为也许
printf()
实现有问题(尽管它对我来说看起来很好,其他指针打印得很好),所以我尝试将函数指针转换为无符号 64 位 int,然后循环多次迭代(我认识到这一点)技术上是 UB,但在实践中通常有效),但循环执行精确的 0 次迭代:
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;
}
这会产生以下输出:
Testing printf...
fp addr: 0x0000000000003FA8
Hello, world!
fp: 0x0000000000000000
这是用于编译的命令:
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
这是我用来运行 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
。
在我看来,函数指针总是为零,除非我尝试将其作为函数调用。在我看来,这就像编译器错误或我的环境中发生的一些奇怪的事情,但我想我应该把它扔到 Stack Overflow 上。我是不是错过了什么?
看起来系统实际上是将代码段放置在地址 0x0 处,并且编译器将函数放置在代码段的开头。对于系统的一部分来说,这是一个非常糟糕的主意。地址 0x0 上不应该有任何东西,永远。
以下代码:
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", &testfn);
printf("fp: %p\n", &testfn2);
return 0;
}
产生以下输出:
fp: 0x000000000000001A
fp: 0x0000000000000000
天啊。 🤦u200d♂️