Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
100 changes: 93 additions & 7 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ 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;
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_errors::{DiagCtxtHandle, LintDiagnostic};
Expand Down Expand Up @@ -2185,6 +2186,76 @@ fn add_rpath_args(
}
}

fn add_c_staticlib_symbols(
sess: &Session,
lib: &NativeLib,
out: &mut Vec<(String, SymbolExportKind)>,
) -> io::Result<()> {
let file_path = find_native_static_library(lib.name.as_str(), lib.verbatim, sess);

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));
}
}

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 +2288,33 @@ 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 {
let mut seen = FxHashSet::default();

for lib in &codegen_results.crate_info.used_libraries {
if let NativeLibKind::Static { export_symbols: Some(true), .. } = lib.kind
&& seen.insert((lib.name, lib.verbatim))
{
if let Err(err) = add_c_staticlib_symbols(&sess, lib, &mut export_symbols) {
sess.dcx().fatal(format!(
"failed to process C static library `{}`: {}",
lib.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 +2764,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
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
15 changes: 12 additions & 3 deletions compiler/rustc_session/src/config/native_libs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ fn parse_native_lib(cx: &ParseNativeLibCx<'_>, value: &str) -> NativeLib {
let NativeLibParts { kind, modifiers, name, new_name } = split_native_lib_value(value);

let kind = kind.map_or(NativeLibKind::Unspecified, |kind| match kind {
"static" => NativeLibKind::Static { bundle: None, whole_archive: None },
"static" => {
NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }
}
"dylib" => NativeLibKind::Dylib { as_needed: None },
"framework" => NativeLibKind::Framework { as_needed: None },
"link-arg" => {
Expand Down Expand Up @@ -105,7 +107,7 @@ fn parse_and_apply_modifier(cx: &ParseNativeLibCx<'_>, modifier: &str, native_li
Some(("-", m)) => (m, false),
_ => cx.early_dcx.early_fatal(
"invalid linking modifier syntax, expected '+' or '-' prefix \
before one of: bundle, verbatim, whole-archive, as-needed",
before one of: bundle, verbatim, whole-archive, as-needed, export-symbols",
),
};

Expand All @@ -125,6 +127,13 @@ fn parse_and_apply_modifier(cx: &ParseNativeLibCx<'_>, modifier: &str, native_li
("bundle", _) => early_dcx
.early_fatal("linking modifier `bundle` is only compatible with `static` linking kind"),

("export-symbols", NativeLibKind::Static { export_symbols, .. }) => {
assign_modifier(export_symbols)
}
("export-symbols", _) => early_dcx.early_fatal(
"linking modifier `export-symbols` is only compatible with `static` linking kind",
),

("verbatim", _) => assign_modifier(&mut native_lib.verbatim),

("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
Expand All @@ -151,7 +160,7 @@ fn parse_and_apply_modifier(cx: &ParseNativeLibCx<'_>, modifier: &str, native_li

_ => early_dcx.early_fatal(format!(
"unknown linking modifier `{modifier}`, expected one \
of: bundle, verbatim, whole-archive, as-needed"
of: bundle, verbatim, whole-archive, as-needed, export-symbols"
)),
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,7 @@ symbols! {
explicit_tail_calls,
export_name,
export_stable,
export_symbols: "export-symbols",
expr,
expr_2021,
expr_fragment_specifier_2024,
Expand Down
1 change: 1 addition & 0 deletions tests/run-make/cdylib-export-c-library-symbols/foo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
void my_function() {}
10 changes: 10 additions & 0 deletions tests/run-make/cdylib-export-c-library-symbols/foo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
extern "C" {
pub fn my_function();
}

#[no_mangle]
pub extern "C" fn rust_entry() {
unsafe {
my_function();
}
}
Loading
Loading