[英]Rust linker errors: using "-Wl,--as-needed" when linking system libraries
I'm trying to write an application primarily in rust that uses a gtk-based frontend written in C++. I've gotten pretty far in getting the build setup in Cargo, but it's failing in the linking stage.我正在尝试主要在 rust 中编写一个应用程序,该应用程序使用在 C++ 中编写的基于 gtk 的前端。我在 Cargo 中获得构建设置已经很远了,但它在链接阶段失败了。
I've created a minimal reproducible example.我创建了一个最小的可重现示例。
// build.rs
extern crate cc;
extern crate pkg_config;
fn main() {
let gtk = pkg_config::probe_library("gtk+-3.0").unwrap();
cc::Build::new()
.cpp(true)
.file("src/gui.cc")
.includes(gtk.include_paths)
.compile("gui");
}
// src/gui.cc
// This is essentially the first example from https://docs.gtk.org/gtk3/getting_started.html
#include <gtk/gtk.h>
extern "C" {
int run_gui();
}
static void activate(GtkApplication* app, gpointer) {
GtkWidget* window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Window");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
gtk_widget_show_all (window);
}
int run_gui() {
GtkApplication* app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (activate), nullptr);
int status = g_application_run (G_APPLICATION (app), 0, nullptr);
g_object_unref (app);
return status;
}
// src/main.rs
extern "C" {
fn run_gui() -> core::ffi::c_int;
}
fn main() {
unsafe { run_gui(); }
}
When I cargo build
the package, compiling the c++ and rust files seems to work fine, but then I get undefined reference errors on the link stage.当我cargo build
package 时,编译 c++ 和 rust 文件似乎工作正常,但随后我在链接阶段遇到未定义的引用错误。
/usr/bin/ld: /home/pete/workspaces/csvtk/so-minimal-gtk/target/debug/build/so-minimal-gtk-75c7a9a61d32d2e6/out/libgui.a(gui.o): in function `activate(_GtkApplication*, void*)':
/home/pete/workspaces/csvtk/so-minimal-gtk/src/gui.cc:9: undefined reference to `gtk_application_window_new'
/usr/bin/ld: /home/pete/workspaces/csvtk/so-minimal-gtk/src/gui.cc:10: undefined reference to `gtk_window_get_type'
...
The link line, which is quite long, is also logged before that error.很长的链接行也在该错误之前记录。 It includes all the required gtk libraries, -lgtk-3
, -lcairo
, etc. When I run it outside of cargo, it fails with the same errors, but, when I remove the -Wl,--as-needed
flag from the link line, it links correctly and the program runs fine.它包括所有必需的 gtk 库、-lgtk -Wl,--as-needed
-lgtk-3
、 -lcairo
等。当我在 cargo 之外运行它时,它失败并出现相同的错误,但是,当我从链接线,它链接正确,程序运行良好。
Why did rustc add -Wl,--as-needed
?为什么 rustc 添加 -Wl -Wl,--as-needed
? Is there a reason those gtk symbols aren't "needed" from the perspective of the linker?从 linker 的角度来看,这些 gtk 符号不是“需要”的原因吗? Any tips on how to fix this problem elegantly?关于如何优雅地解决这个问题的任何提示?
In general, using --as-needed
as a flag to the linker is extremely helpful because it prevents your binary from being linked to libraries it doesn't use directly.通常,使用--as-needed
作为 linker 的标志非常有用,因为它可以防止您的二进制文件链接到它不直接使用的库。 As an example of a reason why this is beneficial, if you depend on libfoo 1.0, which depends on libbar 1.0, and later on libfoo 1.1 updates to libbar 2.0, then as long as you don't use libbar directly, your code will continue to work with --as-needed
, but won't work without it (unless libbar has symbol versioning).举例说明为什么这是有益的,如果你依赖 libfoo 1.0,它依赖于 libbar 1.0,后来 libfoo 1.1 更新到 libbar 2.0,那么只要你不直接使用 libbar,你的代码就会继续与--as-needed
一起工作,但没有它就无法工作(除非 libbar 有符号版本控制)。
The kind of impact is visible here when you use objdump -x
on the binary after making the change I suggest below:当您在进行我在下面建议的更改后对二进制文件使用objdump -x
时,这种影响在这里是可见的:
NEEDED libgtk-3.so.0
NEEDED libgio-2.0.so.0
NEEDED libgobject-2.0.so.0
NEEDED libgcc_s.so.1
NEEDED libc.so.6
NEEDED ld-linux-x86-64.so.2
ldd
lists 70 libraries linked into your program, and you're only directly requiring to 6, including the dynamic linker. ldd
列出了 70 个链接到您的程序的库,您只需要直接要求 6 个,包括动态的 linker。
You can usually rely on other crates to specify their link dependencies correctly.您通常可以依靠其他 crate 来正确指定它们的链接依赖项。 However, you need to be careful because when you're creating your own dependency on a library using C or C++, you have to specify the libraries to link with yourself.但是,您需要小心,因为当您使用 C 或 C++ 创建自己对库的依赖项时,您必须指定要与自己链接的库。 This can be seen if you use cargo build -v
, where you can notice that your gui
library is linked after the dependencies of libgtk-3:如果您使用cargo build -v
可以看到这一点,您可以注意到您的gui
库链接在libgtk-3 的依赖项之后:
Running `rustc --crate-name test_repo --edition=2021 src/main.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --crate-type bin --emit=dep-info,link -C embed-bitcode=no -C debuginfo=2 -C metadata=c60f7e6ab8348a08 -C extra-filename=-c60f7e6ab8348a08 --out-dir /tmp/user/1000/test-repo/target/debug/deps -C incremental=/tmp/user/1000/test-repo/target/debug/incremental -L dependency=/tmp/user/1000/test-repo/target/debug/deps -L native=/usr/lib/x86_64-linux-gnu -L native=/tmp/user/1000/test-repo/target/debug/build/test-repo-c89dae0927f2103a/out -l gtk-3 -l gdk-3 -l z -l pangocairo-1.0 -l pango-1.0 -l harfbuzz -l atk-1.0 -l cairo-gobject -l cairo -l gdk_pixbuf-2.0 -l gio-2.0 -l gobject-2.0 -l glib-2.0 -l static=gui -l stdc++`
So you'd want to do something more like this in your build.rs
:所以你想在你的build.rs
中做更多类似的事情:
// build.rs
extern crate cc;
extern crate pkg_config;
fn main() {
let gtk = pkg_config::probe_library("gtk+-3.0").unwrap();
cc::Build::new()
.cpp(true)
.file("src/gui.cc")
.includes(gtk.include_paths)
.compile("gui");
for path in gtk.link_paths {
println!("cargo:rustc-link-search={}", path.display());
}
for lib in gtk.libs {
println!("cargo:rustc-link-lib={}", lib);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.