1+ use std:: fs;
12use std:: path:: { Path , PathBuf } ;
23
34use rustc_codegen_ssa:: back:: archive:: {
@@ -15,11 +16,289 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder {
1516 fn create_dll_import_lib (
1617 & self ,
1718 _sess : & Session ,
18- _lib_name : & str ,
19- _dll_imports : & [ rustc_session:: cstore:: DllImport ] ,
20- _tmpdir : & Path ,
19+ lib_name : & str ,
20+ dll_imports : & [ rustc_session:: cstore:: DllImport ] ,
21+ tmpdir : & Path ,
2122 _is_direct_dependency : bool ,
2223 ) -> PathBuf {
23- unimplemented ! ( "creating dll imports is not yet supported" ) ;
24+ let mut import_names = Vec :: new ( ) ;
25+ for dll_import in dll_imports {
26+ import_names. push ( dll_import. name . as_str ( ) ) ;
27+ }
28+ let lib_path = tmpdir. join ( format ! ( "{}.lib" , lib_name) ) ;
29+ // todo: emit session error instead of expects
30+ fs:: write ( & lib_path, windows_import_lib:: generate ( lib_name, & import_names) )
31+ . expect ( "failed to write import library" ) ;
32+
33+ lib_path
34+ }
35+ }
36+
37+ // todo: pull out to a proper location. Really should be in `object` crate!
38+ // todo: support ordinals
39+ // todo: support name types (e.g. verbatim+)
40+ // todo: support long member names
41+ // todo: support windows-gnu flavor?
42+ // todo: provide machine
43+ // todo: remove any panics, nice errors
44+ mod windows_import_lib {
45+ // https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#archive-library-file-format
46+ //
47+ // Windows .lib files are System-V (aka. GUN) flavored ar files with a couple of extra lookup
48+ // members.
49+ //
50+ // An archive is the 8 bytes b"!<arch>\n"
51+ // followed by a sequence of 60 byte member headers:
52+ // 0: name: [u8; 16], // member name, terminated with "/". If it is longer than 15, then
53+ // // use "/n" where "n" is a decimal for the offset in bytes into
54+ // // the longnames ("//") member contents.
55+ // 16: date: [u8; 12], // ASCII decimal seconds since UNIX epoch - always -1 for MSVC
56+ // 28: uid: [u8; 6], // ASCII decimal user id. Always blank for MSVC
57+ // 34: gid: [u8; 6], // ditto for group id.
58+ // 40: mode: [u8; 8], // ASCII octal UNIX mode. 0 for MSVC
59+ // 48: size: [u8; 10], // ASCII decimal data size.
60+ // 58: end: b"`\n",
61+ // then size bytes of payload. If payload is odd sized, pad
62+ // to an even offset with \n.
63+ //
64+ // You must store two extra members at the start, a legacy member lookup table member
65+ // and the current member lookup and symbol table, both with empty ("/") names.
66+ //
67+ // The legacy table member has the name "/" with following contents, using big-endian numbers:
68+ // count: u32, // number of indexed symbols
69+ // offsets: [u32, count], // file offsets to the header of the member that contains
70+ // // that symbol.
71+ // names: * // sequence of null terminated symbol names.
72+ //
73+ // The current table member also has the name "/", and has the following contents, using
74+ // little-endian numbers:
75+ // member_count: u32, // number of members
76+ // member_offsets: [u32; member_count], // file offsets to each member header
77+ // symbol_count: u32, // number of symbols
78+ // symbol_member: [u16; symbol_count], // *1-based* index of the member that contains
79+ // // each symbol
80+ // symbol_names: * // sequence of null terminated symbol names
81+ //
82+ // Then the long names member ("//") as with regular GNU ar files, just a sequence of
83+ // null terminated strings indexed by members using the long name format "/n" as described
84+ // above.
85+ //
86+ // Then regular members follow.
87+ //
88+ // This library emits only import libraries, that is, libraries with a short import object
89+ // describing an import from a dll. That means each member contains exactly one symbol. The member
90+ // name doesn't seem to matter, including duplicates, we use the dll name since that's what's in the
91+ // files generated by MSVC tools.
92+ //
93+ // The short import object has the form:
94+ // header:
95+ // sig1: 0u16
96+ // sig2: 0xFFFFu16
97+ // version: u16, // normally 0
98+ // machine: u16, // IMAGE_MACHINE_* value, e.g. 0x8664 for AMD64
99+ // time_date_stamp: u32, // normally 0
100+ // size_of_data: u32, // size following the header
101+ // ordinal_or_hint: u16, // depending on flag
102+ // object_type: u2, // IMPORT_OBJECT_{CODE,DATA,CONST} = 0, 1, 2
103+ // name_type: u3, // IMPORT_OBJECT_{ORDINAL,NAME,NAME_NO_PREFIX,NAME_UNDECORATE,NAME_EXPORTAS} = 0, 1, 2, 3, 4
104+ // reserved: u11,
105+ // data: // size_of_data bytes
106+ // name: * // import name; null terminated string
107+ // dll_name: * // dll name; null terminated string
108+ pub fn generate ( dll_name : & str , import_names : & [ & str ] ) -> Vec < u8 > {
109+ assert ! ( dll_name. len( ) < 16 , "long member names not supported yet" ) ;
110+ assert ! ( import_names. len( ) <= 0xFFFF , "too many import names" ) ;
111+ // number of symbols, and members containing symbols for symbol lookup members
112+ let symbol_count = import_names. len ( ) ;
113+
114+ let mut writer = Writer :: new ( ) ;
115+
116+ // legacy symbol directory
117+ let mut legacy_symbol_directory = writer. start_member_raw ( ) ;
118+ legacy_symbol_directory. set_raw_name ( b"/" ) ;
119+ legacy_symbol_directory. write_u32_be ( symbol_count as u32 ) ;
120+ // reserve space for offsets.
121+ let legacy_member_table_offset = legacy_symbol_directory. reserve_bytes ( symbol_count * 4 ) ;
122+ // string table
123+ for name in import_names {
124+ legacy_symbol_directory. write_c_str ( name) ;
125+ }
126+ // done with legacy symbol directory
127+ drop ( legacy_symbol_directory) ;
128+
129+ // current symbol directory
130+ let mut current_symbol_directory = writer. start_member_raw ( ) ;
131+ current_symbol_directory. set_raw_name ( b"/" ) ;
132+ // member count: same as symbol count for import library
133+ current_symbol_directory. write_u32_le ( symbol_count as u32 ) ;
134+ // reserve space for member offsets
135+ let current_member_table_offset = current_symbol_directory. reserve_bytes ( symbol_count * 4 ) ;
136+ // symbol count
137+ current_symbol_directory. write_u32_le ( symbol_count as u32 ) ;
138+ // we assume symbol members are already in order
139+ for index in 0 ..import_names. len ( ) as u16 {
140+ current_symbol_directory. write_u16_le ( 1 + index) ;
141+ }
142+ // string table again (could just copy from legacy string table above?)
143+ for name in import_names {
144+ current_symbol_directory. write_c_str ( name) ;
145+ }
146+ // done with current symbol directory
147+ drop ( current_symbol_directory) ;
148+
149+ // long names member not supported yet
150+
151+ // import members
152+ for ( index, name) in import_names. iter ( ) . enumerate ( ) {
153+ let mut member = writer. start_member ( dll_name) ;
154+ // update member offsets
155+ let member_offset = member. header_offset as u32 ;
156+ member. data [ legacy_member_table_offset + index * 4 ..] [ ..4 ]
157+ . copy_from_slice ( & member_offset. to_be_bytes ( ) ) ;
158+ member. data [ current_member_table_offset + index * 4 ..] [ ..4 ]
159+ . copy_from_slice ( & member_offset. to_le_bytes ( ) ) ;
160+ // write import object:
161+ // signature
162+ member. write_u16_le ( 0 ) ;
163+ member. write_u16_le ( 0xFFFF ) ;
164+ // version
165+ member. write_u16_le ( 0 ) ;
166+ // machine = AMD64
167+ member. write_u16_le ( 0x8664 ) ;
168+ // time_date_stamp
169+ member. write_u32_le ( 0 ) ;
170+ // size_of_data
171+ member. write_u32_le ( ( dll_name. len ( ) + 1 + name. len ( ) + 1 ) as u32 ) ;
172+ // ordinal_or_hint
173+ member. write_u16_le ( 0 ) ;
174+ // object_type | name_type = IMPORT_OBJECT_CODE | IMPORT_OBJECT_NAME
175+ member. write_u16_le ( 1 << 2 | 0 ) ;
176+ // data:
177+ // name
178+ member. write_c_str ( name) ;
179+ // dll_name
180+ member. write_c_str ( dll_name) ;
181+
182+ drop ( member) ;
183+ }
184+
185+ writer. data
186+ }
187+
188+ struct Writer {
189+ data : Vec < u8 > ,
190+ }
191+
192+ impl Writer {
193+ fn new ( ) -> Self {
194+ Self { data : Vec :: from ( * b"!<arch>\n " ) }
195+ }
196+
197+ fn start_member_raw ( & mut self ) -> Member < ' _ > {
198+ let header_offset = self . data . len ( ) ;
199+ // fill the header with blanks...
200+ self . data . resize ( header_offset + Member :: HEADER_SIZE - 2 , b' ' ) ;
201+ // except for end marker
202+ self . data . extend_from_slice ( b"`\n " ) ;
203+
204+ let mut member = Member :: new ( & mut self . data , header_offset) ;
205+ // init date, mode to default values as produced by MSVC tools
206+ member. set_time_date_stamp ( -1 ) ;
207+ member. set_mode ( 0 ) ;
208+ member
209+ }
210+
211+ fn start_member ( & mut self , name : & str ) -> Member < ' _ > {
212+ let mut member = self . start_member_raw ( ) ;
213+ member. set_name ( name) ;
214+ member
215+ }
216+ }
217+
218+ struct Member < ' a > {
219+ data : & ' a mut Vec < u8 > ,
220+ header_offset : usize ,
221+ }
222+
223+ impl < ' a > Member < ' a > {
224+ const HEADER_SIZE : usize = 60 ;
225+
226+ fn new ( data : & ' a mut Vec < u8 > , header_offset : usize ) -> Self {
227+ Self { data, header_offset }
228+ }
229+
230+ fn header_slice ( & mut self , offset : usize , len : usize ) -> & mut [ u8 ] {
231+ & mut self . data [ self . header_offset + offset..] [ ..len]
232+ }
233+
234+ fn set_name ( & mut self , name : & str ) {
235+ assert ! ( name. len( ) < 16 , "long member names not supported yet" ) ;
236+ self . set_raw_name ( name. as_bytes ( ) ) ;
237+ self . data [ self . header_offset + name. len ( ) ] = b'/' ;
238+ }
239+
240+ fn set_raw_name ( & mut self , raw_name : & [ u8 ] ) {
241+ assert ! ( raw_name. len( ) <= 16 , "raw name must be <= 16 bytes" ) ;
242+ self . header_slice ( 0 , raw_name. len ( ) ) . copy_from_slice ( raw_name) ;
243+ }
244+
245+ fn set_time_date_stamp ( & mut self , value : i32 ) {
246+ self . set_decimal_field ( 16 , 12 , value) ;
247+ }
248+
249+ fn set_uid ( & mut self , value : i32 ) {
250+ self . set_decimal_field ( 28 , 6 , value) ;
251+ }
252+
253+ fn set_gid ( & mut self , value : i32 ) {
254+ self . set_decimal_field ( 34 , 6 , value) ;
255+ }
256+
257+ fn set_mode ( & mut self , value : i32 ) {
258+ use std:: io:: Write ;
259+ write ! ( std:: io:: Cursor :: new( self . header_slice( 40 , 8 ) ) , "{value:o}" )
260+ . expect ( "value too large" ) ;
261+ }
262+
263+ fn set_decimal_field ( & mut self , offset : usize , size : usize , value : i32 ) {
264+ use std:: io:: Write ;
265+ write ! ( std:: io:: Cursor :: new( self . header_slice( offset, size) ) , "{value}" )
266+ . expect ( "value too large" ) ;
267+ }
268+
269+ fn write_c_str ( & mut self , data : & str ) {
270+ self . data . extend_from_slice ( data. as_bytes ( ) ) ;
271+ self . data . push ( 0 ) ;
272+ }
273+
274+ fn write_u16_le ( & mut self , data : u16 ) {
275+ self . data . extend_from_slice ( & data. to_le_bytes ( ) ) ;
276+ }
277+
278+ fn write_u32_be ( & mut self , data : u32 ) {
279+ self . data . extend_from_slice ( & data. to_be_bytes ( ) ) ;
280+ }
281+
282+ fn write_u32_le ( & mut self , data : u32 ) {
283+ self . data . extend_from_slice ( & data. to_le_bytes ( ) ) ;
284+ }
285+
286+ fn reserve_bytes ( & mut self , count : usize ) -> usize {
287+ let offset = self . data . len ( ) ;
288+ self . data . resize ( offset + count, 0 ) ;
289+ offset
290+ }
291+ }
292+
293+ impl < ' a > Drop for Member < ' a > {
294+ fn drop ( & mut self ) {
295+ let data_size = self . data . len ( ) - self . header_offset - Self :: HEADER_SIZE ;
296+ assert ! ( data_size < i32 :: MAX as usize ) ;
297+ self . set_decimal_field ( 48 , 10 , data_size as i32 ) ;
298+ // pad to even address
299+ if data_size % 2 == 1 {
300+ self . data . push ( b'\n' ) ;
301+ }
302+ }
24303 }
25304}
0 commit comments