Skip to content

Commit ab3b658

Browse files
committed
Add Mach-O section parsing
Mach-O sections have a type and a number of attributes, and are grouped in segments. There is a canonical textual representation that allows specifying this with the assembly .section directive (mostly defined by LLVM's source). Cranelift supported this with `DataDescription::custom_segment_section` by letting the user implement the parsing themselves, but this complicates the API (and will require breaking changes if we want to support specifying e.g. the stub size). Instead, `DataDescription::custom_section` is now just a `String`, and `cranelift-object` implements the parsing of the section name into its components. This is the same "layering" that LLVM has chosen, and it seems to work well there. This makes it easy for `rustc_cranelift_backend` to implement Rust's `#[link_section = ...]` to support things such as the `ctor` crate.
1 parent c911bb4 commit ab3b658

3 files changed

Lines changed: 213 additions & 20 deletions

File tree

cranelift/jit/src/backend.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ impl Module for JITModule {
584584
data_decls: _,
585585
function_relocs: _,
586586
data_relocs: _,
587-
custom_segment_section: _,
587+
custom_section: _,
588588
align,
589589
used: _,
590590
} = data;

cranelift/module/src/data_context.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,11 @@ pub struct DataDescription {
6060
pub function_relocs: Vec<(CodeOffset, ir::FuncRef)>,
6161
/// Data addresses to write at specified offsets.
6262
pub data_relocs: Vec<(CodeOffset, ir::GlobalValue, Addend)>,
63-
/// Object file segment, section and Mach-O flags.
64-
pub custom_segment_section: Option<(String, String, u32)>,
63+
/// Object file section.
64+
///
65+
/// Tries to support the same format as LLVM:
66+
/// <https://llvm.org/docs/LangRef.html#sections>.
67+
pub custom_section: Option<String>,
6568
/// Alignment in bytes. `None` means that the default alignment of the
6669
/// respective module should be used.
6770
pub align: Option<u64>,
@@ -79,7 +82,7 @@ impl DataDescription {
7982
data_decls: PrimaryMap::new(),
8083
function_relocs: vec![],
8184
data_relocs: vec![],
82-
custom_segment_section: None,
85+
custom_section: None,
8386
align: None,
8487
used: false,
8588
}
@@ -92,7 +95,7 @@ impl DataDescription {
9295
self.data_decls.clear();
9396
self.function_relocs.clear();
9497
self.data_relocs.clear();
95-
self.custom_segment_section = None;
98+
self.custom_section = None;
9699
self.align = None;
97100
self.used = false;
98101
}
@@ -111,9 +114,9 @@ impl DataDescription {
111114
self.init = Init::Bytes { contents };
112115
}
113116

114-
/// Override the segment/section for data, only supported on Object backend
115-
pub fn set_segment_section(&mut self, seg: &str, sec: &str, macho_flags: u32) {
116-
self.custom_segment_section = Some((seg.to_owned(), sec.to_owned(), macho_flags))
117+
/// Override the section for data, only supported on Object backend
118+
pub fn set_custom_section(&mut self, section: &str) {
119+
self.custom_section = Some(section.to_owned());
117120
}
118121

119122
/// Set the alignment for data. The alignment must be a power of two.

cranelift/object/src/backend.rs

Lines changed: 202 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ use object::write::{
1515
Object, Relocation, SectionId, StandardSection, Symbol, SymbolId, SymbolSection,
1616
};
1717
use object::{
18-
RelocationEncoding, RelocationFlags, RelocationKind, SectionFlags, SectionKind, SymbolFlags,
19-
SymbolKind, SymbolScope, elf,
18+
BinaryFormat, RelocationEncoding, RelocationFlags, RelocationKind, SectionFlags, SectionKind,
19+
SymbolFlags, SymbolKind, SymbolScope, elf,
2020
};
2121
use std::collections::HashMap;
2222
use std::collections::hash_map::Entry;
23+
use std::fmt::Write as _;
2324
use std::mem;
2425
use target_lexicon::{PointerWidth, Triple};
2526

@@ -514,7 +515,7 @@ impl Module for ObjectModule {
514515
data_decls: _,
515516
function_relocs: _,
516517
data_relocs: _,
517-
ref custom_segment_section,
518+
ref custom_section,
518519
align,
519520
used,
520521
} = data;
@@ -529,7 +530,7 @@ impl Module for ObjectModule {
529530
.map(|record| self.process_reloc(&record))
530531
.collect::<Vec<_>>();
531532

532-
let section = if custom_segment_section.is_none() {
533+
let section = if custom_section.is_none() {
533534
let section_kind = if let Init::Zeros { .. } = *init {
534535
if decl.tls {
535536
StandardSection::UninitializedTls
@@ -560,10 +561,12 @@ impl Module for ObjectModule {
560561
"Custom section not supported for TLS"
561562
)));
562563
}
563-
let (seg, sec, macho_flags) = &custom_segment_section.as_ref().unwrap();
564+
let (segment, section, macho_flags) =
565+
parse_section(custom_section.as_ref().unwrap(), self.object.format())
566+
.map_err(ModuleError::Backend)?;
564567
let section = self.object.add_section(
565-
seg.clone().into_bytes(),
566-
sec.clone().into_bytes(),
568+
segment.to_string().into_bytes(),
569+
section.to_string().into_bytes(),
567570
if decl.writable {
568571
SectionKind::Data
569572
} else if relocs.is_empty() {
@@ -582,13 +585,11 @@ impl Module for ObjectModule {
582585
// with how we set these, to ensure we set the section
583586
// type properly).
584587
assert_eq!(*flags, 0);
585-
*flags = *macho_flags;
588+
*flags = macho_flags;
586589
}
587590
_ => {
588-
if *macho_flags != 0 {
589-
return Err(cranelift_module::ModuleError::Backend(anyhow::anyhow!(
590-
"unsupported Mach-O flags for this platform: {macho_flags:?}"
591-
)));
591+
if macho_flags != 0 {
592+
unreachable!("unsupported Mach-O flags for this platform: {macho_flags:?}");
592593
}
593594
}
594595
}
@@ -1176,3 +1177,192 @@ struct ObjectRelocRecord {
11761177
flags: RelocationFlags,
11771178
addend: Addend,
11781179
}
1180+
1181+
fn parse_section(
1182+
section: &str,
1183+
binary_format: BinaryFormat,
1184+
) -> Result<(&str, &str, u32), anyhow::Error> {
1185+
match binary_format {
1186+
// See https://github.com/llvm/llvm-project/blob/main/llvm/lib/MC/MCSectionMachO.cpp
1187+
BinaryFormat::MachO => {
1188+
let mut parts = section.split(',');
1189+
1190+
let section_err = |msg| {
1191+
Err(anyhow!(
1192+
"section `{section}` is not valid for Mach-O target: {msg}"
1193+
))
1194+
};
1195+
1196+
let segment_name = parts.next().unwrap();
1197+
if segment_name.len() > 16 {
1198+
return section_err("segment name larger than 16 bytes");
1199+
}
1200+
1201+
let Some(section_name) = parts.next() else {
1202+
return section_err("must be segment and section separated by comma");
1203+
};
1204+
if section_name.len() > 16 {
1205+
return section_err("section name larger than 16 bytes");
1206+
}
1207+
1208+
let section_type = parts.next().unwrap_or("regular");
1209+
1210+
// The custom Mach-O section flags. This is the section type
1211+
// (8 bits) packed together with the attributes (24 bits).
1212+
let mut macho_flags = if let Some((_, val)) = MACHO_SECTION_TYPES
1213+
.iter()
1214+
.find(|(name, _)| *name == section_type)
1215+
{
1216+
*val
1217+
} else {
1218+
let types = list_valid_values(MACHO_SECTION_TYPES);
1219+
return section_err(&format!(
1220+
"unsupported section type `{section_type}`, valid values are {types}"
1221+
));
1222+
};
1223+
1224+
if let Some(section_attributes) = parts.next() {
1225+
for attr in section_attributes.split('+') {
1226+
macho_flags |= if let Some((_, val)) = MACHO_SECTION_ATTRIBUTES
1227+
.iter()
1228+
.find(|(name, _)| *name == attr)
1229+
{
1230+
*val
1231+
} else {
1232+
let attributes = list_valid_values(MACHO_SECTION_ATTRIBUTES);
1233+
return section_err(&format!(
1234+
"unsupported section attribute `{attr}`, valid values are {attributes}"
1235+
));
1236+
};
1237+
}
1238+
}
1239+
1240+
if parts.next().is_some() {
1241+
return section_err("too many components");
1242+
}
1243+
1244+
Ok((segment_name, section_name, macho_flags))
1245+
}
1246+
// Otherwise, assume no segment and flags.
1247+
_ => Ok(("", section, 0)),
1248+
}
1249+
}
1250+
1251+
// We support the same custom section type / attrs naming as LLVM:
1252+
// <https://github.com/llvm/llvm-project/blob/llvmorg-22.1.3/llvm/lib/MC/MCSectionMachO.cpp#L23-L91>
1253+
// <https://github.com/llvm/llvm-project/blob/llvmorg-22.1.3/llvm/include/llvm/BinaryFormat/MachO.h#L120-L223>
1254+
//
1255+
// See also the Mac OS X Assembler Reference:
1256+
// <https://leopard-adc.pepas.com/documentation/DeveloperTools/Reference/Assembler/040-Assembler_Directives/asm_directives.html#//apple_ref/doc/uid/TP30000823-TPXREF102>
1257+
#[rustfmt::skip]
1258+
const MACHO_SECTION_TYPES: &[(&str, u32)] = {
1259+
use object::macho::*;
1260+
&[
1261+
("regular", S_REGULAR),
1262+
("zerofill", S_ZEROFILL),
1263+
("cstring_literals", S_CSTRING_LITERALS),
1264+
("4byte_literals", S_4BYTE_LITERALS),
1265+
("8byte_literals", S_8BYTE_LITERALS),
1266+
("literal_pointers", S_LITERAL_POINTERS),
1267+
("non_lazy_symbol_pointers", S_NON_LAZY_SYMBOL_POINTERS),
1268+
("lazy_symbol_pointers", S_LAZY_SYMBOL_POINTERS),
1269+
// ("symbol_stubs", S_SYMBOL_STUBS) (requires extra param stub size)
1270+
("mod_init_funcs", S_MOD_INIT_FUNC_POINTERS),
1271+
("mod_term_funcs", S_MOD_TERM_FUNC_POINTERS),
1272+
("coalesced", S_COALESCED),
1273+
// S_GB_ZEROFILL (not supported by LLVM)
1274+
("interposing", S_INTERPOSING),
1275+
("16byte_literals", S_16BYTE_LITERALS),
1276+
// S_DTRACE_DOF (not supported by LLVM)
1277+
// S_LAZY_DYLIB_SYMBOL_POINTERS (not supported by LLVM)
1278+
("thread_local_regular", S_THREAD_LOCAL_REGULAR),
1279+
("thread_local_zerofill", S_THREAD_LOCAL_ZEROFILL),
1280+
("thread_local_variables", S_THREAD_LOCAL_VARIABLES),
1281+
("thread_local_variable_pointers", S_THREAD_LOCAL_VARIABLE_POINTERS),
1282+
("thread_local_init_function_pointers", S_THREAD_LOCAL_INIT_FUNCTION_POINTERS),
1283+
// S_INIT_FUNC_OFFSETS (not supported by LLVM)
1284+
]
1285+
};
1286+
1287+
const MACHO_SECTION_ATTRIBUTES: &[(&str, u32)] = {
1288+
use object::macho::*;
1289+
&[
1290+
("pure_instructions", S_ATTR_PURE_INSTRUCTIONS),
1291+
("no_toc", S_ATTR_NO_TOC),
1292+
("strip_static_syms", S_ATTR_STRIP_STATIC_SYMS),
1293+
("no_dead_strip", S_ATTR_NO_DEAD_STRIP),
1294+
("live_support", S_ATTR_LIVE_SUPPORT),
1295+
("self_modifying_code", S_ATTR_SELF_MODIFYING_CODE),
1296+
("debug", S_ATTR_DEBUG),
1297+
// System settable attributes are not supported by LLVM:
1298+
// S_ATTR_SOME_INSTRUCTIONS
1299+
// S_ATTR_EXT_RELOC
1300+
// S_ATTR_LOC_RELOC
1301+
]
1302+
};
1303+
1304+
fn list_valid_values(items: &[(&str, u32)]) -> String {
1305+
let mut items = items.iter().peekable();
1306+
let mut result = String::new();
1307+
if let Some((item, _)) = items.next() {
1308+
write!(&mut result, "`{item}`").unwrap();
1309+
}
1310+
while let Some((item, _)) = items.next() {
1311+
if items.peek().is_none() {
1312+
write!(&mut result, " and `{item}`").unwrap();
1313+
} else {
1314+
write!(&mut result, ", `{item}`").unwrap();
1315+
}
1316+
}
1317+
result
1318+
}
1319+
1320+
#[cfg(test)]
1321+
mod tests {
1322+
use super::*;
1323+
1324+
#[test]
1325+
fn section() {
1326+
assert_eq!(
1327+
parse_section("__DATA,__mod_init_func,mod_init_funcs", BinaryFormat::MachO).unwrap(),
1328+
("__DATA", "__mod_init_func", 0x9),
1329+
);
1330+
assert_eq!(
1331+
parse_section(
1332+
"__OBJC,__module_info,regular,no_dead_strip",
1333+
BinaryFormat::MachO,
1334+
)
1335+
.unwrap(),
1336+
("__OBJC", "__module_info", 0x1000_0000),
1337+
);
1338+
1339+
assert_eq!(
1340+
parse_section("__TEXT,__text", BinaryFormat::MachO).unwrap(),
1341+
("__TEXT", "__text", 0),
1342+
);
1343+
assert_eq!(
1344+
parse_section("__TEXT,__text,regular", BinaryFormat::MachO).unwrap(),
1345+
("__TEXT", "__text", 0),
1346+
);
1347+
assert_eq!(
1348+
parse_section(
1349+
"foo,bar,literal_pointers,no_toc+no_dead_strip",
1350+
BinaryFormat::MachO
1351+
)
1352+
.unwrap(),
1353+
("foo", "bar", 0x5000_0005),
1354+
);
1355+
1356+
assert!(parse_section("foo", BinaryFormat::MachO).is_err());
1357+
assert!(parse_section("12345678901234567,bar", BinaryFormat::MachO).is_err());
1358+
assert!(parse_section("foo,12345678901234567", BinaryFormat::MachO).is_err());
1359+
assert!(parse_section("foo,bar,unknown", BinaryFormat::MachO).is_err());
1360+
assert!(parse_section("foo,bar,regular,unknown", BinaryFormat::MachO).is_err());
1361+
assert!(
1362+
parse_section("foo,bar,regular,no_dead_strip+unknown", BinaryFormat::MachO).is_err()
1363+
);
1364+
assert!(
1365+
parse_section("foo,bar,regular,no_dead_strip,unknown", BinaryFormat::MachO).is_err()
1366+
);
1367+
}
1368+
}

0 commit comments

Comments
 (0)