@@ -6,8 +6,8 @@ use std::path::Path;
6
6
7
7
use object:: write:: { self , StandardSegment , Symbol , SymbolSection } ;
8
8
use object:: {
9
- elf, pe, Architecture , BinaryFormat , Endianness , FileFlags , Object , ObjectSection ,
10
- SectionFlags , SectionKind , SymbolFlags , SymbolKind , SymbolScope ,
9
+ elf, pe, xcoff , Architecture , BinaryFormat , Endianness , FileFlags , Object , ObjectSection ,
10
+ ObjectSymbol , SectionFlags , SectionKind , SymbolFlags , SymbolKind , SymbolScope ,
11
11
} ;
12
12
13
13
use snap:: write:: FrameEncoder ;
@@ -35,6 +35,8 @@ use rustc_target::spec::{RelocModel, Target};
35
35
#[ derive( Debug ) ]
36
36
pub struct DefaultMetadataLoader ;
37
37
38
+ static AIX_METADATA_SYMBOL_NAME : & ' static str = "__aix_rust_metadata" ;
39
+
38
40
fn load_metadata_with (
39
41
path : & Path ,
40
42
f : impl for < ' a > FnOnce ( & ' a [ u8 ] ) -> Result < & ' a [ u8 ] , String > ,
@@ -48,7 +50,7 @@ fn load_metadata_with(
48
50
}
49
51
50
52
impl MetadataLoader for DefaultMetadataLoader {
51
- fn get_rlib_metadata ( & self , _target : & Target , path : & Path ) -> Result < OwnedSlice , String > {
53
+ fn get_rlib_metadata ( & self , target : & Target , path : & Path ) -> Result < OwnedSlice , String > {
52
54
load_metadata_with ( path, |data| {
53
55
let archive = object:: read:: archive:: ArchiveFile :: parse ( & * data)
54
56
. map_err ( |e| format ! ( "failed to parse rlib '{}': {}" , path. display( ) , e) ) ?;
@@ -60,16 +62,24 @@ impl MetadataLoader for DefaultMetadataLoader {
60
62
let data = entry
61
63
. data ( data)
62
64
. map_err ( |e| format ! ( "failed to parse rlib '{}': {}" , path. display( ) , e) ) ?;
63
- return search_for_section ( path, data, ".rmeta" ) ;
65
+ if target. is_like_aix {
66
+ return get_metadata_xcoff ( path, data) ;
67
+ } else {
68
+ return search_for_section ( path, data, ".rmeta" ) ;
69
+ }
64
70
}
65
71
}
66
72
67
73
Err ( format ! ( "metadata not found in rlib '{}'" , path. display( ) ) )
68
74
} )
69
75
}
70
76
71
- fn get_dylib_metadata ( & self , _target : & Target , path : & Path ) -> Result < OwnedSlice , String > {
72
- load_metadata_with ( path, |data| search_for_section ( path, data, ".rustc" ) )
77
+ fn get_dylib_metadata ( & self , target : & Target , path : & Path ) -> Result < OwnedSlice , String > {
78
+ if target. is_like_aix {
79
+ load_metadata_with ( path, |data| get_metadata_xcoff ( path, data) )
80
+ } else {
81
+ load_metadata_with ( path, |data| search_for_section ( path, data, ".rustc" ) )
82
+ }
73
83
}
74
84
}
75
85
@@ -141,6 +151,33 @@ fn add_gnu_property_note(
141
151
file. append_section_data ( section, & data, 8 ) ;
142
152
}
143
153
154
+ pub ( super ) fn get_metadata_xcoff < ' a > ( path : & Path , data : & ' a [ u8 ] ) -> Result < & ' a [ u8 ] , String > {
155
+ let Ok ( file) = object:: File :: parse ( data) else {
156
+ return Ok ( data) ;
157
+ } ;
158
+ let info_data = search_for_section ( path, data, ".info" ) ?;
159
+ if let Some ( metadata_symbol) =
160
+ file. symbols ( ) . find ( |sym| sym. name ( ) == Ok ( AIX_METADATA_SYMBOL_NAME ) )
161
+ {
162
+ let offset = metadata_symbol. address ( ) as usize ;
163
+ if offset < 4 {
164
+ return Err ( format ! ( "Invalid metadata symbol offset: {}" , offset) ) ;
165
+ }
166
+ // The offset specifies the location of rustc metadata in the comment section.
167
+ // The metadata is preceded by a 4-byte length field.
168
+ let len = u32:: from_be_bytes ( info_data[ ( offset - 4 ) ..offset] . try_into ( ) . unwrap ( ) ) as usize ;
169
+ if offset + len > ( info_data. len ( ) as usize ) {
170
+ return Err ( format ! (
171
+ "Metadata at offset {} with size {} is beyond .info section" ,
172
+ offset, len
173
+ ) ) ;
174
+ }
175
+ return Ok ( & info_data[ offset..( offset + len) ] ) ;
176
+ } else {
177
+ return Err ( format ! ( "Unable to find symbol {}" , AIX_METADATA_SYMBOL_NAME ) ) ;
178
+ } ;
179
+ }
180
+
144
181
pub ( crate ) fn create_object_file ( sess : & Session ) -> Option < write:: Object < ' static > > {
145
182
let endianness = match sess. target . options . endian {
146
183
Endian :: Little => Endianness :: Little ,
@@ -183,6 +220,8 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
183
220
BinaryFormat :: MachO
184
221
} else if sess. target . is_like_windows {
185
222
BinaryFormat :: Coff
223
+ } else if sess. target . is_like_aix {
224
+ BinaryFormat :: Xcoff
186
225
} else {
187
226
BinaryFormat :: Elf
188
227
} ;
@@ -351,11 +390,15 @@ pub fn create_wrapper_file(
351
390
// to add a case above.
352
391
return ( data. to_vec ( ) , MetadataPosition :: Last ) ;
353
392
} ;
354
- let section = file. add_section (
355
- file. segment_name ( StandardSegment :: Debug ) . to_vec ( ) ,
356
- section_name,
357
- SectionKind :: Debug ,
358
- ) ;
393
+ let section = if file. format ( ) == BinaryFormat :: Xcoff {
394
+ file. add_section ( Vec :: new ( ) , b".info" . to_vec ( ) , SectionKind :: Debug )
395
+ } else {
396
+ file. add_section (
397
+ file. segment_name ( StandardSegment :: Debug ) . to_vec ( ) ,
398
+ section_name,
399
+ SectionKind :: Debug ,
400
+ )
401
+ } ;
359
402
match file. format ( ) {
360
403
BinaryFormat :: Coff => {
361
404
file. section_mut ( section) . flags =
@@ -365,6 +408,31 @@ pub fn create_wrapper_file(
365
408
file. section_mut ( section) . flags =
366
409
SectionFlags :: Elf { sh_flags : elf:: SHF_EXCLUDE as u64 } ;
367
410
}
411
+ BinaryFormat :: Xcoff => {
412
+ // AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss.
413
+ file. add_section ( Vec :: new ( ) , b".text" . to_vec ( ) , SectionKind :: Text ) ;
414
+ file. section_mut ( section) . flags =
415
+ SectionFlags :: Xcoff { s_flags : xcoff:: STYP_INFO as u32 } ;
416
+
417
+ let len = data. len ( ) as u32 ;
418
+ let offset = file. append_section_data ( section, & len. to_be_bytes ( ) , 1 ) ;
419
+ // Add a symbol referring to the data in .info section.
420
+ file. add_symbol ( Symbol {
421
+ name : AIX_METADATA_SYMBOL_NAME . into ( ) ,
422
+ value : offset + 4 ,
423
+ size : 0 ,
424
+ kind : SymbolKind :: Unknown ,
425
+ scope : SymbolScope :: Compilation ,
426
+ weak : false ,
427
+ section : SymbolSection :: Section ( section) ,
428
+ flags : SymbolFlags :: Xcoff {
429
+ n_sclass : xcoff:: C_INFO ,
430
+ x_smtyp : xcoff:: C_HIDEXT ,
431
+ x_smclas : xcoff:: C_HIDEXT ,
432
+ containing_csect : None ,
433
+ } ,
434
+ } ) ;
435
+ }
368
436
_ => { }
369
437
} ;
370
438
file. append_section_data ( section, data, 1 ) ;
@@ -401,6 +469,9 @@ pub fn create_compressed_metadata_file(
401
469
let Some ( mut file) = create_object_file ( sess) else {
402
470
return compressed. to_vec ( ) ;
403
471
} ;
472
+ if file. format ( ) == BinaryFormat :: Xcoff {
473
+ return create_compressed_metadata_file_for_xcoff ( file, & compressed, symbol_name) ;
474
+ }
404
475
let section = file. add_section (
405
476
file. segment_name ( StandardSegment :: Data ) . to_vec ( ) ,
406
477
b".rustc" . to_vec ( ) ,
@@ -430,3 +501,61 @@ pub fn create_compressed_metadata_file(
430
501
431
502
file. write ( ) . unwrap ( )
432
503
}
504
+
505
+ /// * Xcoff - On AIX, custom sections are merged into predefined sections,
506
+ /// so custom .rustc section is not preserved during linking.
507
+ /// For this reason, we store metadata in predefined .info section, and
508
+ /// define a symbol to reference the metadata. To preserve metadata during
509
+ /// linking on AIX, we have to
510
+ /// 1. Create an empty .text section, a empty .data section.
511
+ /// 2. Define an empty symbol named `symbol_name` inside .data section.
512
+ /// 3. Define an symbol named `AIX_METADATA_SYMBOL_NAME` referencing
513
+ /// data inside .info section.
514
+ /// From XCOFF's view, (2) creates a csect entry in the symbol table, the
515
+ /// symbol created by (3) is a info symbol for the preceding csect. Thus
516
+ /// two symbols are preserved during linking and we can use the second symbol
517
+ /// to reference the metadata.
518
+ pub fn create_compressed_metadata_file_for_xcoff (
519
+ mut file : write:: Object < ' _ > ,
520
+ data : & [ u8 ] ,
521
+ symbol_name : & str ,
522
+ ) -> Vec < u8 > {
523
+ assert ! ( file. format( ) == BinaryFormat :: Xcoff ) ;
524
+ // AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss.
525
+ file. add_section ( Vec :: new ( ) , b".text" . to_vec ( ) , SectionKind :: Text ) ;
526
+ let data_section = file. add_section ( Vec :: new ( ) , b".data" . to_vec ( ) , SectionKind :: Data ) ;
527
+ let section = file. add_section ( Vec :: new ( ) , b".info" . to_vec ( ) , SectionKind :: Debug ) ;
528
+ file. add_file_symbol ( "lib.rmeta" . into ( ) ) ;
529
+ file. section_mut ( section) . flags = SectionFlags :: Xcoff { s_flags : xcoff:: STYP_INFO as u32 } ;
530
+ // Add a global symbol to data_section.
531
+ file. add_symbol ( Symbol {
532
+ name : symbol_name. as_bytes ( ) . into ( ) ,
533
+ value : 0 ,
534
+ size : 0 ,
535
+ kind : SymbolKind :: Data ,
536
+ scope : SymbolScope :: Dynamic ,
537
+ weak : true ,
538
+ section : SymbolSection :: Section ( data_section) ,
539
+ flags : SymbolFlags :: None ,
540
+ } ) ;
541
+ let len = data. len ( ) as u32 ;
542
+ let offset = file. append_section_data ( section, & len. to_be_bytes ( ) , 1 ) ;
543
+ // Add a symbol referring to the rustc metadata.
544
+ file. add_symbol ( Symbol {
545
+ name : AIX_METADATA_SYMBOL_NAME . into ( ) ,
546
+ value : offset + 4 , // The metadata is preceded by a 4-byte length field.
547
+ size : 0 ,
548
+ kind : SymbolKind :: Unknown ,
549
+ scope : SymbolScope :: Dynamic ,
550
+ weak : false ,
551
+ section : SymbolSection :: Section ( section) ,
552
+ flags : SymbolFlags :: Xcoff {
553
+ n_sclass : xcoff:: C_INFO ,
554
+ x_smtyp : xcoff:: C_HIDEXT ,
555
+ x_smclas : xcoff:: C_HIDEXT ,
556
+ containing_csect : None ,
557
+ } ,
558
+ } ) ;
559
+ file. append_section_data ( section, data, 1 ) ;
560
+ file. write ( ) . unwrap ( )
561
+ }
0 commit comments