GNU Arm嵌入式工具链:undefined reference to `__sync_synchronize'

4
我正在尝试更新裸金属嵌入式项目的工具链。我们在Windows上构建,到目前为止,我们一直使用版本5.4.1 20160609(发布)[ARM / embedded-5-branch修订版237715]。
现在我正在尝试版本9.3.1 20200408(发布)(9-2020-q2-update),但是在链接时出现了未定义的符号问题:报告缺少__sync_synchronize,并且我不知道应该从哪个源解决此符号。我需要链接一个我缺失的库吗?是否应该给编译器不同的标志,以便为该函数生成代码?
下面是一个示例,在旧工具链中编译和链接都很好,但在新工具链中失败。两种情况下都使用了这个命令行:
``` arm-none-eabi-g++ -mthumb -specs=nosys.specs sample.cpp ```
精确的错误消息如下所示:
``` c:/projects/cpt_tools/gcc-arm-none-eabi/9.3.1/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\Users\noone\AppData\Local\Temp\ccSZkMXN.o: in function 'use_static_inst(int)': sample.cpp:(.text+0xc): undefined reference to '__sync_synchronize' collect2.exe: error: ld returned 1 exit status ```
顺便说一句:在Linux上运行我的测试时没有任何区别:
``` noone@nowhere:~/gcc-arm-none-eabi-9-2020-q2-update/bin$ ./arm-none-eabi-g++ -mthumb -specs=nosys.specs sample.cpp /media/persistent_storage/home/rmatano/gcc-arm-none-eabi-9-2020-q2-update/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld: /tmp/ccltslyj.o: in function 'use_static_inst(int)': />sample.cpp:(.text+0xc): undefined reference to '__sync_synchronize' collect2: error: ld returned 1 exit status ```
我发现这个问题的解决方法是:GCC Linaro cross compile fails on linker step on a Windows host,但由于我的代码应该在相当陈旧的硬件('-cpu = arm926ej-s')上运行,因此我不能使用指定的解决方法-mcpu=cortex-a9
以下是sample.cpp的内容:
// compile with 'arm-none-eabi-g++.exe -mthumb -specs=nosys.specs sample.cpp'
//
// result: in function `use_static_inst(int)':
// sample.cpp:(.text+0x18): undefined reference to `__sync_synchronize'

class A
{
int m_i;

public:

    A(int i) : m_i(i)
    {
    }

    int value(int x)
    {
        return m_i + x;
    }
};


int use_static_inst(int x)
{
    // in preparation for calling the ctor of this static instance
    // the compiler generates a call to __sync_synchronize
    static A a(0);

    return a.value(x);
}

int main(int argc, char* argv[])
{
    return use_static_inst(argc);
}

我假设您不需要线程安全。如果是这样,可以使用命令行参数“-fno-threadsafe-statics”来摆脱对__sync_synchronize的调用。 - Codo
实际上,使用-fno-threadsafe-statics可以解决链接器问题。不幸的是,我必须处理线程,并且该标志不仅抑制对__sync_synchronize的调用,还抑制了对__cxa_guard_acquire__cxa_guard_release的调用,而我希望保留这些调用。 - RoMa
2个回答

2

我在使用gcc-arm-none-eabi-6-2017-q1-update链接Cortex-R4时遇到了相同的错误。链接器错误如下:

Invoking: GNU Linker
"C:/ti/ccs1040/ccs/tools/compiler/gcc-arm-none-eabi-6-2017-q1-update/bin/arm-none-eabi-gcc-6.3.1.exe" -Og -g -gdwarf-3 -gstrict-dwarf -Wall -specs="nosys.specs" -mfloat-abi=hard -Wl,-Map,"RM46L850_GCC_halcogen_cpp.map" -o"RM46L850_GCC_halcogen_cpp.out" "./source/cpp_test.o" "./source/dabort.o" "./source/errata_SSWF021_45.o" "./source/esm.o" "./source/notification.o" "./source/pinmux.o" "./source/sci.o" "./source/sys_core.o" "./source/sys_dma.o" "./source/sys_intvecs.o" "./source/sys_main.o" "./source/sys_mpu.o" "./source/sys_pcr.o" "./source/sys_phantom.o" "./source/sys_pmm.o" "./source/sys_pmu.o" "./source/sys_selftest.o" "./source/sys_startup.o" "./source/sys_vim.o" "./source/system.o" -Wl,-T"../source/sys_link.ld"  -Wl,--start-group -lc -lstdc++ -Wl,--end-group 
makefile:163: recipe for target 'RM46L850_GCC_halcogen_cpp.out' failed
c:/ti/ccs1040/ccs/tools/compiler/gcc-arm-none-eabi-6-2017-q1-update/bin/../lib/gcc/arm-none-eabi/6.3.1/../../../../arm-none-eabi/lib/hard\libstdc++.a(locale_init.o): In function `(anonymous namespace)::get_locale_mutex()':
locale_init.cc:(.text._ZN12_GLOBAL__N_116get_locale_mutexEv+0xc): undefined reference to `__sync_synchronize'
c:/ti/ccs1040/ccs/tools/compiler/gcc-arm-none-eabi-6-2017-q1-update/bin/../lib/gcc/arm-none-eabi/6.3.1/../../../../arm-none-eabi/lib/hard\libstdc++.a(locale.o): In function `std::locale::_Impl::_M_install_cache(std::locale::facet const*, unsigned int)':
locale.cc:(.text._ZNSt6locale5_Impl16_M_install_cacheEPKNS_5facetEj+0x18): undefined reference to `__sync_synchronize'
c:/ti/ccs1040/ccs/tools/compiler/gcc-arm-none-eabi-6-2017-q1-update/bin/../lib/gcc/arm-none-eabi/6.3.1/../../../../arm-none-eabi/lib/hard\libstdc++.a(future.o): In function `std::future_category()':
future.cc:(.text._ZSt15future_categoryv+0xc): undefined reference to `__sync_synchronize'
collect2.exe: error: ld returned 1 exit status

在我的情况下,问题是基于TI Eclipse的Code Composer Studio创建Makefile时没有将相同的运行时选项传递给链接器以选择multi-lib库,就像传递给编译器一样。
编译器被赋予了-mcpu=cortex-r4 -mfloat-abi=hard -mfpu=vfpv3-d16,但链接器只被赋予了-mfloat-abi=hard。这导致链接器选择了具有对__sync_synchronize未定义引用的hard库。
一旦链接器被赋予与编译器相同的-mcpu=cortex-r4 -mfloat-abi=hard -mfpu=vfpv3-d16选项,就会选择thumb/v7-ar/fpv3/hard multi-lib库,链接成功。

参考https://e2e.ti.com/support/tools/code-composer-studio-group/ccs/f/code-composer-studio-forum/1023679/ccs-10-4-0-00006-for-the-gcc-arm-compiler-how-does-ccs-determine-how-many-of-the-compiler-runtime-options-to-pass-to-the-linker,询问为什么CCS没有将一致的Runtime选项传递给链接器和编译器。


2
免责声明:这并不是我的专业领域。
缺失的函数是实现内存屏障。这在多核CPU(如Cortex-A9)中是必需的,但在您的arm926ej-s上可能不需要。由于您使用的工具链明确地针对Cortex-M和Cortex-R CPU,如果新工具链未经过ARM9 CPU的大量测试(或根本没有测试),我不会感到惊讶。
我的第一个评论是,在我看来,您不应该让编译器使用其默认设置,并明确指定您正在针对的确切ARM架构和CPU-请注意,这并不能解决您的问题,链接器错误仍将存在。
arm-none-eabi-g++ -march=armv5tej -mcpu=arm926ej-s -mthumb -specs=nosys.specs sample.cpp 

一个有趣的事情值得注意的是,使用-marm编译不会触发链接器错误:
arm-none-eabi-g++ -march=armv5tej -mcpu=arm926ej-s -marm -specs=nosys.specs sample.cpp 

在这种情况下,为__sync_synchronize()生成的代码将是:
00011014 <__sync_synchronize>:
   11014:   f44f 637a   mov.w   r3, #4000   ; 0xfa0
   11018:   f6cf 73ff   movt    r3, #65535  ; 0xffff
   1101c:   4718        bx  r3
   1101e:   bf00        nop

我的理解是,这将导致程序分支到地址0xffff0fa0,这似乎有点奇怪:我们使用的是arm-none-eabi工具链,生成的代码似乎试图触发Linux kuser_memory_barrier的执行-请参见文档here。这可能相当于在Linux环境中调用__sync_synchronize()。我认为这是工具链中未检测到的错误。
现在,__sync_synchronize()是gcc内置函数,并确实发出完整的内存屏障。例如,针对Cortex-A9,它将生成一个数据内存屏障dmb ish指令。此指令是必需的,因为Cortex-A9支持乱序执行,因此程序在某些点上可能需要执行完整的内存屏障,更具体地说,如果存在多个核心。

值得注意的是,在Linux 5.9.6中实现kuser_memory_barrier的代码(文件arch/arm64/kernel/kuser32.S)也使用了dmb ish

__kuser_memory_barrier:         // 0xffff0fa0
    .inst   0xf57ff05b      //  dmb     ish
    .inst   0xe12fff1e      //  bx      lr

也就是说,如果需要数据内存屏障,则底层硬件可能需要提供对内置实现的最小支持。由于看起来arm926ej-s只有指令内存屏障IMB指令可用,因此这可能只是arm926ej-s不支持乱序执行,如果是这种情况,则不需要调用__sync_synchronize(),您可以尝试提供一个空/不执行任何操作的__sync_synchronize()实现,并仍然是安全的。您需要确定是否是这种情况。请注意,当涉及到紧密耦合的内存接口事务时,arm926ej-s参考手册仅提及乱序,但再次需要深入研究。您还应该查看为__cxa_guard_acquire()和__cxa_guard_release()生成的代码,以充分了解它们与您的硬件的关系。您可能不想在多线程应用程序中调试奇怪的问题。

例如,在 gcc-arm-none-eabi-9-2020-q2-update/arm-none-eabi/lib/libstdc++_nano.a 的实现中,我没有看到任何特定的指令可以阻止它们在您的目标上使用,但是我不熟悉 arm926ej-s 指令集,最终决策应由您做出:

Disassembly of section .text.__cxa_guard_acquire:

00000000 <__cxa_guard_acquire>:
   0:   e5902000    ldr r2, [r0]
   4:   e3120001    tst r2, #1
   8:   e1a03000    mov r3, r0
   c:   1a000006    bne 2c <__cxa_guard_acquire+0x2c>
  10:   e5d02001    ldrb    r2, [r0, #1]
  14:   e3520000    cmp r2, #0
  18:   0a000000    beq 20 <__cxa_guard_acquire+0x20>
  1c:   e7f000f0    udf #0
  20:   e3a00001    mov r0, #1
  24:   e5c30001    strb    r0, [r3, #1]
  28:   e12fff1e    bx  lr
  2c:   e3a00000    mov r0, #0
  30:   e12fff1e    bx  lr

Disassembly of section .text.__cxa_guard_abort:

00000000 <__cxa_guard_abort>:
   0:   e3a03000    mov r3, #0
   4:   e5c03001    strb    r3, [r0, #1]
   8:   e12fff1e    bx  lr

Disassembly of section .text.__cxa_guard_release:

00000000 <__cxa_guard_release>:
   0:   e3a03001    mov r3, #1
   4:   e5803000    str r3, [r0]
   8:   e12fff1e    bx  lr

guard_error.o:     file format elf32-littlearm

由于您在使用一个发布于2019年的C++编译器,而且该CPU系列已经接近20年的历史,因此这些额外的预防措施可能是必要的。


我不确定,但我正在思考是否接受您的答案作为解决方案。一方面,我非常确定,为__sync_synchronize()提供空实现是正确的方法(实例化静态类对象的结果代码与旧编译器相同)。另一方面,我仍然希望得到一个问题的答案:如果编译器没有提供实现,为什么会生成对内部函数的调用? - RoMa
我认为这可能是一个未被注意到的错误,或者存在于gcc错误列表中但尚未修复。ARM9架构已经有近20年的历史了,我不知道在发布gcc-arm-none-eabi-9-2020-q2-update之前执行的g++测试在arm926ej-s上执行了多少,并且是否正在执行与您的程序相同的结构。再次强调,这不是我的专业领域,您可能需要在接受答案之前得到其他人批准的答案。 - Frant
但我确信关键是要知道 arm926ej-s 是否能够按照需要执行乱序执行,就像需要内存屏障一样。如果不能,那么对 '__sync_synchronize' 的“什么也不做”实现应该可以工作。顺便问一下,你之前的代码是否使用了任何等效于 __sync_synchronize()__cxa_guard_acquire()__cxa_guard_acquire() 的东西?如果没有,那意味着使用空的 __sync_synchronize() 不会让事情变得更糟。通过查看旧代码的反汇编结果,您应该能够知道。 - Frant
这可能只是确认在arm926ej-s上不需要使用__sync_synchronize(),并且是为了支持更现代的架构而引入的。同样,这可能是我已知的gcc错误或未报告的错误,在编译ARM9时遇到。要100%确定,唯一的方法是找到何时添加对__sync_synchronize()的调用以及确切原因。顺便问一下,您的gcc 5.4.1是否支持-fno-threadsafe-statics选项? - Frant
两个编译器版本都支持 -fno-threadsafe-statics 选项,并在其激活时创建几乎完全相同的汇编代码(通过 -Wa,-adhlns="listing.file")。虽然存在指令和变量分配上的小差异,但代码的效果完全相同。 - RoMa
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接