diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 105a4cb81f0d1..cc95431ccb5bd 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -1,3 +1,5 @@
+mod raw_dylib;
+
use std::collections::BTreeSet;
use std::ffi::OsString;
use std::fs::{File, OpenOptions, read};
@@ -12,7 +14,7 @@ use itertools::Itertools;
use regex::Regex;
use rustc_arena::TypedArena;
use rustc_ast::CRATE_NODE_ID;
-use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
+use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_errors::{DiagCtxtHandle, LintDiagnostic};
@@ -30,7 +32,6 @@ use rustc_session::config::{
self, CFGuard, CrateType, DebugInfo, LinkerFeaturesCli, OutFileName, OutputFilenames,
OutputType, PrintKind, SplitDwarfKind, Strip,
};
-use rustc_session::cstore::DllImport;
use rustc_session::lint::builtin::LINKER_MESSAGES;
use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
use rustc_session::search_paths::PathKind;
@@ -48,15 +49,14 @@ use rustc_target::spec::{
use tempfile::Builder as TempFileBuilder;
use tracing::{debug, info, warn};
-use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder, ImportLibraryItem};
+use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
use super::command::Command;
use super::linker::{self, Linker};
use super::metadata::{MetadataPosition, create_wrapper_file};
use super::rpath::{self, RPathConfig};
use super::{apple, versioned_llvm_target};
use crate::{
- CodegenResults, CompiledModule, CrateInfo, NativeLib, common, errors,
- looks_like_rust_object_file,
+ CodegenResults, CompiledModule, CrateInfo, NativeLib, errors, looks_like_rust_object_file,
};
pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) {
@@ -381,16 +381,22 @@ fn link_rlib<'a>(
}
}
- for output_path in create_dll_import_libs(
- sess,
- archive_builder_builder,
- codegen_results.crate_info.used_libraries.iter(),
- tmpdir.as_ref(),
- true,
- ) {
- ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| {
- sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: output_path, error });
- });
+ // On Windows, we add the raw-dylib import libraries to the rlibs already.
+ // But on ELF, this is not possible, as a shared object cannot be a member of a static library.
+ // Instead, we add all raw-dylibs to the final link on ELF.
+ if sess.target.is_like_windows {
+ for output_path in raw_dylib::create_raw_dylib_dll_import_libs(
+ sess,
+ archive_builder_builder,
+ codegen_results.crate_info.used_libraries.iter(),
+ tmpdir.as_ref(),
+ true,
+ ) {
+ ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| {
+ sess.dcx()
+ .emit_fatal(errors::AddNativeLibrary { library_path: output_path, error });
+ });
+ }
}
if let Some(trailing_metadata) = trailing_metadata {
@@ -431,108 +437,6 @@ fn link_rlib<'a>(
ab
}
-/// Extract all symbols defined in raw-dylib libraries, collated by library name.
-///
-/// If we have multiple extern blocks that specify symbols defined in the same raw-dylib library,
-/// then the CodegenResults value contains one NativeLib instance for each block. However, the
-/// linker appears to expect only a single import library for each library used, so we need to
-/// collate the symbols together by library name before generating the import libraries.
-fn collate_raw_dylibs<'a>(
- sess: &Session,
- used_libraries: impl IntoIterator,
-) -> Vec<(String, Vec)> {
- // Use index maps to preserve original order of imports and libraries.
- let mut dylib_table = FxIndexMap::>::default();
-
- for lib in used_libraries {
- if lib.kind == NativeLibKind::RawDylib {
- let ext = if lib.verbatim { "" } else { ".dll" };
- let name = format!("{}{}", lib.name, ext);
- let imports = dylib_table.entry(name.clone()).or_default();
- for import in &lib.dll_imports {
- if let Some(old_import) = imports.insert(import.name, import) {
- // FIXME: when we add support for ordinals, figure out if we need to do anything
- // if we have two DllImport values with the same name but different ordinals.
- if import.calling_convention != old_import.calling_convention {
- sess.dcx().emit_err(errors::MultipleExternalFuncDecl {
- span: import.span,
- function: import.name,
- library_name: &name,
- });
- }
- }
- }
- }
- }
- sess.dcx().abort_if_errors();
- dylib_table
- .into_iter()
- .map(|(name, imports)| {
- (name, imports.into_iter().map(|(_, import)| import.clone()).collect())
- })
- .collect()
-}
-
-fn create_dll_import_libs<'a>(
- sess: &Session,
- archive_builder_builder: &dyn ArchiveBuilderBuilder,
- used_libraries: impl IntoIterator,
- tmpdir: &Path,
- is_direct_dependency: bool,
-) -> Vec {
- collate_raw_dylibs(sess, used_libraries)
- .into_iter()
- .map(|(raw_dylib_name, raw_dylib_imports)| {
- let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" };
- let output_path = tmpdir.join(format!("{raw_dylib_name}{name_suffix}.lib"));
-
- let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(&sess.target);
-
- let items: Vec = raw_dylib_imports
- .iter()
- .map(|import: &DllImport| {
- if sess.target.arch == "x86" {
- ImportLibraryItem {
- name: common::i686_decorated_name(
- import,
- mingw_gnu_toolchain,
- false,
- false,
- ),
- ordinal: import.ordinal(),
- symbol_name: import.is_missing_decorations().then(|| {
- common::i686_decorated_name(
- import,
- mingw_gnu_toolchain,
- false,
- true,
- )
- }),
- is_data: !import.is_fn,
- }
- } else {
- ImportLibraryItem {
- name: import.name.to_string(),
- ordinal: import.ordinal(),
- symbol_name: None,
- is_data: !import.is_fn,
- }
- }
- })
- .collect();
-
- archive_builder_builder.create_dll_import_lib(
- sess,
- &raw_dylib_name,
- items,
- &output_path,
- );
-
- output_path
- })
- .collect()
-}
-
/// Create a static archive.
///
/// This is essentially the same thing as an rlib, but it also involves adding all of the upstream
@@ -2370,15 +2274,39 @@ fn linker_with_args(
link_output_kind,
);
+ // Raw-dylibs from all crates.
+ let raw_dylib_dir = tmpdir.join("raw-dylibs");
+ if sess.target.binary_format() == object::BinaryFormat::Elf {
+ // On ELF we can't pass the raw-dylibs stubs to the linker as a path,
+ // instead we need to pass them via -l. To find the stub, we need to add
+ // the directory of the stub to the linker search path.
+ // We make an extra directory for this to avoid polluting the search path.
+ if let Err(error) = fs::create_dir(&raw_dylib_dir) {
+ sess.dcx().emit_fatal(errors::CreateTempDir { error })
+ }
+ cmd.include_path(&raw_dylib_dir);
+ }
+
// Link with the import library generated for any raw-dylib functions.
- for output_path in create_dll_import_libs(
- sess,
- archive_builder_builder,
- codegen_results.crate_info.used_libraries.iter(),
- tmpdir,
- true,
- ) {
- cmd.add_object(&output_path);
+ if sess.target.is_like_windows {
+ for output_path in raw_dylib::create_raw_dylib_dll_import_libs(
+ sess,
+ archive_builder_builder,
+ codegen_results.crate_info.used_libraries.iter(),
+ tmpdir,
+ true,
+ ) {
+ cmd.add_object(&output_path);
+ }
+ } else {
+ for link_path in raw_dylib::create_raw_dylib_elf_stub_shared_objects(
+ sess,
+ codegen_results.crate_info.used_libraries.iter(),
+ &raw_dylib_dir,
+ ) {
+ // Always use verbatim linkage, see comments in create_raw_dylib_elf_stub_shared_objects.
+ cmd.link_dylib_by_name(&link_path, true, false);
+ }
}
// As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case
// they are used within inlined functions or instantiated generic functions. We do this *after*
@@ -2397,19 +2325,35 @@ fn linker_with_args(
.native_libraries
.iter()
.filter_map(|(&cnum, libraries)| {
- (dependency_linkage[cnum] != Linkage::Static).then_some(libraries)
+ if sess.target.is_like_windows {
+ (dependency_linkage[cnum] != Linkage::Static).then_some(libraries)
+ } else {
+ Some(libraries)
+ }
})
.flatten()
.collect::>();
native_libraries_from_nonstatics.sort_unstable_by(|a, b| a.name.as_str().cmp(b.name.as_str()));
- for output_path in create_dll_import_libs(
- sess,
- archive_builder_builder,
- native_libraries_from_nonstatics,
- tmpdir,
- false,
- ) {
- cmd.add_object(&output_path);
+
+ if sess.target.is_like_windows {
+ for output_path in raw_dylib::create_raw_dylib_dll_import_libs(
+ sess,
+ archive_builder_builder,
+ native_libraries_from_nonstatics,
+ tmpdir,
+ false,
+ ) {
+ cmd.add_object(&output_path);
+ }
+ } else {
+ for link_path in raw_dylib::create_raw_dylib_elf_stub_shared_objects(
+ sess,
+ native_libraries_from_nonstatics,
+ &raw_dylib_dir,
+ ) {
+ // Always use verbatim linkage, see comments in create_raw_dylib_elf_stub_shared_objects.
+ cmd.link_dylib_by_name(&link_path, true, false);
+ }
}
// Library linking above uses some global state for things like `-Bstatic`/`-Bdynamic` to make
diff --git a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs
new file mode 100644
index 0000000000000..4bc8fec6fb5da
--- /dev/null
+++ b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs
@@ -0,0 +1,372 @@
+use std::fs;
+use std::io::{BufWriter, Write};
+use std::path::{Path, PathBuf};
+
+use rustc_abi::Endian;
+use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN};
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_data_structures::stable_hasher::{Hash128, StableHasher};
+use rustc_session::Session;
+use rustc_session::cstore::DllImport;
+use rustc_session::utils::NativeLibKind;
+use rustc_span::Symbol;
+
+use crate::back::archive::ImportLibraryItem;
+use crate::back::link::ArchiveBuilderBuilder;
+use crate::errors::ErrorCreatingImportLibrary;
+use crate::{NativeLib, common, errors};
+
+/// Extract all symbols defined in raw-dylib libraries, collated by library name.
+///
+/// If we have multiple extern blocks that specify symbols defined in the same raw-dylib library,
+/// then the CodegenResults value contains one NativeLib instance for each block. However, the
+/// linker appears to expect only a single import library for each library used, so we need to
+/// collate the symbols together by library name before generating the import libraries.
+fn collate_raw_dylibs_windows<'a>(
+ sess: &Session,
+ used_libraries: impl IntoIterator,
+) -> Vec<(String, Vec)> {
+ // Use index maps to preserve original order of imports and libraries.
+ let mut dylib_table = FxIndexMap::>::default();
+
+ for lib in used_libraries {
+ if lib.kind == NativeLibKind::RawDylib {
+ let ext = if lib.verbatim { "" } else { ".dll" };
+ let name = format!("{}{}", lib.name, ext);
+ let imports = dylib_table.entry(name.clone()).or_default();
+ for import in &lib.dll_imports {
+ if let Some(old_import) = imports.insert(import.name, import) {
+ // FIXME: when we add support for ordinals, figure out if we need to do anything
+ // if we have two DllImport values with the same name but different ordinals.
+ if import.calling_convention != old_import.calling_convention {
+ sess.dcx().emit_err(errors::MultipleExternalFuncDecl {
+ span: import.span,
+ function: import.name,
+ library_name: &name,
+ });
+ }
+ }
+ }
+ }
+ }
+ sess.dcx().abort_if_errors();
+ dylib_table
+ .into_iter()
+ .map(|(name, imports)| {
+ (name, imports.into_iter().map(|(_, import)| import.clone()).collect())
+ })
+ .collect()
+}
+
+pub(super) fn create_raw_dylib_dll_import_libs<'a>(
+ sess: &Session,
+ archive_builder_builder: &dyn ArchiveBuilderBuilder,
+ used_libraries: impl IntoIterator,
+ tmpdir: &Path,
+ is_direct_dependency: bool,
+) -> Vec {
+ collate_raw_dylibs_windows(sess, used_libraries)
+ .into_iter()
+ .map(|(raw_dylib_name, raw_dylib_imports)| {
+ let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" };
+ let output_path = tmpdir.join(format!("{raw_dylib_name}{name_suffix}.lib"));
+
+ let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(&sess.target);
+
+ let items: Vec = raw_dylib_imports
+ .iter()
+ .map(|import: &DllImport| {
+ if sess.target.arch == "x86" {
+ ImportLibraryItem {
+ name: common::i686_decorated_name(
+ import,
+ mingw_gnu_toolchain,
+ false,
+ false,
+ ),
+ ordinal: import.ordinal(),
+ symbol_name: import.is_missing_decorations().then(|| {
+ common::i686_decorated_name(
+ import,
+ mingw_gnu_toolchain,
+ false,
+ true,
+ )
+ }),
+ is_data: !import.is_fn,
+ }
+ } else {
+ ImportLibraryItem {
+ name: import.name.to_string(),
+ ordinal: import.ordinal(),
+ symbol_name: None,
+ is_data: !import.is_fn,
+ }
+ }
+ })
+ .collect();
+
+ archive_builder_builder.create_dll_import_lib(
+ sess,
+ &raw_dylib_name,
+ items,
+ &output_path,
+ );
+
+ output_path
+ })
+ .collect()
+}
+
+/// Extract all symbols defined in raw-dylib libraries, collated by library name.
+///
+/// If we have multiple extern blocks that specify symbols defined in the same raw-dylib library,
+/// then the CodegenResults value contains one NativeLib instance for each block. However, the
+/// linker appears to expect only a single import library for each library used, so we need to
+/// collate the symbols together by library name before generating the import libraries.
+fn collate_raw_dylibs_elf<'a>(
+ sess: &Session,
+ used_libraries: impl IntoIterator,
+) -> Vec<(String, Vec)> {
+ // Use index maps to preserve original order of imports and libraries.
+ let mut dylib_table = FxIndexMap::>::default();
+
+ for lib in used_libraries {
+ if lib.kind == NativeLibKind::RawDylib {
+ let filename = if lib.verbatim {
+ lib.name.as_str().to_owned()
+ } else {
+ let ext = sess.target.dll_suffix.as_ref();
+ let prefix = sess.target.dll_prefix.as_ref();
+ format!("{prefix}{}{ext}", lib.name)
+ };
+
+ let imports = dylib_table.entry(filename.clone()).or_default();
+ for import in &lib.dll_imports {
+ imports.insert(import.name, import);
+ }
+ }
+ }
+ sess.dcx().abort_if_errors();
+ dylib_table
+ .into_iter()
+ .map(|(name, imports)| {
+ (name, imports.into_iter().map(|(_, import)| import.clone()).collect())
+ })
+ .collect()
+}
+
+pub(super) fn create_raw_dylib_elf_stub_shared_objects<'a>(
+ sess: &Session,
+ used_libraries: impl IntoIterator,
+ raw_dylib_so_dir: &Path,
+) -> Vec {
+ collate_raw_dylibs_elf(sess, used_libraries)
+ .into_iter()
+ .map(|(load_filename, raw_dylib_imports)| {
+ use std::hash::Hash;
+
+ // `load_filename` is the *target/loader* filename that will end up in NEEDED.
+ // Usually this will be something like `libc.so` or `libc.so.6` but with
+ // verbatim it might also be an absolute path.
+ // To be able to support this properly, we always put this load filename
+ // into the SONAME of the library and link it via a temporary file with a random name.
+ // This also avoids naming conflicts with non-raw-dylib linkage of the same library.
+
+ let shared_object = create_elf_raw_dylib_stub(sess, &load_filename, &raw_dylib_imports);
+
+ let mut file_name_hasher = StableHasher::new();
+ load_filename.hash(&mut file_name_hasher);
+ for raw_dylib in raw_dylib_imports {
+ raw_dylib.name.as_str().hash(&mut file_name_hasher);
+ }
+
+ let library_filename: Hash128 = file_name_hasher.finish();
+ let temporary_lib_name = format!(
+ "{}{}{}",
+ sess.target.dll_prefix,
+ library_filename.as_u128().to_base_fixed_len(CASE_INSENSITIVE),
+ sess.target.dll_suffix
+ );
+ let link_path = raw_dylib_so_dir.join(&temporary_lib_name);
+
+ let file = match fs::File::create_new(&link_path) {
+ Ok(file) => file,
+ Err(error) => sess.dcx().emit_fatal(ErrorCreatingImportLibrary {
+ lib_name: &load_filename,
+ error: error.to_string(),
+ }),
+ };
+ if let Err(error) = BufWriter::new(file).write_all(&shared_object) {
+ sess.dcx().emit_fatal(ErrorCreatingImportLibrary {
+ lib_name: &load_filename,
+ error: error.to_string(),
+ });
+ };
+
+ temporary_lib_name
+ })
+ .collect()
+}
+
+/// Create an ELF .so stub file for raw-dylib.
+/// It exports all the provided symbols, but is otherwise empty.
+fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]) -> Vec {
+ use object::write::elf as write;
+ use object::{Architecture, elf};
+
+ let mut stub_buf = Vec::new();
+
+ // Build the stub ELF using the object crate.
+ // The high-level portable API does not allow for the fine-grained control we need,
+ // so this uses the low-level object::write::elf API.
+ // The low-level API consists of two stages: reservation and writing.
+ // We first reserve space for all the things in the binary and then write them.
+ // It is important that the order of reservation matches the order of writing.
+ // The object crate contains many debug asserts that fire if you get this wrong.
+
+ let endianness = match sess.target.options.endian {
+ Endian::Little => object::Endianness::Little,
+ Endian::Big => object::Endianness::Big,
+ };
+ let mut stub = write::Writer::new(endianness, true, &mut stub_buf);
+
+ // These initial reservations don't reserve any bytes in the binary yet,
+ // they just allocate in the internal data structures.
+
+ // First, we crate the dynamic symbol table. It starts with a null symbol
+ // and then all the symbols and their dynamic strings.
+ stub.reserve_null_dynamic_symbol_index();
+
+ let dynstrs = symbols
+ .iter()
+ .map(|sym| {
+ stub.reserve_dynamic_symbol_index();
+ (sym, stub.add_dynamic_string(sym.name.as_str().as_bytes()))
+ })
+ .collect::>();
+
+ let soname = stub.add_dynamic_string(soname.as_bytes());
+
+ // Reserve the sections.
+ // We have the minimal sections for a dynamic SO and .text where we point our dummy symbols to.
+ stub.reserve_shstrtab_section_index();
+ let text_section_name = stub.add_section_name(".text".as_bytes());
+ let text_section = stub.reserve_section_index();
+ stub.reserve_dynstr_section_index();
+ stub.reserve_dynsym_section_index();
+ stub.reserve_dynamic_section_index();
+
+ // These reservations now determine the actual layout order of the object file.
+ stub.reserve_file_header();
+ stub.reserve_shstrtab();
+ stub.reserve_section_headers();
+ stub.reserve_dynstr();
+ stub.reserve_dynsym();
+ stub.reserve_dynamic(2); // DT_SONAME, DT_NULL
+
+ // First write the ELF header with the arch information.
+ let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.unstable_target_features)
+ else {
+ sess.dcx().fatal(format!(
+ "raw-dylib is not supported for the architecture `{}`",
+ sess.target.arch
+ ));
+ };
+ let e_machine = match (arch, sub_arch) {
+ (Architecture::Aarch64, None) => elf::EM_AARCH64,
+ (Architecture::Aarch64_Ilp32, None) => elf::EM_AARCH64,
+ (Architecture::Arm, None) => elf::EM_ARM,
+ (Architecture::Avr, None) => elf::EM_AVR,
+ (Architecture::Bpf, None) => elf::EM_BPF,
+ (Architecture::Csky, None) => elf::EM_CSKY,
+ (Architecture::E2K32, None) => elf::EM_MCST_ELBRUS,
+ (Architecture::E2K64, None) => elf::EM_MCST_ELBRUS,
+ (Architecture::I386, None) => elf::EM_386,
+ (Architecture::X86_64, None) => elf::EM_X86_64,
+ (Architecture::X86_64_X32, None) => elf::EM_X86_64,
+ (Architecture::Hexagon, None) => elf::EM_HEXAGON,
+ (Architecture::LoongArch64, None) => elf::EM_LOONGARCH,
+ (Architecture::M68k, None) => elf::EM_68K,
+ (Architecture::Mips, None) => elf::EM_MIPS,
+ (Architecture::Mips64, None) => elf::EM_MIPS,
+ (Architecture::Mips64_N32, None) => elf::EM_MIPS,
+ (Architecture::Msp430, None) => elf::EM_MSP430,
+ (Architecture::PowerPc, None) => elf::EM_PPC,
+ (Architecture::PowerPc64, None) => elf::EM_PPC64,
+ (Architecture::Riscv32, None) => elf::EM_RISCV,
+ (Architecture::Riscv64, None) => elf::EM_RISCV,
+ (Architecture::S390x, None) => elf::EM_S390,
+ (Architecture::Sbf, None) => elf::EM_SBF,
+ (Architecture::Sharc, None) => elf::EM_SHARC,
+ (Architecture::Sparc, None) => elf::EM_SPARC,
+ (Architecture::Sparc32Plus, None) => elf::EM_SPARC32PLUS,
+ (Architecture::Sparc64, None) => elf::EM_SPARCV9,
+ (Architecture::Xtensa, None) => elf::EM_XTENSA,
+ _ => {
+ sess.dcx().fatal(format!(
+ "raw-dylib is not supported for the architecture `{}`",
+ sess.target.arch
+ ));
+ }
+ };
+
+ stub.write_file_header(&write::FileHeader {
+ os_abi: crate::back::metadata::elf_os_abi(sess),
+ abi_version: 0,
+ e_type: object::elf::ET_DYN,
+ e_machine,
+ e_entry: 0,
+ e_flags: crate::back::metadata::elf_e_flags(arch, sess),
+ })
+ .unwrap();
+
+ // .shstrtab
+ stub.write_shstrtab();
+
+ // Section headers
+ stub.write_null_section_header();
+ stub.write_shstrtab_section_header();
+ // Create a dummy .text section for our dummy symbols.
+ stub.write_section_header(&write::SectionHeader {
+ name: Some(text_section_name),
+ sh_type: elf::SHT_PROGBITS,
+ sh_flags: 0,
+ sh_addr: 0,
+ sh_offset: 0,
+ sh_size: 0,
+ sh_link: 0,
+ sh_info: 0,
+ sh_addralign: 1,
+ sh_entsize: 0,
+ });
+ stub.write_dynstr_section_header(0);
+ stub.write_dynsym_section_header(0, 1);
+ stub.write_dynamic_section_header(0);
+
+ // .dynstr
+ stub.write_dynstr();
+
+ // .dynsym
+ stub.write_null_dynamic_symbol();
+ for (_, name) in dynstrs {
+ stub.write_dynamic_symbol(&write::Sym {
+ name: Some(name),
+ st_info: (elf::STB_GLOBAL << 4) | elf::STT_NOTYPE,
+ st_other: elf::STV_DEFAULT,
+ section: Some(text_section),
+ st_shndx: 0, // ignored by object in favor of the `section` field
+ st_value: 0,
+ st_size: 0,
+ });
+ }
+
+ // .dynamic
+ // the DT_SONAME will be used by the linker to populate DT_NEEDED
+ // which the loader uses to find the library.
+ // DT_NULL terminates the .dynamic table.
+ stub.write_dynamic_string(elf::DT_SONAME, soname);
+ stub.write_dynamic(elf::DT_NULL, 0);
+
+ stub_buf
+}
diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs
index ba7b53321a83d..40ad70057ee9f 100644
--- a/compiler/rustc_codegen_ssa/src/back/metadata.rs
+++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs
@@ -9,8 +9,7 @@ use itertools::Itertools;
use object::write::{self, StandardSegment, Symbol, SymbolSection};
use object::{
Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection, ObjectSymbol,
- SectionFlags, SectionKind, SubArchitecture, SymbolFlags, SymbolKind, SymbolScope, elf, pe,
- xcoff,
+ SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope, elf, pe, xcoff,
};
use rustc_abi::Endian;
use rustc_data_structures::memmap::Mmap;
@@ -206,61 +205,12 @@ pub(crate) fn create_object_file(sess: &Session) -> Option Endianness::Little,
Endian::Big => Endianness::Big,
};
- let (architecture, sub_architecture) = match &sess.target.arch[..] {
- "arm" => (Architecture::Arm, None),
- "aarch64" => (
- if sess.target.pointer_width == 32 {
- Architecture::Aarch64_Ilp32
- } else {
- Architecture::Aarch64
- },
- None,
- ),
- "x86" => (Architecture::I386, None),
- "s390x" => (Architecture::S390x, None),
- "mips" | "mips32r6" => (Architecture::Mips, None),
- "mips64" | "mips64r6" => (Architecture::Mips64, None),
- "x86_64" => (
- if sess.target.pointer_width == 32 {
- Architecture::X86_64_X32
- } else {
- Architecture::X86_64
- },
- None,
- ),
- "powerpc" => (Architecture::PowerPc, None),
- "powerpc64" => (Architecture::PowerPc64, None),
- "riscv32" => (Architecture::Riscv32, None),
- "riscv64" => (Architecture::Riscv64, None),
- "sparc" => {
- if sess.unstable_target_features.contains(&sym::v8plus) {
- // Target uses V8+, aka EM_SPARC32PLUS, aka 64-bit V9 but in 32-bit mode
- (Architecture::Sparc32Plus, None)
- } else {
- // Target uses V7 or V8, aka EM_SPARC
- (Architecture::Sparc, None)
- }
- }
- "sparc64" => (Architecture::Sparc64, None),
- "avr" => (Architecture::Avr, None),
- "msp430" => (Architecture::Msp430, None),
- "hexagon" => (Architecture::Hexagon, None),
- "bpf" => (Architecture::Bpf, None),
- "loongarch64" => (Architecture::LoongArch64, None),
- "csky" => (Architecture::Csky, None),
- "arm64ec" => (Architecture::Aarch64, Some(SubArchitecture::Arm64EC)),
- // Unsupported architecture.
- _ => return None,
- };
- let binary_format = if sess.target.is_like_osx {
- BinaryFormat::MachO
- } else if sess.target.is_like_windows {
- BinaryFormat::Coff
- } else if sess.target.is_like_aix {
- BinaryFormat::Xcoff
- } else {
- BinaryFormat::Elf
+ let Some((architecture, sub_architecture)) =
+ sess.target.object_architecture(&sess.unstable_target_features)
+ else {
+ return None;
};
+ let binary_format = sess.target.binary_format();
let mut file = write::Object::new(binary_format, architecture, endianness);
file.set_sub_architecture(sub_architecture);
@@ -300,7 +250,26 @@ pub(crate) fn create_object_file(sess: &Session) -> Option u8 {
+ match sess.target.options.os.as_ref() {
+ "hermit" => elf::ELFOSABI_STANDALONE,
+ "freebsd" => elf::ELFOSABI_FREEBSD,
+ "solaris" => elf::ELFOSABI_SOLARIS,
+ _ => elf::ELFOSABI_NONE,
+ }
+}
+
+pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 {
+ match architecture {
Architecture::Mips => {
let arch = match sess.target.options.cpu.as_ref() {
"mips1" => elf::EF_MIPS_ARCH_1,
@@ -391,18 +360,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option 0,
- };
- // adapted from LLVM's `MCELFObjectTargetWriter::getOSABI`
- let os_abi = match sess.target.options.os.as_ref() {
- "hermit" => elf::ELFOSABI_STANDALONE,
- "freebsd" => elf::ELFOSABI_FREEBSD,
- "solaris" => elf::ELFOSABI_SOLARIS,
- _ => elf::ELFOSABI_NONE,
- };
- let abi_version = 0;
- add_gnu_property_note(&mut file, architecture, binary_format, endianness);
- file.flags = FileFlags::Elf { os_abi, abi_version, e_flags };
- Some(file)
+ }
}
/// Mach-O files contain information about:
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index ecf9745b77945..8f33e0806cbce 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -57,6 +57,8 @@ pub fn provide(providers: &mut Providers) {
providers.check_validity_requirement = |tcx, (init_kind, param_env_and_ty)| {
util::check_validity_requirement(tcx, init_kind, param_env_and_ty)
};
+ providers.hooks.validate_scalar_in_layout =
+ |tcx, scalar, layout| util::validate_scalar_in_layout(tcx, scalar, layout);
}
/// `rustc_driver::main` installs a handler that will set this to `true` if
diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
index a729d9325c84a..83d45b12cd746 100644
--- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
+++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
@@ -1,9 +1,10 @@
use rustc_abi::{BackendRepr, FieldsShape, Scalar, Variants};
-use rustc_middle::bug;
+use rustc_middle::query::TyCtxtAt;
use rustc_middle::ty::layout::{
HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement,
};
-use rustc_middle::ty::{PseudoCanonicalInput, Ty, TyCtxt};
+use rustc_middle::ty::{PseudoCanonicalInput, ScalarInt, Ty, TyCtxt};
+use rustc_middle::{bug, span_bug, ty};
use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeMachine};
use crate::interpret::{InterpCx, MemoryKind};
@@ -34,7 +35,7 @@ pub fn check_validity_requirement<'tcx>(
let layout_cx = LayoutCx::new(tcx, input.typing_env);
if kind == ValidityRequirement::Uninit || tcx.sess.opts.unstable_opts.strict_init_checks {
- check_validity_requirement_strict(layout, &layout_cx, kind)
+ Ok(check_validity_requirement_strict(layout, &layout_cx, kind))
} else {
check_validity_requirement_lax(layout, &layout_cx, kind)
}
@@ -46,7 +47,7 @@ fn check_validity_requirement_strict<'tcx>(
ty: TyAndLayout<'tcx>,
cx: &LayoutCx<'tcx>,
kind: ValidityRequirement,
-) -> Result> {
+) -> bool {
let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error);
let mut cx = InterpCx::new(cx.tcx(), rustc_span::DUMMY_SP, cx.typing_env, machine);
@@ -69,14 +70,13 @@ fn check_validity_requirement_strict<'tcx>(
// due to this.
// The value we are validating is temporary and discarded at the end of this function, so
// there is no point in reseting provenance and padding.
- Ok(cx
- .validate_operand(
- &allocated.into(),
- /*recursive*/ false,
- /*reset_provenance_and_padding*/ false,
- )
- .discard_err()
- .is_some())
+ cx.validate_operand(
+ &allocated.into(),
+ /*recursive*/ false,
+ /*reset_provenance_and_padding*/ false,
+ )
+ .discard_err()
+ .is_some()
}
/// Implements the 'lax' (default) version of the [`check_validity_requirement`] checks; see that
@@ -168,3 +168,31 @@ fn check_validity_requirement_lax<'tcx>(
Ok(true)
}
+
+pub(crate) fn validate_scalar_in_layout<'tcx>(
+ tcx: TyCtxtAt<'tcx>,
+ scalar: ScalarInt,
+ ty: Ty<'tcx>,
+) -> bool {
+ let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error);
+
+ let typing_env = ty::TypingEnv::fully_monomorphized();
+ let mut cx = InterpCx::new(tcx.tcx, tcx.span, typing_env, machine);
+
+ let Ok(layout) = cx.layout_of(ty) else {
+ span_bug!(tcx.span, "could not compute layout of {scalar:?}:{ty:?}")
+ };
+ let allocated = cx
+ .allocate(layout, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap))
+ .expect("OOM: failed to allocate for uninit check");
+
+ cx.write_scalar(scalar, &allocated).unwrap();
+
+ cx.validate_operand(
+ &allocated.into(),
+ /*recursive*/ false,
+ /*reset_provenance_and_padding*/ false,
+ )
+ .discard_err()
+ .is_some()
+}
diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs
index 25a9dbb2c1117..5be5ee8d1ae97 100644
--- a/compiler/rustc_const_eval/src/util/mod.rs
+++ b/compiler/rustc_const_eval/src/util/mod.rs
@@ -8,6 +8,7 @@ mod type_name;
pub use self::alignment::{is_disaligned, is_within_packed};
pub use self::check_validity_requirement::check_validity_requirement;
+pub(crate) use self::check_validity_requirement::validate_scalar_in_layout;
pub use self::compare_types::{relate_types, sub_types};
pub use self::type_name::type_name;
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 1a216ebf117c6..b6a28af048036 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -592,6 +592,8 @@ declare_features! (
(unstable, precise_capturing_in_traits, "1.83.0", Some(130044)),
/// Allows macro attributes on expressions, statements and non-inline modules.
(unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
+ /// Allows the use of raw-dylibs on ELF platforms
+ (incomplete, raw_dylib_elf, "CURRENT_RUSTC_VERSION", Some(135694)),
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024.
(incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)),
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural variant
diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl
index d2b5ae5318592..bebbc1a8d5a8b 100644
--- a/compiler/rustc_metadata/messages.ftl
+++ b/compiler/rustc_metadata/messages.ftl
@@ -233,6 +233,9 @@ metadata_prev_alloc_error_handler =
metadata_prev_global_alloc =
previous global allocator defined here
+metadata_raw_dylib_elf_unstable =
+ link kind `raw-dylib` is unstable on ELF platforms
+
metadata_raw_dylib_no_nul =
link name must not contain NUL characters if link kind is `raw-dylib`
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index 2a1e4b261e737..4be54a2a3511d 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -17,7 +17,7 @@ use rustc_session::search_paths::PathKind;
use rustc_session::utils::NativeLibKind;
use rustc_span::def_id::{DefId, LOCAL_CRATE};
use rustc_span::{Symbol, sym};
-use rustc_target::spec::LinkSelfContainedComponents;
+use rustc_target::spec::{BinaryFormat, LinkSelfContainedComponents};
use crate::{errors, fluent_generated};
@@ -263,9 +263,26 @@ impl<'tcx> Collector<'tcx> {
NativeLibKind::Framework { as_needed: None }
}
"raw-dylib" => {
- if !sess.target.is_like_windows {
+ if sess.target.is_like_windows {
+ // raw-dylib is stable and working on Windows
+ } else if sess.target.binary_format() == BinaryFormat::Elf
+ && features.raw_dylib_elf()
+ {
+ // raw-dylib is unstable on ELF, but the user opted in
+ } else if sess.target.binary_format() == BinaryFormat::Elf
+ && sess.is_nightly_build()
+ {
+ feature_err(
+ sess,
+ sym::raw_dylib_elf,
+ span,
+ fluent_generated::metadata_raw_dylib_elf_unstable,
+ )
+ .emit();
+ } else {
sess.dcx().emit_err(errors::RawDylibOnlyWindows { span });
}
+
NativeLibKind::RawDylib
}
"link-arg" => {
diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs
index 2be242364c111..9822eb23a2367 100644
--- a/compiler/rustc_middle/src/hooks/mod.rs
+++ b/compiler/rustc_middle/src/hooks/mod.rs
@@ -112,6 +112,10 @@ declare_hooks! {
hook save_dep_graph() -> ();
hook query_key_hash_verify_all() -> ();
+
+ /// Ensure the given scalar is valid for the given type.
+ /// This checks non-recursive runtime validity.
+ hook validate_scalar_in_layout(scalar: crate::ty::ScalarInt, ty: Ty<'tcx>) -> bool;
}
#[cold]
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 6c8591dae895b..f603add3c1153 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -1741,7 +1741,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
" as ",
)?;
}
- ty::Pat(base_ty, pat) => {
+ ty::Pat(base_ty, pat) if self.tcx().validate_scalar_in_layout(int, ty) => {
self.pretty_print_const_scalar_int(int, *base_ty, print_ty)?;
p!(write(" is {pat:?}"));
}
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 20a728d6d5b2c..0aa61152330a7 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -155,42 +155,41 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
fn lower_pattern_range_endpoint(
&mut self,
expr: Option<&'tcx hir::PatExpr<'tcx>>,
- ) -> Result<
- (Option>, Option>, Option),
- ErrorGuaranteed,
- > {
- match expr {
- None => Ok((None, None, None)),
- Some(expr) => {
- let (kind, ascr, inline_const) = match self.lower_lit(expr) {
- PatKind::ExpandedConstant { subpattern, def_id, is_inline: true } => {
- (subpattern.kind, None, def_id.as_local())
- }
- PatKind::ExpandedConstant { subpattern, is_inline: false, .. } => {
- (subpattern.kind, None, None)
- }
- PatKind::AscribeUserType { ascription, subpattern: box Pat { kind, .. } } => {
- (kind, Some(ascription), None)
- }
- kind => (kind, None, None),
- };
- let value = match kind {
- PatKind::Constant { value } => value,
- PatKind::ExpandedConstant { subpattern, .. }
- if let PatKind::Constant { value } = subpattern.kind =>
- {
- value
- }
- _ => {
- let msg = format!(
- "found bad range pattern endpoint `{expr:?}` outside of error recovery"
- );
- return Err(self.tcx.dcx().span_delayed_bug(expr.span, msg));
+ // Out-parameters collecting extra data to be reapplied by the caller
+ ascriptions: &mut Vec>,
+ inline_consts: &mut Vec,
+ ) -> Result
\
In older versions of Rust, dyn compatibility was called \"object safety\", \
so this trait is not object safe.
",
- base = crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL
+ base = crate::clean::utils::DOC_RUST_LANG_ORG_VERSION
),
);
}
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index ccf4002bb300d..bfd5cb7764fd4 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -1534,10 +1534,10 @@ function preLoadCss(cssUrl) {
function buildHelpMenu() {
const book_info = document.createElement("span");
- const channel = getVar("channel");
+ const drloChannel = `https://doc.rust-lang.org/${getVar("channel")}`;
book_info.className = "top";
book_info.innerHTML = `You can find more information in \
-the rustdoc book.`;
+the rustdoc book.`;
const shortcuts = [
["?", "Show this help dialog"],
@@ -1557,8 +1557,8 @@ function preLoadCss(cssUrl) {
div_shortcuts.innerHTML = "
Keyboard Shortcuts
" + shortcuts + "
";
const infos = [
- `For a full list of all search features, take a look here.`,
+ `For a full list of all search features, take a look \
+ here.`,
"Prefix searches with a type followed by a colon (e.g., fn:) to \
restrict the search to a given item kind.",
"Accepted kinds are: fn, mod, struct, \
@@ -1568,10 +1568,10 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm
-> vec or String, enum:Cow -> bool)",
"You can look for items with an exact name by putting double quotes around \
your request: \"string\"",
- "Look for functions that accept or return \
- slices and \
- arrays by writing \
- square brackets (e.g., -> [u8] or [] -> Option)",
+ `Look for functions that accept or return \
+ slices and \
+ arrays by writing square \
+ brackets (e.g., -> [u8] or [] -> Option)`,
"Look for items inside another one by searching for a path: vec::Vec",
].map(x => "