diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 2c3ee1bae09f8..ab8566399df18 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -62,7 +62,10 @@ use super::metadata::{MetadataPosition, create_wrapper_file}; use super::rpath::{self, RPathConfig}; use super::{apple, rmeta_link, versioned_llvm_target}; use crate::base::needs_allocator_shim_for_linking; -use crate::{CodegenLintLevelSpecs, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors}; +use crate::{ + CodegenLintLevelSpecs, CompiledModule, CompiledModules, CrateInfo, NativeLib, SymbolExport, + errors, +}; pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { if let Err(e) = fs::remove_file(path) { @@ -588,7 +591,7 @@ fn link_staticlib( crate_info .exported_symbols .get(&CrateType::StaticLib) - .map(|symbols| symbols.iter().map(|(s, _)| s.clone()).collect()) + .map(|symbols| symbols.iter().map(|symbol| symbol.name.clone()).collect()) } } else { None @@ -2189,9 +2192,15 @@ fn add_linked_symbol_object( cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, - symbols: &[(String, SymbolExportKind)], + crate_type: CrateType, + linked_symbols: &[(String, SymbolExportKind)], + exported_symbols: &[SymbolExport], ) { - if symbols.is_empty() { + let should_export_symbols = sess.target.is_like_msvc + && !exported_symbols.is_empty() + && (crate_type != CrateType::Executable + || sess.opts.unstable_opts.export_executable_symbols); + if linked_symbols.is_empty() && !should_export_symbols { return; } @@ -2228,7 +2237,7 @@ fn add_linked_symbol_object( None }; - for (sym, kind) in symbols.iter() { + for (sym, kind) in linked_symbols.iter() { let symbol = file.add_symbol(object::write::Symbol { name: sym.clone().into(), value: 0, @@ -2286,6 +2295,37 @@ fn add_linked_symbol_object( } } + if should_export_symbols { + // Currently the compiler doesn't use `dllexport` (an LLVM attribute) to + // export symbols from a dynamic library. When building a dynamic library, + // however, we're going to want some symbols exported, so this adds a + // `.drectve` section which lists all the symbols using /EXPORT arguments. + // + // The linker will read these arguments from the `.drectve` section and + // export all the symbols from the dynamic library. Note that this is not + // as simple as just exporting all the symbols in the current crate (as + // specified by `codegen.reachable`) but rather we also need to possibly + // export the symbols of upstream 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 msvc_drectve_export(symbol: &SymbolExport) -> String { + let data = if symbol.kind == SymbolExportKind::Data { ",DATA" } else { "" }; + + if let Some(link_name) = symbol.link_name.as_deref() { + // The first name is the decorated symbol used by the import library, while + // EXPORTAS gives the public name written to the DLL export table. + format!(" /EXPORT:\"{link_name}\"{data},EXPORTAS,\"{}\"", symbol.name) + } else { + format!(" /EXPORT:\"{}\"{data}", symbol.name) + } + } + + let drectve = exported_symbols.iter().map(msvc_drectve_export).collect::(); + + let section = file.add_section(vec![], b".drectve".to_vec(), object::SectionKind::Linker); + file.append_section_data(section, drectve.as_bytes(), 1); + } + let path = tmpdir.join("symbols.o"); let result = std::fs::write(&path, file.write().unwrap()); if let Err(error) = result { @@ -2416,7 +2456,7 @@ fn add_rpath_args( fn add_c_staticlib_symbols( sess: &Session, lib: &NativeLib, - out: &mut Vec<(String, SymbolExportKind)>, + out: &mut Vec, ) -> io::Result<()> { let file_path = find_native_static_library(lib.name.as_str(), lib.verbatim, sess); @@ -2469,9 +2509,9 @@ fn add_c_staticlib_symbols( _ => continue, }; - // FIXME:The symbol mangle rules are slightly different in Windows(32-bit) and Apple. - // Need to be resolved. - out.push((name.to_string(), export_kind)); + // Names read from the object file are already linker-visible. + // Do not apply symbol decoration again here. + out.push(SymbolExport::new(name.to_string(), export_kind)); } } @@ -2550,7 +2590,14 @@ fn linker_with_args( // Pre-link CRT objects. add_pre_link_objects(cmd, sess, flavor, link_output_kind, self_contained_crt_objects); - add_linked_symbol_object(cmd, sess, tmpdir, &crate_info.linked_symbols[&crate_type]); + add_linked_symbol_object( + cmd, + sess, + tmpdir, + crate_type, + &crate_info.linked_symbols[&crate_type], + &export_symbols, + ); // Sanitizer libraries. add_sanitizer_libraries(sess, flavor, crate_type, cmd); diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 4041bfaa24cf0..9f981016c18af 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -15,7 +15,7 @@ use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::{ self, ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel, }; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{SymbolName, TyCtxt}; use rustc_session::Session; use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip}; use rustc_target::spec::{Arch, Cc, CfgAbi, LinkOutputKind, LinkerFlavor, Lld, Os}; @@ -25,7 +25,7 @@ use super::command::Command; use super::symbol_export; use crate::back::symbol_export::allocator_shim_symbols; use crate::base::needs_allocator_shim_for_linking; -use crate::errors; +use crate::{SymbolExport, errors}; #[cfg(test)] mod tests; @@ -338,12 +338,7 @@ 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, SymbolExportKind)], - ); + fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[SymbolExport]); fn windows_subsystem(&mut self, subsystem: WindowsSubsystemKind); fn linker_plugin_lto(&mut self); fn add_eh_frame_header(&mut self) {} @@ -794,12 +789,7 @@ impl<'a> Linker for GccLinker<'a> { } } - fn export_symbols( - &mut self, - tmpdir: &Path, - crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[SymbolExport]) { // Symbol visibility in object files typically takes care of this. if crate_type == CrateType::Executable { let should_export_executable_symbols = @@ -826,9 +816,9 @@ impl<'a> Linker for GccLinker<'a> { // Write a plain, newline-separated list of symbols let res = try { let mut f = File::create_buffered(&path)?; - for (sym, _) in symbols { - debug!(" _{sym}"); - writeln!(f, "_{sym}")?; + for sym in symbols { + debug!(" _{}", sym.name); + writeln!(f, "_{}", sym.name)?; } }; if let Err(error) = res { @@ -842,12 +832,13 @@ 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, kind) in symbols { - let kind_marker = if *kind == SymbolExportKind::Data { " DATA" } else { "" }; - debug!(" _{symbol}"); + for symbol in symbols { + let kind_marker = + if symbol.kind == SymbolExportKind::Data { " DATA" } else { "" }; + debug!(" _{}", symbol.name); // Quote the name in case it's reserved by linker in some way // (this accounts for names with dots in particular). - writeln!(f, " \"{symbol}\"{kind_marker}")?; + writeln!(f, " \"{}\"{kind_marker}", symbol.name)?; } }; if let Err(error) = res { @@ -856,16 +847,16 @@ impl<'a> Linker for GccLinker<'a> { self.link_arg(path); } else if self.sess.target.is_like_wasm { self.link_arg("--no-export-dynamic"); - for (sym, _) in symbols { - self.link_arg("--export").link_arg(sym); + for sym in symbols { + self.link_arg("--export").link_arg(&sym.name); } } else if crate_type == CrateType::Executable && !self.sess.target.is_like_solaris { let res = try { let mut f = File::create_buffered(&path)?; writeln!(f, "{{")?; - for (sym, _) in symbols { - debug!(sym); - writeln!(f, " {sym};")?; + for sym in symbols { + debug!("{}", sym.name); + writeln!(f, " {};", sym.name)?; } writeln!(f, "}};")?; }; @@ -880,9 +871,9 @@ impl<'a> Linker for GccLinker<'a> { writeln!(f, "{{")?; if !symbols.is_empty() { writeln!(f, " global:")?; - for (sym, _) in symbols { - debug!(" {sym};"); - writeln!(f, " {sym};")?; + for sym in symbols { + debug!(" {};", sym.name); + writeln!(f, " {};", sym.name)?; } } writeln!(f, "\n local:\n *;\n}};")?; @@ -1126,53 +1117,13 @@ impl<'a> Linker for MsvcLinker<'a> { } } - // Currently the compiler doesn't use `dllexport` (an LLVM attribute) to - // export symbols from a dynamic library. When building a dynamic library, - // however, we're going to want some symbols exported, so this function - // generates a DEF file which lists all the symbols. - // - // The linker will read this `*.def` file and export all the symbols from - // the dynamic library. Note that this is not as simple as just exporting - // all the symbols in the current crate (as specified by `codegen.reachable`) - // but rather we also need to possibly export the symbols of upstream - // 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, SymbolExportKind)], + _tmpdir: &Path, + _crate_type: CrateType, + _symbols: &[SymbolExport], ) { - // Symbol visibility takes care of this typically - if crate_type == CrateType::Executable { - let should_export_executable_symbols = - self.sess.opts.unstable_opts.export_executable_symbols; - if !should_export_executable_symbols { - return; - } - } - - let path = tmpdir.join("lib.def"); - let res = try { - let mut f = File::create_buffered(&path)?; - - // Start off with the standard module name header and then go - // straight to exports. - writeln!(f, "LIBRARY")?; - writeln!(f, "EXPORTS")?; - for (symbol, kind) in symbols { - let kind_marker = if *kind == SymbolExportKind::Data { " DATA" } else { "" }; - debug!(" _{symbol}"); - writeln!(f, " {symbol}{kind_marker}")?; - } - }; - if let Err(error) = res { - self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error }); - } - let mut arg = OsString::from("/DEF:"); - arg.push(path); - self.link_arg(&arg); + // We already add /EXPORT arguments to the .drectve section of symbols.o. } fn windows_subsystem(&mut self, subsystem: WindowsSubsystemKind) { @@ -1313,19 +1264,14 @@ impl<'a> Linker for EmLinker<'a> { self.cc_arg("-nodefaultlibs"); } - fn export_symbols( - &mut self, - _tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[SymbolExport]) { 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::>(), + &symbols.iter().map(|sym| "_".to_owned() + &sym.name).collect::>(), ) .unwrap(); debug!("{encoded}"); @@ -1453,14 +1399,9 @@ impl<'a> Linker for WasmLd<'a> { fn no_default_libraries(&mut self) {} - fn export_symbols( - &mut self, - _tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { - for (sym, _) in symbols { - self.link_args(&["--export", sym]); + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[SymbolExport]) { + for sym in symbols { + self.link_args(&["--export", &sym.name]); } } @@ -1581,7 +1522,7 @@ impl<'a> Linker for L4Bender<'a> { self.cc_arg("-nostdlib"); } - fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[(String, SymbolExportKind)]) { + fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[SymbolExport]) { // ToDo, not implemented, copy from GCC self.sess.dcx().emit_warn(errors::L4BenderExportingSymbolsUnimplemented); } @@ -1735,19 +1676,14 @@ impl<'a> Linker for AixLinker<'a> { fn no_default_libraries(&mut self) {} - fn export_symbols( - &mut self, - tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[SymbolExport]) { let path = tmpdir.join("list.exp"); let res = try { let mut f = File::create_buffered(&path)?; // FIXME: use llvm-nm to generate export list. - for (symbol, _) in symbols { - debug!(" _{symbol}"); - writeln!(f, " {symbol}")?; + for symbol in symbols { + debug!(" _{}", symbol.name); + writeln!(f, " {}", symbol.name)?; } }; if let Err(e) = res { @@ -1792,15 +1728,36 @@ fn for_each_exported_symbols_include_dep<'tcx>( } } -pub(crate) fn exported_symbols( +fn symbol_export_from_exported_symbol<'tcx>( + tcx: TyCtxt<'tcx>, + symbol: ExportedSymbol<'tcx>, + kind: SymbolExportKind, + cnum: CrateNum, +) -> SymbolExport { + let name = symbol_export::exporting_symbol_name_for_instance_in_crate(tcx, symbol, cnum); + let link_name = + symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, kind, cnum); + SymbolExport::with_link_name(name, kind, link_name) +} + +fn symbol_export_from_raw_name( tcx: TyCtxt<'_>, - crate_type: CrateType, -) -> Vec<(String, SymbolExportKind)> { + name: String, + kind: SymbolExportKind, +) -> SymbolExport { + let symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &name)); + let link_name = + symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, kind, LOCAL_CRATE); + SymbolExport::with_link_name(name, kind, link_name) +} + +pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec { if let Some(ref exports) = tcx.sess.target.override_export_symbols { return exports .iter() .map(|name| { - ( + symbol_export_from_raw_name( + tcx, 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 @@ -1825,7 +1782,11 @@ pub(crate) fn exported_symbols( && !tcx.sess.target.is_like_wasm { let metadata_symbol_name = exported_symbols::metadata_symbol_name(tcx); - symbols.push((metadata_symbol_name, SymbolExportKind::Data)); + symbols.push(symbol_export_from_raw_name( + tcx, + metadata_symbol_name, + SymbolExportKind::Data, + )); } symbols @@ -1834,7 +1795,7 @@ pub(crate) fn exported_symbols( fn exported_symbols_for_non_proc_macro( tcx: TyCtxt<'_>, crate_type: CrateType, -) -> Vec<(String, SymbolExportKind)> { +) -> Vec { 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| { @@ -1842,10 +1803,7 @@ fn exported_symbols_for_non_proc_macro( // from any dylib. 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), - info.kind, - )); + symbols.push(symbol_export_from_exported_symbol(tcx, symbol, info.kind, cnum)); symbol_export::extend_exported_symbols(&mut symbols, tcx, symbol, cnum); } }); @@ -1855,13 +1813,16 @@ fn exported_symbols_for_non_proc_macro( && needs_allocator_shim_for_linking(tcx.dependency_formats(()), crate_type) && let Some(kind) = tcx.allocator_kind(()) { - symbols.extend(allocator_shim_symbols(tcx, kind)); + symbols.extend( + allocator_shim_symbols(tcx, kind) + .map(|(name, kind)| symbol_export_from_raw_name(tcx, name, kind)), + ); } symbols } -fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<(String, SymbolExportKind)> { +fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec { // `exported_symbols` will be empty when !should_codegen. if !tcx.sess.opts.output_types.should_codegen() { return Vec::new(); @@ -1870,7 +1831,7 @@ fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<(String, Symbol let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE); let proc_macro_decls_name = rustc_session::generate_proc_macro_decls_symbol(stable_crate_id); - vec![(proc_macro_decls_name, SymbolExportKind::Data)] + vec![symbol_export_from_raw_name(tcx, proc_macro_decls_name, SymbolExportKind::Data)] } pub(crate) fn linked_symbols( @@ -1984,16 +1945,11 @@ impl<'a> Linker for LlbcLinker<'a> { fn ehcont_guard(&mut self) {} - fn export_symbols( - &mut self, - _tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[SymbolExport]) { match _crate_type { CrateType::Cdylib => { - for (sym, _) in symbols { - self.link_args(&["--export-symbol", sym]); + for sym in symbols { + self.link_args(&["--export-symbol", &sym.name]); } } _ => (), @@ -2064,17 +2020,12 @@ impl<'a> Linker for BpfLinker<'a> { fn ehcont_guard(&mut self) {} - fn export_symbols( - &mut self, - tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[SymbolExport]) { let path = tmpdir.join("symbols"); let res = try { let mut f = File::create_buffered(&path)?; - for (sym, _) in symbols { - writeln!(f, "{sym}")?; + for sym in symbols { + writeln!(f, "{}", sym.name)?; } }; if let Err(error) = res { diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 014fd5cf3a0e5..997ca2b6080ce 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -19,6 +19,7 @@ use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{Arch, Os, TlsModel}; use tracing::debug; +use crate::SymbolExport; use crate::back::symbol_export; use crate::base::allocator_shim_contents; @@ -713,7 +714,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, SymbolExportKind)>, + symbols: &mut Vec, tcx: TyCtxt<'tcx>, symbol: ExportedSymbol<'tcx>, instantiating_crate: CrateNum, @@ -729,7 +730,7 @@ pub(crate) fn extend_exported_symbols<'tcx>( // Add the symbol for the kernel descriptor (with .kd suffix) // 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)); + symbols.push(SymbolExport::new(format!("{undecorated}.kd"), SymbolExportKind::Data)); } fn maybe_emutls_symbol_name<'tcx>( diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 10ae8a9ee0b38..f3f19f9e90d83 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -233,6 +233,28 @@ impl From<&cstore::NativeLib> for NativeLib { } } +/// A symbol to make visible from a linked artifact. +#[derive(Clone, Debug, Encodable, Decodable)] +pub struct SymbolExport { + /// Name to make visible from the linked artifact. + pub name: String, + /// Kind of symbol, used for target-specific export directives and name decoration. + pub kind: SymbolExportKind, + /// Name of the symbol as seen by the linker, when it differs from `name`. + pub link_name: Option, +} + +impl SymbolExport { + pub fn new(name: String, kind: SymbolExportKind) -> SymbolExport { + SymbolExport { name, kind, link_name: None } + } + + pub fn with_link_name(name: String, kind: SymbolExportKind, link_name: String) -> SymbolExport { + let link_name = if link_name == name { None } else { Some(link_name) }; + SymbolExport { name, kind, link_name } + } +} + /// Misc info we load from metadata to persist beyond the tcx. /// /// Note: though `CrateNum` is only meaningful within the same tcx, information within `CrateInfo` @@ -247,7 +269,7 @@ pub struct CrateInfo { pub target_cpu: String, pub target_features: Vec, pub crate_types: Vec, - pub exported_symbols: UnordMap>, + pub exported_symbols: UnordMap>, pub linked_symbols: FxIndexMap>, pub local_crate_name: Symbol, pub compiler_builtins: Option, diff --git a/tests/ui/linking/dll-weak-definition.rs b/tests/ui/linking/dll-weak-definition.rs new file mode 100644 index 0000000000000..198a94ccee97e --- /dev/null +++ b/tests/ui/linking/dll-weak-definition.rs @@ -0,0 +1,20 @@ +// Regression test for MSVC link.exe failing to export weak definitions from dlls. +// See https://github.com/rust-lang/rust/pull/142568 + +//@ build-pass +//@ only-msvc +//@ revisions: link_exe lld +//@[lld] needs-rust-lld +//@[link_exe] compile-flags: -Zunstable-options -Clink-self-contained=-linker -Clinker-features=-lld +//@[lld] compile-flags: -Zunstable-options -Clink-self-contained=+linker -Clinker-features=+lld + +#![feature(linkage)] +#![crate_type = "cdylib"] + +#[linkage = "weak"] +#[no_mangle] +pub fn weak_function() {} + +#[linkage = "weak"] +#[no_mangle] +pub static WEAK_STATIC: u8 = 42; diff --git a/typos.toml b/typos.toml index f680f5b0e8abf..1e3d5c5e89d09 100644 --- a/typos.toml +++ b/typos.toml @@ -21,6 +21,7 @@ extend-exclude = [ # right now. Entries should look like `mipsel = "mipsel"`. # # tidy-alphabetical-start +EXPORTAS = "EXPORTAS" # MSVC linker keyword used with /EXPORT directives anser = "anser" # an ANSI parsing package used by rust-analyzer arange = "arange" # short for A-range childs = "childs"