为什么 `cargo build` 成功,但 `cargo build --release` 因静态库中未定义的引用而失败?
我正在编写一个 Rust CLI 工具,它使用我静态链接的 C 库。我按照 bindgen 教程 开始,目前可以创建一个可以与 cargo build
一起按预期工作的二进制文件。
但是,当我尝试使用 cargo build --release
构建项目时,我遇到了构建错误,表明我正在使用的外部 C 函数未定义,例如:
stumpless-logger/target/release/deps/libstumpless-292541b9e494f811.rlib(stumpless-292541b9e494f811.stumpless.7e987498-cgu.0.rcgu.o): In function `stumpless::FileTarget::new':
stumpless.7e987498-cgu.0:(.text._ZN9stumpless10FileTarget3new17hef1370275727c33dE+0x82): undefined reference to `stumpless_open_file_target'
nm
显示调试和发布库在各自目标目录中构建的预期导出:
# first with the debug
stumpless-logger$ nm --defined-only target/debug/build/stumpless-sys-ca96d217a4e3d9db/out/lib/libstumpless.a | grep file
0000000000000000 T raise_file_open_failure
0000000000000000 T raise_file_write_failure
file.c.o:
0000000000000000 T destroy_file_target
0000000000000000 T file_open_default_target
0000000000000000 T new_file_target
0000000000000000 T sendto_file_target
0000000000000000 T stumpless_close_file_target
0000000000000000 T stumpless_open_file_target
# and now with the release
stumpless-logger$ nm --defined-only target/release/build/stumpless-sys-6f81199b96080c30/out/lib/libstumpless.a | grep file
00000000 T raise_file_open_failure
00000000 T raise_file_write_failure
file.c.o:
00000000 T destroy_file_target
00000000 T file_open_default_target
00000000 T new_file_target
00000000 T sendto_file_target
00000000 T stumpless_close_file_target
00000000 T stumpless_open_file_target
我还可以构建并运行 使用示例来自 C 库,它使用以下之一据称是未定义的函数,它在调试和发布库中都按预期工作:
# first with the debug version
stumpless/docs/examples/file$ gcc file_example.c -L "stumpless-logger/target/debug/build/stumpless-sys-ca96d217a4e3d9db/out/lib" -l:libstumpless.a -I "stumpless-logger/target/debug/build/stumpless-sys-ca96d217a4e3d9db/out/include" -o example-with-debug
stumpless/docs/examples/file$ ./example-with-debug && cat example.log
<14>1 2022-03-06T16:18:11.649456Z Angus example-app-name - example-msgid [basic-element basic-param-name="basic-param-value"] This is an example message.
# and then with the release
rm example.log
stumpless/docs/examples/file$ gcc file_example.c -L "stumpless-logger/target/release/build/stumpless-sys-6f81199b96080c30/out/lib" -l:libstumpless.a -I "stumpless-logger/target/release/build/stumpless-sys-6f81199b96080c30/out/include" -o example-with-release
stumpless/docs/examples/file$ ./example-with-release && cat example.log
<14>1 2022-03-06T16:21:16.255854Z Angus example-app-name - example-msgid [basic-element basic-param-name="basic-param-value"] This is an example message.
起初我认为可以通过将 FFI 绑定拆分为 单独的 -sys crate 这似乎是标准做法。然而,这样做后问题仍然存在(但至少现在已经完成了,我想还有一线希望)。
我可以通过修改 release
构建配置文件以将优化级别设置为 0 来解决该问题。也就是说,将其添加到我的 Cargo.toml
清单中可以解决该问题:
[profile.release]
opt-level = 0
但这会将其带回来:
[profile.release]
opt-level = 1
我真的希望我的发布版本能够得到优化,因为我怀疑最终二进制文件的性能会更好。但不幸的是,在我明白为什么会发生这个问题之前,我不知道如何实现这一目标。任何能够解释为什么会发生这种情况和/或如何优化我的发布版本的人都将受到高度赞赏。
我是 Rust 开发的新手,所以我很可能错过了一些明显的东西。我确实在工具链的其他部分遇到了困难。然而,经过一天左右的研究和摆弄后,我无法在这个问题上找到任何答案,所以我向这里寻求帮助。至少,未来遇到同样问题的灵魂会更容易解决他们的问题。
附注,如果您将内容追溯到 C 构建本身,请注意我在调整某些内容时使用非默认分支,release-tune
Stumpless 分支 是相关的。
编辑 1我忘记包括我的 Rust 环境信息:我正在使用rustup
来管理我的安装,我的活动工具链如下:
stable-x86_64-unknown-linux-gnu (default)
rustc 1.59.0 (9d1b2106e 2022-02-23)
编辑2好吧,我设法解决了这个问题,但我仍然感到困惑至于为什么这可以解决它。我真的很感激您能提供的任何解释。
在我的 build.rs
文件中,处理静态库链接的代码包含这一行:
println!("cargo:rustc-link-lib=static=stumpless");
在将我的代码与教程进行比较时,我记得我添加了 =static
部分,而查看其他示例并决定最好更明确一些。我把它拿出来,你瞧,发布版本现在可以正常工作了。也就是说,该行变为:
println!("cargo:rustc-link-lib=stumpless");
它仍然是静态的,至少据我所知:我在 CMake 构建步骤中禁用共享库构建,因此没有动态库可供它查找。有谁知道为什么指定库的类型会对发布版本产生如此灾难性的影响?我确实检查过以确保我没有意外安装动态库,并且它不存在(即 ldconfig -p | grep Stumpless 不会产生任何输出。
I am writing a Rust CLI tool which uses a C library that I'm linking statically. I followed the bindgen tutorial to get started, and can currently create a binary that works as expected with cargo build
.
However, when I try to build the project with cargo build --release
, I am met with build errors indicating that the external C functions that I'm using are undefined, for example:
stumpless-logger/target/release/deps/libstumpless-292541b9e494f811.rlib(stumpless-292541b9e494f811.stumpless.7e987498-cgu.0.rcgu.o): In function `stumpless::FileTarget::new':
stumpless.7e987498-cgu.0:(.text._ZN9stumpless10FileTarget3new17hef1370275727c33dE+0x82): undefined reference to `stumpless_open_file_target'
nm
shows the expected exports for both the debug and release library builds in their respective target directories:
# first with the debug
stumpless-logger$ nm --defined-only target/debug/build/stumpless-sys-ca96d217a4e3d9db/out/lib/libstumpless.a | grep file
0000000000000000 T raise_file_open_failure
0000000000000000 T raise_file_write_failure
file.c.o:
0000000000000000 T destroy_file_target
0000000000000000 T file_open_default_target
0000000000000000 T new_file_target
0000000000000000 T sendto_file_target
0000000000000000 T stumpless_close_file_target
0000000000000000 T stumpless_open_file_target
# and now with the release
stumpless-logger$ nm --defined-only target/release/build/stumpless-sys-6f81199b96080c30/out/lib/libstumpless.a | grep file
00000000 T raise_file_open_failure
00000000 T raise_file_write_failure
file.c.o:
00000000 T destroy_file_target
00000000 T file_open_default_target
00000000 T new_file_target
00000000 T sendto_file_target
00000000 T stumpless_close_file_target
00000000 T stumpless_open_file_target
I can also build and run a usage example from the C library which uses one of the allegedly undefined functions, which works as expected with both the debug and release libraries:
# first with the debug version
stumpless/docs/examples/file$ gcc file_example.c -L "stumpless-logger/target/debug/build/stumpless-sys-ca96d217a4e3d9db/out/lib" -l:libstumpless.a -I "stumpless-logger/target/debug/build/stumpless-sys-ca96d217a4e3d9db/out/include" -o example-with-debug
stumpless/docs/examples/file$ ./example-with-debug && cat example.log
<14>1 2022-03-06T16:18:11.649456Z Angus example-app-name - example-msgid [basic-element basic-param-name="basic-param-value"] This is an example message.
# and then with the release
rm example.log
stumpless/docs/examples/file$ gcc file_example.c -L "stumpless-logger/target/release/build/stumpless-sys-6f81199b96080c30/out/lib" -l:libstumpless.a -I "stumpless-logger/target/release/build/stumpless-sys-6f81199b96080c30/out/include" -o example-with-release
stumpless/docs/examples/file$ ./example-with-release && cat example.log
<14>1 2022-03-06T16:21:16.255854Z Angus example-app-name - example-msgid [basic-element basic-param-name="basic-param-value"] This is an example message.
At first I thought this was something that would be resolved by splitting the FFI bindings into a separate -sys crate which seems to be standard practice. However the problem persists after doing this (but at least that's done now, silver linings I suppose).
I can make the problem go away by modifying the release
build profile to have an optimization level of 0. That is, adding this to my Cargo.toml
manifest resolves the problem:
[profile.release]
opt-level = 0
But this will bring it back:
[profile.release]
opt-level = 1
I tried explicitly disabling other optimizations listed in the profiles documentation, particularly LTO, to no avail. I thought perhaps my functions had mistakenly been removed during an LTO pass, but it is disabled by default (at least according to the docs), so this was unlikely to be the culprit anyway. And indeed, it still fails even with lto = false
in the manifest. Predictably, it also fails with lto = true
(it was a longshot, but I tried).
I'd really like my release builds to be optimized, as I suspect the end binary's performance would be better that way. But until I understand why this issue is happening, I'm unfortunately at a loss about how to achieve that. Anyone who can explain why this is happening and/or how to optimize my release builds would be highly appreciated.
I'm new to Rust development, so it's quite possible that I've missed something obvious. I've certainly struggled with other parts of the toolchain. However, I haven't been able to turn anything up on this issue after a day or so of research and fiddling, so I'm turning here for help. At the very least some future soul that encounters the same problem will have an easier time fixing their issue.
A side note, if you're tracing things to the C build itself note that I'm using a non-default branch as I tweak some things, the release-tune
branch of stumpless is what's relevant here.
EDIT 1 I forgot to include my Rust environmental information: I'm using rustup
to manage my install, with my active toolchain as such:
stable-x86_64-unknown-linux-gnu (default)
rustc 1.59.0 (9d1b2106e 2022-02-23)
EDIT 2 Well, I managed to resolve the issue, but I'm still perplexed as to why this resolves it. I would really appreciate any explanations that could be offered.
In my build.rs
file, the code that handles the static library linking contained this line:
println!("cargo:rustc-link-lib=static=stumpless");
While comparing my code to the tutorial I remembered that I added the =static
portion while looking at other examples and decided it was better to be more explicit. I took it out, and lo and behold, release builds now work correctly. That is, that line becomes:
println!("cargo:rustc-link-lib=stumpless");
It's still static, at least to my knowledge: I disable shared library building in the CMake build step, so there's no dynamic library for it to find. Does anyone know why specifying the type of library would have such a catastrophic effect on release builds? I did check to make sure that I haven't accidentally installed the dynamic library, and it is not there (that is, ldconfig -p | grep stumpless
yields no output.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我刚刚在尝试通过
lz4
板条箱链接lz4
时也遇到了这个问题。cargo test
编译良好并且测试是绿色的,因此代码可以运行。但是,cargo test --release
失败,并出现对 LZ4_decompress_safe_partial 的未定义引用
,我个人通过简单地使用不同的链接器来“修复”它。我将以下内容放入我的
~/.cargo/config
文件中,然后它就可以正常工作了。I just ran into this issue as well while trying to link with
lz4
via thelz4
crate.cargo test
compiled fine and tests were green, so the code was run. However,cargo test --release
fails withundefined reference to LZ4_decompress_safe_partial
I personally "fixed" it by simply using a different linker. I put the following in my
~/.cargo/config
file and then it worked without problems.