mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 15:59:20 +03:00
Merge pull request #4381 from roc-lang/windows-rust-platforms
Windows rust platforms
This commit is contained in:
commit
9bb45f5856
@ -524,36 +524,33 @@ pub fn rebuild_host(
|
||||
let swift_host_src = host_input_path.with_file_name("host.swift");
|
||||
let swift_host_header_src = host_input_path.with_file_name("host.h");
|
||||
|
||||
let os = roc_target::OperatingSystem::from(target.operating_system);
|
||||
let executable_extension = match os {
|
||||
roc_target::OperatingSystem::Windows => "exe",
|
||||
roc_target::OperatingSystem::Unix => "",
|
||||
roc_target::OperatingSystem::Wasi => "",
|
||||
};
|
||||
|
||||
let object_extension = match os {
|
||||
roc_target::OperatingSystem::Windows => "obj",
|
||||
roc_target::OperatingSystem::Unix => "o",
|
||||
roc_target::OperatingSystem::Wasi => "o",
|
||||
};
|
||||
|
||||
let host_dest = if matches!(target.architecture, Architecture::Wasm32) {
|
||||
if matches!(opt_level, OptLevel::Development) {
|
||||
host_input_path.with_file_name("host.o")
|
||||
} else {
|
||||
host_input_path.with_file_name("host.bc")
|
||||
}
|
||||
} else if shared_lib_path.is_some() {
|
||||
host_input_path
|
||||
.with_file_name("dynhost")
|
||||
.with_extension(executable_extension)
|
||||
} else {
|
||||
let os = roc_target::OperatingSystem::from(target.operating_system);
|
||||
|
||||
if shared_lib_path.is_some() {
|
||||
let extension = match os {
|
||||
roc_target::OperatingSystem::Windows => "exe",
|
||||
roc_target::OperatingSystem::Unix => "",
|
||||
roc_target::OperatingSystem::Wasi => "",
|
||||
};
|
||||
|
||||
host_input_path
|
||||
.with_file_name("dynhost")
|
||||
.with_extension(extension)
|
||||
} else {
|
||||
let extension = match os {
|
||||
roc_target::OperatingSystem::Windows => "obj",
|
||||
roc_target::OperatingSystem::Unix => "o",
|
||||
roc_target::OperatingSystem::Wasi => "o",
|
||||
};
|
||||
|
||||
host_input_path
|
||||
.with_file_name("host")
|
||||
.with_extension(extension)
|
||||
}
|
||||
host_input_path
|
||||
.with_file_name("host")
|
||||
.with_extension(object_extension)
|
||||
};
|
||||
|
||||
let env_path = env::var("PATH").unwrap_or_else(|_| "".to_string());
|
||||
@ -656,6 +653,7 @@ pub fn rebuild_host(
|
||||
if matches!(opt_level, OptLevel::Optimize | OptLevel::Size) {
|
||||
command.arg("--release");
|
||||
}
|
||||
|
||||
let source_file = if shared_lib_path.is_some() {
|
||||
command.env("RUSTFLAGS", "-C link-dead-code");
|
||||
command.args(&["--bin", "host"]);
|
||||
@ -664,13 +662,16 @@ pub fn rebuild_host(
|
||||
command.arg("--lib");
|
||||
"src/lib.rs"
|
||||
};
|
||||
|
||||
let output = command.output().unwrap();
|
||||
|
||||
validate_output(source_file, "cargo build", output);
|
||||
|
||||
if shared_lib_path.is_some() {
|
||||
// For surgical linking, just copy the dynamically linked rust app.
|
||||
std::fs::copy(cargo_out_dir.join("host"), &host_dest).unwrap();
|
||||
let mut exe_path = cargo_out_dir.join("host");
|
||||
exe_path.set_extension(executable_extension);
|
||||
std::fs::copy(&exe_path, &host_dest).unwrap();
|
||||
} else {
|
||||
// Cargo hosts depend on a c wrapper for the api. Compile host.c as well.
|
||||
|
||||
|
Binary file not shown.
@ -1634,7 +1634,7 @@ mod tests {
|
||||
let dylib_bytes = crate::generate_dylib::create_dylib_elf64(&names).unwrap();
|
||||
std::fs::write(dir.join("libapp.so"), dylib_bytes).unwrap();
|
||||
|
||||
// now we can compile the host (it uses libapp.obj, hence the order here)
|
||||
// now we can compile the host (it uses libapp.so, hence the order here)
|
||||
let output = std::process::Command::new(&zig)
|
||||
.current_dir(dir)
|
||||
.args(&[
|
||||
|
@ -1,7 +1,7 @@
|
||||
use object::pe;
|
||||
use object::LittleEndian as LE;
|
||||
|
||||
pub(crate) const APP_DLL: &str = "roc-cheaty-lib.dll";
|
||||
pub(crate) const APP_DLL: &str = "libapp.dll";
|
||||
|
||||
fn synthetic_image_export_directory(
|
||||
name: &str,
|
||||
|
@ -5,7 +5,7 @@ use roc_error_macros::internal_error;
|
||||
use roc_mono::ir::OptLevel;
|
||||
use std::cmp::Ordering;
|
||||
use std::mem;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use target_lexicon::Triple;
|
||||
|
||||
mod elf;
|
||||
@ -55,7 +55,7 @@ pub fn build_and_preprocess_host(
|
||||
exported_closure_types: Vec<String>,
|
||||
) {
|
||||
let dummy_lib = if let target_lexicon::OperatingSystem::Windows = target.operating_system {
|
||||
host_input_path.with_file_name("libapp.obj")
|
||||
host_input_path.with_file_name("libapp.dll")
|
||||
} else {
|
||||
host_input_path.with_file_name("libapp.so")
|
||||
};
|
||||
@ -123,15 +123,87 @@ fn make_dummy_dll_symbols(
|
||||
custom_names
|
||||
}
|
||||
|
||||
fn generate_dynamic_lib(target: &Triple, custom_names: &[String], dummy_lib_path: &Path) {
|
||||
if !dummy_lib_is_up_to_date(target, dummy_lib_path, custom_names) {
|
||||
let bytes = crate::generate_dylib::generate(target, custom_names)
|
||||
fn generate_dynamic_lib(target: &Triple, dummy_dll_symbols: &[String], dummy_lib_path: &Path) {
|
||||
if !dummy_lib_is_up_to_date(target, dummy_lib_path, dummy_dll_symbols) {
|
||||
let bytes = crate::generate_dylib::generate(target, dummy_dll_symbols)
|
||||
.unwrap_or_else(|e| internal_error!("{e}"));
|
||||
|
||||
std::fs::write(dummy_lib_path, &bytes).unwrap_or_else(|e| internal_error!("{e}"))
|
||||
std::fs::write(dummy_lib_path, &bytes).unwrap_or_else(|e| internal_error!("{e}"));
|
||||
|
||||
if let target_lexicon::OperatingSystem::Windows = target.operating_system {
|
||||
generate_import_library(dummy_lib_path, dummy_dll_symbols);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_import_library(dummy_lib_path: &Path, custom_names: &[String]) {
|
||||
let def_file_content = generate_def_file(custom_names).expect("write to string never fails");
|
||||
|
||||
let mut def_path = dummy_lib_path.to_owned();
|
||||
def_path.set_extension("def");
|
||||
|
||||
std::fs::write(def_path, def_file_content.as_bytes())
|
||||
.unwrap_or_else(|e| internal_error!("{e}"));
|
||||
|
||||
let mut def_filename = PathBuf::from(generate_dylib::APP_DLL);
|
||||
def_filename.set_extension("def");
|
||||
|
||||
let mut lib_filename = PathBuf::from(generate_dylib::APP_DLL);
|
||||
lib_filename.set_extension("lib");
|
||||
|
||||
let zig = std::env::var("ROC_ZIG").unwrap_or_else(|_| "zig".into());
|
||||
|
||||
// use zig to generate the .lib file. Here is a good description of what is in an import library
|
||||
//
|
||||
// > https://www.codeproject.com/Articles/1253835/The-Structure-of-import-Library-File-lib
|
||||
//
|
||||
// For when we want to do this in-memory in the future. We can also consider using
|
||||
//
|
||||
// > https://github.com/messense/implib-rs
|
||||
let output = std::process::Command::new(&zig)
|
||||
.current_dir(dummy_lib_path.parent().unwrap())
|
||||
.args(&[
|
||||
"dlltool",
|
||||
"-d",
|
||||
def_filename.to_str().unwrap(),
|
||||
"-m",
|
||||
"i386:x86-64",
|
||||
"-D",
|
||||
generate_dylib::APP_DLL,
|
||||
"-l",
|
||||
lib_filename.to_str().unwrap(),
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
if !output.status.success() {
|
||||
use std::io::Write;
|
||||
|
||||
std::io::stdout().write_all(&output.stdout).unwrap();
|
||||
std::io::stderr().write_all(&output.stderr).unwrap();
|
||||
|
||||
panic!("zig dlltool failed");
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_def_file(custom_names: &[String]) -> Result<String, std::fmt::Error> {
|
||||
use std::fmt::Write;
|
||||
|
||||
let mut def_file = String::new();
|
||||
|
||||
writeln!(def_file, "LIBRARY libapp")?;
|
||||
writeln!(def_file, "EXPORTS")?;
|
||||
|
||||
for (i, name) in custom_names.iter().enumerate() {
|
||||
// 1-indexed of course...
|
||||
let index = i + 1;
|
||||
|
||||
writeln!(def_file, " {name} @{index}")?;
|
||||
}
|
||||
|
||||
Ok(def_file)
|
||||
}
|
||||
|
||||
fn object_matches_target<'a>(target: &Triple, object: &object::File<'a, &'a [u8]>) -> bool {
|
||||
use target_lexicon::{Architecture as TLA, OperatingSystem as TLO};
|
||||
|
||||
|
@ -55,7 +55,7 @@ struct PeMetadata {
|
||||
thunks_start_offset_in_section: usize,
|
||||
|
||||
/// Virtual address of the .rdata section
|
||||
rdata_virtual_address: u32,
|
||||
dummy_dll_thunk_section_virtual_address: u32,
|
||||
|
||||
/// The offset into the file of the .reloc section
|
||||
reloc_offset_in_file: usize,
|
||||
@ -108,18 +108,16 @@ impl PeMetadata {
|
||||
.unwrap();
|
||||
|
||||
let dynamic_relocations = DynamicRelocationsPe::new(preprocessed_data);
|
||||
let thunks_start_offset_in_file =
|
||||
find_thunks_start_offset(preprocessed_data, &dynamic_relocations);
|
||||
|
||||
let rdata_section = dynhost_obj
|
||||
.sections()
|
||||
.find(|s| s.name() == Ok(".rdata"))
|
||||
.unwrap();
|
||||
let dummy_dll_thunks = find_thunks_start_offset(preprocessed_data, &dynamic_relocations);
|
||||
let thunks_start_offset_in_file = dummy_dll_thunks.offset_in_file as usize;
|
||||
|
||||
let thunks_start_offset_in_section =
|
||||
thunks_start_offset_in_file - rdata_section.file_range().unwrap().0 as usize;
|
||||
let dummy_dll_thunk_section_virtual_address =
|
||||
dummy_dll_thunks.section.virtual_address.get(LE);
|
||||
|
||||
let rdata_virtual_address = rdata_section.address() as u32;
|
||||
let thunks_start_offset_in_section = (dummy_dll_thunks.offset_in_file
|
||||
- dummy_dll_thunks.section.pointer_to_raw_data.get(LE) as u64)
|
||||
as usize;
|
||||
|
||||
let (reloc_section_index, reloc_section) = dynhost_obj
|
||||
.sections()
|
||||
@ -175,7 +173,7 @@ impl PeMetadata {
|
||||
dynamic_relocations,
|
||||
thunks_start_offset_in_file,
|
||||
thunks_start_offset_in_section,
|
||||
rdata_virtual_address,
|
||||
dummy_dll_thunk_section_virtual_address,
|
||||
reloc_offset_in_file,
|
||||
reloc_section_index,
|
||||
}
|
||||
@ -191,6 +189,7 @@ pub(crate) fn preprocess_windows(
|
||||
_time: bool,
|
||||
) -> object::read::Result<()> {
|
||||
let data = open_mmap(host_exe_filename);
|
||||
|
||||
let new_sections = [*b".text\0\0\0", *b".rdata\0\0"];
|
||||
let mut preprocessed = Preprocessor::preprocess(
|
||||
preprocessed_filename,
|
||||
@ -214,23 +213,42 @@ pub(crate) fn preprocess_windows(
|
||||
dir.size.set(LE, new);
|
||||
}
|
||||
|
||||
// clear out the import table entry. we do implicitly assume that our dummy .dll is the last
|
||||
{
|
||||
const W: usize = std::mem::size_of::<ImageImportDescriptor>();
|
||||
|
||||
let start = md.dynamic_relocations.imports_offset_in_file as usize
|
||||
+ W * md.dynamic_relocations.dummy_import_index as usize;
|
||||
|
||||
for b in preprocessed[start..][..W].iter_mut() {
|
||||
*b = 0;
|
||||
}
|
||||
}
|
||||
remove_dummy_dll_import_table_entry(&mut preprocessed, &md);
|
||||
|
||||
md.write_to_file(metadata_filename);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_dummy_dll_import_table_entry(executable: &mut [u8], md: &PeMetadata) {
|
||||
const W: usize = std::mem::size_of::<ImageImportDescriptor>();
|
||||
|
||||
let dr = &md.dynamic_relocations;
|
||||
|
||||
// there is one zeroed-out descriptor at the back
|
||||
let count = dr.import_directory_size as usize / W - 1;
|
||||
|
||||
let descriptors = load_structs_inplace_mut::<ImageImportDescriptor>(
|
||||
executable,
|
||||
dr.import_directory_offset_in_file as usize,
|
||||
count,
|
||||
);
|
||||
|
||||
// move the dummy to the final position
|
||||
descriptors.swap(dr.dummy_import_index as usize, count - 1);
|
||||
|
||||
// make this the new zeroed-out descriptor
|
||||
if let Some(d) = descriptors.last_mut() {
|
||||
*d = ImageImportDescriptor {
|
||||
original_first_thunk: Default::default(),
|
||||
time_date_stamp: Default::default(),
|
||||
forwarder_chain: Default::default(),
|
||||
name: Default::default(),
|
||||
first_thunk: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn surgery_pe(executable_path: &Path, metadata_path: &Path, roc_app_bytes: &[u8]) {
|
||||
let md = PeMetadata::read_from_file(metadata_path);
|
||||
|
||||
@ -431,7 +449,10 @@ struct DynamicRelocationsPe {
|
||||
section_offset_in_file: u32,
|
||||
|
||||
/// Offset in the file of the imports directory
|
||||
imports_offset_in_file: u32,
|
||||
import_directory_offset_in_file: u32,
|
||||
|
||||
/// Size in the file of the imports directory
|
||||
import_directory_size: u32,
|
||||
|
||||
/// Offset in the file of the data directories
|
||||
data_directories_offset_in_file: u32,
|
||||
@ -536,8 +557,9 @@ impl DynamicRelocationsPe {
|
||||
|
||||
let import_table = ImportTable::new(section_data, section_va, import_va);
|
||||
|
||||
let imports_offset_in_section = import_va.wrapping_sub(section_va);
|
||||
let imports_offset_in_file = offset_in_file + imports_offset_in_section;
|
||||
let (import_directory_offset_in_file, import_directory_size) = data_dir
|
||||
.file_range(§ions)
|
||||
.expect("import directory exists");
|
||||
|
||||
let (descriptor, dummy_import_index) = Self::find_roc_dummy_dll(&import_table)?.unwrap();
|
||||
|
||||
@ -546,10 +568,11 @@ impl DynamicRelocationsPe {
|
||||
address_and_offset: Default::default(),
|
||||
section_virtual_address: section_va,
|
||||
section_offset_in_file: offset_in_file,
|
||||
imports_offset_in_file,
|
||||
import_directory_offset_in_file,
|
||||
data_directories_offset_in_file,
|
||||
dummy_import_index,
|
||||
section_headers_offset_in_file,
|
||||
import_directory_size,
|
||||
};
|
||||
|
||||
this.append_roc_imports(&import_table, &descriptor)?;
|
||||
@ -820,12 +843,16 @@ impl Preprocessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
struct DummyDllThunks<'a> {
|
||||
section: &'a object::pe::ImageSectionHeader,
|
||||
offset_in_file: u64,
|
||||
}
|
||||
|
||||
/// Find the place in the executable where the thunks for our dummy .dll are stored
|
||||
fn find_thunks_start_offset(
|
||||
executable: &[u8],
|
||||
fn find_thunks_start_offset<'a>(
|
||||
executable: &'a [u8],
|
||||
dynamic_relocations: &DynamicRelocationsPe,
|
||||
) -> usize {
|
||||
) -> DummyDllThunks<'a> {
|
||||
// The host executable contains indirect calls to functions that the host should provide
|
||||
//
|
||||
// 14000105d: e8 8e 27 00 00 call 0x1400037f0
|
||||
@ -854,7 +881,7 @@ fn find_thunks_start_offset(
|
||||
// - https://learn.microsoft.com/en-us/archive/msdn-magazine/2002/february/inside-windows-win32-portable-executable-file-format-in-detail
|
||||
const W: usize = std::mem::size_of::<ImageImportDescriptor>();
|
||||
|
||||
let dummy_import_desc_start = dynamic_relocations.imports_offset_in_file as usize
|
||||
let dummy_import_desc_start = dynamic_relocations.import_directory_offset_in_file as usize
|
||||
+ W * dynamic_relocations.dummy_import_index as usize;
|
||||
|
||||
let dummy_import_desc =
|
||||
@ -880,19 +907,21 @@ fn find_thunks_start_offset(
|
||||
let sections = nt_headers.sections(executable, offset).unwrap();
|
||||
|
||||
// find the section the virtual address is in
|
||||
let (section_va, offset_in_file) = sections
|
||||
let (section_virtual_address, section) = sections
|
||||
.iter()
|
||||
.find_map(|section| {
|
||||
section
|
||||
.pe_data_containing(executable, dummy_thunks_address)
|
||||
.map(|(_section_data, section_va)| {
|
||||
(section_va, section.pointer_to_raw_data.get(LE))
|
||||
})
|
||||
.map(|(_section_data, section_va)| (section_va, section))
|
||||
})
|
||||
.expect("Invalid thunk virtual address");
|
||||
|
||||
// and get the offset in the file of 0x1400037f0
|
||||
(dummy_thunks_address - section_va + offset_in_file) as usize
|
||||
DummyDllThunks {
|
||||
section,
|
||||
// and get the offset in the file of 0x1400037f0
|
||||
offset_in_file: dummy_thunks_address as u64 - section_virtual_address as u64
|
||||
+ section.pointer_to_raw_data.get(LE) as u64,
|
||||
}
|
||||
}
|
||||
|
||||
/// Make the thunks point to our actual roc application functions
|
||||
@ -903,9 +932,10 @@ fn redirect_dummy_dll_functions(
|
||||
thunks_start_offset: usize,
|
||||
) {
|
||||
// it could be that a symbol exposed by the app is not used by the host. We must skip unused symbols
|
||||
let mut targets = function_definition_vas.iter();
|
||||
// this is an O(n^2) loop, hopefully that does not become a problem. If it does we can sort
|
||||
// both vectors to get linear complexity in the loop.
|
||||
'outer: for (i, host_name) in imports.iter().enumerate() {
|
||||
for (roc_app_target_name, roc_app_target_va) in targets.by_ref() {
|
||||
for (roc_app_target_name, roc_app_target_va) in function_definition_vas {
|
||||
if host_name == roc_app_target_name {
|
||||
// addresses are 64-bit values
|
||||
let address_bytes = &mut executable[thunks_start_offset + i * 8..][..8];
|
||||
@ -1309,8 +1339,8 @@ fn write_image_base_relocation(
|
||||
/// in a table to find the actual address of the app function. This table must be relocated,
|
||||
/// because it contains absolute addresses to jump to.
|
||||
fn relocate_dummy_dll_entries(executable: &mut [u8], md: &PeMetadata) {
|
||||
let thunks_start_va = (md.rdata_virtual_address - md.image_base as u32)
|
||||
+ md.thunks_start_offset_in_section as u32;
|
||||
let thunks_start_va =
|
||||
md.dummy_dll_thunk_section_virtual_address + md.thunks_start_offset_in_section as u32;
|
||||
|
||||
// relocations are defined per 4kb page
|
||||
const BLOCK_SIZE: u32 = 4096;
|
||||
@ -1354,17 +1384,20 @@ fn relocate_dummy_dll_entries(executable: &mut [u8], md: &PeMetadata) {
|
||||
"new .reloc section is too big, and runs into the next section!",
|
||||
);
|
||||
|
||||
// // in the data directories, update the length of the base relocations
|
||||
// let dir = load_struct_inplace_mut::<pe::ImageDataDirectory>(
|
||||
// executable,
|
||||
// md.dynamic_relocations.data_directories_offset_in_file as usize
|
||||
// + object::pe::IMAGE_DIRECTORY_ENTRY_BASERELOC
|
||||
// * std::mem::size_of::<pe::ImageDataDirectory>(),
|
||||
// );
|
||||
//
|
||||
// let old_dir_size = dir.size.get(LE);
|
||||
// debug_assert_eq!(old_section_size, old_dir_size);
|
||||
// dir.size.set(LE, new_virtual_size);
|
||||
// in the data directories, update the length of the base relocations
|
||||
let dir = load_struct_inplace_mut::<pe::ImageDataDirectory>(
|
||||
executable,
|
||||
md.dynamic_relocations.data_directories_offset_in_file as usize
|
||||
+ object::pe::IMAGE_DIRECTORY_ENTRY_BASERELOC
|
||||
* std::mem::size_of::<pe::ImageDataDirectory>(),
|
||||
);
|
||||
|
||||
// it is crucial that the directory size is rounded up to a multiple of 8!
|
||||
let old = dir.size.get(LE);
|
||||
let new = new_virtual_size;
|
||||
let delta = next_multiple_of((new - old) as usize, 8);
|
||||
|
||||
dir.size.set(LE, old + delta as u32);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -1554,7 +1587,7 @@ mod test {
|
||||
remove_dummy_dll_import_table_test(
|
||||
&mut data,
|
||||
dynamic_relocations.data_directories_offset_in_file,
|
||||
dynamic_relocations.imports_offset_in_file,
|
||||
dynamic_relocations.import_directory_offset_in_file,
|
||||
dynamic_relocations.dummy_import_index,
|
||||
);
|
||||
|
||||
@ -1696,14 +1729,14 @@ mod test {
|
||||
// make the dummy dylib based on the app object
|
||||
let names: Vec<_> = symbols.iter().map(|s| s.name.clone()).collect();
|
||||
let dylib_bytes = crate::generate_dylib::synthetic_dll(&names);
|
||||
std::fs::write(dir.join("libapp.obj"), dylib_bytes).unwrap();
|
||||
std::fs::write(dir.join("libapp.dll"), dylib_bytes).unwrap();
|
||||
|
||||
// now we can compile the host (it uses libapp.obj, hence the order here)
|
||||
// now we can compile the host (it uses libapp.dll, hence the order here)
|
||||
let output = std::process::Command::new(&zig)
|
||||
.current_dir(dir)
|
||||
.args(&[
|
||||
"build-exe",
|
||||
"libapp.obj",
|
||||
"libapp.dll",
|
||||
"host.zig",
|
||||
"-lc",
|
||||
"-target",
|
||||
|
@ -1,4 +1,9 @@
|
||||
fn main() {
|
||||
#[cfg(not(windows))]
|
||||
println!("cargo:rustc-link-lib=dylib=app");
|
||||
|
||||
#[cfg(windows)]
|
||||
println!("cargo:rustc-link-lib=dylib=libapp");
|
||||
|
||||
println!("cargo:rustc-link-search=.");
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
use core::ffi::c_void;
|
||||
use roc_std::RocStr;
|
||||
use std::ffi::CStr;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::io::Write;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
extern "C" {
|
||||
@ -56,21 +56,11 @@ pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main() -> i32 {
|
||||
unsafe {
|
||||
// ManuallyDrop must be used here in order to prevent the RocStr from
|
||||
// getting dropped as soon as it's no longer referenced anywhere, which
|
||||
// happens earlier than the libc::write that receives a pointer to its data.
|
||||
let mut roc_str = ManuallyDrop::new(RocStr::default());
|
||||
roc_main(&mut roc_str);
|
||||
let mut roc_str = RocStr::default();
|
||||
unsafe { roc_main(&mut roc_str) };
|
||||
|
||||
let len = roc_str.len();
|
||||
let str_bytes = roc_str.as_bytes().as_ptr() as *const libc::c_void;
|
||||
|
||||
if libc::write(1, str_bytes, len) < 0 {
|
||||
panic!("Writing to stdout failed!");
|
||||
}
|
||||
|
||||
ManuallyDrop::drop(&mut roc_str)
|
||||
if let Err(e) = std::io::stdout().write_all(roc_str.as_bytes()) {
|
||||
panic!("Writing to stdout failed! {:?}", e);
|
||||
}
|
||||
|
||||
// Exit code
|
||||
|
Loading…
Reference in New Issue
Block a user