Move metadata object generation for dylibs to the linker code

This deduplicates some code between codegen backends and may in the
future allow adding extra metadata that is only known at link time.
This commit is contained in:
bjorn3
2025-05-30 12:51:15 +00:00
parent badabab01f
commit 0bd7aa1116
17 changed files with 92 additions and 220 deletions

View File

@@ -14,11 +14,12 @@ use object::read::macho::FatArch;
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::memmap::Mmap;
use rustc_fs_util::TempDirBuilder;
use rustc_metadata::EncodedMetadata;
use rustc_session::Session;
use rustc_span::Symbol;
use tracing::trace;
use super::metadata::search_for_section;
use super::metadata::{create_compressed_metadata_file, search_for_section};
use crate::common;
// Re-exporting for rustc_codegen_llvm::back::archive
pub use crate::errors::{ArchiveBuildFailure, ExtractBundledLibsError, UnknownArchiveKind};
@@ -58,6 +59,15 @@ impl From<ImportLibraryItem> for COFFShortExport {
pub trait ArchiveBuilderBuilder {
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a>;
fn create_dylib_metadata_wrapper(
&self,
sess: &Session,
metadata: &EncodedMetadata,
symbol_name: &str,
) -> Vec<u8> {
create_compressed_metadata_file(sess, metadata, symbol_name)
}
/// Creates a DLL Import Library <https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-creation#creating-an-import-library>.
/// and returns the path on disk to that import library.
/// This functions doesn't take `self` so that it can be called from

View File

@@ -167,6 +167,7 @@ pub fn link_binary(
crate_type,
&out_filename,
&codegen_results,
&metadata,
path.as_ref(),
);
}
@@ -230,11 +231,7 @@ pub fn link_binary(
let remove_temps_from_module =
|module: &CompiledModule| maybe_remove_temps_from_module(false, false, module);
// Otherwise, always remove the metadata and allocator module temporaries.
if let Some(ref metadata_module) = codegen_results.metadata_module {
remove_temps_from_module(metadata_module);
}
// Otherwise, always remove the allocator module temporaries.
if let Some(ref allocator_module) = codegen_results.allocator_module {
remove_temps_from_module(allocator_module);
}
@@ -326,7 +323,7 @@ fn link_rlib<'a>(
RlibFlavor::Normal => {
let (metadata, metadata_position) =
create_wrapper_file(sess, ".rmeta".to_string(), metadata.stub_or_full());
let metadata = emit_wrapper_file(sess, &metadata, tmpdir, METADATA_FILENAME);
let metadata = emit_wrapper_file(sess, &metadata, tmpdir.as_ref(), METADATA_FILENAME);
match metadata_position {
MetadataPosition::First => {
// Most of the time metadata in rlib files is wrapped in a "dummy" object
@@ -394,7 +391,7 @@ fn link_rlib<'a>(
let src = read(path)
.unwrap_or_else(|e| sess.dcx().emit_fatal(errors::ReadFileError { message: e }));
let (data, _) = create_wrapper_file(sess, ".bundled_lib".to_string(), &src);
let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str());
let wrapper_file = emit_wrapper_file(sess, &data, tmpdir.as_ref(), filename.as_str());
packed_bundled_libs.push(wrapper_file);
} else {
let path = find_native_static_library(lib.name.as_str(), lib.verbatim, sess);
@@ -698,6 +695,7 @@ fn link_natively(
crate_type: CrateType,
out_filename: &Path,
codegen_results: &CodegenResults,
metadata: &EncodedMetadata,
tmpdir: &Path,
) {
info!("preparing {:?} to {:?}", crate_type, out_filename);
@@ -722,6 +720,7 @@ fn link_natively(
tmpdir,
temp_filename,
codegen_results,
metadata,
self_contained_components,
);
@@ -2098,17 +2097,25 @@ fn add_local_crate_allocator_objects(cmd: &mut dyn Linker, codegen_results: &Cod
/// Add object files containing metadata for the current crate.
fn add_local_crate_metadata_objects(
cmd: &mut dyn Linker,
sess: &Session,
archive_builder_builder: &dyn ArchiveBuilderBuilder,
crate_type: CrateType,
tmpdir: &Path,
codegen_results: &CodegenResults,
metadata: &EncodedMetadata,
) {
// When linking a dynamic library, we put the metadata into a section of the
// executable. This metadata is in a separate object file from the main
// object file, so we link that in here.
if matches!(crate_type, CrateType::Dylib | CrateType::ProcMacro)
&& let Some(m) = &codegen_results.metadata_module
&& let Some(obj) = &m.object
{
cmd.add_object(obj);
// object file, so we create and link it in here.
if matches!(crate_type, CrateType::Dylib | CrateType::ProcMacro) {
let data = archive_builder_builder.create_dylib_metadata_wrapper(
sess,
&metadata,
&codegen_results.crate_info.metadata_symbol,
);
let obj = emit_wrapper_file(sess, &data, tmpdir, "rmeta.o");
cmd.add_object(&obj);
}
}
@@ -2198,6 +2205,7 @@ fn linker_with_args(
tmpdir: &Path,
out_filename: &Path,
codegen_results: &CodegenResults,
metadata: &EncodedMetadata,
self_contained_components: LinkSelfContainedComponents,
) -> Command {
let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled();
@@ -2272,7 +2280,15 @@ fn linker_with_args(
// in this DAG so far because they can only depend on other native libraries
// and such dependencies are also required to be specified.
add_local_crate_regular_objects(cmd, codegen_results);
add_local_crate_metadata_objects(cmd, crate_type, codegen_results);
add_local_crate_metadata_objects(
cmd,
sess,
archive_builder_builder,
crate_type,
tmpdir,
codegen_results,
metadata,
);
add_local_crate_allocator_objects(cmd, codegen_results);
// Avoid linking to dynamic libraries unless they satisfy some undefined symbols

View File

@@ -141,7 +141,6 @@ impl ModuleConfig {
|| match kind {
ModuleKind::Regular => sess.opts.output_types.contains_key(&OutputType::Object),
ModuleKind::Allocator => false,
ModuleKind::Metadata => sess.opts.output_types.contains_key(&OutputType::Metadata),
};
let emit_obj = if !should_emit_obj {
@@ -349,7 +348,6 @@ pub struct CodegenContext<B: WriteBackendMethods> {
pub output_filenames: Arc<OutputFilenames>,
pub invocation_temp: Option<String>,
pub regular_module_config: Arc<ModuleConfig>,
pub metadata_module_config: Arc<ModuleConfig>,
pub allocator_module_config: Arc<ModuleConfig>,
pub tm_factory: TargetMachineFactoryFn<B>,
pub msvc_imps_needed: bool,
@@ -394,7 +392,6 @@ impl<B: WriteBackendMethods> CodegenContext<B> {
pub fn config(&self, kind: ModuleKind) -> &ModuleConfig {
match kind {
ModuleKind::Regular => &self.regular_module_config,
ModuleKind::Metadata => &self.metadata_module_config,
ModuleKind::Allocator => &self.allocator_module_config,
}
}
@@ -473,7 +470,6 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
backend: B,
tcx: TyCtxt<'_>,
target_cpu: String,
metadata_module: Option<CompiledModule>,
) -> OngoingCodegen<B> {
let (coordinator_send, coordinator_receive) = channel();
@@ -483,7 +479,6 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
let crate_info = CrateInfo::new(tcx, target_cpu);
let regular_config = ModuleConfig::new(ModuleKind::Regular, tcx, no_builtins);
let metadata_config = ModuleConfig::new(ModuleKind::Metadata, tcx, no_builtins);
let allocator_config = ModuleConfig::new(ModuleKind::Allocator, tcx, no_builtins);
let (shared_emitter, shared_emitter_main) = SharedEmitter::new();
@@ -497,14 +492,12 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
codegen_worker_send,
coordinator_receive,
Arc::new(regular_config),
Arc::new(metadata_config),
Arc::new(allocator_config),
coordinator_send.clone(),
);
OngoingCodegen {
backend,
metadata_module,
crate_info,
codegen_worker_receive,
@@ -840,12 +833,6 @@ pub(crate) fn compute_per_cgu_lto_type(
sess_crate_types: &[CrateType],
module_kind: ModuleKind,
) -> ComputedLtoType {
// Metadata modules never participate in LTO regardless of the lto
// settings.
if module_kind == ModuleKind::Metadata {
return ComputedLtoType::No;
}
// If the linker does LTO, we don't have to do it. Note that we
// keep doing full LTO, if it is requested, as not to break the
// assumption that the output will be a single module.
@@ -1026,10 +1013,7 @@ fn finish_intra_module_work<B: ExtraBackendMethods>(
let dcx = cgcx.create_dcx();
let dcx = dcx.handle();
if !cgcx.opts.unstable_opts.combine_cgu
|| module.kind == ModuleKind::Metadata
|| module.kind == ModuleKind::Allocator
{
if !cgcx.opts.unstable_opts.combine_cgu || module.kind == ModuleKind::Allocator {
let module = B::codegen(cgcx, dcx, module, module_config)?;
Ok(WorkItemResult::Finished(module))
} else {
@@ -1120,7 +1104,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
codegen_worker_send: Sender<CguMessage>,
coordinator_receive: Receiver<Box<dyn Any + Send>>,
regular_config: Arc<ModuleConfig>,
metadata_config: Arc<ModuleConfig>,
allocator_config: Arc<ModuleConfig>,
tx_to_llvm_workers: Sender<Box<dyn Any + Send>>,
) -> thread::JoinHandle<Result<CompiledModules, ()>> {
@@ -1213,7 +1196,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
diag_emitter: shared_emitter.clone(),
output_filenames: Arc::clone(tcx.output_filenames(())),
regular_module_config: regular_config,
metadata_module_config: metadata_config,
allocator_module_config: allocator_config,
tm_factory: backend.target_machine_factory(tcx.sess, ol, backend_features),
msvc_imps_needed: msvc_imps_needed(tcx),
@@ -1670,7 +1652,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
assert!(compiled_allocator_module.is_none());
compiled_allocator_module = Some(compiled_module);
}
ModuleKind::Metadata => bug!("Should be handled separately"),
}
}
Ok(WorkItemResult::NeedsLink(module)) => {
@@ -2052,7 +2033,6 @@ impl<B: ExtraBackendMethods> Drop for Coordinator<B> {
pub struct OngoingCodegen<B: ExtraBackendMethods> {
pub backend: B,
pub metadata_module: Option<CompiledModule>,
pub crate_info: CrateInfo,
pub codegen_worker_receive: Receiver<CguMessage>,
pub shared_emitter_main: SharedEmitterMain,
@@ -2096,7 +2076,6 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
modules: compiled_modules.modules,
allocator_module: compiled_modules.allocator_module,
metadata_module: self.metadata_module,
},
work_products,
)

View File

@@ -15,11 +15,10 @@ 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_metadata::EncodedMetadata;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType};
use rustc_middle::middle::exported_symbols::SymbolExportKind;
use rustc_middle::middle::{exported_symbols, lang_items};
use rustc_middle::middle::exported_symbols::{self, SymbolExportKind};
use rustc_middle::middle::lang_items;
use rustc_middle::mir::BinOp;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem, MonoItemPartitions};
@@ -28,7 +27,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_session::Session;
use rustc_session::config::{self, CrateType, EntryFnType, OutputType};
use rustc_session::config::{self, CrateType, EntryFnType};
use rustc_span::{DUMMY_SP, Symbol, sym};
use rustc_symbol_mangling::mangle_internal_symbol;
use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt};
@@ -37,7 +36,6 @@ use tracing::{debug, info};
use crate::assert_module_sources::CguReuse;
use crate::back::link::are_upstream_rust_objects_already_included;
use crate::back::metadata::create_compressed_metadata_file;
use crate::back::write::{
ComputedLtoType, OngoingCodegen, compute_per_cgu_lto_type, start_async_codegen,
submit_codegened_module_to_llvm, submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm,
@@ -48,8 +46,7 @@ use crate::mir::operand::OperandValue;
use crate::mir::place::PlaceRef;
use crate::traits::*;
use crate::{
CachedModuleCodegen, CodegenLintLevels, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind,
errors, meth, mir,
CachedModuleCodegen, CodegenLintLevels, CrateInfo, ModuleCodegen, ModuleKind, errors, meth, mir,
};
pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate {
@@ -669,11 +666,10 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
backend: B,
tcx: TyCtxt<'_>,
target_cpu: String,
metadata: Option<&EncodedMetadata>,
) -> OngoingCodegen<B> {
// Skip crate items and just output metadata in -Z no-codegen mode.
if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, None);
let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu);
ongoing_codegen.codegen_finished(tcx);
@@ -706,40 +702,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
}
}
let metadata_module = if let Some(metadata) = metadata {
// Emit compressed metadata object.
let metadata_cgu_name =
cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("metadata")).to_string();
tcx.sess.time("write_compressed_metadata", || {
let file_name = tcx.output_filenames(()).temp_path_for_cgu(
OutputType::Metadata,
&metadata_cgu_name,
tcx.sess.invocation_temp.as_deref(),
);
let data = create_compressed_metadata_file(
tcx.sess,
metadata,
&exported_symbols::metadata_symbol_name(tcx),
);
if let Err(error) = std::fs::write(&file_name, data) {
tcx.dcx().emit_fatal(errors::MetadataObjectFileWrite { error });
}
Some(CompiledModule {
name: metadata_cgu_name,
kind: ModuleKind::Metadata,
object: Some(file_name),
dwarf_object: None,
bytecode: None,
assembly: None,
llvm_ir: None,
links_from_incr_cache: Vec::new(),
})
})
} else {
None
};
let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu, metadata_module);
let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu);
// Codegen an allocator shim, if necessary.
if let Some(kind) = allocator_kind_for_codegen(tcx) {
@@ -1010,6 +973,7 @@ impl CrateInfo {
windows_subsystem,
natvis_debugger_visualizers: Default::default(),
lint_levels: CodegenLintLevels::from_tcx(tcx),
metadata_symbol: exported_symbols::metadata_symbol_name(tcx),
};
info.native_libraries.reserve(n_crates);

View File

@@ -777,12 +777,6 @@ pub(crate) struct MultipleMainFunctions {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_metadata_object_file_write)]
pub(crate) struct MetadataObjectFileWrite {
pub error: Error,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_invalid_windows_subsystem)]
pub(crate) struct InvalidWindowsSubsystem {

View File

@@ -170,7 +170,6 @@ pub(crate) struct CachedModuleCodegen {
#[derive(Copy, Clone, Debug, PartialEq, Encodable, Decodable)]
pub enum ModuleKind {
Regular,
Metadata,
Allocator,
}
@@ -234,6 +233,7 @@ pub struct CrateInfo {
pub windows_subsystem: Option<String>,
pub natvis_debugger_visualizers: BTreeSet<DebuggerVisualizerFile>,
pub lint_levels: CodegenLintLevels,
pub metadata_symbol: String,
}
/// Target-specific options that get set in `cfg(...)`.
@@ -258,7 +258,6 @@ pub struct TargetConfig {
pub struct CodegenResults {
pub modules: Vec<CompiledModule>,
pub allocator_module: Option<CompiledModule>,
pub metadata_module: Option<CompiledModule>,
pub crate_info: CrateInfo,
}

View File

@@ -74,11 +74,7 @@ pub trait CodegenBackend {
fn provide(&self, _providers: &mut Providers) {}
fn codegen_crate<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
metadata: Option<&EncodedMetadata>,
) -> Box<dyn Any>;
fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box<dyn Any>;
/// This is called on the returned `Box<dyn Any>` from [`codegen_crate`](Self::codegen_crate)
///