未定义的符号:带有 picolibc 的“stdout”-尽管在我的启动代码中定义了该符号(仅适用于 LTO)

Undefined symbol: `stdout` with picolibc - despite the symbol being defined in my startup code (only with LTO)

提问人:burnpanck 提问时间:11/9/2023 更新时间:11/9/2023 访问量:27

问:

我正在尝试将嵌入式项目从 CMake(通过短暂的绕道到 meson)转换为 Bazel。我们正在使用使用 picolibcllvm 嵌入式工具链。我或多或少地基于 bazel-embeded(它不直接支持 llvm-embed)手动设置工具链。

到目前为止,一切似乎都正常,除了链接失败,该链接从 的一部分引用自 。事实上,“Picolibc 和操作系统”解释了我们必须提供该符号。我们这样做是作为我们启动 C 文件的一部分,基本上完全复制了该文档。undefined symbol: stdouttinystdio/puts.cpicolibc

使用 CMake 和 meson,它可以完美地链接,但我无法让它与 bazel 链接。我确实注意到的一些差异是 bazel 使用 C 驱动程序 () 而不是 C++ 驱动程序 (),请参阅 bazel 问题 11122。假设差异大多不重要,C++ 链接问题可以通过直接链接到 和 或传递 来解决。clangclang++libc++libc++abi--driver-mode=g++

此外,它确实在没有 . 虽然对于这个测试,我们并不真正需要LTO,但我们在更大的构建中确实依赖于LTO,我们需要它来工作。-flto=thinblinky

下面是 bazel 在一次失败的尝试中创建的完整链接器命令行:

/path/to/LLVMEmbeddedToolchainForArm-17.0.1-Darwin-AArch64/bin/clang
  -o bazel-out/.../blinky
  bazel-out/.../blinky.o
  ...some-dependency-libs.a...
  bazel-out/.../libapollo4-fam-picolibc-startup.a
  -nodefaultlibs -nostdlib
  -T mcus/apollo4l/llvm-picolibc.ld
  -Wl,--start-group
    -lc -lc++ -lc++abi -lunwind -lclang_rt.builtins -lm -lcrt0
    bazel-out/.../libapollo4-fam-picolibc-startup.a
  -Wl,--end-group
  -Wl,--as-needed -Wl,--no-undefined
  -Wl,-v -v -Wl,-S -Wl,--gc-sections
  '-march=armv7em' '-mfloat-abi=hard' '-mfpu=fpv4-sp-d16' -mlittle-endian
  -Os '-flto=thin' '-fuse-ld=lld' '--driver-mode=g++'
  -fno-exceptions -fno-rtti
  '-mabi=aapcs' -mthumb '--target=armv7em-none-eabi'
  -static

该符号在 中提供。,然后打包所有 stdlib 之间,是我能够召集的最重的枪,但没有任何效果。stdoutlibapollo4-fam-picolibc-startup.a-nodefaultlibs -nostdlib-Wl,--start-group-Wl,--end-group

为了进行比较,这里是成功的介子命令行:

/path/to/LLVMEmbeddedToolchainForArm-17.0.1-Darwin-AArch64/bin/clang++
  -o blinky
  blinky.afx.p/blinky.cpp.o
  -flto=thin -Wl,--as-needed -Wl,--no-undefined
  -fuse-ld=/path/zo/LLVMEmbeddedToolchainForArm-17.0.1-Darwin-AArch64/bin/ld.lld
  -ffunction-sections
  -fdata-sections
  -fomit-frame-pointer
  --target=armv7em-none-eabi
  -mfpu=fpv4-sp-d16
  -Wl,--start-group
    libstartup.a
    ...dependency-libs.a...
    -T/path/to/llvm-picolibc.ld
    -Wl,-Map,/path/to/blinky.map
    /path/to/LLVMEmbeddedToolchainForArm-17.0.1-Darwin-AArch64//lib/clang-runtimes/arm-none-eabi/armv7m_soft_fpv4_sp_d16/lib/libcrt0.a
  -Wl,--end-group

在这里,可以在 中找到。stdoutlibstartup.a

我不知道还能尝试什么。

Linker Embedded Clang Bazel-CPP

评论

0赞 Clifford 11/10/2023
您告诉我们这是在您的启动代码中定义的,但您没有证明这一点。您提到 C++ 编译,是在 C++ 模块中定义的吗?如果是这样,那么您需要防止符号被篡改。它必须是 C 符号,而不是 C++ 符号,它将在 C++ 中使用类型和范围信息进行扩充。stdoutstdoutextern "C"
0赞 Clifford 11/10/2023
LTO 擅长删除它认为不需要的东西。即使您使用编译器指令来防止我发现。
1赞 burnpanck 11/10/2023
@Clifford:是的,我没有表现出来。启动代码基本上是我在项目中的唯一文件,它将该文件的编译器置于 C 模式。此外,on 的输出按名称列出全局可写数据符号,这表示不会发生 C++ 名称修改。.cllvm-nmlibapollo4-fam-picolibc-startup.a-------- D stdoutstdout
1赞 burnpanck 11/10/2023
有趣的是,如果没有 LTO,则 的输出是 ,因此是全局只读数据符号。llvm-nm00000000 R stdout
0赞 rivimey 11/16/2023
过去(使用不太智能的链接器),我遇到过这样的情况:您必须不止一次地提及一个库才能解析一个符号,因为它被另一个库中的某些东西引用,而该库本身必须被拉入。也许问题是您需要将具有 stdout 符号的库包含在链接行的不同位置,甚至两次(例如,在 -lc 之前和之后)?

答: 暂无答案