[Arm64EC] Only decorate functions with #

This commit is contained in:
Daniel Paoliello 2025-04-25 16:44:58 -07:00
parent 42245d34d2
commit 2602653424
9 changed files with 197 additions and 51 deletions

View file

@ -337,7 +337,12 @@ pub(crate) trait Linker {
fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]);
fn no_crt_objects(&mut self);
fn no_default_libraries(&mut self);
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]);
fn export_symbols(
&mut self,
tmpdir: &Path,
crate_type: CrateType,
symbols: &[(String, SymbolExportKind)],
);
fn subsystem(&mut self, subsystem: &str);
fn linker_plugin_lto(&mut self);
fn add_eh_frame_header(&mut self) {}
@ -770,7 +775,12 @@ impl<'a> Linker for GccLinker<'a> {
}
}
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) {
fn export_symbols(
&mut self,
tmpdir: &Path,
crate_type: CrateType,
symbols: &[(String, SymbolExportKind)],
) {
// Symbol visibility in object files typically takes care of this.
if crate_type == CrateType::Executable {
let should_export_executable_symbols =
@ -799,7 +809,7 @@ impl<'a> Linker for GccLinker<'a> {
// Write a plain, newline-separated list of symbols
let res: io::Result<()> = try {
let mut f = File::create_buffered(&path)?;
for sym in symbols {
for (sym, _) in symbols {
debug!(" _{sym}");
writeln!(f, "_{sym}")?;
}
@ -814,11 +824,12 @@ impl<'a> Linker for GccLinker<'a> {
// .def file similar to MSVC one but without LIBRARY section
// because LD doesn't like when it's empty
writeln!(f, "EXPORTS")?;
for symbol in symbols {
for (symbol, kind) in symbols {
let kind_marker = if *kind == SymbolExportKind::Data { " DATA" } else { "" };
debug!(" _{symbol}");
// Quote the name in case it's reserved by linker in some way
// (this accounts for names with dots in particular).
writeln!(f, " \"{symbol}\"")?;
writeln!(f, " \"{symbol}\"{kind_marker}")?;
}
};
if let Err(error) = res {
@ -831,7 +842,7 @@ impl<'a> Linker for GccLinker<'a> {
writeln!(f, "{{")?;
if !symbols.is_empty() {
writeln!(f, " global:")?;
for sym in symbols {
for (sym, _) in symbols {
debug!(" {sym};");
writeln!(f, " {sym};")?;
}
@ -1098,7 +1109,12 @@ impl<'a> Linker for MsvcLinker<'a> {
// crates. Upstream rlibs may be linked statically to this dynamic library,
// in which case they may continue to transitively be used and hence need
// their symbols exported.
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) {
fn export_symbols(
&mut self,
tmpdir: &Path,
crate_type: CrateType,
symbols: &[(String, SymbolExportKind)],
) {
// Symbol visibility takes care of this typically
if crate_type == CrateType::Executable {
let should_export_executable_symbols =
@ -1116,9 +1132,10 @@ impl<'a> Linker for MsvcLinker<'a> {
// straight to exports.
writeln!(f, "LIBRARY")?;
writeln!(f, "EXPORTS")?;
for symbol in symbols {
for (symbol, kind) in symbols {
let kind_marker = if *kind == SymbolExportKind::Data { " DATA" } else { "" };
debug!(" _{symbol}");
writeln!(f, " {symbol}")?;
writeln!(f, " {symbol}{kind_marker}")?;
}
};
if let Err(error) = res {
@ -1259,14 +1276,19 @@ impl<'a> Linker for EmLinker<'a> {
self.cc_arg("-nodefaultlibs");
}
fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
fn export_symbols(
&mut self,
_tmpdir: &Path,
_crate_type: CrateType,
symbols: &[(String, SymbolExportKind)],
) {
debug!("EXPORTED SYMBOLS:");
self.cc_arg("-s");
let mut arg = OsString::from("EXPORTED_FUNCTIONS=");
let encoded = serde_json::to_string(
&symbols.iter().map(|sym| "_".to_owned() + sym).collect::<Vec<_>>(),
&symbols.iter().map(|(sym, _)| "_".to_owned() + sym).collect::<Vec<_>>(),
)
.unwrap();
debug!("{encoded}");
@ -1428,8 +1450,13 @@ impl<'a> Linker for WasmLd<'a> {
fn no_default_libraries(&mut self) {}
fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
for sym in symbols {
fn export_symbols(
&mut self,
_tmpdir: &Path,
_crate_type: CrateType,
symbols: &[(String, SymbolExportKind)],
) {
for (sym, _) in symbols {
self.link_args(&["--export", sym]);
}
@ -1563,7 +1590,7 @@ impl<'a> Linker for L4Bender<'a> {
self.cc_arg("-nostdlib");
}
fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[String]) {
fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[(String, SymbolExportKind)]) {
// ToDo, not implemented, copy from GCC
self.sess.dcx().emit_warn(errors::L4BenderExportingSymbolsUnimplemented);
}
@ -1720,12 +1747,17 @@ impl<'a> Linker for AixLinker<'a> {
fn no_default_libraries(&mut self) {}
fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
fn export_symbols(
&mut self,
tmpdir: &Path,
_crate_type: CrateType,
symbols: &[(String, SymbolExportKind)],
) {
let path = tmpdir.join("list.exp");
let res: io::Result<()> = try {
let mut f = File::create_buffered(&path)?;
// FIXME: use llvm-nm to generate export list.
for symbol in symbols {
for (symbol, _) in symbols {
debug!(" _{symbol}");
writeln!(f, " {symbol}")?;
}
@ -1769,9 +1801,23 @@ fn for_each_exported_symbols_include_dep<'tcx>(
}
}
pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<String> {
pub(crate) fn exported_symbols(
tcx: TyCtxt<'_>,
crate_type: CrateType,
) -> Vec<(String, SymbolExportKind)> {
if let Some(ref exports) = tcx.sess.target.override_export_symbols {
return exports.iter().map(ToString::to_string).collect();
return exports
.iter()
.map(|name| {
(
name.to_string(),
// FIXME use the correct export kind for this symbol. override_export_symbols
// can't directly specify the SymbolExportKind as it is defined in rustc_middle
// which rustc_target can't depend on.
SymbolExportKind::Text,
)
})
.collect();
}
if let CrateType::ProcMacro = crate_type {
@ -1781,7 +1827,10 @@ pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<St
}
}
fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<String> {
fn exported_symbols_for_non_proc_macro(
tcx: TyCtxt<'_>,
crate_type: CrateType,
) -> Vec<(String, SymbolExportKind)> {
let mut symbols = Vec::new();
let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
@ -1789,8 +1838,9 @@ fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -
// from any cdylib. The latter doesn't work anyway as we use hidden visibility for
// compiler-builtins. Most linkers silently ignore it, but ld64 gives a warning.
if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) {
symbols.push(symbol_export::exporting_symbol_name_for_instance_in_crate(
tcx, symbol, cnum,
symbols.push((
symbol_export::exporting_symbol_name_for_instance_in_crate(tcx, symbol, cnum),
info.kind,
));
symbol_export::extend_exported_symbols(&mut symbols, tcx, symbol, cnum);
}
@ -1799,7 +1849,7 @@ fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -
symbols
}
fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<String> {
fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<(String, SymbolExportKind)> {
// `exported_symbols` will be empty when !should_codegen.
if !tcx.sess.opts.output_types.should_codegen() {
return Vec::new();
@ -1809,7 +1859,10 @@ fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<String> {
let proc_macro_decls_name = tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id);
let metadata_symbol_name = exported_symbols::metadata_symbol_name(tcx);
vec![proc_macro_decls_name, metadata_symbol_name]
vec![
(proc_macro_decls_name, SymbolExportKind::Data),
(metadata_symbol_name, SymbolExportKind::Data),
]
}
pub(crate) fn linked_symbols(
@ -1831,7 +1884,9 @@ pub(crate) fn linked_symbols(
|| info.used
{
symbols.push((
symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, cnum),
symbol_export::linking_symbol_name_for_instance_in_crate(
tcx, symbol, info.kind, cnum,
),
info.kind,
));
}
@ -1906,7 +1961,13 @@ impl<'a> Linker for PtxLinker<'a> {
fn ehcont_guard(&mut self) {}
fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, _symbols: &[String]) {}
fn export_symbols(
&mut self,
_tmpdir: &Path,
_crate_type: CrateType,
_symbols: &[(String, SymbolExportKind)],
) {
}
fn subsystem(&mut self, _subsystem: &str) {}
@ -1975,10 +2036,15 @@ impl<'a> Linker for LlbcLinker<'a> {
fn ehcont_guard(&mut self) {}
fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
fn export_symbols(
&mut self,
_tmpdir: &Path,
_crate_type: CrateType,
symbols: &[(String, SymbolExportKind)],
) {
match _crate_type {
CrateType::Cdylib => {
for sym in symbols {
for (sym, _) in symbols {
self.link_args(&["--export-symbol", sym]);
}
}
@ -2052,11 +2118,16 @@ impl<'a> Linker for BpfLinker<'a> {
fn ehcont_guard(&mut self) {}
fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
fn export_symbols(
&mut self,
tmpdir: &Path,
_crate_type: CrateType,
symbols: &[(String, SymbolExportKind)],
) {
let path = tmpdir.join("symbols");
let res: io::Result<()> = try {
let mut f = File::create_buffered(&path)?;
for sym in symbols {
for (sym, _) in symbols {
writeln!(f, "{sym}")?;
}
};

View file

@ -680,6 +680,7 @@ fn calling_convention_for_symbol<'tcx>(
pub(crate) fn linking_symbol_name_for_instance_in_crate<'tcx>(
tcx: TyCtxt<'tcx>,
symbol: ExportedSymbol<'tcx>,
export_kind: SymbolExportKind,
instantiating_crate: CrateNum,
) -> String {
let mut undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate);
@ -700,8 +701,9 @@ pub(crate) fn linking_symbol_name_for_instance_in_crate<'tcx>(
let prefix = match &target.arch[..] {
"x86" => Some('_'),
"x86_64" => None,
"arm64ec" => Some('#'),
// Only x86/64 use symbol decorations.
// Only functions are decorated for arm64ec.
"arm64ec" if export_kind == SymbolExportKind::Text => Some('#'),
// Only x86/64 and arm64ec use symbol decorations.
_ => return undecorated,
};
@ -741,7 +743,7 @@ pub(crate) fn exporting_symbol_name_for_instance_in_crate<'tcx>(
/// Add it to the symbols list for all kernel functions, so that it is exported in the linked
/// object.
pub(crate) fn extend_exported_symbols<'tcx>(
symbols: &mut Vec<String>,
symbols: &mut Vec<(String, SymbolExportKind)>,
tcx: TyCtxt<'tcx>,
symbol: ExportedSymbol<'tcx>,
instantiating_crate: CrateNum,
@ -755,7 +757,9 @@ pub(crate) fn extend_exported_symbols<'tcx>(
let undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate);
// Add the symbol for the kernel descriptor (with .kd suffix)
symbols.push(format!("{undecorated}.kd"));
// Per https://llvm.org/docs/AMDGPUUsage.html#symbols these will always be `STT_OBJECT` so
// export as data.
symbols.push((format!("{undecorated}.kd"), SymbolExportKind::Data));
}
fn maybe_emutls_symbol_name<'tcx>(

View file

@ -12,9 +12,9 @@ use rustc_data_structures::fx::{FxHashMap, 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::ItemId;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ItemId, Target};
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType};
use rustc_middle::middle::exported_symbols::{self, SymbolExportKind};
@ -1003,21 +1003,35 @@ impl CrateInfo {
// by the compiler, but that's ok because all this stuff is unstable anyway.
let target = &tcx.sess.target;
if !are_upstream_rust_objects_already_included(tcx.sess) {
let missing_weak_lang_items: FxIndexSet<Symbol> = info
let add_prefix = match (target.is_like_windows, target.arch.as_ref()) {
(true, "x86") => |name: String, _: SymbolExportKind| format!("_{name}"),
(true, "arm64ec") => {
// Only functions are decorated for arm64ec.
|name: String, export_kind: SymbolExportKind| match export_kind {
SymbolExportKind::Text => format!("#{name}"),
_ => name,
}
}
_ => |name: String, _: SymbolExportKind| name,
};
let missing_weak_lang_items: FxIndexSet<(Symbol, SymbolExportKind)> = info
.used_crates
.iter()
.flat_map(|&cnum| tcx.missing_lang_items(cnum))
.filter(|l| l.is_weak())
.filter_map(|&l| {
let name = l.link_name()?;
lang_items::required(tcx, l).then_some(name)
let export_kind = match l.target() {
Target::Fn => SymbolExportKind::Text,
Target::Static => SymbolExportKind::Data,
_ => bug!(
"Don't know what the export kind is for lang item of kind {:?}",
l.target()
),
};
lang_items::required(tcx, l).then_some((name, export_kind))
})
.collect();
let prefix = match (target.is_like_windows, target.arch.as_ref()) {
(true, "x86") => "_",
(true, "arm64ec") => "#",
_ => "",
};
// This loop only adds new items to values of the hash map, so the order in which we
// iterate over the values is not important.
@ -1030,10 +1044,13 @@ impl CrateInfo {
.for_each(|(_, linked_symbols)| {
let mut symbols = missing_weak_lang_items
.iter()
.map(|item| {
.map(|(item, export_kind)| {
(
format!("{prefix}{}", mangle_internal_symbol(tcx, item.as_str())),
SymbolExportKind::Text,
add_prefix(
mangle_internal_symbol(tcx, item.as_str()),
*export_kind,
),
*export_kind,
)
})
.collect::<Vec<_>>();
@ -1048,12 +1065,12 @@ impl CrateInfo {
// errors.
linked_symbols.extend(ALLOCATOR_METHODS.iter().map(|method| {
(
format!(
"{prefix}{}",
add_prefix(
mangle_internal_symbol(
tcx,
global_fn_name(method.name).as_str()
)
global_fn_name(method.name).as_str(),
),
SymbolExportKind::Text,
),
SymbolExportKind::Text,
)

View file

@ -218,7 +218,7 @@ pub struct CrateInfo {
pub target_cpu: String,
pub target_features: Vec<String>,
pub crate_types: Vec<CrateType>,
pub exported_symbols: UnordMap<CrateType, Vec<String>>,
pub exported_symbols: UnordMap<CrateType, Vec<(String, SymbolExportKind)>>,
pub linked_symbols: FxIndexMap<CrateType, Vec<(String, SymbolExportKind)>>,
pub local_crate_name: Symbol,
pub compiler_builtins: Option<CrateNum>,

View file

@ -22,7 +22,7 @@ impl SymbolExportLevel {
}
/// Kind of exported symbols.
#[derive(Eq, PartialEq, Debug, Copy, Clone, Encodable, Decodable, HashStable)]
#[derive(Eq, PartialEq, Debug, Copy, Clone, Encodable, Decodable, HashStable, Hash)]
pub enum SymbolExportKind {
Text,
Data,

View file

@ -0,0 +1,27 @@
#![crate_type = "dylib"]
#![allow(internal_features)]
#![feature(no_core, lang_items)]
#![no_core]
#![no_std]
// This is needed because of #![no_core]:
#[lang = "pointee_sized"]
pub trait PointeeSized {}
#[lang = "meta_sized"]
pub trait MetaSized: PointeeSized {}
#[lang = "sized"]
pub trait Sized: MetaSized {}
#[lang = "sync"]
trait Sync {}
impl Sync for i32 {}
#[lang = "copy"]
pub trait Copy {}
impl Copy for i32 {}
#[lang = "drop_in_place"]
pub unsafe fn drop_in_place<T: ?Sized>(_: *mut T) {}
#[no_mangle]
extern "system" fn _DllMainCRTStartup(_: *const u8, _: u32, _: *const u8) -> u32 {
1
}
pub static VALUE: i32 = 42;

View file

@ -0,0 +1,12 @@
#![crate_type = "cdylib"]
#![allow(internal_features)]
#![feature(no_core)]
#![no_std]
#![no_core]
extern crate export;
#[no_mangle]
pub extern "C" fn func() -> i32 {
export::VALUE
}

View file

@ -0,0 +1,15 @@
// Test that a static can be exported from one crate and imported into another.
//
// This was broken for Arm64EC as only functions, not variables, should be
// decorated with `#`.
// See https://github.com/rust-lang/rust/issues/138541
//@ needs-llvm-components: aarch64
//@ only-windows
use run_make_support::rustc;
fn main() {
rustc().input("export.rs").target("aarch64-pc-windows-msvc").panic("abort").run();
rustc().input("import.rs").target("aarch64-pc-windows-msvc").panic("abort").run();
}

View file

@ -1,4 +1,4 @@
#[cfg_attr(windows, link(name = "library.dll.lib", modifiers = "+verbatim"))]
#[cfg_attr(windows, link(name = "library", kind = "raw-dylib"))]
#[cfg_attr(not(windows), link(name = "library"))]
extern "C" {
fn overflow();