|
1 | 1 | //! A helper class for dealing with static archives
|
2 | 2 |
|
3 |
| -use std::ffi::{CStr, CString}; |
| 3 | +use std::env; |
| 4 | +use std::ffi::{CStr, CString, OsString}; |
4 | 5 | use std::io;
|
5 | 6 | use std::mem;
|
6 | 7 | use std::path::{Path, PathBuf};
|
7 | 8 | use std::ptr;
|
8 | 9 | use std::str;
|
9 | 10 |
|
10 | 11 | use crate::llvm::archive_ro::{ArchiveRO, Child};
|
11 |
| -use crate::llvm::{self, ArchiveKind, LLVMMachineType}; |
| 12 | +use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport}; |
12 | 13 | use rustc_codegen_ssa::back::archive::ArchiveBuilder;
|
13 | 14 | use rustc_data_structures::temp_dir::MaybeTempDir;
|
14 | 15 | use rustc_middle::middle::cstore::{DllCallingConvention, DllImport};
|
@@ -54,7 +55,6 @@ fn archive_config<'a>(sess: &'a Session, output: &Path, input: Option<&Path>) ->
|
54 | 55 | ArchiveConfig { sess, dst: output.to_path_buf(), src: input.map(|p| p.to_path_buf()) }
|
55 | 56 | }
|
56 | 57 |
|
57 |
| -#[allow(dead_code)] |
58 | 58 | /// Map machine type strings to values of LLVM's MachineTypes enum.
|
59 | 59 | fn llvm_machine_type(cpu: &str) -> LLVMMachineType {
|
60 | 60 | match cpu {
|
@@ -153,47 +153,107 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
|
153 | 153 | dll_imports: &[DllImport],
|
154 | 154 | tmpdir: &MaybeTempDir,
|
155 | 155 | ) {
|
156 |
| - let mut def_file_path = tmpdir.as_ref().to_path_buf(); |
157 |
| - def_file_path.push(format!("{}_imports", lib_name)); |
158 |
| - def_file_path.with_extension("def"); |
159 |
| - let mut output_path: PathBuf = tmpdir.as_ref().to_path_buf(); |
160 |
| - output_path.push(format!("{}_imports", lib_name)); |
161 |
| - output_path.with_extension("dll.a"); |
162 |
| - |
163 |
| - let import_name_vector: Vec<String> = dll_imports |
| 156 | + let output_path = { |
| 157 | + let mut output_path: PathBuf = tmpdir.as_ref().to_path_buf(); |
| 158 | + output_path.push(format!("{}_imports", lib_name)); |
| 159 | + output_path.with_extension("lib") |
| 160 | + }; |
| 161 | + |
| 162 | + // BFD doesn't work properly with short imports |
| 163 | + let mingw_gnu_toolchain = self.config.sess.target.llvm_target.ends_with("pc-windows-gnu"); |
| 164 | + |
| 165 | + // All import names are Rust identifiers and therefore cannot contain \0 characters. |
| 166 | + // FIXME: when support for #[link_name] implemented, ensure that import.name values don't |
| 167 | + // have any \0 characters |
| 168 | + let import_name_vector: Vec<CString> = dll_imports |
164 | 169 | .iter()
|
165 | 170 | .map(|import: &DllImport| {
|
166 | 171 | if self.config.sess.target.arch == "x86" {
|
167 |
| - LlvmArchiveBuilder::i686_decorated_name(import, true).into_string().unwrap() |
| 172 | + LlvmArchiveBuilder::i686_decorated_name(import, mingw_gnu_toolchain) |
168 | 173 | } else {
|
169 |
| - import.name.to_string() |
| 174 | + CString::new(import.name.to_string()).unwrap() |
170 | 175 | }
|
171 | 176 | })
|
172 | 177 | .collect();
|
173 | 178 |
|
174 |
| - let def_file_content = format!("EXPORTS\n{}", &import_name_vector.join("\n")); |
175 |
| - std::fs::write(&def_file_path, &def_file_content).unwrap(); |
176 |
| - std::fs::copy(&def_file_path, "d:/imports.def").unwrap(); |
177 |
| - |
178 |
| - let result = std::process::Command::new("dlltool") |
179 |
| - .args([ |
180 |
| - "-d", |
181 |
| - def_file_path.to_str().unwrap(), |
182 |
| - "-D", |
183 |
| - lib_name, |
184 |
| - "-l", |
185 |
| - output_path.to_str().unwrap(), |
186 |
| - ]) |
187 |
| - .status(); |
188 |
| - |
189 |
| - if let Err(e) = result { |
190 |
| - self.config.sess.fatal(&format!( |
191 |
| - "Error creating import library for {}: {}", |
192 |
| - lib_name, |
193 |
| - e.to_string() |
194 |
| - )); |
195 |
| - } |
196 |
| - std::fs::copy(&output_path, "d:/imports.dll.a").unwrap(); |
| 179 | + if mingw_gnu_toolchain { |
| 180 | + let mut def_file_path = tmpdir.as_ref().to_path_buf(); |
| 181 | + def_file_path.push(format!("{}_imports", lib_name)); |
| 182 | + def_file_path.with_extension("def"); |
| 183 | + |
| 184 | + let def_file_content = format!( |
| 185 | + "EXPORTS\n{}", |
| 186 | + import_name_vector |
| 187 | + .iter() |
| 188 | + .map(|cstring| cstring.to_str().unwrap()) |
| 189 | + .intersperse("\n") |
| 190 | + .collect::<String>() |
| 191 | + ); |
| 192 | + std::fs::write(&def_file_path, def_file_content).unwrap(); |
| 193 | + |
| 194 | + let dlltool = find_dlltool(self.config.sess); |
| 195 | + let result = std::process::Command::new(dlltool) |
| 196 | + .args([ |
| 197 | + "-d", |
| 198 | + def_file_path.to_str().unwrap(), |
| 199 | + "-D", |
| 200 | + lib_name, |
| 201 | + "-l", |
| 202 | + output_path.to_str().unwrap(), |
| 203 | + ]) |
| 204 | + .output(); |
| 205 | + |
| 206 | + match result { |
| 207 | + Err(e) => { |
| 208 | + self.config.sess.fatal(&format!("Error calling dlltool: {}", e.to_string())) |
| 209 | + } |
| 210 | + Ok(output) if !output.status.success() => self.config.sess.fatal(&format!( |
| 211 | + "Dlltool could not create import library: {}\n{}", |
| 212 | + String::from_utf8_lossy(&output.stdout), |
| 213 | + String::from_utf8_lossy(&output.stderr) |
| 214 | + )), |
| 215 | + _ => {} |
| 216 | + } |
| 217 | + } else { |
| 218 | + // we've checked for \0 characters in the library name already |
| 219 | + let dll_name_z = CString::new(lib_name).unwrap(); |
| 220 | + let output_path_z = rustc_fs_util::path_to_c_string(&output_path); |
| 221 | + |
| 222 | + tracing::trace!("invoking LLVMRustWriteImportLibrary"); |
| 223 | + tracing::trace!(" dll_name {:#?}", dll_name_z); |
| 224 | + tracing::trace!(" output_path {}", output_path.display()); |
| 225 | + tracing::trace!( |
| 226 | + " import names: {}", |
| 227 | + dll_imports |
| 228 | + .iter() |
| 229 | + .map(|import| import.name.to_string()) |
| 230 | + .collect::<Vec<_>>() |
| 231 | + .join(", "), |
| 232 | + ); |
| 233 | + |
| 234 | + let ffi_exports: Vec<LLVMRustCOFFShortExport> = import_name_vector |
| 235 | + .iter() |
| 236 | + .map(|name_z| LLVMRustCOFFShortExport::from_name(name_z.as_ptr())) |
| 237 | + .collect(); |
| 238 | + let result = unsafe { |
| 239 | + crate::llvm::LLVMRustWriteImportLibrary( |
| 240 | + dll_name_z.as_ptr(), |
| 241 | + output_path_z.as_ptr(), |
| 242 | + ffi_exports.as_ptr(), |
| 243 | + ffi_exports.len(), |
| 244 | + llvm_machine_type(&self.config.sess.target.arch) as u16, |
| 245 | + !self.config.sess.target.is_like_msvc, |
| 246 | + ) |
| 247 | + }; |
| 248 | + |
| 249 | + if result == crate::llvm::LLVMRustResult::Failure { |
| 250 | + self.config.sess.fatal(&format!( |
| 251 | + "Error creating import library for {}: {}", |
| 252 | + lib_name, |
| 253 | + llvm::last_error().unwrap_or("unknown LLVM error".to_string()) |
| 254 | + )); |
| 255 | + } |
| 256 | + }; |
197 | 257 |
|
198 | 258 | self.add_archive(&output_path, |_| false).unwrap_or_else(|e| {
|
199 | 259 | self.config.sess.fatal(&format!(
|
@@ -343,3 +403,26 @@ impl<'a> LlvmArchiveBuilder<'a> {
|
343 | 403 | fn string_to_io_error(s: String) -> io::Error {
|
344 | 404 | io::Error::new(io::ErrorKind::Other, format!("bad archive: {}", s))
|
345 | 405 | }
|
| 406 | + |
| 407 | +fn find_dlltool(sess: &Session) -> OsString { |
| 408 | + // When cross-compiling first try binary prefixed with target triple |
| 409 | + if sess.host.llvm_target != sess.target.llvm_target { |
| 410 | + let prefixed_dlltool = if sess.target.arch == "x86" { |
| 411 | + "i686-w64-mingw32-dlltool" |
| 412 | + } else { |
| 413 | + "x86_64-w64-mingw32-dlltool" |
| 414 | + }; |
| 415 | + let prefixed_dlltool = if cfg!(windows) { |
| 416 | + [prefixed_dlltool, "exe"].concat() |
| 417 | + } else { |
| 418 | + prefixed_dlltool.to_string() |
| 419 | + }; |
| 420 | + for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { |
| 421 | + let full_path = dir.join(&prefixed_dlltool); |
| 422 | + if full_path.is_file() { |
| 423 | + return full_path.into_os_string(); |
| 424 | + } |
| 425 | + } |
| 426 | + } |
| 427 | + OsString::from("dlltool") |
| 428 | +} |
0 commit comments