Skip to content

Commit

Permalink
rust: add external objects to the link command line
Browse files Browse the repository at this point in the history
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 <pbonzini@redhat.com>
  • Loading branch information
bonzini committed Dec 20, 2024
1 parent 6b29548 commit 20dcac3
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 3 deletions.
4 changes: 4 additions & 0 deletions docs/markdown/snippets/rust-objects.md
Original file line number Diff line number Diff line change
@@ -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.
13 changes: 10 additions & 3 deletions mesonbuild/backend/ninjabackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -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] = []
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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']
Expand All @@ -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
Expand All @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions test cases/rust/27 objects/lib1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <stdio.h>
#include "lib1.h"
#include "lib2.h"

void from_lib2(void) {
printf("hello world");
}

void c_func(void) {
from_lib1();
}
4 changes: 4 additions & 0 deletions test cases/rust/27 objects/lib1.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#pragma once

void from_lib2(void);
void c_func(void);
8 changes: 8 additions & 0 deletions test cases/rust/27 objects/lib2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <stdio.h>
#include "lib1.h"
#include "lib2.h"

void from_lib1(void)
{
from_lib2();
}
3 changes: 3 additions & 0 deletions test cases/rust/27 objects/lib2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

void from_lib1(void);
9 changes: 9 additions & 0 deletions test cases/rust/27 objects/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
extern "C" {
fn c_func();
}

fn main() {
unsafe {
c_func();
}
}
16 changes: 16 additions & 0 deletions test cases/rust/27 objects/meson.build
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit 20dcac3

Please sign in to comment.