Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use decorated names for linked_symbols on Windows #96444

Merged
merged 1 commit into from
Apr 29, 2022
Merged
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
4 changes: 4 additions & 0 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1696,6 +1696,10 @@ fn add_linked_symbol_object(
// so add an empty section.
if file.format() == object::BinaryFormat::Coff {
file.add_section(Vec::new(), ".text".into(), object::SectionKind::Text);

// We handle the name decoration of COFF targets in `symbol_export.rs`, so disable the
// default mangler in `object` crate.
file.set_mangling(object::write::Mangling::None);
}

for (sym, kind) in symbols.iter() {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1576,7 +1576,7 @@ pub(crate) fn linked_symbols(
for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
if info.level.is_below_threshold(export_threshold) || info.used {
symbols.push((
symbol_export::symbol_name_for_instance_in_crate(tcx, symbol, cnum),
symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, cnum),
info.kind,
));
}
Expand Down
72 changes: 71 additions & 1 deletion compiler/rustc_codegen_ssa/src/back/symbol_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc_middle::middle::exported_symbols::{
use rustc_middle::ty::query::{ExternProviders, Providers};
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
use rustc_middle::ty::Instance;
use rustc_middle::ty::{SymbolName, TyCtxt};
use rustc_middle::ty::{self, SymbolName, TyCtxt};
use rustc_session::config::CrateType;
use rustc_target::spec::SanitizerSet;

Expand Down Expand Up @@ -493,6 +493,76 @@ pub fn symbol_name_for_instance_in_crate<'tcx>(
}
}

/// This is the symbol name of the given instance as seen by the linker.
///
/// On 32-bit Windows symbols are decorated according to their calling conventions.
pub fn linking_symbol_name_for_instance_in_crate<'tcx>(
tcx: TyCtxt<'tcx>,
symbol: ExportedSymbol<'tcx>,
instantiating_crate: CrateNum,
) -> String {
use rustc_target::abi::call::Conv;

let mut undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate);

let target = &tcx.sess.target;
if !target.is_like_windows {
// Mach-O has a global "_" suffix and `object` crate will handle it.
// ELF does not have any symbol decorations.
return undecorated;
}

let x86 = match &target.arch[..] {
"x86" => true,
"x86_64" => false,
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like this case also always results in undecorated names.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

x86_64 code can still use "vectorcall" convention

Copy link
Contributor

Choose a reason for hiding this comment

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

The linked MS doc says "Note that in a 64-bit environment, functions are not decorated.", apparently it applies to vectorcall too?
Raw-dylib didn't attempt to support vector call yet, so I don't know what is the correct behavior there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's an inaccurate statement, it should rather say that functions are not decorated by default. Vector call is still a separate calling convention on x64 however, and is still decorated (https://docs.microsoft.com/en-us/cpp/cpp/vectorcall?view=msvc-170, https://godbolt.org/z/asdbdzePz)

Copy link
Contributor

Choose a reason for hiding this comment

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

Vector call is still a separate calling convention on x64

Yeah, I was aware of that, but wasn't sure about the decoration.
Just checked it with Visual Studio's C compiler, and looks like it's indeed the only calling convention that is decorated on x64.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Ha, I didn't even know this stuff is in open source now.

// Only x86/64 use symbol decorations.
_ => return undecorated,
};

let instance = match symbol {
ExportedSymbol::NonGeneric(def_id) | ExportedSymbol::Generic(def_id, _)
if tcx.is_static(def_id) =>
{
None
}
ExportedSymbol::NonGeneric(def_id) => Some(Instance::mono(tcx, def_id)),
ExportedSymbol::Generic(def_id, substs) => Some(Instance::new(def_id, substs)),
// DropGlue always use the Rust calling convention and thus follow the target's default
// symbol decoration scheme.
ExportedSymbol::DropGlue(..) => None,
// NoDefId always follow the target's default symbol decoration scheme.
ExportedSymbol::NoDefId(..) => None,
};

let (conv, args) = instance
.map(|i| {
tcx.fn_abi_of_instance(ty::ParamEnv::reveal_all().and((i, ty::List::empty())))
.unwrap_or_else(|_| bug!("fn_abi_of_instance({i:?}) failed"))
})
.map(|fnabi| (fnabi.conv, &fnabi.args[..]))
.unwrap_or((Conv::Rust, &[]));

// Decorate symbols with prefices, suffices and total number of bytes of arguments.
// Reference: https://docs.microsoft.com/en-us/cpp/build/reference/decorated-names?view=msvc-170
let (prefix, suffix) = match conv {
Conv::X86Fastcall => ("@", "@"),
Conv::X86Stdcall => ("_", "@"),
Conv::X86VectorCall => ("", "@@"),
_ => {
if x86 {
undecorated.insert(0, '_');
}
return undecorated;
}
};

let args_in_bytes: u64 = args
.iter()
.map(|abi| abi.layout.size.bytes().next_multiple_of(target.pointer_width as u64 / 8))
.sum();
format!("{prefix}{undecorated}{suffix}{args_in_bytes}")
Copy link
Contributor

Choose a reason for hiding this comment

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

I've seen this logic elsewhere, it's split into fn i686_decorated_name and fn i686_arg_list_size as a part of implementation of the raw-dylib feature.

Could you make sure these two implementations do the same thing?
fn i686_decorated_name, for example, adds or not adds underscore depending on the mingw flag.
(Ideally the code would be shared, but not necessarily in this PR.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just checked that the current behaviour in i686_decorated_name to not add _ in MinGW is due to the fact that it uses dlltool to create a library, and i686 dlltool will automatically add _ to symbols if it does not start with @. For the generated object files we need to add _ ourselves.

There are no other behavioural differences.

}

fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> FxHashMap<DefId, String> {
// Build up a map from DefId to a `NativeLib` structure, where
// `NativeLib` internally contains information about
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 @@ -7,6 +7,7 @@
#![feature(nll)]
#![feature(associated_type_bounds)]
#![feature(strict_provenance)]
#![feature(int_roundings)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]

Expand Down
13 changes: 13 additions & 0 deletions src/test/ui/symbol-names/x86-stdcall.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// build-pass
// only-x86-windows
#![crate_type = "cdylib"]
#![feature(abi_vectorcall)]

#[no_mangle]
extern "stdcall" fn foo(_: bool) {}

#[no_mangle]
extern "fastcall" fn bar(_: u8) {}

#[no_mangle]
extern "vectorcall" fn baz(_: u16) {}