Skip to content

Commit 5d3c6ee

Browse files
committed
Auto merge of #132362 - mustartt:aix-dylib-detection, r=jieyouxu
[AIX] change system dynamic library format Historically on AIX, almost all dynamic libraries are distributed in `.a` Big Archive Format which can consists of both static and shared objects in the same archive (e.g. `libc++abi.a(libc++abi.so.1)`). During the initial porting process, the dynamic libraries are kept as `.a` to simplify the migration, but semantically having an XCOFF object under the archive extension is wrong. For crate type `cdylib` we want to be able to distribute the libraries as archives as well. We are migrating to archives with the following format: ``` $ ar -t lib<name>.a lib<name>.so ``` where each archive contains a single member that is a shared XCOFF object that can be loaded.
2 parents b19329a + 0db9059 commit 5d3c6ee

File tree

10 files changed

+127
-22
lines changed

10 files changed

+127
-22
lines changed

Diff for: Cargo.lock

+3-2
Original file line numberDiff line numberDiff line change
@@ -1965,9 +1965,9 @@ checksum = "baff4b617f7df3d896f97fe922b64817f6cd9a756bb81d40f8883f2f66dcb401"
19651965

19661966
[[package]]
19671967
name = "libc"
1968-
version = "0.2.161"
1968+
version = "0.2.164"
19691969
source = "registry+https://github.com/rust-lang/crates.io-index"
1970-
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
1970+
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
19711971

19721972
[[package]]
19731973
name = "libdbus-sys"
@@ -3990,6 +3990,7 @@ name = "rustc_metadata"
39903990
version = "0.0.0"
39913991
dependencies = [
39923992
"bitflags 2.6.0",
3993+
"libc",
39933994
"libloading",
39943995
"odht",
39953996
"rustc_abi",

Diff for: compiler/rustc_codegen_ssa/src/back/link.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -777,14 +777,24 @@ fn link_natively(
777777
info!("preparing {:?} to {:?}", crate_type, out_filename);
778778
let (linker_path, flavor) = linker_and_flavor(sess);
779779
let self_contained_components = self_contained_components(sess, crate_type);
780+
781+
// On AIX, we ship all libraries as .a big_af archive
782+
// the expected format is lib<name>.a(libname.so) for the actual
783+
// dynamic library. So we link to a temporary .so file to be archived
784+
// at the final out_filename location
785+
let should_archive = crate_type != CrateType::Executable && sess.target.is_like_aix;
786+
let archive_member =
787+
should_archive.then(|| tmpdir.join(out_filename.file_name().unwrap()).with_extension("so"));
788+
let temp_filename = archive_member.as_deref().unwrap_or(out_filename);
789+
780790
let mut cmd = linker_with_args(
781791
&linker_path,
782792
flavor,
783793
sess,
784794
archive_builder_builder,
785795
crate_type,
786796
tmpdir,
787-
out_filename,
797+
temp_filename,
788798
codegen_results,
789799
self_contained_components,
790800
)?;
@@ -1158,6 +1168,12 @@ fn link_natively(
11581168
}
11591169
}
11601170

1171+
if should_archive {
1172+
let mut ab = archive_builder_builder.new_archive_builder(sess);
1173+
ab.add_file(temp_filename);
1174+
ab.build(out_filename);
1175+
}
1176+
11611177
Ok(())
11621178
}
11631179

Diff for: compiler/rustc_codegen_ssa/src/back/metadata.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::fs::File;
55
use std::io::Write;
66
use std::path::Path;
77

8+
use itertools::Itertools;
89
use object::write::{self, StandardSegment, Symbol, SymbolSection};
910
use object::{
1011
Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection, ObjectSymbol,
@@ -21,6 +22,7 @@ use rustc_middle::bug;
2122
use rustc_session::Session;
2223
use rustc_span::sym;
2324
use rustc_target::spec::{RelocModel, Target, ef_avr_arch};
25+
use tracing::debug;
2426

2527
use super::apple;
2628

@@ -53,6 +55,7 @@ fn load_metadata_with(
5355

5456
impl MetadataLoader for DefaultMetadataLoader {
5557
fn get_rlib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
58+
debug!("getting rlib metadata for {}", path.display());
5659
load_metadata_with(path, |data| {
5760
let archive = object::read::archive::ArchiveFile::parse(&*data)
5861
.map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
@@ -77,8 +80,26 @@ impl MetadataLoader for DefaultMetadataLoader {
7780
}
7881

7982
fn get_dylib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
83+
debug!("getting dylib metadata for {}", path.display());
8084
if target.is_like_aix {
81-
load_metadata_with(path, |data| get_metadata_xcoff(path, data))
85+
load_metadata_with(path, |data| {
86+
let archive = object::read::archive::ArchiveFile::parse(&*data).map_err(|e| {
87+
format!("failed to parse aix dylib '{}': {}", path.display(), e)
88+
})?;
89+
90+
match archive.members().exactly_one() {
91+
Ok(lib) => {
92+
let lib = lib.map_err(|e| {
93+
format!("failed to parse aix dylib '{}': {}", path.display(), e)
94+
})?;
95+
let data = lib.data(data).map_err(|e| {
96+
format!("failed to parse aix dylib '{}': {}", path.display(), e)
97+
})?;
98+
get_metadata_xcoff(path, data)
99+
}
100+
Err(e) => Err(format!("failed to parse aix dylib '{}': {}", path.display(), e)),
101+
}
102+
})
82103
} else {
83104
load_metadata_with(path, |data| search_for_section(path, data, ".rustc"))
84105
}

Diff for: compiler/rustc_metadata/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ edition = "2021"
66
[dependencies]
77
# tidy-alphabetical-start
88
bitflags = "2.4.1"
9+
libc = "0.2"
910
libloading = "0.8.0"
1011
odht = { version = "0.3.1", features = ["nightly"] }
1112
rustc_abi = { path = "../rustc_abi" }

Diff for: compiler/rustc_metadata/src/creader.rs

+49-2
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
540540
Some(cnum)
541541
}
542542
Err(err) => {
543+
debug!("failed to resolve crate {} {:?}", name, dep_kind);
543544
let missing_core =
544545
self.maybe_resolve_crate(sym::core, CrateDepKind::Explicit, None).is_err();
545546
err.report(self.sess, span, missing_core);
@@ -588,6 +589,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
588589
match self.load(&mut locator)? {
589590
Some(res) => (res, None),
590591
None => {
592+
info!("falling back to loading proc_macro");
591593
dep_kind = CrateDepKind::MacrosOnly;
592594
match self.load_proc_macro(&mut locator, path_kind, host_hash)? {
593595
Some(res) => res,
@@ -599,6 +601,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
599601

600602
match result {
601603
(LoadResult::Previous(cnum), None) => {
604+
info!("library for `{}` was loaded previously", name);
602605
// When `private_dep` is none, it indicates the directly dependent crate. If it is
603606
// not specified by `--extern` on command line parameters, it may be
604607
// `private-dependency` when `register_crate` is called for the first time. Then it must be updated to
@@ -613,6 +616,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
613616
Ok(cnum)
614617
}
615618
(LoadResult::Loaded(library), host_library) => {
619+
info!("register newly loaded library for `{}`", name);
616620
self.register_crate(host_library, root, library, dep_kind, name, private_dep)
617621
}
618622
_ => panic!(),
@@ -696,7 +700,25 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
696700
stable_crate_id: StableCrateId,
697701
) -> Result<&'static [ProcMacro], CrateError> {
698702
let sym_name = self.sess.generate_proc_macro_decls_symbol(stable_crate_id);
699-
Ok(unsafe { *load_symbol_from_dylib::<*const &[ProcMacro]>(path, &sym_name)? })
703+
debug!("trying to dlsym proc_macros {} for symbol `{}`", path.display(), sym_name);
704+
705+
unsafe {
706+
let result = load_symbol_from_dylib::<*const &[ProcMacro]>(path, &sym_name);
707+
match result {
708+
Ok(result) => {
709+
debug!("loaded dlsym proc_macros {} for symbol `{}`", path.display(), sym_name);
710+
Ok(*result)
711+
}
712+
Err(err) => {
713+
debug!(
714+
"failed to dlsym proc_macros {} for symbol `{}`",
715+
path.display(),
716+
sym_name
717+
);
718+
Err(err.into())
719+
}
720+
}
721+
}
700722
}
701723

702724
fn inject_panic_runtime(&mut self, krate: &ast::Crate) {
@@ -1141,6 +1163,29 @@ fn format_dlopen_err(e: &(dyn std::error::Error + 'static)) -> String {
11411163
e.sources().map(|e| format!(": {e}")).collect()
11421164
}
11431165

1166+
fn attempt_load_dylib(path: &Path) -> Result<libloading::Library, libloading::Error> {
1167+
#[cfg(target_os = "aix")]
1168+
if let Some(ext) = path.extension()
1169+
&& ext.eq("a")
1170+
{
1171+
// On AIX, we ship all libraries as .a big_af archive
1172+
// the expected format is lib<name>.a(libname.so) for the actual
1173+
// dynamic library
1174+
let library_name = path.file_stem().expect("expect a library name");
1175+
let mut archive_member = OsString::from("a(");
1176+
archive_member.push(library_name);
1177+
archive_member.push(".so)");
1178+
let new_path = path.with_extension(archive_member);
1179+
1180+
// On AIX, we need RTLD_MEMBER to dlopen an archived shared
1181+
let flags = libc::RTLD_LAZY | libc::RTLD_LOCAL | libc::RTLD_MEMBER;
1182+
return unsafe { libloading::os::unix::Library::open(Some(&new_path), flags) }
1183+
.map(|lib| lib.into());
1184+
}
1185+
1186+
unsafe { libloading::Library::new(&path) }
1187+
}
1188+
11441189
// On Windows the compiler would sometimes intermittently fail to open the
11451190
// proc-macro DLL with `Error::LoadLibraryExW`. It is suspected that something in the
11461191
// system still holds a lock on the file, so we retry a few times before calling it
@@ -1151,7 +1196,8 @@ fn load_dylib(path: &Path, max_attempts: usize) -> Result<libloading::Library, S
11511196
let mut last_error = None;
11521197

11531198
for attempt in 0..max_attempts {
1154-
match unsafe { libloading::Library::new(&path) } {
1199+
debug!("Attempt to load proc-macro `{}`.", path.display());
1200+
match attempt_load_dylib(path) {
11551201
Ok(lib) => {
11561202
if attempt > 0 {
11571203
debug!(
@@ -1165,6 +1211,7 @@ fn load_dylib(path: &Path, max_attempts: usize) -> Result<libloading::Library, S
11651211
Err(err) => {
11661212
// Only try to recover from this specific error.
11671213
if !matches!(err, libloading::Error::LoadLibraryExW { .. }) {
1214+
debug!("Failed to load proc-macro `{}`. Not retrying", path.display());
11681215
let err = format_dlopen_err(&err);
11691216
// We include the path of the dylib in the error ourselves, so
11701217
// if it's in the error, we strip it.

Diff for: compiler/rustc_metadata/src/locator.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,10 @@ fn get_metadata_section<'p>(
847847
)));
848848
};
849849
match blob.check_compatibility(cfg_version) {
850-
Ok(()) => Ok(blob),
850+
Ok(()) => {
851+
debug!("metadata blob read okay");
852+
Ok(blob)
853+
}
851854
Err(None) => Err(MetadataError::LoadFailure(format!(
852855
"invalid metadata version found: {}",
853856
filename.display()

Diff for: src/bootstrap/src/core/build_steps/compile.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1457,7 +1457,7 @@ impl Step for CodegenBackend {
14571457
}
14581458
let mut files = files.into_iter().filter(|f| {
14591459
let filename = f.file_name().unwrap().to_str().unwrap();
1460-
is_dylib(filename) && filename.contains("rustc_codegen_")
1460+
is_dylib(f) && filename.contains("rustc_codegen_")
14611461
});
14621462
let codegen_backend = match files.next() {
14631463
Some(f) => f,
@@ -1936,7 +1936,7 @@ impl Step for Assemble {
19361936
let filename = f.file_name().into_string().unwrap();
19371937

19381938
let is_proc_macro = proc_macros.contains(&filename);
1939-
let is_dylib_or_debug = is_dylib(&filename) || is_debug_info(&filename);
1939+
let is_dylib_or_debug = is_dylib(&f.path()) || is_debug_info(&filename);
19401940

19411941
// If we link statically to stdlib, do not copy the libstd dynamic library file
19421942
// FIXME: Also do this for Windows once incremental post-optimization stage0 tests
@@ -2089,7 +2089,7 @@ pub fn run_cargo(
20892089
if filename.ends_with(".lib")
20902090
|| filename.ends_with(".a")
20912091
|| is_debug_info(&filename)
2092-
|| is_dylib(&filename)
2092+
|| is_dylib(Path::new(&*filename))
20932093
{
20942094
// Always keep native libraries, rust dylibs and debuginfo
20952095
keep = true;
@@ -2189,7 +2189,7 @@ pub fn run_cargo(
21892189
Some(triple) => triple.0.to_str().unwrap(),
21902190
None => panic!("no output generated for {prefix:?} {extension:?}"),
21912191
};
2192-
if is_dylib(path_to_add) {
2192+
if is_dylib(Path::new(path_to_add)) {
21932193
let candidate = format!("{path_to_add}.lib");
21942194
let candidate = PathBuf::from(candidate);
21952195
if candidate.exists() {

Diff for: src/bootstrap/src/core/build_steps/dist.rs

+4-7
Original file line numberDiff line numberDiff line change
@@ -436,13 +436,10 @@ impl Step for Rustc {
436436
if libdir_relative.to_str() != Some("bin") {
437437
let libdir = builder.rustc_libdir(compiler);
438438
for entry in builder.read_dir(&libdir) {
439-
let name = entry.file_name();
440-
if let Some(s) = name.to_str() {
441-
if is_dylib(s) {
442-
// Don't use custom libdir here because ^lib/ will be resolved again
443-
// with installer
444-
builder.install(&entry.path(), &image.join("lib"), 0o644);
445-
}
439+
if is_dylib(&entry.path()) {
440+
// Don't use custom libdir here because ^lib/ will be resolved again
441+
// with installer
442+
builder.install(&entry.path(), &image.join("lib"), 0o644);
446443
}
447444
}
448445
}

Diff for: src/bootstrap/src/core/build_steps/test.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -2949,8 +2949,7 @@ impl Step for RemoteCopyLibs {
29492949
// Push all our dylibs to the emulator
29502950
for f in t!(builder.sysroot_target_libdir(compiler, target).read_dir()) {
29512951
let f = t!(f);
2952-
let name = f.file_name().into_string().unwrap();
2953-
if helpers::is_dylib(&name) {
2952+
if helpers::is_dylib(&f.path()) {
29542953
command(&tool).arg("push").arg(f.path()).run(builder);
29552954
}
29562955
}

Diff for: src/bootstrap/src/utils/helpers.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::time::{Instant, SystemTime, UNIX_EPOCH};
1111
use std::{env, fs, io, str};
1212

1313
use build_helper::util::fail;
14+
use object::read::archive::ArchiveFile;
1415

1516
use crate::LldMode;
1617
use crate::core::builder::Builder;
@@ -53,8 +54,27 @@ pub fn exe(name: &str, target: TargetSelection) -> String {
5354
}
5455

5556
/// Returns `true` if the file name given looks like a dynamic library.
56-
pub fn is_dylib(name: &str) -> bool {
57-
name.ends_with(".dylib") || name.ends_with(".so") || name.ends_with(".dll")
57+
pub fn is_dylib(path: &Path) -> bool {
58+
path.extension().and_then(|ext| ext.to_str()).map_or(false, |ext| {
59+
ext == "dylib" || ext == "so" || ext == "dll" || (ext == "a" && is_aix_shared_archive(path))
60+
})
61+
}
62+
63+
fn is_aix_shared_archive(path: &Path) -> bool {
64+
// FIXME(#133268): reading the entire file as &[u8] into memory seems excessive
65+
// look into either mmap it or use the ReadCache
66+
let data = match fs::read(path) {
67+
Ok(data) => data,
68+
Err(_) => return false,
69+
};
70+
let file = match ArchiveFile::parse(&*data) {
71+
Ok(file) => file,
72+
Err(_) => return false,
73+
};
74+
75+
file.members()
76+
.filter_map(Result::ok)
77+
.any(|entry| String::from_utf8_lossy(entry.name()).contains(".so"))
5878
}
5979

6080
/// Returns `true` if the file name given looks like a debug info file

0 commit comments

Comments
 (0)