Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support the rustc metadata for AIX #107583

Merged
merged 2 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ libc = "0.2.50"
[dependencies.object]
version = "0.31.1"
default-features = false
features = ["read_core", "elf", "macho", "pe", "unaligned", "archive", "write"]
features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write"]

[target.'cfg(windows)'.dependencies.windows]
version = "0.48.0"
Expand Down
151 changes: 140 additions & 11 deletions compiler/rustc_codegen_ssa/src/back/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use std::path::Path;

use object::write::{self, StandardSegment, Symbol, SymbolSection};
use object::{
elf, pe, Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection,
SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
elf, pe, xcoff, Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection,
ObjectSymbol, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
};

use snap::write::FrameEncoder;
Expand Down Expand Up @@ -35,6 +35,8 @@ use rustc_target::spec::{RelocModel, Target};
#[derive(Debug)]
pub struct DefaultMetadataLoader;

static AIX_METADATA_SYMBOL_NAME: &'static str = "__aix_rust_metadata";

fn load_metadata_with(
path: &Path,
f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>,
Expand All @@ -48,7 +50,7 @@ fn load_metadata_with(
}

impl MetadataLoader for DefaultMetadataLoader {
fn get_rlib_metadata(&self, _target: &Target, path: &Path) -> Result<OwnedSlice, String> {
fn get_rlib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
load_metadata_with(path, |data| {
let archive = object::read::archive::ArchiveFile::parse(&*data)
.map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
Expand All @@ -60,16 +62,24 @@ impl MetadataLoader for DefaultMetadataLoader {
let data = entry
.data(data)
.map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
return search_for_section(path, data, ".rmeta");
if target.is_like_aix {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though xcoff is bound to AIX, I think checking the format is more precise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have format info in this function. While according to your comments, I changed such checks in fn create_wrapper_file() and fn create_compressed_metadata_file(). Thanks.

return get_metadata_xcoff(path, data);
} else {
return search_for_section(path, data, ".rmeta");
}
}
}

Err(format!("metadata not found in rlib '{}'", path.display()))
})
}

fn get_dylib_metadata(&self, _target: &Target, path: &Path) -> Result<OwnedSlice, String> {
load_metadata_with(path, |data| search_for_section(path, data, ".rustc"))
fn get_dylib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
if target.is_like_aix {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dito

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

load_metadata_with(path, |data| get_metadata_xcoff(path, data))
} else {
load_metadata_with(path, |data| search_for_section(path, data, ".rustc"))
}
}
}

Expand Down Expand Up @@ -141,6 +151,33 @@ fn add_gnu_property_note(
file.append_section_data(section, &data, 8);
}

pub(super) fn get_metadata_xcoff<'a>(path: &Path, data: &'a [u8]) -> Result<&'a [u8], String> {
let Ok(file) = object::File::parse(data) else {
return Ok(data);
};
let info_data = search_for_section(path, data, ".info")?;
if let Some(metadata_symbol) =
file.symbols().find(|sym| sym.name() == Ok(AIX_METADATA_SYMBOL_NAME))
{
let offset = metadata_symbol.address() as usize;
if offset < 4 {
return Err(format!("Invalid metadata symbol offset: {}", offset));
}
// The offset specifies the location of rustc metadata in the comment section.
// The metadata is preceded by a 4-byte length field.
let len = u32::from_be_bytes(info_data[(offset - 4)..offset].try_into().unwrap()) as usize;
if offset + len > (info_data.len() as usize) {
return Err(format!(
"Metadata at offset {} with size {} is beyond .info section",
offset, len
));
}
return Ok(&info_data[offset..(offset + len)]);
} else {
return Err(format!("Unable to find symbol {}", AIX_METADATA_SYMBOL_NAME));
};
}

pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
let endianness = match sess.target.options.endian {
Endian::Little => Endianness::Little,
Expand Down Expand Up @@ -183,6 +220,8 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
BinaryFormat::MachO
} else if sess.target.is_like_windows {
BinaryFormat::Coff
} else if sess.target.is_like_aix {
BinaryFormat::Xcoff
} else {
BinaryFormat::Elf
};
Expand Down Expand Up @@ -319,11 +358,15 @@ pub fn create_wrapper_file(
// to add a case above.
return (data.to_vec(), MetadataPosition::Last);
};
let section = file.add_section(
file.segment_name(StandardSegment::Debug).to_vec(),
section_name,
SectionKind::Debug,
);
let section = if file.format() == BinaryFormat::Xcoff {
file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug)
} else {
file.add_section(
file.segment_name(StandardSegment::Debug).to_vec(),
section_name,
SectionKind::Debug,
)
};
match file.format() {
BinaryFormat::Coff => {
file.section_mut(section).flags =
Expand All @@ -333,6 +376,31 @@ pub fn create_wrapper_file(
file.section_mut(section).flags =
SectionFlags::Elf { sh_flags: elf::SHF_EXCLUDE as u64 };
}
BinaryFormat::Xcoff => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it missing a C_INFO symbol to reference the data in .info section?
As @bjorn3 said, we don't need to preserve the metadata here, so it doesn't need a data symbol and a csect here compared to create_compressed_metadata_file_for_xcoff.

// AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss.
file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
file.section_mut(section).flags =
SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };

let len = data.len() as u32;
let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
// Add a symbol referring to the data in .info section.
file.add_symbol(Symbol {
name: AIX_METADATA_SYMBOL_NAME.into(),
value: offset + 4,
size: 0,
kind: SymbolKind::Unknown,
scope: SymbolScope::Compilation,
weak: false,
section: SymbolSection::Section(section),
flags: SymbolFlags::Xcoff {
n_sclass: xcoff::C_INFO,
x_smtyp: xcoff::C_HIDEXT,
x_smclas: xcoff::C_HIDEXT,
containing_csect: None,
},
});
}
_ => {}
};
file.append_section_data(section, data, 1);
Expand Down Expand Up @@ -369,6 +437,9 @@ pub fn create_compressed_metadata_file(
let Some(mut file) = create_object_file(sess) else {
return compressed.to_vec();
};
if file.format() == BinaryFormat::Xcoff {
return create_compressed_metadata_file_for_xcoff(file, &compressed, symbol_name);
}
let section = file.add_section(
file.segment_name(StandardSegment::Data).to_vec(),
b".rustc".to_vec(),
Expand Down Expand Up @@ -398,3 +469,61 @@ pub fn create_compressed_metadata_file(

file.write().unwrap()
}

/// * Xcoff - On AIX, custom sections are merged into predefined sections,
/// so custom .rustc section is not preserved during linking.
/// For this reason, we store metadata in predefined .info section, and
/// define a symbol to reference the metadata. To preserve metadata during
/// linking on AIX, we have to
/// 1. Create an empty .text section, a empty .data section.
/// 2. Define an empty symbol named `symbol_name` inside .data section.
/// 3. Define an symbol named `AIX_METADATA_SYMBOL_NAME` referencing
/// data inside .info section.
/// From XCOFF's view, (2) creates a csect entry in the symbol table, the
/// symbol created by (3) is a info symbol for the preceding csect. Thus
/// two symbols are preserved during linking and we can use the second symbol
/// to reference the metadata.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two kinds of object files created. One (create_wrapper_file) is for use in rlibs and we do not want to preserve metadata in there. It is only wrapped in an rlib to prevent linkers from complaining about files that are not valid object files in archives. The other kind (create_compressed_metadata_file) contains compressed metadata and is used for rust dylibs. This one should be preserved.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AIX linker will abort if it finds lib.rmeta is a valid XCOFF file but only has .info section (no .bss, .data or .text). Fortunately, we can just add a stub .text section to make linker happy:

file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);

pub fn create_compressed_metadata_file_for_xcoff(
mut file: write::Object<'_>,
data: &[u8],
symbol_name: &str,
) -> Vec<u8> {
assert!(file.format() == BinaryFormat::Xcoff);
// AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss.
file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
bjorn3 marked this conversation as resolved.
Show resolved Hide resolved
let data_section = file.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data);
let section = file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug);
file.add_file_symbol("lib.rmeta".into());
file.section_mut(section).flags = SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };
// Add a global symbol to data_section.
file.add_symbol(Symbol {
name: symbol_name.as_bytes().into(),
value: 0,
size: 0,
kind: SymbolKind::Data,
scope: SymbolScope::Dynamic,
weak: true,
section: SymbolSection::Section(data_section),
flags: SymbolFlags::None,
});
let len = data.len() as u32;
let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
// Add a symbol referring to the rustc metadata.
file.add_symbol(Symbol {
name: AIX_METADATA_SYMBOL_NAME.into(),
value: offset + 4, // The metadata is preceded by a 4-byte length field.
size: 0,
kind: SymbolKind::Unknown,
scope: SymbolScope::Dynamic,
weak: false,
section: SymbolSection::Section(section),
flags: SymbolFlags::Xcoff {
n_sclass: xcoff::C_INFO,
x_smtyp: xcoff::C_HIDEXT,
x_smclas: xcoff::C_HIDEXT,
containing_csect: None,
},
});
file.append_section_data(section, data, 1);
file.write().unwrap()
}