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