Skip to content

Commit eeb83f9

Browse files
committed
aix: fix archive format
1 parent 86d69c7 commit eeb83f9

File tree

8 files changed

+121
-20
lines changed

8 files changed

+121
-20
lines changed

compiler/rustc_codegen_ssa/src/back/link.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -761,14 +761,20 @@ fn link_natively(
761761
info!("preparing {:?} to {:?}", crate_type, out_filename);
762762
let (linker_path, flavor) = linker_and_flavor(sess);
763763
let self_contained_components = self_contained_components(sess, crate_type);
764+
765+
let should_archive = crate_type != CrateType::Executable && sess.target.is_like_aix;
766+
let archive_member =
767+
should_archive.then(|| tmpdir.join(out_filename.file_name().unwrap()).with_extension("so"));
768+
let temp_filename = archive_member.as_deref().unwrap_or(out_filename);
769+
764770
let mut cmd = linker_with_args(
765771
&linker_path,
766772
flavor,
767773
sess,
768774
archive_builder_builder,
769775
crate_type,
770776
tmpdir,
771-
out_filename,
777+
temp_filename,
772778
codegen_results,
773779
self_contained_components,
774780
)?;
@@ -1136,6 +1142,12 @@ fn link_natively(
11361142
}
11371143
}
11381144

1145+
if should_archive {
1146+
let mut ab = archive_builder_builder.new_archive_builder(sess);
1147+
ab.add_file(temp_filename);
1148+
ab.build(out_filename);
1149+
}
1150+
11391151
Ok(())
11401152
}
11411153

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_session::Session;
2122
use rustc_span::sym;
2223
use rustc_target::abi::Endian;
2324
use rustc_target::spec::{RelocModel, Target, ef_avr_arch};
25+
use tracing::debug;
2426

2527
/// The default metadata loader. This is used by cg_llvm and cg_clif.
2628
///
@@ -51,6 +53,7 @@ fn load_metadata_with(
5153

5254
impl MetadataLoader for DefaultMetadataLoader {
5355
fn get_rlib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
56+
debug!("getting rlib metadata for {}", path.display());
5457
load_metadata_with(path, |data| {
5558
let archive = object::read::archive::ArchiveFile::parse(&*data)
5659
.map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
@@ -75,8 +78,26 @@ impl MetadataLoader for DefaultMetadataLoader {
7578
}
7679

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

compiler/rustc_metadata/src/creader.rs

+51-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Validates all used crates and extern libraries and loads their metadata
22
33
use std::error::Error;
4+
use std::ffi::OsString;
45
use std::ops::Fn;
56
use std::path::Path;
67
use std::str::FromStr;
@@ -539,6 +540,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
539540
Some(cnum)
540541
}
541542
Err(err) => {
543+
debug!("failed to resolve crate {} {:?}", name, dep_kind);
542544
let missing_core =
543545
self.maybe_resolve_crate(sym::core, CrateDepKind::Explicit, None).is_err();
544546
err.report(self.sess, span, missing_core);
@@ -587,6 +589,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
587589
match self.load(&mut locator)? {
588590
Some(res) => (res, None),
589591
None => {
592+
info!("falling back to loading proc_macro");
590593
dep_kind = CrateDepKind::MacrosOnly;
591594
match self.load_proc_macro(&mut locator, path_kind, host_hash)? {
592595
Some(res) => res,
@@ -598,6 +601,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
598601

599602
match result {
600603
(LoadResult::Previous(cnum), None) => {
604+
info!("library for `{}` was loaded previously", name);
601605
// When `private_dep` is none, it indicates the directly dependent crate. If it is
602606
// not specified by `--extern` on command line parameters, it may be
603607
// `private-dependency` when `register_crate` is called for the first time. Then it must be updated to
@@ -612,6 +616,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
612616
Ok(cnum)
613617
}
614618
(LoadResult::Loaded(library), host_library) => {
619+
info!("register newly loaded library for `{}`", name);
615620
self.register_crate(host_library, root, library, dep_kind, name, private_dep)
616621
}
617622
_ => panic!(),
@@ -695,7 +700,25 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
695700
stable_crate_id: StableCrateId,
696701
) -> Result<&'static [ProcMacro], CrateError> {
697702
let sym_name = self.sess.generate_proc_macro_decls_symbol(stable_crate_id);
698-
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+
}
699722
}
700723

701724
fn inject_panic_runtime(&mut self, krate: &ast::Crate) {
@@ -1142,6 +1165,30 @@ fn format_dlopen_err(e: &(dyn std::error::Error + 'static)) -> String {
11421165
e.sources().map(|e| format!(": {e}")).collect()
11431166
}
11441167

1168+
fn attempt_load_dylib(path: &Path) -> Result<libloading::Library, libloading::Error> {
1169+
if let Some(ext) = path.extension()
1170+
&& cfg!(target_os = "aix")
1171+
&& ext.eq("a")
1172+
{
1173+
// On AIX, we ship all libraries as .a big_af archive
1174+
// the expected format is lib<name>.a(libname.so) for the actual
1175+
// dynamic library
1176+
let library_name = path.file_stem().expect("expect a library name");
1177+
let mut archive_member = OsString::from("a(");
1178+
archive_member.push(library_name);
1179+
archive_member.push(".so)");
1180+
let new_path = path.with_extension(archive_member);
1181+
1182+
// On AIX, we need RTLD_MEMBER to dlopen an archived shared
1183+
// flags = RTLD_LAZY | RTLD_LOCAL | RTLD_MEMBER
1184+
// these are not yet in libc https://github.com/rust-lang/libc/pull/4044
1185+
let flags = 0x4 | 0x80000 | 0x40000;
1186+
unsafe { libloading::os::unix::Library::open(Some(&new_path), flags) }.map(|lib| lib.into())
1187+
} else {
1188+
unsafe { libloading::Library::new(&path) }
1189+
}
1190+
}
1191+
11451192
// On Windows the compiler would sometimes intermittently fail to open the
11461193
// proc-macro DLL with `Error::LoadLibraryExW`. It is suspected that something in the
11471194
// system still holds a lock on the file, so we retry a few times before calling it
@@ -1152,7 +1199,8 @@ fn load_dylib(path: &Path, max_attempts: usize) -> Result<libloading::Library, S
11521199
let mut last_error = None;
11531200

11541201
for attempt in 0..max_attempts {
1155-
match unsafe { libloading::Library::new(&path) } {
1202+
debug!("Attempt to load proc-macro `{}`.", path.display());
1203+
match attempt_load_dylib(path) {
11561204
Ok(lib) => {
11571205
if attempt > 0 {
11581206
debug!(
@@ -1166,6 +1214,7 @@ fn load_dylib(path: &Path, max_attempts: usize) -> Result<libloading::Library, S
11661214
Err(err) => {
11671215
// Only try to recover from this specific error.
11681216
if !matches!(err, libloading::Error::LoadLibraryExW { .. }) {
1217+
debug!("Failed to load proc-macro `{}`. Not retrying", path.display());
11691218
let err = format_dlopen_err(&err);
11701219
// We include the path of the dylib in the error ourselves, so
11711220
// if it's in the error, we strip it.

compiler/rustc_metadata/src/locator.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -864,7 +864,10 @@ fn get_metadata_section<'p>(
864864
)));
865865
};
866866
match blob.check_compatibility(cfg_version) {
867-
Ok(()) => Ok(blob),
867+
Ok(()) => {
868+
debug!("metadata blob read okay");
869+
Ok(blob)
870+
},
868871
Err(None) => Err(MetadataError::LoadFailure(format!(
869872
"invalid metadata version found: {}",
870873
filename.display()

src/bootstrap/src/core/build_steps/compile.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1458,7 +1458,7 @@ impl Step for CodegenBackend {
14581458
}
14591459
let mut files = files.into_iter().filter(|f| {
14601460
let filename = f.file_name().unwrap().to_str().unwrap();
1461-
is_dylib(filename) && filename.contains("rustc_codegen_")
1461+
is_dylib(f) && filename.contains("rustc_codegen_")
14621462
});
14631463
let codegen_backend = match files.next() {
14641464
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
@@ -2077,7 +2077,7 @@ pub fn run_cargo(
20772077
if filename.ends_with(".lib")
20782078
|| filename.ends_with(".a")
20792079
|| is_debug_info(&filename)
2080-
|| is_dylib(&filename)
2080+
|| is_dylib(Path::new(&*filename))
20812081
{
20822082
// Always keep native libraries, rust dylibs and debuginfo
20832083
keep = true;
@@ -2177,7 +2177,7 @@ pub fn run_cargo(
21772177
Some(triple) => triple.0.to_str().unwrap(),
21782178
None => panic!("no output generated for {prefix:?} {extension:?}"),
21792179
};
2180-
if is_dylib(path_to_add) {
2180+
if is_dylib(Path::new(path_to_add)) {
21812181
let candidate = format!("{path_to_add}.lib");
21822182
let candidate = PathBuf::from(candidate);
21832183
if candidate.exists() {

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
}

src/bootstrap/src/core/build_steps/test.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -2937,8 +2937,7 @@ impl Step for RemoteCopyLibs {
29372937
// Push all our dylibs to the emulator
29382938
for f in t!(builder.sysroot_libdir(compiler, target).read_dir()) {
29392939
let f = t!(f);
2940-
let name = f.file_name().into_string().unwrap();
2941-
if helpers::is_dylib(&name) {
2940+
if helpers::is_dylib(&f.path()) {
29422941
command(&tool).arg("push").arg(f.path()).run(builder);
29432942
}
29442943
}

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+
// todo: reading the entire file as &[u8] into memory seems excessive
65+
// look into either mmap it or use the &CacheReader
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)