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_hir::attrs::NativeLibKind;
11use rustc_session::Session;
12use rustc_session::cstore::DllImport;
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 let NativeLibKind::RawDylib { .. } = lib.kind {
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>, bool)> {
132 let mut dylib_table = FxIndexMap::<String, (FxIndexMap<Symbol, &DllImport>, bool)>::default();
134
135 for lib in used_libraries {
136 if let NativeLibKind::RawDylib { as_needed } = lib.kind {
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 (stub_imports, stub_as_needed) =
146 dylib_table.entry(filename.clone()).or_insert((Default::default(), true));
147 for import in &lib.dll_imports {
148 stub_imports.insert(import.name, import);
149 }
150 *stub_as_needed = *stub_as_needed && as_needed.unwrap_or(true);
151 }
152 }
153 sess.dcx().abort_if_errors();
154 dylib_table
155 .into_iter()
156 .map(|(name, (imports, as_needed))| {
157 (name, imports.into_iter().map(|(_, import)| import.clone()).collect(), as_needed)
158 })
159 .collect()
160}
161
162pub(super) fn create_raw_dylib_elf_stub_shared_objects<'a>(
163 sess: &Session,
164 used_libraries: impl IntoIterator<Item = &'a NativeLib>,
165 raw_dylib_so_dir: &Path,
166) -> Vec<(String, bool)> {
167 collate_raw_dylibs_elf(sess, used_libraries)
168 .into_iter()
169 .map(|(load_filename, raw_dylib_imports, as_needed)| {
170 use std::hash::Hash;
171
172 let shared_object = create_elf_raw_dylib_stub(sess, &load_filename, &raw_dylib_imports);
180
181 let mut file_name_hasher = StableHasher::new();
182 load_filename.hash(&mut file_name_hasher);
183 for raw_dylib in raw_dylib_imports {
184 raw_dylib.name.as_str().hash(&mut file_name_hasher);
185 }
186
187 let library_filename: Hash128 = file_name_hasher.finish();
188 let temporary_lib_name = format!(
189 "{}{}{}",
190 sess.target.dll_prefix,
191 library_filename.as_u128().to_base_fixed_len(CASE_INSENSITIVE),
192 sess.target.dll_suffix
193 );
194 let link_path = raw_dylib_so_dir.join(&temporary_lib_name);
195
196 let file = match fs::File::create_new(&link_path) {
197 Ok(file) => file,
198 Err(error) => sess.dcx().emit_fatal(ErrorCreatingImportLibrary {
199 lib_name: &load_filename,
200 error: error.to_string(),
201 }),
202 };
203 if let Err(error) = BufWriter::new(file).write_all(&shared_object) {
204 sess.dcx().emit_fatal(ErrorCreatingImportLibrary {
205 lib_name: &load_filename,
206 error: error.to_string(),
207 });
208 };
209
210 (temporary_lib_name, as_needed)
211 })
212 .collect()
213}
214
215fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]) -> Vec<u8> {
218 use object::write::elf as write;
219 use object::{AddressSize, Architecture, elf};
220
221 let mut stub_buf = Vec::new();
222
223 let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.unstable_target_features)
232 else {
233 sess.dcx().fatal(format!(
234 "raw-dylib is not supported for the architecture `{}`",
235 sess.target.arch
236 ));
237 };
238
239 let endianness = match sess.target.options.endian {
240 Endian::Little => object::Endianness::Little,
241 Endian::Big => object::Endianness::Big,
242 };
243
244 let is_64 = match arch.address_size() {
245 Some(AddressSize::U8 | AddressSize::U16 | AddressSize::U32) => false,
246 Some(AddressSize::U64) => true,
247 _ => sess.dcx().fatal(format!(
248 "raw-dylib is not supported for the architecture `{}`",
249 sess.target.arch
250 )),
251 };
252
253 let mut stub = write::Writer::new(endianness, is_64, &mut stub_buf);
254
255 let mut vers = Vec::new();
256 let mut vers_map = FxHashMap::default();
257 let mut syms = Vec::new();
258
259 for symbol in symbols {
260 let symbol_name = symbol.name.as_str();
261 if let Some((name, version_name)) = symbol_name.split_once('@') {
262 assert!(!version_name.contains('@'));
263 let dynstr = stub.add_dynamic_string(name.as_bytes());
264 let ver = if let Some(&ver_id) = vers_map.get(version_name) {
265 ver_id
266 } else {
267 let id = vers.len();
268 vers_map.insert(version_name, id);
269 let dynstr = stub.add_dynamic_string(version_name.as_bytes());
270 vers.push((version_name, dynstr));
271 id
272 };
273 syms.push((name, dynstr, Some(ver)));
274 } else {
275 let dynstr = stub.add_dynamic_string(symbol_name.as_bytes());
276 syms.push((symbol_name, dynstr, None));
277 }
278 }
279
280 let soname = stub.add_dynamic_string(soname.as_bytes());
281
282 stub.reserve_null_dynamic_symbol_index();
288
289 for _ in syms.iter() {
290 stub.reserve_dynamic_symbol_index();
291 }
292
293 stub.reserve_shstrtab_section_index();
296 let text_section_name = stub.add_section_name(".text".as_bytes());
297 let text_section = stub.reserve_section_index();
298 stub.reserve_dynsym_section_index();
299 stub.reserve_dynstr_section_index();
300 if !vers.is_empty() {
301 stub.reserve_gnu_versym_section_index();
302 stub.reserve_gnu_verdef_section_index();
303 }
304 stub.reserve_dynamic_section_index();
305
306 stub.reserve_file_header();
308 stub.reserve_shstrtab();
309 stub.reserve_section_headers();
310 stub.reserve_dynsym();
311 stub.reserve_dynstr();
312 let verdef_count = 1 + vers.len();
313 let mut dynamic_entries = 2; if !vers.is_empty() {
315 stub.reserve_gnu_versym();
316 stub.reserve_gnu_verdef(verdef_count, verdef_count);
317 dynamic_entries += 1; }
319 stub.reserve_dynamic(dynamic_entries);
320
321 let e_machine = match (arch, sub_arch) {
323 (Architecture::Aarch64, None) => elf::EM_AARCH64,
324 (Architecture::Aarch64_Ilp32, None) => elf::EM_AARCH64,
325 (Architecture::Arm, None) => elf::EM_ARM,
326 (Architecture::Avr, None) => elf::EM_AVR,
327 (Architecture::Bpf, None) => elf::EM_BPF,
328 (Architecture::Csky, None) => elf::EM_CSKY,
329 (Architecture::E2K32, None) => elf::EM_MCST_ELBRUS,
330 (Architecture::E2K64, None) => elf::EM_MCST_ELBRUS,
331 (Architecture::I386, None) => elf::EM_386,
332 (Architecture::X86_64, None) => elf::EM_X86_64,
333 (Architecture::X86_64_X32, None) => elf::EM_X86_64,
334 (Architecture::Hexagon, None) => elf::EM_HEXAGON,
335 (Architecture::LoongArch32, None) => elf::EM_LOONGARCH,
336 (Architecture::LoongArch64, None) => elf::EM_LOONGARCH,
337 (Architecture::M68k, None) => elf::EM_68K,
338 (Architecture::Mips, None) => elf::EM_MIPS,
339 (Architecture::Mips64, None) => elf::EM_MIPS,
340 (Architecture::Mips64_N32, None) => elf::EM_MIPS,
341 (Architecture::Msp430, None) => elf::EM_MSP430,
342 (Architecture::PowerPc, None) => elf::EM_PPC,
343 (Architecture::PowerPc64, None) => elf::EM_PPC64,
344 (Architecture::Riscv32, None) => elf::EM_RISCV,
345 (Architecture::Riscv64, None) => elf::EM_RISCV,
346 (Architecture::S390x, None) => elf::EM_S390,
347 (Architecture::Sbf, None) => elf::EM_SBF,
348 (Architecture::Sharc, None) => elf::EM_SHARC,
349 (Architecture::Sparc, None) => elf::EM_SPARC,
350 (Architecture::Sparc32Plus, None) => elf::EM_SPARC32PLUS,
351 (Architecture::Sparc64, None) => elf::EM_SPARCV9,
352 (Architecture::Xtensa, None) => elf::EM_XTENSA,
353 _ => {
354 sess.dcx().fatal(format!(
355 "raw-dylib is not supported for the architecture `{}`",
356 sess.target.arch
357 ));
358 }
359 };
360
361 stub.write_file_header(&write::FileHeader {
362 os_abi: crate::back::metadata::elf_os_abi(sess),
363 abi_version: 0,
364 e_type: object::elf::ET_DYN,
365 e_machine,
366 e_entry: 0,
367 e_flags: crate::back::metadata::elf_e_flags(arch, sess),
368 })
369 .unwrap();
370
371 stub.write_shstrtab();
373
374 stub.write_null_section_header();
376 stub.write_shstrtab_section_header();
377 stub.write_section_header(&write::SectionHeader {
379 name: Some(text_section_name),
380 sh_type: elf::SHT_PROGBITS,
381 sh_flags: 0,
382 sh_addr: 0,
383 sh_offset: 0,
384 sh_size: 0,
385 sh_link: 0,
386 sh_info: 0,
387 sh_addralign: 1,
388 sh_entsize: 0,
389 });
390 stub.write_dynsym_section_header(0, 1);
391 stub.write_dynstr_section_header(0);
392 if !vers.is_empty() {
393 stub.write_gnu_versym_section_header(0);
394 stub.write_gnu_verdef_section_header(0);
395 }
396 stub.write_dynamic_section_header(0);
397
398 stub.write_null_dynamic_symbol();
400 for (_name, dynstr, _ver) in syms.iter().copied() {
401 stub.write_dynamic_symbol(&write::Sym {
402 name: Some(dynstr),
403 st_info: (elf::STB_GLOBAL << 4) | elf::STT_NOTYPE,
404 st_other: elf::STV_DEFAULT,
405 section: Some(text_section),
406 st_shndx: 0, st_value: 0,
408 st_size: 0,
409 });
410 }
411
412 stub.write_dynstr();
414
415 if !vers.is_empty() {
417 stub.write_null_gnu_versym();
419 for (_name, _dynstr, ver) in syms.iter().copied() {
420 stub.write_gnu_versym(if let Some(ver) = ver {
421 assert!((2 + ver as u16) < elf::VERSYM_HIDDEN);
422 elf::VERSYM_HIDDEN | (2 + ver as u16)
423 } else {
424 1
425 });
426 }
427
428 stub.write_align_gnu_verdef();
430 stub.write_gnu_verdef(&write::Verdef {
431 version: elf::VER_DEF_CURRENT,
432 flags: elf::VER_FLG_BASE,
433 index: 1,
434 aux_count: 1,
435 name: soname,
436 });
437 for (ver, (_name, dynstr)) in vers.into_iter().enumerate() {
438 stub.write_gnu_verdef(&write::Verdef {
439 version: elf::VER_DEF_CURRENT,
440 flags: 0,
441 index: 2 + ver as u16,
442 aux_count: 1,
443 name: dynstr,
444 });
445 }
446 }
447
448 stub.write_align_dynamic();
452 stub.write_dynamic_string(elf::DT_SONAME, soname);
453 if verdef_count > 1 {
455 stub.write_dynamic(elf::DT_VERDEFNUM, verdef_count as u64);
456 }
457 stub.write_dynamic(elf::DT_NULL, 0);
459
460 stub_buf
461}