You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在无文件系统无stdlib的RISC-V64裸机环境用gcov遇链接错误求方案

在RISC-V裸机环境中使用gcov解决stdlib依赖问题

问题背景

在riscv64-unknown-elf-gcc 13.2.0的无文件系统、无stdlib裸机MCU环境中使用gcov时,参考gcc官方教程在主机系统可正常运行,但移植到RISC-V环境后链接失败。

编译与链接命令

# 编译
riscv64-unknown-elf-gcc -mcmodel=medany -static \
  -O0 -ffast-math -fno-common -fno-builtin-printf \
  --coverage -fprofile-info-section -ffunction-sections -fdata-sections \
  -g -c test_gcov.c

# 链接
riscv64-unknown-elf-gcc -static -nostdlib -nostartfiles \
  --coverage -T ../common/test_gcov.ld *.o -Wl,-Map=test_gcov.riscv.map \
  -Wl,--gc-sections -Wl,--verbose -o test_gcov.riscv

链接报错信息

attempt to open /opt/riscv/lib/gcc/riscv64-unknown-elf/13.2.0/libgcov.a succeeded
/opt/riscv/lib/gcc/riscv64-unknown-elf/13.2.0/libgcov.a
(/opt/riscv/lib/gcc/riscv64-unknown-elf/13.2.0/libgcov.a)_gcov_merge_add.o
(/opt/riscv/lib/gcc/riscv64-unknown-elf/13.2.0/libgcov.a)_gcov.o
/opt/riscv/lib/gcc/riscv64-unknown-elf/13.2.0/../../../../riscv64-unknown-elf/bin/ld: warning: test_gcov.riscv has a LOAD segment with RWX permissions
/opt/riscv/lib/gcc/riscv64-unknown-elf/13.2.0/../../../../riscv64-unknown-elf/bin/ld: /opt/riscv/lib/gcc/riscv64-unknown-elf/13.2.0/libgcov.a(_gcov.o): in function `gcov_read_bytes':
/media/jmst/tmpSSD/riscv-gnu-toolchain/build-gcc-newlib-stage2/riscv64-unknown-elf/libgcc/../../.././gcc/libgcc/libgcov-driver.c:399:(.text+0x1c): undefined reference to `fread'

报错显示libgcov.a依赖stdlib中的fread函数,但环境无文件系统无法使用stdlib,需让覆盖代码脱离stdlib依赖。

解决方案

1. 手动实现gcov所需的最小化标准库桩函数

gcov在裸机环境中仅依赖少量文件操作相关函数,可实现空桩函数满足链接需求,或适配硬件存储逻辑:

  • 若仅需生成新覆盖数据(无需读取历史数据),可实现空版本的fread、fwrite、fopen、fclose:
#include <stddef.h>

// 空实现fread,返回0表示无历史数据可读
size_t fread(void *ptr, size_t size, size_t nmemb, void *stream) {
    (void)ptr;
    (void)size;
    (void)nmemb;
    (void)stream;
    return 0;
}

// 若需将覆盖数据写入硬件存储(如Flash/SRAM),需在此实现对应逻辑
size_t fwrite(const void *ptr, size_t size, size_t nmemb, void *stream) {
    (void)ptr;
    (void)size;
    (void)nmemb;
    (void)stream;
    return 0;
}

// 空实现fopen,返回非NULL占位指针
void *fopen(const char *path, const char *mode) {
    (void)path;
    (void)mode;
    static int dummy;
    return &dummy;
}

// 空实现fclose
int fclose(void *stream) {
    (void)stream;
    return 0;
}

将上述代码保存为gcov_stubs.c,编译后与目标代码一同链接即可。

2. 使用段存储模式替代文件IO

GCC 11+支持将覆盖数据存储到ELF专用段中,彻底避免文件系统依赖:

  • 修改编译命令,添加-fprofile-generate=section参数:
riscv64-unknown-elf-gcc -mcmodel=medany -static \
  -O0 -ffast-math -fno-common -fno-builtin-printf \
  --coverage -fprofile-generate=section -fprofile-info-section -ffunction-sections -fdata-sections \
  -g -c test_gcov.c

链接命令保持不变,需确保链接脚本未排除.gcov相关段(默认会自动包含)。

程序运行后,覆盖数据会存入ELF的.gcov段,可通过以下步骤提取并分析:

# 提取覆盖数据段
riscv64-unknown-elf-objcopy -O binary --only-section=.gcov test_gcov.riscv gcov_data.bin
# 在主机上分析覆盖数据
gcov -b test_gcov.c --gcov-data=gcov_data.bin

3. 定制编译libgcov库

若需深度适配硬件存储,可下载GCC源码,修改libgcov-driver.c中的文件操作逻辑,替换为硬件读写接口后,重新编译riscv64-unknown-elf-gcc的libgcov库。此方式灵活性最高,但需熟悉GCC源码结构。


内容的提问来源于stack exchange,提问作者Jimmy Stone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 23:14:58