Skip to content

Commit

Permalink
rust: add start-group/end-group arguments for libraries
Browse files Browse the repository at this point in the history
rustc tries to use lld to link Rust programs, via the -fuse-ld=lld flag
to gcc or clang.  However, when absent it will use the GNU linker.
While lld seems to be adding --start-group/--end-group, 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).  To improve compatibility, add --start-group and
--end-group options like Meson already does with C programs.

The code is slightly different due to the rustc-specific "--emit" and
"--extern" options, therefore it's not really possible to share it
with the C-like compiler mixin.

Signed-off-by: Paolo Bonzini <[email protected]>
  • Loading branch information
bonzini committed Dec 20, 2024
1 parent 191fa33 commit 502317e
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 0 deletions.
36 changes: 36 additions & 0 deletions mesonbuild/compilers/rust.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
import re
import typing as T

from .. import arglist
from .. import options
from ..linkers.linkers import GnuLikeDynamicLinkerMixin, SolarisDynamicLinker, CompCertDynamicLinker

Check failure

Code scanning / CodeQL

Module-level cyclic import Error

'GnuLikeDynamicLinkerMixin' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GnuLikeDynamicLinkerMixin occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GnuLikeDynamicLinkerMixin' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GnuLikeDynamicLinkerMixin occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GnuLikeDynamicLinkerMixin' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GnuLikeDynamicLinkerMixin occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GnuLikeDynamicLinkerMixin' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GnuLikeDynamicLinkerMixin occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GnuLikeDynamicLinkerMixin' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GnuLikeDynamicLinkerMixin occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GnuLikeDynamicLinkerMixin' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GnuLikeDynamicLinkerMixin occurs after the cyclic
import
of mesonbuild.compilers.rust.

Check failure

Code scanning / CodeQL

Module-level cyclic import Error

'SolarisDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of SolarisDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'SolarisDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of SolarisDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'SolarisDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of SolarisDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'SolarisDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of SolarisDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'SolarisDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of SolarisDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'SolarisDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of SolarisDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.

Check failure

Code scanning / CodeQL

Module-level cyclic import Error

'CompCertDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of CompCertDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'CompCertDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of CompCertDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'CompCertDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of CompCertDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'CompCertDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of CompCertDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'CompCertDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of CompCertDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'CompCertDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of CompCertDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
from ..mesonlib import EnvironmentException, MesonException, Popen_safe_logged
from ..options import OptionKey
from .compilers import Compiler, clike_debug_args
from .mixins.clike import GROUP_FLAGS

Check failure

Code scanning / CodeQL

Module-level cyclic import Error

'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.

if T.TYPE_CHECKING:
from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType
Expand Down Expand Up @@ -62,6 +65,35 @@ def get_rustup_run_and_args(exelist: T.List[str]) -> T.Optional[T.Tuple[T.List[s
except StopIteration:
return None


class RustcCompilerArgs(arglist.CompilerArgs):

Check failure

Code scanning / CodeQL

Module-level cyclic import Error

'CompilerArgs' may not be defined if module
mesonbuild.arglist
is imported before module
mesonbuild.compilers.rust
, as the
definition
of CompilerArgs occurs after the cyclic
import
of mesonbuild.compilers.rust.
def to_native_inplace(self):
# Check if we need to add --start/end-group for circular dependencies
# between static libraries, and for recursively searching for symbols
# needed by static libraries that are provided by object files or
# shared libraries.
# This covers all ld.bfd, ld.gold, ld.gold, and xild on Linux, which
# all act like (or are) gnu ld
if isinstance(self.compiler.linker, (GnuLikeDynamicLinkerMixin, SolarisDynamicLinker, CompCertDynamicLinker)):
group_start = -1
group_end = -1
last_rustc_arg = -1
for i, each in enumerate(self):
if each in {'--emit', '-o', '--extern'}:
last_rustc_arg = i
if last_rustc_arg == i - 1 or not GROUP_FLAGS.search(each):
continue
group_end = i
if group_start < 0:
# First occurrence of a library
group_start = i
# Only add groups if there are multiple libraries.
if group_end > group_start >= 0:
# Last occurrence of a library
self.insert(group_end + 1, '-Clink-arg=-Wl,--end-group')
self.insert(group_start, '-Clink-arg=-Wl,--start-group')


class RustCompiler(Compiler):

# rustc doesn't invoke the compiler itself, it doesn't need a LINKER_PREFIX
Expand Down Expand Up @@ -100,6 +132,10 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic
self.base_options.add(OptionKey('b_vscrt'))
self.native_static_libs: T.List[str] = []

def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> RustcCompilerArgs:
# This is correct, mypy just doesn't understand co-operative inheritance
return RustcCompilerArgs(self, args)

def needs_static_linker(self) -> bool:
return False

Expand Down
11 changes: 11 additions & 0 deletions test cases/rust/27 staticlib group/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 staticlib group/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 staticlib group/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 staticlib group/lib2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

void from_lib1(void);
6 changes: 6 additions & 0 deletions test cases/rust/27 staticlib group/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include "lib1.h"
#include "lib2.h"

void main() {
c_func();
}
9 changes: 9 additions & 0 deletions test cases/rust/27 staticlib group/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();
}
}
13 changes: 13 additions & 0 deletions test cases/rust/27 staticlib group/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
project('staticlib group', 'c', 'rust')

ld_bfd = find_program('ld.bfd')
if not ld_bfd.found()
error('MESON_SKIP_TEST ld.bfd not found')
endif

lib1 = static_library('lib1', 'lib1.c')
lib2 = static_library('lib2', 'lib2.c')
executable('lib1first', 'main.rs', link_with : [lib1, lib2],
rust_args: ['-C', 'lto=n', '-C', 'linker-plugin-lto=n', '-Clink-arg=-fuse-ld=bfd'])
executable('lib2first', 'main.rs', link_with : [lib2, lib1],
rust_args: ['-C', 'lto=n', '-C', 'linker-plugin-lto=n', '-Clink-arg=-fuse-ld=bfd'])

0 comments on commit 502317e

Please sign in to comment.