@@ -15,11 +15,12 @@ use object::write::{
1515 Object , Relocation , SectionId , StandardSection , Symbol , SymbolId , SymbolSection ,
1616} ;
1717use 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} ;
2121use std:: collections:: HashMap ;
2222use std:: collections:: hash_map:: Entry ;
23+ use std:: fmt:: Write as _;
2324use std:: mem;
2425use 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