rustc_codegen_ssa/back/link/
raw_dylib.rs1use std::fs;
2use std::io::{BufWriter, Write};
3use std::path::{Path, PathBuf};
4
5use rustc_abi::Endian;
6use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN};
7use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
8use rustc_data_structures::stable_hasher::StableHasher;
9use rustc_hashes::Hash128;
10use rustc_session::Session;
11use rustc_session::cstore::DllImport;
12use rustc_session::utils::NativeLibKind;
13use rustc_span::Symbol;
14
15use crate::back::archive::ImportLibraryItem;
16use crate::back::link::ArchiveBuilderBuilder;
17use crate::errors::ErrorCreatingImportLibrary;
18use crate::{NativeLib, common, errors};
19
20fn collate_raw_dylibs_windows<'a>(
27 sess: &Session,
28 used_libraries: impl IntoIterator<Item = &'a NativeLib>,
29) -> Vec<(String, Vec<DllImport>)> {
30 let mut dylib_table = FxIndexMap::<String, FxIndexMap<Symbol, &DllImport>>::default();
32
33 for lib in used_libraries {
34 if lib.kind == NativeLibKind::RawDylib {
35 let ext = if lib.verbatim { "" } else { ".dll" };
36 let name = format!("{}{}", lib.name, ext);
37 let imports = dylib_table.entry(name.clone()).or_default();
38 for import in &lib.dll_imports {
39 if let Some(old_import) = imports.insert(import.name, import) {
40 if import.calling_convention != old_import.calling_convention {
43 sess.dcx().emit_err(errors::MultipleExternalFuncDecl {
44 span: import.span,
45 function: import.name,
46 library_name: &name,
47 });
48 }
49 }
50 }
51 }
52 }
53 sess.dcx().abort_if_errors();
54 dylib_table
55 .into_iter()
56 .map(|(name, imports)| {
57 (name, imports.into_iter().map(|(_, import)| import.clone()).collect())
58 })
59 .collect()
60}
61
62pub(super) fn create_raw_dylib_dll_import_libs<'a>(
63 sess: &Session,
64 archive_builder_builder: &dyn ArchiveBuilderBuilder,
65 used_libraries: impl IntoIterator<Item = &'a NativeLib>,
66 tmpdir: &Path,
67 is_direct_dependency: bool,
68) -> Vec<PathBuf> {
69 collate_raw_dylibs_windows(sess, used_libraries)
70 .into_iter()
71 .map(|(raw_dylib_name, raw_dylib_imports)| {
72 let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" };
73 let output_path = tmpdir.join(format!("{raw_dylib_name}{name_suffix}.lib"));
74
75 let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(&sess.target);
76
77 let items: Vec<ImportLibraryItem> = raw_dylib_imports
78 .iter()
79 .map(|import: &DllImport| {
80 if sess.target.arch == "x86" {
81 ImportLibraryItem {
82 name: common::i686_decorated_name(
83 import,
84 mingw_gnu_toolchain,
85 false,
86 false,
87 ),
88 ordinal: import.ordinal(),
89 symbol_name: import.is_missing_decorations().then(|| {
90 common::i686_decorated_name(
91 import,
92 mingw_gnu_toolchain,
93 false,
94 true,
95 )
96 }),
97 is_data: !import.is_fn,
98 }
99 } else {
100 ImportLibraryItem {
101 name: import.name.to_string(),
102 ordinal: import.ordinal(),
103 symbol_name: None,
104 is_data: !import.is_fn,
105 }
106 }
107 })
108 .collect();
109
110 archive_builder_builder.create_dll_import_lib(
111 sess,
112 &raw_dylib_name,
113 items,
114 &output_path,
115 );
116
117 output_path
118 })
119 .collect()
120}
121
122fn collate_raw_dylibs_elf<'a>(
129 sess: &Session,
130 used_libraries: impl IntoIterator<Item = &'a NativeLib>,
131) -> Vec<(String, Vec<DllImport>)> {
132 let mut dylib_table = FxIndexMap::<String, FxIndexMap<Symbol, &DllImport>>::default();
134
135 for lib in used_libraries {
136 if lib.kind == NativeLibKind::RawDylib {
137 let filename = if lib.verbatim {
138 lib.name.as_str().to_owned()
139 } else {
140 let ext = sess.target.dll_suffix.as_ref();
141 let prefix = sess.target.dll_prefix.as_ref();
142 format!("{prefix}{}{ext}", lib.name)
143 };
144
145 let imports = dylib_table.entry(filename.clone()).or_default();
146 for import in &lib.dll_imports {
147 imports.insert(import.name, import);
148 }
149 }
150 }
151 sess.dcx().abort_if_errors();
152 dylib_table
153 .into_iter()
154 .map(|(name, imports)| {
155 (name, imports.into_iter().map(|(_, import)| import.clone()).collect())
156 })
157 .collect()
158}
159
160pub(super) fn create_raw_dylib_elf_stub_shared_objects<'a>(
161 sess: &Session,
162 used_libraries: impl IntoIterator<Item = &'a NativeLib>,
163 raw_dylib_so_dir: &Path,
164) -> Vec<String> {
165 collate_raw_dylibs_elf(sess, used_libraries)
166 .into_iter()
167 .map(|(load_filename, raw_dylib_imports)| {
168 use std::hash::Hash;
169
170 let shared_object = create_elf_raw_dylib_stub(sess, &load_filename, &raw_dylib_imports);
178
179 let mut file_name_hasher = StableHasher::new();
180 load_filename.hash(&mut file_name_hasher);
181 for raw_dylib in raw_dylib_imports {
182 raw_dylib.name.as_str().hash(&mut file_name_hasher);
183 }
184
185 let library_filename: Hash128 = file_name_hasher.finish();
186 let temporary_lib_name = format!(
187 "{}{}{}",
188 sess.target.dll_prefix,
189 library_filename.as_u128().to_base_fixed_len(CASE_INSENSITIVE),
190 sess.target.dll_suffix
191 );
192 let link_path = raw_dylib_so_dir.join(&temporary_lib_name);
193
194 let file = match fs::File::create_new(&link_path) {
195 Ok(file) => file,
196 Err(error) => sess.dcx().emit_fatal(ErrorCreatingImportLibrary {
197 lib_name: &load_filename,
198 error: error.to_string(),
199 }),
200 };
201 if let Err(error) = BufWriter::new(file).write_all(&shared_object) {
202 sess.dcx().emit_fatal(ErrorCreatingImportLibrary {
203 lib_name: &load_filename,
204 error: error.to_string(),
205 });
206 };
207
208 temporary_lib_name
209 })
210 .collect()
211}
212
213fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]) -> Vec<u8> {
216 use object::write::elf as write;
217 use object::{AddressSize, Architecture, elf};
218
219 let mut stub_buf = Vec::new();
220
221 let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.unstable_target_features)
230 else {
231 sess.dcx().fatal(format!(
232 "raw-dylib is not supported for the architecture `{}`",
233 sess.target.arch
234 ));
235 };
236
237 let endianness = match sess.target.options.endian {
238 Endian::Little => object::Endianness::Little,
239 Endian::Big => object::Endianness::Big,
240 };
241
242 let is_64 = match arch.address_size() {
243 Some(AddressSize::U8 | AddressSize::U16 | AddressSize::U32) => false,
244 Some(AddressSize::U64) => true,
245 _ => sess.dcx().fatal(format!(
246 "raw-dylib is not supported for the architecture `{}`",
247 sess.target.arch
248 )),
249 };
250
251 let mut stub = write::Writer::new(endianness, is_64, &mut stub_buf);
252
253 let mut vers = Vec::new();
254 let mut vers_map = FxHashMap::default();
255 let mut syms = Vec::new();
256
257 for symbol in symbols {
258 let symbol_name = symbol.name.as_str();
259 if let Some((name, version_name)) = symbol_name.split_once('@') {
260 assert!(!version_name.contains('@'));
261 let dynstr = stub.add_dynamic_string(name.as_bytes());
262 let ver = if let Some(&ver_id) = vers_map.get(version_name) {
263 ver_id
264 } else {
265 let id = vers.len();
266 vers_map.insert(version_name, id);
267 let dynstr = stub.add_dynamic_string(version_name.as_bytes());
268 vers.push((version_name, dynstr));
269 id
270 };
271 syms.push((name, dynstr, Some(ver)));
272 } else {
273 let dynstr = stub.add_dynamic_string(symbol_name.as_bytes());
274 syms.push((symbol_name, dynstr, None));
275 }
276 }
277
278 let soname = stub.add_dynamic_string(soname.as_bytes());
279
280 stub.reserve_null_dynamic_symbol_index();
286
287 for _ in syms.iter() {
288 stub.reserve_dynamic_symbol_index();
289 }
290
291 stub.reserve_shstrtab_section_index();
294 let text_section_name = stub.add_section_name(".text".as_bytes());
295 let text_section = stub.reserve_section_index();
296 stub.reserve_dynsym_section_index();
297 stub.reserve_dynstr_section_index();
298 if !vers.is_empty() {
299 stub.reserve_gnu_versym_section_index();
300 stub.reserve_gnu_verdef_section_index();
301 }
302 stub.reserve_dynamic_section_index();
303
304 stub.reserve_file_header();
306 stub.reserve_shstrtab();
307 stub.reserve_section_headers();
308 stub.reserve_dynsym();
309 stub.reserve_dynstr();
310 if !vers.is_empty() {
311 stub.reserve_gnu_versym();
312 stub.reserve_gnu_verdef(1 + vers.len(), 1 + vers.len());
313 }
314 stub.reserve_dynamic(2); let e_machine = match (arch, sub_arch) {
318 (Architecture::Aarch64, None) => elf::EM_AARCH64,
319 (Architecture::Aarch64_Ilp32, None) => elf::EM_AARCH64,
320 (Architecture::Arm, None) => elf::EM_ARM,
321 (Architecture::Avr, None) => elf::EM_AVR,
322 (Architecture::Bpf, None) => elf::EM_BPF,
323 (Architecture::Csky, None) => elf::EM_CSKY,
324 (Architecture::E2K32, None) => elf::EM_MCST_ELBRUS,
325 (Architecture::E2K64, None) => elf::EM_MCST_ELBRUS,
326 (Architecture::I386, None) => elf::EM_386,
327 (Architecture::X86_64, None) => elf::EM_X86_64,
328 (Architecture::X86_64_X32, None) => elf::EM_X86_64,
329 (Architecture::Hexagon, None) => elf::EM_HEXAGON,
330 (Architecture::LoongArch32, None) => elf::EM_LOONGARCH,
331 (Architecture::LoongArch64, None) => elf::EM_LOONGARCH,
332 (Architecture::M68k, None) => elf::EM_68K,
333 (Architecture::Mips, None) => elf::EM_MIPS,
334 (Architecture::Mips64, None) => elf::EM_MIPS,
335 (Architecture::Mips64_N32, None) => elf::EM_MIPS,
336 (Architecture::Msp430, None) => elf::EM_MSP430,
337 (Architecture::PowerPc, None) => elf::EM_PPC,
338 (Architecture::PowerPc64, None) => elf::EM_PPC64,
339 (Architecture::Riscv32, None) => elf::EM_RISCV,
340 (Architecture::Riscv64, None) => elf::EM_RISCV,
341 (Architecture::S390x, None) => elf::EM_S390,
342 (Architecture::Sbf, None) => elf::EM_SBF,
343 (Architecture::Sharc, None) => elf::EM_SHARC,
344 (Architecture::Sparc, None) => elf::EM_SPARC,
345 (Architecture::Sparc32Plus, None) => elf::EM_SPARC32PLUS,
346 (Architecture::Sparc64, None) => elf::EM_SPARCV9,
347 (Architecture::Xtensa, None) => elf::EM_XTENSA,
348 _ => {
349 sess.dcx().fatal(format!(
350 "raw-dylib is not supported for the architecture `{}`",
351 sess.target.arch
352 ));
353 }
354 };
355
356 stub.write_file_header(&write::FileHeader {
357 os_abi: crate::back::metadata::elf_os_abi(sess),
358 abi_version: 0,
359 e_type: object::elf::ET_DYN,
360 e_machine,
361 e_entry: 0,
362 e_flags: crate::back::metadata::elf_e_flags(arch, sess),
363 })
364 .unwrap();
365
366 stub.write_shstrtab();
368
369 stub.write_null_section_header();
371 stub.write_shstrtab_section_header();
372 stub.write_section_header(&write::SectionHeader {
374 name: Some(text_section_name),
375 sh_type: elf::SHT_PROGBITS,
376 sh_flags: 0,
377 sh_addr: 0,
378 sh_offset: 0,
379 sh_size: 0,
380 sh_link: 0,
381 sh_info: 0,
382 sh_addralign: 1,
383 sh_entsize: 0,
384 });
385 stub.write_dynsym_section_header(0, 1);
386 stub.write_dynstr_section_header(0);
387 if !vers.is_empty() {
388 stub.write_gnu_versym_section_header(0);
389 stub.write_gnu_verdef_section_header(0);
390 }
391 stub.write_dynamic_section_header(0);
392
393 stub.write_null_dynamic_symbol();
395 for (_name, dynstr, _ver) in syms.iter().copied() {
396 stub.write_dynamic_symbol(&write::Sym {
397 name: Some(dynstr),
398 st_info: (elf::STB_GLOBAL << 4) | elf::STT_NOTYPE,
399 st_other: elf::STV_DEFAULT,
400 section: Some(text_section),
401 st_shndx: 0, st_value: 0,
403 st_size: 0,
404 });
405 }
406
407 stub.write_dynstr();
409
410 if !vers.is_empty() {
412 stub.write_null_gnu_versym();
414 for (_name, _dynstr, ver) in syms.iter().copied() {
415 stub.write_gnu_versym(if let Some(ver) = ver {
416 assert!((2 + ver as u16) < elf::VERSYM_HIDDEN);
417 elf::VERSYM_HIDDEN | (2 + ver as u16)
418 } else {
419 1
420 });
421 }
422
423 stub.write_align_gnu_verdef();
425 stub.write_gnu_verdef(&write::Verdef {
426 version: elf::VER_DEF_CURRENT,
427 flags: elf::VER_FLG_BASE,
428 index: 1,
429 aux_count: 1,
430 name: soname,
431 });
432 for (ver, (_name, dynstr)) in vers.into_iter().enumerate() {
433 stub.write_gnu_verdef(&write::Verdef {
434 version: elf::VER_DEF_CURRENT,
435 flags: 0,
436 index: 2 + ver as u16,
437 aux_count: 1,
438 name: dynstr,
439 });
440 }
441 }
442
443 stub.write_align_dynamic();
448 stub.write_dynamic_string(elf::DT_SONAME, soname);
449 stub.write_dynamic(elf::DT_NULL, 0);
450
451 stub_buf
452}