Skip to content

Commit 9eb2cdf

Browse files
committed
Implement raw dylib for macos
1 parent 817b6d1 commit 9eb2cdf

File tree

5 files changed

+168
-15
lines changed

5 files changed

+168
-15
lines changed

compiler/rustc_codegen_llvm/src/back/archive.rs

+81-12
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::ptr;
99
use std::str;
1010

1111
use crate::llvm::archive_ro::{ArchiveRO, Child};
12-
use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport};
12+
use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport, LLVMRustTbdExport};
1313
use rustc_codegen_ssa::back::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
1414
use rustc_session::cstore::{DllCallingConvention, DllImport};
1515
use rustc_session::Session;
@@ -104,33 +104,102 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
104104
dll_imports: &[DllImport],
105105
tmpdir: &Path,
106106
) -> PathBuf {
107+
let target = &sess.target;
108+
let windows_toolchain = target.vendor == "pc" && target.os == "windows";
109+
let mingw_gnu_toolchain = windows_toolchain && target.env == "gnu" && target.abi.is_empty();
110+
let apple_toolchain = target.is_like_osx;
111+
107112
let output_path = {
108-
let mut output_path: PathBuf = tmpdir.to_path_buf();
109-
output_path.push(format!("{}_imports", lib_name));
110-
output_path.with_extension("lib")
113+
let mut filename = lib_name.replace('/', "_");
114+
if apple_toolchain {
115+
filename.push_str("tbd");
116+
} else {
117+
filename.push_str("lib");
118+
}
119+
tmpdir.join(filename)
111120
};
112121

113-
let target = &sess.target;
114-
let mingw_gnu_toolchain = target.vendor == "pc"
115-
&& target.os == "windows"
116-
&& target.env == "gnu"
117-
&& target.abi.is_empty();
118-
119122
let import_name_and_ordinal_vector: Vec<(String, Option<u16>)> = dll_imports
120123
.iter()
121124
.map(|import: &DllImport| {
122-
if sess.target.arch == "x86" {
125+
if sess.target.arch == "x86" && windows_toolchain {
123126
(
124127
LlvmArchiveBuilder::i686_decorated_name(import, mingw_gnu_toolchain),
125128
import.ordinal,
126129
)
130+
} else if apple_toolchain {
131+
(format!("_{}", import.name), import.ordinal)
127132
} else {
128133
(import.name.to_string(), import.ordinal)
129134
}
130135
})
131136
.collect();
132137

133-
if mingw_gnu_toolchain {
138+
if apple_toolchain {
139+
// we've checked for \0 characters in the library name already
140+
let dll_name_z = CString::new(lib_name).unwrap();
141+
142+
let output_path_z = rustc_fs_util::path_to_c_string(&output_path);
143+
144+
tracing::info!("invoking LLVMRustWriteTbdFile");
145+
tracing::info!(" dll_name {:#?}", dll_name_z);
146+
tracing::info!(" output_path {}", output_path.display());
147+
tracing::info!(
148+
" import names: {}",
149+
dll_imports
150+
.iter()
151+
.map(|import| import.name.to_string())
152+
.collect::<Vec<_>>()
153+
.join(", "),
154+
);
155+
156+
let cstring_import_name_vector: Vec<CString> = import_name_and_ordinal_vector
157+
.into_iter()
158+
.map(|(name, _ordinal)| CString::new(name).unwrap())
159+
.collect();
160+
161+
let ffi_exports: Vec<LLVMRustTbdExport> = cstring_import_name_vector
162+
.iter()
163+
.map(|name_z| LLVMRustTbdExport::new(name_z.as_ptr(), 0))
164+
.collect();
165+
166+
// Get LLVM architecture from the target triple.
167+
let arch = sess.target.llvm_target.split("-").nth(0).unwrap();
168+
169+
let plat = match (&*target.options.os, &*target.options.abi) {
170+
("macos", "") => "macos",
171+
("ios", "") => "ios",
172+
("tvos", "") => "tvos",
173+
("watchos", "") => "watchos",
174+
("macos", "macabi") => "maccatalyst",
175+
("ios", "sim") => "ios-simulator",
176+
("tvos", "sim") => "tvos-simulator",
177+
("watchos", "sim") => "watchos-simulator",
178+
_ => "unknown",
179+
};
180+
181+
let target = CString::new(format!("{}-{}", arch, plat)).unwrap();
182+
let ffi_targets = &[target.as_ptr()];
183+
184+
let result = unsafe {
185+
crate::llvm::LLVMRustWriteTbdFile(
186+
dll_name_z.as_ptr(),
187+
output_path_z.as_ptr(),
188+
ffi_exports.as_ptr(),
189+
ffi_exports.len(),
190+
ffi_targets.as_ptr(),
191+
ffi_targets.len(),
192+
)
193+
};
194+
195+
if result == crate::llvm::LLVMRustResult::Failure {
196+
sess.fatal(&format!(
197+
"Error creating apple import library for {}: {}",
198+
lib_name,
199+
llvm::last_error().unwrap_or("unknown LLVM error".to_string())
200+
));
201+
}
202+
} else if mingw_gnu_toolchain {
134203
// The binutils linker used on -windows-gnu targets cannot read the import
135204
// libraries generated by LLVM: in our attempts, the linker produced an .EXE
136205
// that loaded but crashed with an AV upon calling one of the imported

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+22
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@ impl LLVMRustCOFFShortExport {
4949
}
5050
}
5151

52+
// Rust version of the C struct with the same name in rustc_llvm/llvm-wrapper/RustWrapper.cpp.
53+
#[repr(C)]
54+
pub struct LLVMRustTbdExport {
55+
pub name: *const c_char,
56+
pub symbol_flags: u8,
57+
}
58+
59+
impl LLVMRustTbdExport {
60+
pub fn new(name: *const c_char, symbol_flags: u8) -> LLVMRustTbdExport {
61+
LLVMRustTbdExport { name, symbol_flags }
62+
}
63+
}
64+
5265
/// Translation of LLVM's MachineTypes enum, defined in llvm\include\llvm\BinaryFormat\COFF.h.
5366
///
5467
/// We include only architectures supported on Windows.
@@ -2552,4 +2565,13 @@ extern "C" {
25522565
pub fn LLVMRustGetMangledName(V: &Value, out: &RustString);
25532566

25542567
pub fn LLVMRustGetElementTypeArgIndex(CallSite: &Value) -> i32;
2568+
2569+
pub fn LLVMRustWriteTbdFile(
2570+
ImportName: *const c_char,
2571+
Path: *const c_char,
2572+
Exports: *const LLVMRustTbdExport,
2573+
NumExports: usize,
2574+
LlvmTriples: *const *const c_char,
2575+
NumLlvmTriples: usize,
2576+
) -> LLVMRustResult;
25552577
}

compiler/rustc_codegen_ssa/src/back/link.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,13 @@ fn collate_raw_dylibs(
407407
let all_libs = crate_info.used_libraries.iter().chain(crate_info.native_libraries.values().flatten());
408408
for lib in all_libs {
409409
if lib.kind == NativeLibKind::RawDylib {
410-
let ext = if matches!(lib.verbatim, Some(true)) { "" } else { ".dll" };
410+
let ext = if matches!(lib.verbatim, Some(true)) {
411+
""
412+
} else if sess.target.is_like_osx {
413+
".dylib"
414+
} else {
415+
".dll"
416+
};
411417
let name = format!("{}{}", lib.name.expect("unnamed raw-dylib library"), ext);
412418
let imports = dylib_table.entry(name.clone()).or_default();
413419
for import in &lib.dll_imports {

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

+56
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include "llvm/Bitcode/BitcodeWriterPass.h"
1616
#include "llvm/Support/Signals.h"
1717
#include "llvm/ADT/Optional.h"
18+
#include "llvm/TextAPI/InterfaceFile.h"
19+
#include "llvm/TextAPI/TextAPIWriter.h"
1820

1921
#include <iostream>
2022

@@ -1959,3 +1961,57 @@ extern "C" int32_t LLVMRustGetElementTypeArgIndex(LLVMValueRef CallSite) {
19591961
#endif
19601962
return -1;
19611963
}
1964+
1965+
1966+
// This struct contains all necessary info about a symbol exported from a tbd.
1967+
struct LLVMRustTbdExport {
1968+
const char* name;
1969+
uint8_t symbol_flags;
1970+
};
1971+
1972+
extern "C" LLVMRustResult LLVMRustWriteTbdFile(
1973+
const char* ImportName,
1974+
const char* Path,
1975+
const LLVMRustTbdExport* Exports,
1976+
size_t NumExports,
1977+
const char** LlvmTriples,
1978+
size_t NumLlvmTriples)
1979+
{
1980+
llvm::MachO::InterfaceFile tbd;
1981+
1982+
tbd.setFileType(llvm::MachO::FileType::TBD_V4);
1983+
tbd.setTwoLevelNamespace();
1984+
1985+
llvm::MachO::TargetList targets;
1986+
for (size_t i = 0; i < NumLlvmTriples; ++i) {
1987+
printf("Target is %s\n", LlvmTriples[i]);
1988+
auto target = llvm::MachO::Target::create(LlvmTriples[i]);
1989+
if (!target) {
1990+
return LLVMRustResult::Failure;
1991+
}
1992+
targets.emplace_back(target.get());
1993+
}
1994+
1995+
for (auto target = targets.begin(); target != targets.end(); ++target) {
1996+
tbd.addTarget(*target);
1997+
}
1998+
1999+
tbd.setInstallName(StringRef(ImportName));
2000+
2001+
for (size_t i = 0; i < NumExports; ++i) {
2002+
auto symbol_kind = llvm::MachO::SymbolKind::GlobalSymbol;
2003+
auto flags = static_cast<llvm::MachO::SymbolFlags>(Exports[i].symbol_flags);
2004+
tbd.addSymbol(symbol_kind, StringRef(Exports[i].name), targets, flags);
2005+
}
2006+
2007+
std::error_code ec;
2008+
llvm::raw_fd_ostream fd(Path, ec);
2009+
if (ec) {
2010+
return LLVMRustResult::Failure;
2011+
}
2012+
llvm::Error err = llvm::MachO::TextAPIWriter::writeToStream(fd, tbd);
2013+
if (err) {
2014+
return LLVMRustResult::Failure;
2015+
}
2016+
return LLVMRustResult::Success;
2017+
}

compiler/rustc_metadata/src/native_libs.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,12 @@ impl<'tcx> Collector<'tcx> {
111111
NativeLibKind::Framework { as_needed: None }
112112
}
113113
"raw-dylib" => {
114-
if !sess.target.is_like_windows {
114+
if !sess.target.is_like_windows && !sess.target.is_like_osx {
115115
struct_span_err!(
116116
sess,
117117
span,
118118
E0455,
119-
"link kind `raw-dylib` is only supported on Windows targets"
119+
"link kind `raw-dylib` is only supported on Windows and Apple targets"
120120
)
121121
.emit();
122122
} else if !features.raw_dylib {

0 commit comments

Comments
 (0)