尝试覆盖 C 中的弱函数时出现“错误:重新定义”

Getting “error: redefinition” when trying to override weak functions in C

提问人:Dale Wheat 提问时间:11/3/2023 更新时间:11/3/2023 访问量:66

问:

我正在尝试编写一些C库代码,为RV32EC微控制器(CH32V003)创建系统和设备中断向量表。它与 ARM Cortex-M 向量表的布局方式几乎相同:除非另有说明,否则弱定义的函数指针数组指向默认处理程序。然后,当我或库的某个未来用户想要定义他们自己的中断处理程序时,只需创建它,它就会覆盖默认条目。当我尝试覆盖默认函数时,我收到“错误:重新定义”。

我在 MacOS Sonoma 上使用 xpack-riscv-none-elf-gcc-13.2.0-2 GCC 工具链。我已经为这些芯片编写了几十个其他小程序,这些程序要么使用无中断,要么使用CH32V003上可用的其他一些中断处理方案,例如无向量表和均匀中断,并取得了成功。

在我的main.c文件的顶部附近,我有这样的代码:

// system vectors

// these are function prototypes that are supposed to be in the main.h header file

__attribute__ ((weak, interrupt)) void system_vector_default(void); // reserved slot(s) in system vector table
__attribute__ ((weak, interrupt, alias("system_vector_default"))) void system_vector_NMI(void); // non-maskable interrupt handler
__attribute__ ((weak, interrupt, alias("system_vector_default"))) void system_vector_HardFault(void); // hard fault handler
__attribute__ ((weak, interrupt, alias("system_vector_default"))) void system_vector_SysTick(void); // SysTick interrupt handler
__attribute__ ((weak, interrupt, alias("system_vector_default"))) void system_vector_SW(void); // software interrupt

void system_vector_default(void) { // reserved slot(s) in system vector table

    // note: this definition is required, as all the other weakly-linked functions point to it
}

void system_vector_NMI(void) {} // non-maskable interrupt handler
//void system_vector_HardFault(void) {} // hard fault handler
//void system_vector_SysTick(void) {} // SysTick interrupt handler
//void system_vector_SW(void) {} // software interrupt

__attribute__ ((section(".system_vector"))) void (* const system_vector_table[]) (void) = { // system vector table, skipping vector 0 (reserved)
    system_vector_default,              // 1 - reserved
    system_vector_NMI,                  // 2 - non-maskable interrupt handler
    system_vector_HardFault,            // 3 - hard fault interrupt handler
    system_vector_default,              // 4 - reserved
    system_vector_default,              // 5 - reserved
    system_vector_default,              // 6 - reserved
    system_vector_default,              // 7 - reserved
    system_vector_default,              // 8 - reserved
    system_vector_default,              // 9 - reserved
    system_vector_default,              // 10 - reserved
    system_vector_default,              // 11 - reserved
    system_vector_SysTick,              // 12 - SysTick interrupt handler
    system_vector_default,              // 13 - reserved
    system_vector_SW,                   // 14 - software interrupt handler
    system_vector_default,              // 15 - reserved
};

当我定义“system_vector_default()”函数而没有定义其他函数时,代码可以完美地编译并生成一个仅包含指向“system_default_vector()”函数的指针的向量表。这正是我想要的。

当我取消注释“system_vector_NMI()”函数的定义(如上所述)或任何其他“system_vector_*()”函数时,我收到以下错误消息:

main.c:22:6: error: redefinition of 'system_vector_NMI'
   22 | void system_vector_NMI(void) {} // non-maskable interrupt handler
      |      ^~~~~~~~~~~~~~~~~
main.c:12:72: note: previous definition of 'system_vector_NMI' with type 'void(void)'
   12 | __attribute__ ((weak, interrupt, alias("system_vector_default"))) void system_vector_NMI(void); // non-maskable interrupt handler
      |                                                                        ^~~~~~~~~~~~~~~~~
make: *** [J4-weak.elf] Error 1

编译器准确地告诉我它认为令人反感的东西:“system_vector_NMI()”函数的“原型”。也许我不明白函数原型是什么,也不知道如何使用:我以为它是一个“函数签名”,即返回类型和参数类型,没有函数体定义。

在互联网上搜索这个错误给我带来的只有羞耻。这是一个常见的初学者问题,尤其是当你不小心递归地包含一些带有声明的头文件时。我努力将“#include 守卫”添加到我的所有头文件中。在此示例中,为了清楚起见,我将所有代码直接导入到单个源文件中。我什至尝试将所有与矢量相关的东西放入其自己的vector.c/vector.h文件中,并发生相同的编译器错误。

因此,我希望编译器做的是创建我的系统向量表,使用未定义函数的默认值,但替换库用户提供的任何函数的地址。

所以我正在做“错误的事情”,我敢肯定,在这一点上,它要么是基于我对这些事情应该如何运作的无知,要么是我在此过程中做出的一些不正确的假设。

c gcc 中断 riscv32

评论

0赞 Lundin 11/3/2023
我认为是问题所在 - 您告诉编译器,您的所有 ISR 实际上都与 .然后你最终会得到多个函数定义,因为 和 是相同的函数。为什么需要首先使用?只需实现 etc 没有别名但链接较弱。aliassystem_vector_defaultsystem_vector_defaultsystem_vector_NMIaliasvoid system_vector_NMI(void) {}
0赞 Lundin 11/3/2023
更“老派”的向量表只是涉及将函数指针换成某些驱动程序中的函数指针,而不会出现所有弱链接头疼的问题,同时还坚持使用 100% 标准 C。然后你会得到一个特定于项目的向量表,但谁在乎呢。中断是特定于项目的。
0赞 Dale Wheat 11/3/2023
@Lundin - 是的,我同意你的看法,“别名”很可能是问题所在。我告诉编译器,所有的 ISR 实际上都与 system_vector_default() 完全相同,除非我用新函数覆盖它们。如果我将 system_vector_NMI() 声明为没有别名的弱地址,然后没有在其他地方实际实现/定义它,程序会编译,但将 0x0000 0000 的地址放在向量表的那个插槽中。如果 NMI 触发(毕竟它是不可屏蔽的),它将向量化为 0x0000 0000,这在当前设置中就像一个设备重置事件。形式不好。
0赞 Dale Wheat 11/3/2023
@Lundin - 任何其他 ISR 都存在相同的问题。虽然这个特殊的芯片提供了 4 个特定于系统的中断处理程序和 23 个特定于设备的中断处理程序,但现实情况是,其中只有两三个会在嵌入式设计中实际实现。有问题的芯片总共只有 16KB 的程序内存,我不希望在代码中定义很多“空”中断处理程序。我知道我正在尝试做的事情可以使用一些(尚未确定的组合)技术来完成。AVR libc 和 STM32 HAL 是我想到的两个例子。
0赞 Dale Wheat 11/3/2023
@Lundin - 另外,我应该重申,我正在尝试为这个芯片系列编写一个系统函数库。我为自己设定的一个设计要求是,除非将来的我(或其他图书馆用户)想去那里,否则低级细节是看不见的。

答: 暂无答案