Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP]: raw-dylib support for apple targets #100703

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 81 additions & 12 deletions compiler/rustc_codegen_llvm/src/back/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::ptr;
use std::str;

use crate::llvm::archive_ro::{ArchiveRO, Child};
use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport};
use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport, LLVMRustTbdExport};
use rustc_codegen_ssa::back::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
use rustc_session::cstore::{DllCallingConvention, DllImport};
use rustc_session::Session;
Expand Down Expand Up @@ -104,33 +104,102 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
dll_imports: &[DllImport],
tmpdir: &Path,
) -> PathBuf {
let target = &sess.target;
let windows_toolchain = target.vendor == "pc" && target.os == "windows";
let mingw_gnu_toolchain = windows_toolchain && target.env == "gnu" && target.abi.is_empty();
let apple_toolchain = target.is_like_osx;

let output_path = {
let mut output_path: PathBuf = tmpdir.to_path_buf();
output_path.push(format!("{}_imports", lib_name));
output_path.with_extension("lib")
let mut filename = lib_name.replace('/', "_");
if apple_toolchain {
filename.push_str("tbd");
} else {
filename.push_str("lib");
}
tmpdir.join(filename)
};

let target = &sess.target;
let mingw_gnu_toolchain = target.vendor == "pc"
&& target.os == "windows"
&& target.env == "gnu"
&& target.abi.is_empty();

let import_name_and_ordinal_vector: Vec<(String, Option<u16>)> = dll_imports
.iter()
.map(|import: &DllImport| {
if sess.target.arch == "x86" {
if sess.target.arch == "x86" && windows_toolchain {
(
LlvmArchiveBuilder::i686_decorated_name(import, mingw_gnu_toolchain),
import.ordinal,
)
} else if apple_toolchain {
(format!("_{}", import.name), import.ordinal)
} else {
(import.name.to_string(), import.ordinal)
}
})
.collect();

if mingw_gnu_toolchain {
if apple_toolchain {
// we've checked for \0 characters in the library name already
let dll_name_z = CString::new(lib_name).unwrap();

let output_path_z = rustc_fs_util::path_to_c_string(&output_path);

tracing::info!("invoking LLVMRustWriteTbdFile");
tracing::info!(" dll_name {:#?}", dll_name_z);
tracing::info!(" output_path {}", output_path.display());
tracing::info!(
" import names: {}",
dll_imports
.iter()
.map(|import| import.name.to_string())
.collect::<Vec<_>>()
.join(", "),
);

let cstring_import_name_vector: Vec<CString> = import_name_and_ordinal_vector
.into_iter()
.map(|(name, _ordinal)| CString::new(name).unwrap())
.collect();

let ffi_exports: Vec<LLVMRustTbdExport> = cstring_import_name_vector
.iter()
.map(|name_z| LLVMRustTbdExport::new(name_z.as_ptr(), 0))
.collect();

// Get LLVM architecture from the target triple.
let arch = sess.target.llvm_target.split("-").nth(0).unwrap();

let plat = match (&*target.options.os, &*target.options.abi) {
("macos", "") => "macos",
("ios", "") => "ios",
("tvos", "") => "tvos",
("watchos", "") => "watchos",
("macos", "macabi") => "maccatalyst",
("ios", "sim") => "ios-simulator",
("tvos", "sim") => "tvos-simulator",
("watchos", "sim") => "watchos-simulator",
_ => "unknown",
};

let target = CString::new(format!("{}-{}", arch, plat)).unwrap();
let ffi_targets = &[target.as_ptr()];

let result = unsafe {
crate::llvm::LLVMRustWriteTbdFile(
dll_name_z.as_ptr(),
output_path_z.as_ptr(),
ffi_exports.as_ptr(),
ffi_exports.len(),
ffi_targets.as_ptr(),
ffi_targets.len(),
)
};

if result == crate::llvm::LLVMRustResult::Failure {
sess.fatal(&format!(
"Error creating apple import library for {}: {}",
lib_name,
llvm::last_error().unwrap_or("unknown LLVM error".to_string())
));
}
} else if mingw_gnu_toolchain {
// The binutils linker used on -windows-gnu targets cannot read the import
// libraries generated by LLVM: in our attempts, the linker produced an .EXE
// that loaded but crashed with an AV upon calling one of the imported
Expand Down
22 changes: 22 additions & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,19 @@ impl LLVMRustCOFFShortExport {
}
}

// Rust version of the C struct with the same name in rustc_llvm/llvm-wrapper/RustWrapper.cpp.
#[repr(C)]
pub struct LLVMRustTbdExport {
pub name: *const c_char,
pub symbol_flags: u8,
}

impl LLVMRustTbdExport {
pub fn new(name: *const c_char, symbol_flags: u8) -> LLVMRustTbdExport {
LLVMRustTbdExport { name, symbol_flags }
}
}

/// Translation of LLVM's MachineTypes enum, defined in llvm\include\llvm\BinaryFormat\COFF.h.
///
/// We include only architectures supported on Windows.
Expand Down Expand Up @@ -2552,4 +2565,13 @@ extern "C" {
pub fn LLVMRustGetMangledName(V: &Value, out: &RustString);

pub fn LLVMRustGetElementTypeArgIndex(CallSite: &Value) -> i32;

pub fn LLVMRustWriteTbdFile(
ImportName: *const c_char,
Path: *const c_char,
Exports: *const LLVMRustTbdExport,
NumExports: usize,
LlvmTriples: *const *const c_char,
NumLlvmTriples: usize,
) -> LLVMRustResult;
}
30 changes: 11 additions & 19 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,21 +359,6 @@ fn link_rlib<'a>(
}
}

for (raw_dylib_name, raw_dylib_imports) in
collate_raw_dylibs(sess, &codegen_results.crate_info.used_libraries)?
{
let output_path = archive_builder_builder.create_dll_import_lib(
sess,
&raw_dylib_name,
&raw_dylib_imports,
tmpdir.as_ref(),
);

ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|e| {
sess.fatal(&format!("failed to add native library {}: {}", output_path.display(), e));
});
}

if let Some(trailing_metadata) = trailing_metadata {
// Note that it is important that we add all of our non-object "magical
// files" *after* all of the object files in the archive. The reason for
Expand Down Expand Up @@ -414,14 +399,21 @@ fn link_rlib<'a>(
/// collate the symbols together by library name before generating the import libraries.
fn collate_raw_dylibs(
sess: &Session,
used_libraries: &[NativeLib],
crate_info: &CrateInfo,
) -> Result<Vec<(String, Vec<DllImport>)>, ErrorGuaranteed> {
// Use index maps to preserve original order of imports and libraries.
let mut dylib_table = FxIndexMap::<String, FxIndexMap<Symbol, &DllImport>>::default();

for lib in used_libraries {
let all_libs = crate_info.used_libraries.iter().chain(crate_info.native_libraries.values().flatten());
for lib in all_libs {
if lib.kind == NativeLibKind::RawDylib {
let ext = if matches!(lib.verbatim, Some(true)) { "" } else { ".dll" };
let ext = if matches!(lib.verbatim, Some(true)) {
""
} else if sess.target.is_like_osx {
".dylib"
} else {
".dll"
};
let name = format!("{}{}", lib.name.expect("unnamed raw-dylib library"), ext);
let imports = dylib_table.entry(name.clone()).or_default();
for import in &lib.dll_imports {
Expand Down Expand Up @@ -2008,7 +2000,7 @@ fn linker_with_args<'a>(

// Link with the import library generated for any raw-dylib functions.
for (raw_dylib_name, raw_dylib_imports) in
collate_raw_dylibs(sess, &codegen_results.crate_info.used_libraries)?
collate_raw_dylibs(sess, &codegen_results.crate_info)?
{
cmd.add_object(&archive_builder_builder.create_dll_import_lib(
sess,
Expand Down
56 changes: 56 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include "llvm/Bitcode/BitcodeWriterPass.h"
#include "llvm/Support/Signals.h"
#include "llvm/ADT/Optional.h"
#include "llvm/TextAPI/InterfaceFile.h"
#include "llvm/TextAPI/TextAPIWriter.h"

#include <iostream>

Expand Down Expand Up @@ -1959,3 +1961,57 @@ extern "C" int32_t LLVMRustGetElementTypeArgIndex(LLVMValueRef CallSite) {
#endif
return -1;
}


// This struct contains all necessary info about a symbol exported from a tbd.
struct LLVMRustTbdExport {
const char* name;
uint8_t symbol_flags;
};

extern "C" LLVMRustResult LLVMRustWriteTbdFile(
const char* ImportName,
const char* Path,
const LLVMRustTbdExport* Exports,
size_t NumExports,
const char** LlvmTriples,
size_t NumLlvmTriples)
{
llvm::MachO::InterfaceFile tbd;

tbd.setFileType(llvm::MachO::FileType::TBD_V4);
tbd.setTwoLevelNamespace();

llvm::MachO::TargetList targets;
for (size_t i = 0; i < NumLlvmTriples; ++i) {
printf("Target is %s\n", LlvmTriples[i]);
auto target = llvm::MachO::Target::create(LlvmTriples[i]);
if (!target) {
return LLVMRustResult::Failure;
}
targets.emplace_back(target.get());
}

for (auto target = targets.begin(); target != targets.end(); ++target) {
tbd.addTarget(*target);
}

tbd.setInstallName(StringRef(ImportName));

for (size_t i = 0; i < NumExports; ++i) {
auto symbol_kind = llvm::MachO::SymbolKind::GlobalSymbol;
auto flags = static_cast<llvm::MachO::SymbolFlags>(Exports[i].symbol_flags);
tbd.addSymbol(symbol_kind, StringRef(Exports[i].name), targets, flags);
}

std::error_code ec;
llvm::raw_fd_ostream fd(Path, ec);
if (ec) {
return LLVMRustResult::Failure;
}
llvm::Error err = llvm::MachO::TextAPIWriter::writeToStream(fd, tbd);
if (err) {
return LLVMRustResult::Failure;
}
return LLVMRustResult::Success;
}
4 changes: 2 additions & 2 deletions compiler/rustc_metadata/src/native_libs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,12 @@ impl<'tcx> Collector<'tcx> {
NativeLibKind::Framework { as_needed: None }
}
"raw-dylib" => {
if !sess.target.is_like_windows {
if !sess.target.is_like_windows && !sess.target.is_like_osx {
struct_span_err!(
sess,
span,
E0455,
"link kind `raw-dylib` is only supported on Windows targets"
"link kind `raw-dylib` is only supported on Windows and Apple targets"
)
.emit();
} else if !features.raw_dylib {
Expand Down