1
+ use std:: fs;
1
2
use std:: path:: { Path , PathBuf } ;
2
3
3
4
use rustc_codegen_ssa:: back:: archive:: {
@@ -15,11 +16,289 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder {
15
16
fn create_dll_import_lib (
16
17
& self ,
17
18
_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 ,
21
22
_is_direct_dependency : bool ,
22
23
) -> 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
+ }
24
303
}
25
304
}
0 commit comments