rustc_codegen_llvm/back/
archive.rs
1use std::ffi::{CStr, CString, c_char, c_void};
4use std::path::{Path, PathBuf};
5use std::{io, mem, ptr, str};
6
7use rustc_codegen_ssa::back::archive::{
8 ArArchiveBuilder, ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder,
9 DEFAULT_OBJECT_READER, ObjectReader, UnknownArchiveKind, try_extract_macho_fat_archive,
10};
11use rustc_session::Session;
12
13use crate::llvm::archive_ro::{ArchiveRO, Child};
14use crate::llvm::{self, ArchiveKind, last_error};
15
16#[must_use = "must call build() to finish building the archive"]
18pub(crate) struct LlvmArchiveBuilder<'a> {
19 sess: &'a Session,
20 additions: Vec<Addition>,
21}
22
23enum Addition {
24 File { path: PathBuf, name_in_archive: String },
25 Archive { path: PathBuf, archive: ArchiveRO, skip: Box<dyn FnMut(&str) -> bool> },
26}
27
28impl Addition {
29 fn path(&self) -> &Path {
30 match self {
31 Addition::File { path, .. } | Addition::Archive { path, .. } => path,
32 }
33 }
34}
35
36fn is_relevant_child(c: &Child<'_>) -> bool {
37 match c.name() {
38 Some(name) => !name.contains("SYMDEF"),
39 None => false,
40 }
41}
42
43impl<'a> ArchiveBuilder for LlvmArchiveBuilder<'a> {
44 fn add_archive(
45 &mut self,
46 archive: &Path,
47 skip: Box<dyn FnMut(&str) -> bool + 'static>,
48 ) -> io::Result<()> {
49 let mut archive = archive.to_path_buf();
50 if self.sess.target.llvm_target.contains("-apple-macosx") {
51 if let Some(new_archive) = try_extract_macho_fat_archive(self.sess, &archive)? {
52 archive = new_archive
53 }
54 }
55 let archive_ro = match ArchiveRO::open(&archive) {
56 Ok(ar) => ar,
57 Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
58 };
59 if self.additions.iter().any(|ar| ar.path() == archive) {
60 return Ok(());
61 }
62 self.additions.push(Addition::Archive {
63 path: archive,
64 archive: archive_ro,
65 skip: Box::new(skip),
66 });
67 Ok(())
68 }
69
70 fn add_file(&mut self, file: &Path) {
72 let name = file.file_name().unwrap().to_str().unwrap();
73 self.additions
74 .push(Addition::File { path: file.to_path_buf(), name_in_archive: name.to_owned() });
75 }
76
77 fn build(mut self: Box<Self>, output: &Path) -> bool {
80 match self.build_with_llvm(output) {
81 Ok(any_members) => any_members,
82 Err(error) => {
83 self.sess.dcx().emit_fatal(ArchiveBuildFailure { path: output.to_owned(), error })
84 }
85 }
86 }
87}
88
89pub(crate) struct LlvmArchiveBuilderBuilder;
90
91impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
92 fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> {
93 if false {
98 Box::new(LlvmArchiveBuilder { sess, additions: Vec::new() })
99 } else {
100 Box::new(ArArchiveBuilder::new(sess, &LLVM_OBJECT_READER))
101 }
102 }
103}
104
105static LLVM_OBJECT_READER: ObjectReader = ObjectReader {
109 get_symbols: get_llvm_object_symbols,
110 is_64_bit_object_file: llvm_is_64_bit_object_file,
111 is_ec_object_file: llvm_is_ec_object_file,
112 get_xcoff_member_alignment: DEFAULT_OBJECT_READER.get_xcoff_member_alignment,
113};
114
115#[deny(unsafe_op_in_unsafe_fn)]
116fn get_llvm_object_symbols(
117 buf: &[u8],
118 f: &mut dyn FnMut(&[u8]) -> io::Result<()>,
119) -> io::Result<bool> {
120 let mut state = Box::new(f);
121
122 let err = unsafe {
123 llvm::LLVMRustGetSymbols(
124 buf.as_ptr(),
125 buf.len(),
126 (&raw mut *state) as *mut c_void,
127 callback,
128 error_callback,
129 )
130 };
131
132 if err.is_null() {
133 return Ok(true);
134 } else {
135 return Err(unsafe { *Box::from_raw(err as *mut io::Error) });
136 }
137
138 unsafe extern "C" fn callback(state: *mut c_void, symbol_name: *const c_char) -> *mut c_void {
139 let f = unsafe { &mut *(state as *mut &mut dyn FnMut(&[u8]) -> io::Result<()>) };
140 match f(unsafe { CStr::from_ptr(symbol_name) }.to_bytes()) {
141 Ok(()) => std::ptr::null_mut(),
142 Err(err) => Box::into_raw(Box::new(err)) as *mut c_void,
143 }
144 }
145
146 unsafe extern "C" fn error_callback(error: *const c_char) -> *mut c_void {
147 let error = unsafe { CStr::from_ptr(error) };
148 Box::into_raw(Box::new(io::Error::new(
149 io::ErrorKind::Other,
150 format!("LLVM error: {}", error.to_string_lossy()),
151 ))) as *mut c_void
152 }
153}
154
155fn llvm_is_64_bit_object_file(buf: &[u8]) -> bool {
156 unsafe { llvm::LLVMRustIs64BitSymbolicFile(buf.as_ptr(), buf.len()) }
157}
158
159fn llvm_is_ec_object_file(buf: &[u8]) -> bool {
160 unsafe { llvm::LLVMRustIsECObject(buf.as_ptr(), buf.len()) }
161}
162
163impl<'a> LlvmArchiveBuilder<'a> {
164 fn build_with_llvm(&mut self, output: &Path) -> io::Result<bool> {
165 let kind = &*self.sess.target.archive_format;
166 let kind = kind
167 .parse::<ArchiveKind>()
168 .map_err(|_| kind)
169 .unwrap_or_else(|kind| self.sess.dcx().emit_fatal(UnknownArchiveKind { kind }));
170
171 let mut additions = mem::take(&mut self.additions);
172 let mut strings = Vec::new();
175 let mut members = Vec::new();
176
177 let dst = CString::new(output.to_str().unwrap())?;
178
179 unsafe {
180 for addition in &mut additions {
181 match addition {
182 Addition::File { path, name_in_archive } => {
183 let path = CString::new(path.to_str().unwrap())?;
184 let name = CString::new(name_in_archive.as_bytes())?;
185 members.push(llvm::LLVMRustArchiveMemberNew(
186 path.as_ptr(),
187 name.as_ptr(),
188 None,
189 ));
190 strings.push(path);
191 strings.push(name);
192 }
193 Addition::Archive { archive, skip, .. } => {
194 for child in archive.iter() {
195 let child = child.map_err(string_to_io_error)?;
196 if !is_relevant_child(&child) {
197 continue;
198 }
199 let child_name = child.name().unwrap();
200 if skip(child_name) {
201 continue;
202 }
203
204 let child_name =
211 Path::new(child_name).file_name().unwrap().to_str().unwrap();
212 let name = CString::new(child_name)?;
213 let m = llvm::LLVMRustArchiveMemberNew(
214 ptr::null(),
215 name.as_ptr(),
216 Some(child.raw),
217 );
218 members.push(m);
219 strings.push(name);
220 }
221 }
222 }
223 }
224
225 let r = llvm::LLVMRustWriteArchive(
226 dst.as_ptr(),
227 members.len() as libc::size_t,
228 members.as_ptr() as *const &_,
229 true,
230 kind,
231 self.sess.target.arch == "arm64ec",
232 );
233 let ret = if r.into_result().is_err() {
234 let msg = last_error().unwrap_or_else(|| "failed to write archive".into());
235 Err(io::Error::new(io::ErrorKind::Other, msg))
236 } else {
237 Ok(!members.is_empty())
238 };
239 for member in members {
240 llvm::LLVMRustArchiveMemberFree(member);
241 }
242 ret
243 }
244 }
245}
246
247fn string_to_io_error(s: String) -> io::Error {
248 io::Error::new(io::ErrorKind::Other, format!("bad archive: {s}"))
249}