Add support for raw-dylib with stdcall, fastcall functions on i686-pc-windows-msvc.
This commit is contained in:
parent
8b87e85394
commit
a867dd4c7e
19 changed files with 436 additions and 48 deletions
|
@ -12,7 +12,7 @@ use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport};
|
||||||
use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder};
|
use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder};
|
||||||
use rustc_codegen_ssa::{looks_like_rust_object_file, METADATA_FILENAME};
|
use rustc_codegen_ssa::{looks_like_rust_object_file, METADATA_FILENAME};
|
||||||
use rustc_data_structures::temp_dir::MaybeTempDir;
|
use rustc_data_structures::temp_dir::MaybeTempDir;
|
||||||
use rustc_middle::middle::cstore::DllImport;
|
use rustc_middle::middle::cstore::{DllCallingConvention, DllImport};
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
|
|
||||||
|
@ -208,10 +208,12 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
|
||||||
// have any \0 characters
|
// have any \0 characters
|
||||||
let import_name_vector: Vec<CString> = dll_imports
|
let import_name_vector: Vec<CString> = dll_imports
|
||||||
.iter()
|
.iter()
|
||||||
.map(if self.config.sess.target.arch == "x86" {
|
.map(|import: &DllImport| {
|
||||||
|import: &DllImport| CString::new(format!("_{}", import.name.to_string())).unwrap()
|
if self.config.sess.target.arch == "x86" {
|
||||||
} else {
|
LlvmArchiveBuilder::i686_decorated_name(import)
|
||||||
|import: &DllImport| CString::new(import.name.to_string()).unwrap()
|
} else {
|
||||||
|
CString::new(import.name.to_string()).unwrap()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
@ -391,6 +393,21 @@ impl<'a> LlvmArchiveBuilder<'a> {
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn i686_decorated_name(import: &DllImport) -> CString {
|
||||||
|
let name = import.name;
|
||||||
|
// We verified during construction that `name` does not contain any NULL characters, so the
|
||||||
|
// conversion to CString is guaranteed to succeed.
|
||||||
|
CString::new(match import.calling_convention {
|
||||||
|
DllCallingConvention::C => format!("_{}", name),
|
||||||
|
DllCallingConvention::Stdcall(arg_list_size) => format!("_{}@{}", name, arg_list_size),
|
||||||
|
DllCallingConvention::Fastcall(arg_list_size) => format!("@{}@{}", name, arg_list_size),
|
||||||
|
DllCallingConvention::Vectorcall(arg_list_size) => {
|
||||||
|
format!("{}@@{}", name, arg_list_size)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn string_to_io_error(s: String) -> io::Error {
|
fn string_to_io_error(s: String) -> io::Error {
|
||||||
|
|
|
@ -3,7 +3,7 @@ use rustc_data_structures::temp_dir::MaybeTempDir;
|
||||||
use rustc_errors::Handler;
|
use rustc_errors::Handler;
|
||||||
use rustc_fs_util::fix_windows_verbatim_for_gcc;
|
use rustc_fs_util::fix_windows_verbatim_for_gcc;
|
||||||
use rustc_hir::def_id::CrateNum;
|
use rustc_hir::def_id::CrateNum;
|
||||||
use rustc_middle::middle::cstore::DllImport;
|
use rustc_middle::middle::cstore::{DllCallingConvention, DllImport};
|
||||||
use rustc_middle::middle::dependency_format::Linkage;
|
use rustc_middle::middle::dependency_format::Linkage;
|
||||||
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
|
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
|
||||||
use rustc_session::config::{OutputFilenames, OutputType, PrintRequest};
|
use rustc_session::config::{OutputFilenames, OutputType, PrintRequest};
|
||||||
|
@ -34,8 +34,8 @@ use object::write::Object;
|
||||||
use object::{Architecture, BinaryFormat, Endianness, FileFlags, SectionFlags, SectionKind};
|
use object::{Architecture, BinaryFormat, Endianness, FileFlags, SectionFlags, SectionKind};
|
||||||
use tempfile::Builder as TempFileBuilder;
|
use tempfile::Builder as TempFileBuilder;
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
use std::iter::FromIterator;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{ExitStatus, Output, Stdio};
|
use std::process::{ExitStatus, Output, Stdio};
|
||||||
use std::{ascii, char, env, fmt, fs, io, mem, str};
|
use std::{ascii, char, env, fmt, fs, io, mem, str};
|
||||||
|
@ -259,7 +259,7 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
|
||||||
}
|
}
|
||||||
|
|
||||||
for (raw_dylib_name, raw_dylib_imports) in
|
for (raw_dylib_name, raw_dylib_imports) in
|
||||||
collate_raw_dylibs(&codegen_results.crate_info.used_libraries)
|
collate_raw_dylibs(sess, &codegen_results.crate_info.used_libraries)
|
||||||
{
|
{
|
||||||
ab.inject_dll_import_lib(&raw_dylib_name, &raw_dylib_imports, tmpdir);
|
ab.inject_dll_import_lib(&raw_dylib_name, &raw_dylib_imports, tmpdir);
|
||||||
}
|
}
|
||||||
|
@ -451,8 +451,11 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
|
||||||
/// then the CodegenResults value contains one NativeLib instance for each block. However, the
|
/// then the CodegenResults value contains one NativeLib instance for each block. However, the
|
||||||
/// linker appears to expect only a single import library for each library used, so we need to
|
/// linker appears to expect only a single import library for each library used, so we need to
|
||||||
/// collate the symbols together by library name before generating the import libraries.
|
/// collate the symbols together by library name before generating the import libraries.
|
||||||
fn collate_raw_dylibs(used_libraries: &[NativeLib]) -> Vec<(String, Vec<DllImport>)> {
|
fn collate_raw_dylibs(
|
||||||
let mut dylib_table: FxHashMap<String, FxHashSet<Symbol>> = FxHashMap::default();
|
sess: &Session,
|
||||||
|
used_libraries: &[NativeLib],
|
||||||
|
) -> Vec<(String, Vec<DllImport>)> {
|
||||||
|
let mut dylib_table: FxHashMap<String, FxHashSet<DllImport>> = FxHashMap::default();
|
||||||
|
|
||||||
for lib in used_libraries {
|
for lib in used_libraries {
|
||||||
if lib.kind == NativeLibKind::RawDylib {
|
if lib.kind == NativeLibKind::RawDylib {
|
||||||
|
@ -464,35 +467,51 @@ fn collate_raw_dylibs(used_libraries: &[NativeLib]) -> Vec<(String, Vec<DllImpor
|
||||||
} else {
|
} else {
|
||||||
format!("{}.dll", name)
|
format!("{}.dll", name)
|
||||||
};
|
};
|
||||||
dylib_table
|
dylib_table.entry(name).or_default().extend(lib.dll_imports.iter().cloned());
|
||||||
.entry(name)
|
|
||||||
.or_default()
|
|
||||||
.extend(lib.dll_imports.iter().map(|import| import.name));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: when we add support for ordinals, fix this to propagate ordinals. Also figure out
|
// Rustc already signals an error if we have two imports with the same name but different
|
||||||
// what we should do if we have two DllImport values with the same name but different
|
// calling conventions (or function signatures), so we don't have pay attention to those
|
||||||
// ordinals.
|
// when ordering.
|
||||||
let mut result = dylib_table
|
// FIXME: when we add support for ordinals, figure out if we need to do anything if we
|
||||||
|
// have two DllImport values with the same name but different ordinals.
|
||||||
|
let mut result: Vec<(String, Vec<DllImport>)> = dylib_table
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(lib_name, imported_names)| {
|
.map(|(lib_name, import_table)| {
|
||||||
let mut names = imported_names
|
let mut imports = Vec::from_iter(import_table.into_iter());
|
||||||
.iter()
|
imports.sort_unstable_by_key(|x: &DllImport| x.name.as_str());
|
||||||
.map(|name| DllImport { name: *name, ordinal: None })
|
(lib_name, imports)
|
||||||
.collect::<Vec<_>>();
|
|
||||||
names.sort_unstable_by(|a: &DllImport, b: &DllImport| {
|
|
||||||
match a.name.as_str().cmp(&b.name.as_str()) {
|
|
||||||
Ordering::Equal => a.ordinal.cmp(&b.ordinal),
|
|
||||||
x => x,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
(lib_name, names)
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
result.sort_unstable_by(|a: &(String, Vec<DllImport>), b: &(String, Vec<DllImport>)| {
|
result.sort_unstable_by(|a: &(String, Vec<DllImport>), b: &(String, Vec<DllImport>)| {
|
||||||
a.0.cmp(&b.0)
|
a.0.cmp(&b.0)
|
||||||
});
|
});
|
||||||
|
let result = result;
|
||||||
|
|
||||||
|
// Check for multiple imports with the same name but different calling conventions or
|
||||||
|
// (when relevant) argument list sizes. Rustc only signals an error for this if the
|
||||||
|
// declarations are at the same scope level; if one shadows the other, we only get a lint
|
||||||
|
// warning.
|
||||||
|
for (library, imports) in &result {
|
||||||
|
let mut import_table: FxHashMap<Symbol, DllCallingConvention> = FxHashMap::default();
|
||||||
|
for import in imports {
|
||||||
|
if let Some(old_convention) =
|
||||||
|
import_table.insert(import.name, import.calling_convention)
|
||||||
|
{
|
||||||
|
if import.calling_convention != old_convention {
|
||||||
|
sess.span_fatal(
|
||||||
|
import.span,
|
||||||
|
&format!(
|
||||||
|
"multiple definitions of external function `{}` from library `{}` have different calling conventions",
|
||||||
|
import.name,
|
||||||
|
library,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@ use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_errors::struct_span_err;
|
use rustc_errors::struct_span_err;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::itemlikevisit::ItemLikeVisitor;
|
use rustc_hir::itemlikevisit::ItemLikeVisitor;
|
||||||
use rustc_middle::middle::cstore::{DllImport, NativeLib};
|
use rustc_middle::middle::cstore::{DllCallingConvention, DllImport, NativeLib};
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
|
||||||
use rustc_session::parse::feature_err;
|
use rustc_session::parse::feature_err;
|
||||||
use rustc_session::utils::NativeLibKind;
|
use rustc_session::utils::NativeLibKind;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
|
@ -199,22 +199,10 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if lib.kind == NativeLibKind::RawDylib {
|
if lib.kind == NativeLibKind::RawDylib {
|
||||||
match abi {
|
|
||||||
Abi::C { .. } => (),
|
|
||||||
Abi::Cdecl => (),
|
|
||||||
_ => {
|
|
||||||
if sess.target.arch == "x86" {
|
|
||||||
sess.span_fatal(
|
|
||||||
it.span,
|
|
||||||
r#"`#[link(kind = "raw-dylib")]` only supports C and Cdecl ABIs"#,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
lib.dll_imports.extend(
|
lib.dll_imports.extend(
|
||||||
foreign_mod_items
|
foreign_mod_items
|
||||||
.iter()
|
.iter()
|
||||||
.map(|child_item| DllImport { name: child_item.ident.name, ordinal: None }),
|
.map(|child_item| self.build_dll_import(abi, child_item)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,4 +384,58 @@ impl Collector<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn i686_arg_list_size(&self, item: &hir::ForeignItemRef<'_>) -> usize {
|
||||||
|
let argument_types: &List<Ty<'_>> = self.tcx.erase_late_bound_regions(
|
||||||
|
self.tcx
|
||||||
|
.type_of(item.id.def_id)
|
||||||
|
.fn_sig(self.tcx)
|
||||||
|
.inputs()
|
||||||
|
.map_bound(|slice| self.tcx.mk_type_list(slice.iter())),
|
||||||
|
);
|
||||||
|
|
||||||
|
argument_types
|
||||||
|
.iter()
|
||||||
|
.map(|ty| {
|
||||||
|
let layout = self
|
||||||
|
.tcx
|
||||||
|
.layout_of(ParamEnvAnd { param_env: ParamEnv::empty(), value: ty })
|
||||||
|
.expect("layout")
|
||||||
|
.layout;
|
||||||
|
// In both stdcall and fastcall, we always round up the argument size to the
|
||||||
|
// nearest multiple of 4 bytes.
|
||||||
|
(layout.size.bytes_usize() + 3) & !3
|
||||||
|
})
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_dll_import(&self, abi: Abi, item: &hir::ForeignItemRef<'_>) -> DllImport {
|
||||||
|
let calling_convention = if self.tcx.sess.target.arch == "x86" {
|
||||||
|
match abi {
|
||||||
|
Abi::C { .. } | Abi::Cdecl => DllCallingConvention::C,
|
||||||
|
Abi::Stdcall { .. } | Abi::System { .. } => {
|
||||||
|
DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
|
||||||
|
}
|
||||||
|
Abi::Fastcall => DllCallingConvention::Fastcall(self.i686_arg_list_size(item)),
|
||||||
|
// Vectorcall is intentionally not supported at this time.
|
||||||
|
_ => {
|
||||||
|
self.tcx.sess.span_fatal(
|
||||||
|
item.span,
|
||||||
|
r#"ABI not supported by `#[link(kind = "raw-dylib")]` on i686"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match abi {
|
||||||
|
Abi::C { .. } | Abi::Win64 | Abi::System { .. } => DllCallingConvention::C,
|
||||||
|
_ => {
|
||||||
|
self.tcx.sess.span_fatal(
|
||||||
|
item.span,
|
||||||
|
r#"ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
DllImport { name: item.ident.name, ordinal: None, calling_convention, span: item.span }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,10 +77,29 @@ pub struct NativeLib {
|
||||||
pub dll_imports: Vec<DllImport>,
|
pub dll_imports: Vec<DllImport>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Encodable, Decodable, HashStable)]
|
#[derive(Clone, Debug, PartialEq, Eq, Encodable, Decodable, Hash, HashStable)]
|
||||||
pub struct DllImport {
|
pub struct DllImport {
|
||||||
pub name: Symbol,
|
pub name: Symbol,
|
||||||
pub ordinal: Option<u16>,
|
pub ordinal: Option<u16>,
|
||||||
|
/// Calling convention for the function.
|
||||||
|
///
|
||||||
|
/// On x86_64, this is always `DllCallingConvention::C`; on i686, it can be any
|
||||||
|
/// of the values, and we use `DllCallingConvention::C` to represent `"cdecl"`.
|
||||||
|
pub calling_convention: DllCallingConvention,
|
||||||
|
/// Span of import's "extern" declaration; used for diagnostics.
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calling convention for a function defined in an external library.
|
||||||
|
///
|
||||||
|
/// The usize value, where present, indicates the size of the function's argument list
|
||||||
|
/// in bytes.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Encodable, Decodable, Hash, HashStable)]
|
||||||
|
pub enum DllCallingConvention {
|
||||||
|
C,
|
||||||
|
Stdcall(usize),
|
||||||
|
Fastcall(usize),
|
||||||
|
Vectorcall(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)]
|
#[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)]
|
||||||
|
|
18
src/test/run-make/raw-dylib-alt-calling-convention/Makefile
Normal file
18
src/test/run-make/raw-dylib-alt-calling-convention/Makefile
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Test the behavior of #[link(.., kind = "raw-dylib")] with alternative calling conventions.
|
||||||
|
|
||||||
|
# only-i686-pc-windows-msvc
|
||||||
|
|
||||||
|
-include ../../run-make-fulldeps/tools.mk
|
||||||
|
|
||||||
|
all:
|
||||||
|
$(call COMPILE_OBJ,"$(TMPDIR)"/extern.obj,extern.c)
|
||||||
|
$(CC) "$(TMPDIR)"/extern.obj -link -dll -out:"$(TMPDIR)"/extern.dll
|
||||||
|
$(RUSTC) --crate-type lib --crate-name raw_dylib_alt_calling_convention_test lib.rs
|
||||||
|
$(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)"
|
||||||
|
"$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt
|
||||||
|
|
||||||
|
ifdef RUSTC_BLESS_TEST
|
||||||
|
cp "$(TMPDIR)"/output.txt output.txt
|
||||||
|
else
|
||||||
|
$(DIFF) output.txt "$(TMPDIR)"/output.txt
|
||||||
|
endif
|
|
@ -0,0 +1,5 @@
|
||||||
|
extern crate raw_dylib_alt_calling_convention_test;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
raw_dylib_alt_calling_convention_test::library_function();
|
||||||
|
}
|
123
src/test/run-make/raw-dylib-alt-calling-convention/extern.c
Normal file
123
src/test/run-make/raw-dylib-alt-calling-convention/extern.c
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
uint8_t x;
|
||||||
|
int32_t y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct S2 {
|
||||||
|
int32_t x;
|
||||||
|
uint8_t y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct S3 {
|
||||||
|
uint8_t x[5];
|
||||||
|
};
|
||||||
|
|
||||||
|
__declspec(dllexport) void __stdcall stdcall_fn_1(int i) {
|
||||||
|
printf("stdcall_fn_1(%d)\n", i);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) void __stdcall stdcall_fn_2(uint8_t i, float f) {
|
||||||
|
printf("stdcall_fn_2(%d, %.1f)\n", i, f);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) void __stdcall stdcall_fn_3(double d) {
|
||||||
|
printf("stdcall_fn_3(%.1f)\n", d);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) void __stdcall stdcall_fn_4(uint8_t i, uint8_t j, float f) {
|
||||||
|
printf("stdcall_fn_4(%d, %d, %.1f)\n", i, j, f);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) void __stdcall stdcall_fn_5(struct S s, int i) {
|
||||||
|
printf("stdcall_fn_5(S { x: %d, y: %d }, %d)\n", s.x, s.y, i);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that stdcall support works correctly with the nullable pointer optimization.
|
||||||
|
__declspec(dllexport) void __stdcall stdcall_fn_6(struct S* s) {
|
||||||
|
if (s) {
|
||||||
|
printf("stdcall_fn_6(S { x: %d, y: %d })\n", s->x, s->y);
|
||||||
|
} else {
|
||||||
|
printf("stdcall_fn_6(null)\n");
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) void __stdcall stdcall_fn_7(struct S2 s, int i) {
|
||||||
|
printf("stdcall_fn_7(S2 { x: %d, y: %d }, %d)\n", s.x, s.y, i);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that we compute the correct amount of space in the argument list for a 5-byte struct.
|
||||||
|
__declspec(dllexport) void __stdcall stdcall_fn_8(struct S3 s, struct S3 t) {
|
||||||
|
printf("stdcall_fn_8(S3 { x: [%d, %d, %d, %d, %d] }, S3 { x: [%d, %d, %d, %d, %d] })\n",
|
||||||
|
s.x[0], s.x[1], s.x[2], s.x[3], s.x[4],
|
||||||
|
t.x[0], t.x[1], t.x[2], t.x[3], t.x[4]
|
||||||
|
);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// test whether f64/double values are aligned on 4-byte or 8-byte boundaries.
|
||||||
|
__declspec(dllexport) void __stdcall stdcall_fn_9(uint8_t x, double y) {
|
||||||
|
printf("stdcall_fn_9(%d, %.1f)\n", x, y);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) void __fastcall fastcall_fn_1(int i) {
|
||||||
|
printf("fastcall_fn_1(%d)\n", i);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) void __fastcall fastcall_fn_2(uint8_t i, float f) {
|
||||||
|
printf("fastcall_fn_2(%d, %.1f)\n", i, f);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) void __fastcall fastcall_fn_3(double d) {
|
||||||
|
printf("fastcall_fn_3(%.1f)\n", d);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) void __fastcall fastcall_fn_4(uint8_t i, uint8_t j, float f) {
|
||||||
|
printf("fastcall_fn_4(%d, %d, %.1f)\n", i, j, f);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) void __fastcall fastcall_fn_5(struct S s, int i) {
|
||||||
|
printf("fastcall_fn_5(S { x: %d, y: %d }, %d)\n", s.x, s.y, i);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) void __fastcall fastcall_fn_6(struct S* s) {
|
||||||
|
if (s) {
|
||||||
|
printf("fastcall_fn_6(S { x: %d, y: %d })\n", s->x, s->y);
|
||||||
|
} else {
|
||||||
|
printf("fastcall_fn_6(null)\n");
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) void __fastcall fastcall_fn_7(struct S2 s, int i) {
|
||||||
|
printf("fastcall_fn_7(S2 { x: %d, y: %d }, %d)\n", s.x, s.y, i);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) void __fastcall fastcall_fn_8(struct S3 s, struct S3 t) {
|
||||||
|
printf("fastcall_fn_8(S3 { x: [%d, %d, %d, %d, %d] }, S3 { x: [%d, %d, %d, %d, %d] })\n",
|
||||||
|
s.x[0], s.x[1], s.x[2], s.x[3], s.x[4],
|
||||||
|
t.x[0], t.x[1], t.x[2], t.x[3], t.x[4]
|
||||||
|
);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) void __fastcall fastcall_fn_9(uint8_t x, double y) {
|
||||||
|
printf("fastcall_fn_9(%d, %.1f)\n", x, y);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
71
src/test/run-make/raw-dylib-alt-calling-convention/lib.rs
Normal file
71
src/test/run-make/raw-dylib-alt-calling-convention/lib.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
#![feature(raw_dylib)]
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct S {
|
||||||
|
x: u8,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct S2 {
|
||||||
|
x: i32,
|
||||||
|
y: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct S3 {
|
||||||
|
x: [u8; 5],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[link(name = "extern", kind = "raw-dylib")]
|
||||||
|
extern "stdcall" {
|
||||||
|
fn stdcall_fn_1(i: i32);
|
||||||
|
fn stdcall_fn_2(c: u8, f: f32);
|
||||||
|
fn stdcall_fn_3(d: f64);
|
||||||
|
fn stdcall_fn_4(i: u8, j: u8, f: f32);
|
||||||
|
fn stdcall_fn_5(a: S, b: i32);
|
||||||
|
fn stdcall_fn_6(a: Option<&S>);
|
||||||
|
fn stdcall_fn_7(a: S2, b: i32);
|
||||||
|
fn stdcall_fn_8(a: S3, b: S3);
|
||||||
|
fn stdcall_fn_9(x: u8, y: f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[link(name = "extern", kind = "raw-dylib")]
|
||||||
|
extern "fastcall" {
|
||||||
|
fn fastcall_fn_1(i: i32);
|
||||||
|
fn fastcall_fn_2(c: u8, f: f32);
|
||||||
|
fn fastcall_fn_3(d: f64);
|
||||||
|
fn fastcall_fn_4(i: u8, j: u8, f: f32);
|
||||||
|
fn fastcall_fn_5(a: S, b: i32);
|
||||||
|
fn fastcall_fn_6(a: Option<&S>);
|
||||||
|
fn fastcall_fn_7(a: S2, b: i32);
|
||||||
|
fn fastcall_fn_8(a: S3, b: S3);
|
||||||
|
fn fastcall_fn_9(x: u8, y: f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn library_function() {
|
||||||
|
unsafe {
|
||||||
|
stdcall_fn_1(14);
|
||||||
|
stdcall_fn_2(16, 3.5);
|
||||||
|
stdcall_fn_3(3.5);
|
||||||
|
stdcall_fn_4(1, 2, 3.0);
|
||||||
|
stdcall_fn_5(S { x: 1, y: 2 }, 16);
|
||||||
|
stdcall_fn_6(Some(&S { x: 10, y: 12 }));
|
||||||
|
stdcall_fn_7(S2 { x: 15, y: 16 }, 3);
|
||||||
|
stdcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] });
|
||||||
|
stdcall_fn_9(1, 3.0);
|
||||||
|
|
||||||
|
fastcall_fn_1(14);
|
||||||
|
fastcall_fn_2(16, 3.5);
|
||||||
|
fastcall_fn_3(3.5);
|
||||||
|
fastcall_fn_4(1, 2, 3.0);
|
||||||
|
fastcall_fn_5(S { x: 1, y: 2 }, 16);
|
||||||
|
fastcall_fn_6(Some(&S { x: 10, y: 12 }));
|
||||||
|
fastcall_fn_7(S2 { x: 15, y: 16 }, 3);
|
||||||
|
fastcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] });
|
||||||
|
fastcall_fn_9(1, 3.0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
stdcall_fn_1(14)
|
||||||
|
stdcall_fn_2(16, 3.5)
|
||||||
|
stdcall_fn_3(3.5)
|
||||||
|
stdcall_fn_4(1, 2, 3.0)
|
||||||
|
stdcall_fn_5(S { x: 1, y: 2 }, 16)
|
||||||
|
stdcall_fn_6(S { x: 10, y: 12 })
|
||||||
|
stdcall_fn_7(S2 { x: 15, y: 16 }, 3)
|
||||||
|
stdcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] })
|
||||||
|
stdcall_fn_9(1, 3.0)
|
||||||
|
fastcall_fn_1(14)
|
||||||
|
fastcall_fn_2(16, 3.5)
|
||||||
|
fastcall_fn_3(3.5)
|
||||||
|
fastcall_fn_4(1, 2, 3.0)
|
||||||
|
fastcall_fn_5(S { x: 1, y: 2 }, 16)
|
||||||
|
fastcall_fn_6(S { x: 10, y: 12 })
|
||||||
|
fastcall_fn_7(S2 { x: 15, y: 16 }, 3)
|
||||||
|
fastcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] })
|
||||||
|
fastcall_fn_9(1, 3.0)
|
|
@ -1,7 +1,6 @@
|
||||||
# Test the behavior of #[link(.., kind = "raw-dylib")] on windows-msvc
|
# Test the behavior of #[link(.., kind = "raw-dylib")] on windows-msvc
|
||||||
|
|
||||||
# only-windows
|
# only-windows-msvc
|
||||||
# only-msvc
|
|
||||||
|
|
||||||
-include ../../run-make-fulldeps/tools.mk
|
-include ../../run-make-fulldeps/tools.mk
|
||||||
|
|
19
src/test/ui/rfc-2627-raw-dylib/multiple-definitions.rs
Normal file
19
src/test/ui/rfc-2627-raw-dylib/multiple-definitions.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// only-i686-pc-windows-msvc
|
||||||
|
// compile-flags: --crate-type lib --emit link
|
||||||
|
#![allow(clashing_extern_declarations)]
|
||||||
|
#![feature(raw_dylib)]
|
||||||
|
//~^ WARN the feature `raw_dylib` is incomplete
|
||||||
|
#[link(name = "foo", kind = "raw-dylib")]
|
||||||
|
extern "C" {
|
||||||
|
fn f(x: i32);
|
||||||
|
//~^ ERROR multiple definitions of external function `f` from library `foo.dll` have different calling conventions
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lib_main() {
|
||||||
|
#[link(name = "foo", kind = "raw-dylib")]
|
||||||
|
extern "stdcall" {
|
||||||
|
fn f(x: i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { f(42); }
|
||||||
|
}
|
17
src/test/ui/rfc-2627-raw-dylib/multiple-definitions.stderr
Normal file
17
src/test/ui/rfc-2627-raw-dylib/multiple-definitions.stderr
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/multiple-definitions.rs:4:12
|
||||||
|
|
|
||||||
|
LL | #![feature(raw_dylib)]
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
= note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information
|
||||||
|
|
||||||
|
error: multiple definitions of external function `f` from library `foo.dll` have different calling conventions
|
||||||
|
--> $DIR/multiple-definitions.rs:8:5
|
||||||
|
|
|
||||||
|
LL | fn f(x: i32);
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error; 1 warning emitted
|
||||||
|
|
13
src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs
Normal file
13
src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// only-x86_64-pc-windows-msvc
|
||||||
|
// compile-flags: --crate-type lib --emit link
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
#![feature(raw_dylib)]
|
||||||
|
#[link(name = "foo", kind = "raw-dylib")]
|
||||||
|
extern "stdcall" {
|
||||||
|
fn f(x: i32);
|
||||||
|
//~^ ERROR ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lib_main() {
|
||||||
|
unsafe { f(42); }
|
||||||
|
}
|
8
src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr
Normal file
8
src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
error: ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture
|
||||||
|
--> $DIR/unsupported-abi.rs:7:5
|
||||||
|
|
|
||||||
|
LL | fn f(x: i32);
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue