Skip to content
Open
5 changes: 4 additions & 1 deletion compiler/rustc_attr_parsing/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ attr_parsing_expects_feature_list =
attr_parsing_expects_features =
`{$name}` expects feature names
attr_parsing_export_symbols_needs_static =
linking modifier `export-symbols` is only compatible with `static` linking kind
attr_parsing_import_name_type_raw =
import name type can only be used with link kind `raw-dylib`
Expand Down Expand Up @@ -95,7 +98,7 @@ attr_parsing_invalid_issue_string =
.neg_overflow = number too small to fit in target type
attr_parsing_invalid_link_modifier =
invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed
invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols
attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr}
.remove_neg_sugg = negative numbers are not literals, try removing the `-` sign
Expand Down
21 changes: 16 additions & 5 deletions compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ use super::util::parse_single_integer;
use crate::attributes::cfg::parse_cfg_entry;
use crate::fluent_generated;
use crate::session_diagnostics::{
AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ImportNameTypeRaw, ImportNameTypeX86,
IncompatibleWasmLink, InvalidLinkModifier, LinkFrameworkApple, LinkOrdinalOutOfRange,
LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows,
WholeArchiveNeedsStatic,
AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic,
ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier,
LinkFrameworkApple, LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers,
NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, WholeArchiveNeedsStatic,
};

pub(crate) struct LinkNameParser;
Expand Down Expand Up @@ -165,6 +165,14 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
cx.emit_err(BundleNeedsStatic { span });
}

(sym::export_symbols, Some(NativeLibKind::Static { export_symbols, .. })) => {
assign_modifier(export_symbols)
}

(sym::export_symbols, _) => {
cx.emit_err(ExportSymbolsNeedsStatic { span });
}

(sym::verbatim, _) => assign_modifier(&mut verbatim),

(
Expand All @@ -190,6 +198,7 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
span,
&[
sym::bundle,
sym::export_symbols,
sym::verbatim,
sym::whole_dash_archive,
sym::as_dash_needed,
Expand Down Expand Up @@ -285,7 +294,9 @@ impl LinkParser {
};

let link_kind = match link_kind {
kw::Static => NativeLibKind::Static { bundle: None, whole_archive: None },
kw::Static => {
NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }
}
sym::dylib => NativeLibKind::Dylib { as_needed: None },
sym::framework => {
if !sess.target.is_like_darwin {
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,13 @@ pub(crate) struct BundleNeedsStatic {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(attr_parsing_export_symbols_needs_static)]
pub(crate) struct ExportSymbolsNeedsStatic {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(attr_parsing_whole_archive_needs_static)]
pub(crate) struct WholeArchiveNeedsStatic {
Expand Down
101 changes: 95 additions & 6 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::{env, fmt, fs, io, mem, str};

use find_msvc_tools;
use itertools::Itertools;
use object::{Object, ObjectSection, ObjectSymbol};
use regex::Regex;
use rustc_arena::TypedArena;
use rustc_attr_parsing::eval_config_entry;
Expand Down Expand Up @@ -2185,6 +2186,83 @@ fn add_rpath_args(
}
}

fn add_c_staticlib_symbols(
sess: &Session,
name: &str,
out: &mut Vec<(String, SymbolExportKind)>,
) -> io::Result<()> {
for search_path in sess.target_filesearch().search_paths(PathKind::Native) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The search should be done using find_native_static_library.

Ideally NativeLib::filename would be used, but it's currently set only for bundled libraries, and changing that may mess up other things.

let file_path = search_path.dir.join(name);
if !file_path.exists() {
continue;
}

let archive_map = unsafe { Mmap::map(File::open(&file_path)?)? };

let archive = object::read::archive::ArchiveFile::parse(&*archive_map)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;

for member in archive.members() {
let member = member.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;

let data = member
.data(&*archive_map)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;

// clang LTO: raw LLVM bitcode
if data.starts_with(b"BC\xc0\xde") {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"LLVM bitcode object in C static library (LTO not supported)",
));
}

let object = object::File::parse(&*data)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;

// gcc / clang ELF / Mach-O LTO
if object.sections().any(|s| {
s.name().map(|n| n.starts_with(".gnu.lto_") || n == ".llvm.lto").unwrap_or(false)
}) {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"LTO object in C static library is not supported",
));
}

for symbol in object.symbols() {
if symbol.scope() != object::SymbolScope::Dynamic {
continue;
}

let mut name = match symbol.name() {
Ok(n) => n,
Err(_) => continue,
};

if sess.target.is_like_darwin {
if let Some(stripped) = name.strip_prefix('_') {
name = stripped;
}
}

let export_kind = match symbol.kind() {
object::SymbolKind::Text => SymbolExportKind::Text,
object::SymbolKind::Data => SymbolExportKind::Data,
_ => continue,
};

// FIXME:The symbol mangle rules are slightly different in 32-bit Windows. Need to be resolved.
out.push((name.to_string(), export_kind));
}
}

return Ok(());
}

Ok(())
}

/// Produce the linker command line containing linker path and arguments.
///
/// When comments in the function say "order-(in)dependent" they mean order-dependence between
Expand Down Expand Up @@ -2217,18 +2295,29 @@ fn linker_with_args(
);
let link_output_kind = link_output_kind(sess, crate_type);

let mut export_symbols = codegen_results.crate_info.exported_symbols[&crate_type].clone();
if crate_type == CrateType::Cdylib
&& !codegen_results.crate_info.exported_c_static_libraries.is_empty()
{
for lib in &codegen_results.crate_info.exported_c_static_libraries {
let name = format!(
"{}{}{}",
sess.target.staticlib_prefix, lib.name, sess.target.staticlib_suffix
);
if let Err(err) = add_c_staticlib_symbols(&sess, &name, &mut export_symbols) {
sess.dcx().fatal(format!("failed to process C static library `{}`: {}", name, err));
}
}
}

// ------------ Early order-dependent options ------------

// If we're building something like a dynamic library then some platforms
// need to make sure that all symbols are exported correctly from the
// dynamic library.
// Must be passed before any libraries to prevent the symbols to export from being thrown away,
// at least on some platforms (e.g. windows-gnu).
cmd.export_symbols(
tmpdir,
crate_type,
&codegen_results.crate_info.exported_symbols[&crate_type],
);
cmd.export_symbols(tmpdir, crate_type, &export_symbols);

// Can be used for adding custom CRT objects or overriding order-dependent options above.
// FIXME: In practice built-in target specs use this for arbitrary order-independent options,
Expand Down Expand Up @@ -2678,7 +2767,7 @@ fn add_native_libs_from_crate(
let name = lib.name.as_str();
let verbatim = lib.verbatim;
match lib.kind {
NativeLibKind::Static { bundle, whole_archive } => {
NativeLibKind::Static { bundle, whole_archive, .. } => {
if link_static {
let bundle = bundle.unwrap_or(true);
let whole_archive = whole_archive == Some(true);
Expand Down
16 changes: 14 additions & 2 deletions compiler/rustc_codegen_ssa/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ use rustc_ast::expand::allocator::{
ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorMethod, AllocatorMethodInput,
AllocatorTy,
};
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
use rustc_data_structures::sync::{IntoDynSyncSend, par_map};
use rustc_data_structures::unord::UnordMap;
use rustc_hir::attrs::{AttributeKind, DebuggerVisualizerType, OptimizeAttr};
use rustc_hir::attrs::{AttributeKind, DebuggerVisualizerType, NativeLibKind, OptimizeAttr};
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ItemId, Target, find_attr};
Expand Down Expand Up @@ -896,6 +896,17 @@ impl CrateInfo {
let local_crate_name = tcx.crate_name(LOCAL_CRATE);
let windows_subsystem = find_attr!(tcx.get_all_attrs(CRATE_DEF_ID), AttributeKind::WindowsSubsystem(kind, _) => *kind);

let mut seen = FxHashSet::default();
let exported_c_static_libraries = tcx
.native_libraries(LOCAL_CRATE)
.iter()
.filter(|lib| {
matches!(lib.kind, NativeLibKind::Static { export_symbols: Some(true), .. })
})
.filter(|lib| seen.insert(lib.name.clone()))
.map(Into::into)
.collect();

// This list is used when generating the command line to pass through to
// system linker. The linker expects undefined symbols on the left of the
// command line to be defined in libraries on the right, not the other way
Expand Down Expand Up @@ -944,6 +955,7 @@ impl CrateInfo {
natvis_debugger_visualizers: Default::default(),
lint_levels: CodegenLintLevels::from_tcx(tcx),
metadata_symbol: exported_symbols::metadata_symbol_name(tcx),
exported_c_static_libraries,
};

info.native_libraries.reserve(n_crates);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_ssa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ pub struct CrateInfo {
pub natvis_debugger_visualizers: BTreeSet<DebuggerVisualizerFile>,
pub lint_levels: CodegenLintLevels,
pub metadata_symbol: String,
pub exported_c_static_libraries: Vec<NativeLib>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CrateInfo already has used_libraries (or native_libraries) which can be filtered by export_symbols: Some(true) in link.rs, this field shouldn't be necessary.

}

/// Target-specific options that get set in `cfg(...)`.
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,8 @@ pub enum NativeLibKind {
bundle: Option<bool>,
/// Whether to link static library without throwing any object files away
whole_archive: Option<bool>,
/// Whether to export c static library symbols
export_symbols: Option<bool>,
},
/// Dynamic library (e.g. `libfoo.so` on Linux)
/// or an import library corresponding to a dynamic library (e.g. `foo.lib` on Windows/MSVC).
Expand Down Expand Up @@ -363,8 +365,8 @@ pub enum NativeLibKind {
impl NativeLibKind {
pub fn has_modifiers(&self) -> bool {
match self {
NativeLibKind::Static { bundle, whole_archive } => {
bundle.is_some() || whole_archive.is_some()
NativeLibKind::Static { bundle, whole_archive, export_symbols } => {
bundle.is_some() || whole_archive.is_some() || export_symbols.is_some()
}
NativeLibKind::Dylib { as_needed }
| NativeLibKind::Framework { as_needed }
Expand Down
18 changes: 9 additions & 9 deletions compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ fn test_native_libs_tracking_hash_different_values() {
NativeLib {
name: String::from("a"),
new_name: None,
kind: NativeLibKind::Static { bundle: None, whole_archive: None },
kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None },
verbatim: None,
},
NativeLib {
Expand All @@ -401,7 +401,7 @@ fn test_native_libs_tracking_hash_different_values() {
NativeLib {
name: String::from("a"),
new_name: None,
kind: NativeLibKind::Static { bundle: None, whole_archive: None },
kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None },
verbatim: None,
},
NativeLib {
Expand All @@ -423,13 +423,13 @@ fn test_native_libs_tracking_hash_different_values() {
NativeLib {
name: String::from("a"),
new_name: None,
kind: NativeLibKind::Static { bundle: None, whole_archive: None },
kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None },
verbatim: None,
},
NativeLib {
name: String::from("b"),
new_name: None,
kind: NativeLibKind::Static { bundle: None, whole_archive: None },
kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None },
verbatim: None,
},
NativeLib {
Expand All @@ -445,7 +445,7 @@ fn test_native_libs_tracking_hash_different_values() {
NativeLib {
name: String::from("a"),
new_name: None,
kind: NativeLibKind::Static { bundle: None, whole_archive: None },
kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None },
verbatim: None,
},
NativeLib {
Expand All @@ -467,7 +467,7 @@ fn test_native_libs_tracking_hash_different_values() {
NativeLib {
name: String::from("a"),
new_name: None,
kind: NativeLibKind::Static { bundle: None, whole_archive: None },
kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None },
verbatim: None,
},
NativeLib {
Expand Down Expand Up @@ -501,7 +501,7 @@ fn test_native_libs_tracking_hash_different_order() {
NativeLib {
name: String::from("a"),
new_name: None,
kind: NativeLibKind::Static { bundle: None, whole_archive: None },
kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None },
verbatim: None,
},
NativeLib {
Expand All @@ -528,7 +528,7 @@ fn test_native_libs_tracking_hash_different_order() {
NativeLib {
name: String::from("a"),
new_name: None,
kind: NativeLibKind::Static { bundle: None, whole_archive: None },
kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None },
verbatim: None,
},
NativeLib {
Expand All @@ -549,7 +549,7 @@ fn test_native_libs_tracking_hash_different_order() {
NativeLib {
name: String::from("a"),
new_name: None,
kind: NativeLibKind::Static { bundle: None, whole_archive: None },
kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None },
verbatim: None,
},
NativeLib {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_metadata/src/native_libs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ fn find_bundled_library(
tcx: TyCtxt<'_>,
) -> Option<Symbol> {
let sess = tcx.sess;
if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive } = kind
if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive, .. } = kind
&& tcx.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::StaticLib))
&& (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true))
{
Expand Down
Loading
Loading