Skip to content

Commit

Permalink
Implement RFC 2951: Native link modifiers
Browse files Browse the repository at this point in the history
This commit implements both the native linking modifiers infrastructure
as well as an initial attempt at the individual modifiers from the RFC.
It also introduces a feature flag for the general syntax along with
individual feature flags for each modifier.
  • Loading branch information
luqmana committed May 5, 2021
1 parent 8a9fa36 commit 71b5ccb
Show file tree
Hide file tree
Showing 38 changed files with 829 additions and 170 deletions.
39 changes: 39 additions & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,45 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
);
}
}

// Check for unstable modifiers on `#[link(..)]` attribute
if self.sess.check_name(attr, sym::link) {
for nested_meta in attr.meta_item_list().unwrap_or_default() {
if nested_meta.has_name(sym::modifiers) {
gate_feature_post!(
self,
native_link_modifiers,
nested_meta.span(),
"native link modifiers are experimental"
);

if let Some(modifiers) = nested_meta.value_str() {
for modifier in modifiers.as_str().split(',') {
if let Some(modifier) = modifier.strip_prefix(&['+', '-'][..]) {
macro_rules! gate_modifier { ($($name:literal => $feature:ident)*) => {
$(if modifier == $name {
let msg = concat!("`#[link(modifiers=\"", $name, "\")]` is unstable");
gate_feature_post!(
self,
$feature,
nested_meta.name_value_literal_span().unwrap(),
msg
);
})*
}}

gate_modifier!(
"bundle" => native_link_modifiers_bundle
"verbatim" => native_link_modifiers_verbatim
"whole-archive" => native_link_modifiers_whole_archive
"as-needed" => native_link_modifiers_as_needed
);
}
}
}
}
}
}
}

fn visit_item(&mut self, i: &'a ast::Item) {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_codegen_cranelift/src/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
));
}

fn add_native_library(&mut self, name: rustc_span::symbol::Symbol) {
let location = find_library(name, &self.lib_search_paths, self.sess);
fn add_native_library(&mut self, name: rustc_span::symbol::Symbol, verbatim: bool) {
let location = find_library(name, verbatim, &self.lib_search_paths, self.sess);
self.add_archive(location.clone(), |_| false).unwrap_or_else(|e| {
panic!("failed to add native library {}: {}", location.to_string_lossy(), e);
});
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_codegen_llvm/src/back/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,9 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {

/// Adds all of the contents of a native library to this archive. This will
/// search in the relevant locations for a library named `name`.
fn add_native_library(&mut self, name: Symbol) {
let location = find_library(name, &self.config.lib_search_paths, self.config.sess);
fn add_native_library(&mut self, name: Symbol, verbatim: bool) {
let location =
find_library(name, verbatim, &self.config.lib_search_paths, self.config.sess);
self.add_archive(&location, |_| false).unwrap_or_else(|e| {
self.config.sess.fatal(&format!(
"failed to add native library {}: {}",
Expand Down
16 changes: 12 additions & 4 deletions compiler/rustc_codegen_ssa/src/back/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,19 @@ use rustc_span::symbol::Symbol;
use std::io;
use std::path::{Path, PathBuf};

pub fn find_library(name: Symbol, search_paths: &[PathBuf], sess: &Session) -> PathBuf {
pub fn find_library(
name: Symbol,
verbatim: bool,
search_paths: &[PathBuf],
sess: &Session,
) -> PathBuf {
// On Windows, static libraries sometimes show up as libfoo.a and other
// times show up as foo.lib
let oslibname =
format!("{}{}{}", sess.target.staticlib_prefix, name, sess.target.staticlib_suffix);
let oslibname = if verbatim {
name.to_string()
} else {
format!("{}{}{}", sess.target.staticlib_prefix, name, sess.target.staticlib_suffix)
};
let unixlibname = format!("lib{}.a", name);

for path in search_paths {
Expand Down Expand Up @@ -45,7 +53,7 @@ pub trait ArchiveBuilder<'a> {
lto: bool,
skip_objects: bool,
) -> io::Result<()>;
fn add_native_library(&mut self, name: Symbol);
fn add_native_library(&mut self, name: Symbol, verbatim: bool);
fn update_symbols(&mut self);

fn build(self);
Expand Down
73 changes: 47 additions & 26 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,15 +329,15 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
// metadata of the rlib we're generating somehow.
for lib in codegen_results.crate_info.used_libraries.iter() {
match lib.kind {
NativeLibKind::StaticBundle => {}
NativeLibKind::StaticNoBundle
| NativeLibKind::Dylib
| NativeLibKind::Framework
NativeLibKind::Static { bundle: None | Some(true), .. } => {}
NativeLibKind::Static { bundle: Some(false), .. }
| NativeLibKind::Dylib { .. }
| NativeLibKind::Framework { .. }
| NativeLibKind::RawDylib
| NativeLibKind::Unspecified => continue,
}
if let Some(name) = lib.name {
ab.add_native_library(name);
ab.add_native_library(name, lib.verbatim.unwrap_or(false));
}
}

Expand Down Expand Up @@ -430,9 +430,10 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>(
// Clearly this is not sufficient for a general purpose feature, and
// we'd want to read from the library's metadata to determine which
// object files come from where and selectively skip them.
let skip_object_files = native_libs
.iter()
.any(|lib| lib.kind == NativeLibKind::StaticBundle && !relevant_lib(sess, lib));
let skip_object_files = native_libs.iter().any(|lib| {
matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. })
&& !relevant_lib(sess, lib)
});
ab.add_rlib(
path,
&name.as_str(),
Expand Down Expand Up @@ -931,7 +932,7 @@ fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) {
let path = find_sanitizer_runtime(&sess, &filename);
let rpath = path.to_str().expect("non-utf8 component in path");
linker.args(&["-Wl,-rpath", "-Xlinker", rpath]);
linker.link_dylib(Symbol::intern(&filename));
linker.link_dylib(Symbol::intern(&filename), false, true);
} else {
let filename = format!("librustc{}_rt.{}.a", channel, name);
let path = find_sanitizer_runtime(&sess, &filename).join(&filename);
Expand Down Expand Up @@ -1080,21 +1081,25 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) {
.filter_map(|lib| {
let name = lib.name?;
match lib.kind {
NativeLibKind::StaticNoBundle
| NativeLibKind::Dylib
NativeLibKind::Static { bundle: Some(false), .. }
| NativeLibKind::Dylib { .. }
| NativeLibKind::Unspecified => {
let verbatim = lib.verbatim.unwrap_or(false);
if sess.target.is_like_msvc {
Some(format!("{}.lib", name))
Some(format!("{}{}", name, if verbatim { "" } else { ".lib" }))
} else if sess.target.linker_is_gnu {
Some(format!("-l{}{}", if verbatim { ":" } else { "" }, name))
} else {
Some(format!("-l{}", name))
}
}
NativeLibKind::Framework => {
NativeLibKind::Framework { .. } => {
// ld-only syntax, since there are no frameworks in MSVC
Some(format!("-framework {}", name))
}
// These are included, no need to print them
NativeLibKind::StaticBundle | NativeLibKind::RawDylib => None,
NativeLibKind::Static { bundle: None | Some(true), .. }
| NativeLibKind::RawDylib => None,
}
})
.collect();
Expand Down Expand Up @@ -1808,11 +1813,20 @@ fn add_local_native_libraries(
Some(l) => l,
None => continue,
};
let verbatim = lib.verbatim.unwrap_or(false);
match lib.kind {
NativeLibKind::Dylib | NativeLibKind::Unspecified => cmd.link_dylib(name),
NativeLibKind::Framework => cmd.link_framework(name),
NativeLibKind::StaticNoBundle => cmd.link_staticlib(name),
NativeLibKind::StaticBundle => cmd.link_whole_staticlib(name, &search_path),
NativeLibKind::Dylib { as_needed } => {
cmd.link_dylib(name, verbatim, as_needed.unwrap_or(true))
}
NativeLibKind::Unspecified => cmd.link_dylib(name, verbatim, true),
NativeLibKind::Framework { as_needed } => {
cmd.link_framework(name, as_needed.unwrap_or(true))
}
NativeLibKind::Static { bundle: None | Some(true), .. }
| NativeLibKind::Static { whole_archive: Some(true), .. } => {
cmd.link_whole_staticlib(name, verbatim, &search_path);
}
NativeLibKind::Static { .. } => cmd.link_staticlib(name, verbatim),
NativeLibKind::RawDylib => {
// FIXME(#58713): Proper handling for raw dylibs.
bug!("raw_dylib feature not yet implemented");
Expand Down Expand Up @@ -1996,9 +2010,10 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
// there's a static library that's not relevant we skip all object
// files.
let native_libs = &codegen_results.crate_info.native_libraries[&cnum];
let skip_native = native_libs
.iter()
.any(|lib| lib.kind == NativeLibKind::StaticBundle && !relevant_lib(sess, lib));
let skip_native = native_libs.iter().any(|lib| {
matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. })
&& !relevant_lib(sess, lib)
});

if (!are_upstream_rust_objects_already_included(sess)
|| ignored_for_lto(sess, &codegen_results.crate_info, cnum))
Expand Down Expand Up @@ -2136,22 +2151,28 @@ fn add_upstream_native_libraries(
if !relevant_lib(sess, &lib) {
continue;
}
let verbatim = lib.verbatim.unwrap_or(false);
match lib.kind {
NativeLibKind::Dylib | NativeLibKind::Unspecified => cmd.link_dylib(name),
NativeLibKind::Framework => cmd.link_framework(name),
NativeLibKind::StaticNoBundle => {
NativeLibKind::Dylib { as_needed } => {
cmd.link_dylib(name, verbatim, as_needed.unwrap_or(true))
}
NativeLibKind::Unspecified => cmd.link_dylib(name, verbatim, true),
NativeLibKind::Framework { as_needed } => {
cmd.link_framework(name, as_needed.unwrap_or(true))
}
NativeLibKind::Static { bundle: Some(false), .. } => {
// Link "static-nobundle" native libs only if the crate they originate from
// is being linked statically to the current crate. If it's linked dynamically
// or is an rlib already included via some other dylib crate, the symbols from
// native libs will have already been included in that dylib.
if data[cnum.as_usize() - 1] == Linkage::Static {
cmd.link_staticlib(name)
cmd.link_staticlib(name, verbatim)
}
}
// ignore statically included native libraries here as we've
// already included them when we included the rust library
// previously
NativeLibKind::StaticBundle => {}
NativeLibKind::Static { bundle: None | Some(true), .. } => {}
NativeLibKind::RawDylib => {
// FIXME(#58713): Proper handling for raw dylibs.
bug!("raw_dylib feature not yet implemented");
Expand Down
Loading

0 comments on commit 71b5ccb

Please sign in to comment.