From 20dcac35423abbc28f5cafa3631cb39cb46f2727 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 20 Dec 2024 10:37:34 +0100 Subject: [PATCH] rust: add external objects to the link command line Because rustc does not support extract_objects, QEMU creates a static library with all the C objects. It then passes this static library as link_whole, together with another static library containing general purpose utility functions which is passed as link_with. However, this is brittle because the two have a circular dependency and they cannot be merged because of the link_whole/link_with difference. While lld seems to have the --start-group/--end-group semantics automatically, ld.bfd can fail if these options are needed. This can cause difference between distros depending on how Rust is packaged (e.g. Ubuntu 22.04 and Debian bookworm seem to use ld.bfd). The simplest solution is for Meson to implement "objects:" properly for Rust. Then QEMU can use the same internal dependency objects that it already has in place for C programs. Signed-off-by: Paolo Bonzini --- docs/markdown/snippets/rust-objects.md | 4 ++++ mesonbuild/backend/ninjabackend.py | 13 ++++++++++--- mesonbuild/interpreter/interpreter.py | 2 ++ test cases/rust/27 objects/lib1.c | 11 +++++++++++ test cases/rust/27 objects/lib1.h | 4 ++++ test cases/rust/27 objects/lib2.c | 8 ++++++++ test cases/rust/27 objects/lib2.h | 3 +++ test cases/rust/27 objects/main.rs | 9 +++++++++ test cases/rust/27 objects/meson.build | 16 ++++++++++++++++ 9 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 docs/markdown/snippets/rust-objects.md create mode 100644 test cases/rust/27 objects/lib1.c create mode 100644 test cases/rust/27 objects/lib1.h create mode 100644 test cases/rust/27 objects/lib2.c create mode 100644 test cases/rust/27 objects/lib2.h create mode 100644 test cases/rust/27 objects/main.rs create mode 100644 test cases/rust/27 objects/meson.build diff --git a/docs/markdown/snippets/rust-objects.md b/docs/markdown/snippets/rust-objects.md new file mode 100644 index 000000000000..b1a005fefb6f --- /dev/null +++ b/docs/markdown/snippets/rust-objects.md @@ -0,0 +1,4 @@ +== `objects` added correctly to Rust executables == + +Any objects included in a Rust executable were previously ignored. They +are now added correctly. diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index a7057682d0fb..c441da38e8a0 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1985,7 +1985,7 @@ def get_rust_compiler_args(self, target: build.BuildTarget, rustc: Compiler, args += target.get_extra_args('rust') return args - def get_rust_compiler_deps_and_args(self, target: build.BuildTarget, rustc: Compiler) -> T.Tuple[T.List[str], T.List[RustDep], T.List[str]]: + def get_rust_compiler_deps_and_args(self, target: build.BuildTarget, rustc: Compiler) -> T.Tuple[T.List[str], T.List[str], T.List[RustDep], T.List[str]]: deps: T.List[str] = [] project_deps: T.List[RustDep] = [] args: T.List[str] = [] @@ -2017,6 +2017,11 @@ def _link_library(libname: str, static: bool, bundle: bool = False): type_ += ':' + ','.join(modifiers) args.append(f'-l{type_}={libname}') + objs, od = self.flatten_object_list(target) + for o in objs: + args.append(f'-Clink-arg={o}') + fortran_order_deps = self.get_fortran_order_deps(od) + linkdirs = mesonlib.OrderedSet() external_deps = target.external_deps.copy() target_deps = target.get_dependencies() @@ -2102,7 +2107,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False): if isinstance(target, build.SharedLibrary) or has_shared_deps: args += self.get_build_rpath_args(target, rustc) - return deps, project_deps, args + return deps, fortran_order_deps, project_deps, args def generate_rust_target(self, target: build.BuildTarget) -> None: rustc = target.compilers['rust'] @@ -2126,7 +2131,7 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: depfile = os.path.join(self.get_target_private_dir(target), target.name + '.d') args += self.get_rust_compiler_args(target, rustc, depfile) - deps, project_deps, deps_args = self.get_rust_compiler_deps_and_args(target, rustc) + deps, fortran_order_deps, project_deps, deps_args = self.get_rust_compiler_deps_and_args(target, rustc) args += deps_args proc_macro_dylib_path = None @@ -2144,6 +2149,8 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: element = NinjaBuildElement(self.all_outputs, target_name, compiler_name, main_rust_file) if orderdeps: element.add_orderdep(orderdeps) + if fortran_order_deps: + element.add_orderdep(fortran_order_deps) if deps: # dependencies need to cause a relink, they're not just for ordering element.add_dep(deps) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index d717485e8a1e..c82253be7b3f 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3487,6 +3487,8 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs target = targetclass(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, self.environment, self.compilers[for_machine], kwargs) + if objs and target.uses_rust(): + FeatureNew.single_use('objects in Rust targets', '1.7.0', self.subproject) self.add_target(name, target) self.project_args_frozen = True diff --git a/test cases/rust/27 objects/lib1.c b/test cases/rust/27 objects/lib1.c new file mode 100644 index 000000000000..8e759852e190 --- /dev/null +++ b/test cases/rust/27 objects/lib1.c @@ -0,0 +1,11 @@ +#include +#include "lib1.h" +#include "lib2.h" + +void from_lib2(void) { + printf("hello world"); +} + +void c_func(void) { + from_lib1(); +} diff --git a/test cases/rust/27 objects/lib1.h b/test cases/rust/27 objects/lib1.h new file mode 100644 index 000000000000..8bb18d4bbe4e --- /dev/null +++ b/test cases/rust/27 objects/lib1.h @@ -0,0 +1,4 @@ +#pragma once + +void from_lib2(void); +void c_func(void); diff --git a/test cases/rust/27 objects/lib2.c b/test cases/rust/27 objects/lib2.c new file mode 100644 index 000000000000..a61d5349f878 --- /dev/null +++ b/test cases/rust/27 objects/lib2.c @@ -0,0 +1,8 @@ +#include +#include "lib1.h" +#include "lib2.h" + +void from_lib1(void) +{ + from_lib2(); +} diff --git a/test cases/rust/27 objects/lib2.h b/test cases/rust/27 objects/lib2.h new file mode 100644 index 000000000000..08c4cd30ad1e --- /dev/null +++ b/test cases/rust/27 objects/lib2.h @@ -0,0 +1,3 @@ +#pragma once + +void from_lib1(void); diff --git a/test cases/rust/27 objects/main.rs b/test cases/rust/27 objects/main.rs new file mode 100644 index 000000000000..538359943271 --- /dev/null +++ b/test cases/rust/27 objects/main.rs @@ -0,0 +1,9 @@ +extern "C" { + fn c_func(); +} + +fn main() { + unsafe { + c_func(); + } +} diff --git a/test cases/rust/27 objects/meson.build b/test cases/rust/27 objects/meson.build new file mode 100644 index 000000000000..b8afa0a50141 --- /dev/null +++ b/test cases/rust/27 objects/meson.build @@ -0,0 +1,16 @@ +project('staticlib group', 'c', 'rust', meson_version: '>=1.7.0') + +lib1 = static_library('lib1', 'lib1.c') +lib2 = static_library('lib2', 'lib2.c') +executable('lib1objs', 'main.rs', + objects: lib1.extract_all_objects(recursive: false), + link_with: lib2) +executable('lib2objs', 'main.rs', + objects: lib2.extract_all_objects(recursive: false), + link_with: lib1) +executable('lib1objs_as_dep', 'main.rs', + dependencies: declare_dependency(objects: lib1.extract_all_objects(recursive: false)), + link_with: lib2) +executable('lib2objs_as_dep', 'main.rs', + dependencies: declare_dependency(objects: lib2.extract_all_objects(recursive: false)), + link_with: lib1)