Skip to content

Commit c478bc0

Browse files
authored
Rollup merge of rust-lang#89025 - ricobbe:raw-dylib-link-ordinal, r=michaelwoerister
Implement `#[link_ordinal(n)]` Allows the use of `#[link_ordinal(n)]` with `#[link(kind = "raw-dylib")]`, allowing Rust to link against DLLs that export symbols by ordinal rather than by name. As long as the ordinal matches, the name of the function in Rust is not required to match the name of the corresponding function in the exporting DLL. Part of rust-lang#58713.
2 parents 837a032 + 142f6c0 commit c478bc0

20 files changed

+201
-23
lines changed

compiler/rustc_codegen_llvm/src/back/archive.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,13 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
163163
// All import names are Rust identifiers and therefore cannot contain \0 characters.
164164
// FIXME: when support for #[link_name] implemented, ensure that import.name values don't
165165
// have any \0 characters
166-
let import_name_vector: Vec<CString> = dll_imports
166+
let import_name_and_ordinal_vector: Vec<(CString, Option<u16>)> = dll_imports
167167
.iter()
168168
.map(|import: &DllImport| {
169169
if self.config.sess.target.arch == "x86" {
170-
LlvmArchiveBuilder::i686_decorated_name(import)
170+
(LlvmArchiveBuilder::i686_decorated_name(import), import.ordinal)
171171
} else {
172-
CString::new(import.name.to_string()).unwrap()
172+
(CString::new(import.name.to_string()).unwrap(), import.ordinal)
173173
}
174174
})
175175
.collect();
@@ -184,9 +184,9 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
184184
dll_imports.iter().map(|import| import.name.to_string()).collect::<Vec<_>>().join(", "),
185185
);
186186

187-
let ffi_exports: Vec<LLVMRustCOFFShortExport> = import_name_vector
187+
let ffi_exports: Vec<LLVMRustCOFFShortExport> = import_name_and_ordinal_vector
188188
.iter()
189-
.map(|name_z| LLVMRustCOFFShortExport::from_name(name_z.as_ptr()))
189+
.map(|(name_z, ordinal)| LLVMRustCOFFShortExport::new(name_z.as_ptr(), *ordinal))
190190
.collect();
191191
let result = unsafe {
192192
crate::llvm::LLVMRustWriteImportLibrary(

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,18 @@ pub enum LLVMRustResult {
3434
#[repr(C)]
3535
pub struct LLVMRustCOFFShortExport {
3636
pub name: *const c_char,
37+
pub ordinal_present: bool,
38+
// value of `ordinal` only important when `ordinal_present` is true
39+
pub ordinal: u16,
3740
}
3841

3942
impl LLVMRustCOFFShortExport {
40-
pub fn from_name(name: *const c_char) -> LLVMRustCOFFShortExport {
41-
LLVMRustCOFFShortExport { name }
43+
pub fn new(name: *const c_char, ordinal: Option<u16>) -> LLVMRustCOFFShortExport {
44+
LLVMRustCOFFShortExport {
45+
name,
46+
ordinal_present: ordinal.is_some(),
47+
ordinal: ordinal.unwrap_or(0),
48+
}
4249
}
4350
}
4451

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -1753,10 +1753,11 @@ LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) {
17531753
}
17541754

17551755
// This struct contains all necessary info about a symbol exported from a DLL.
1756-
// At the moment, it's just the symbol's name, but we use a separate struct to
1757-
// make it easier to add other information like ordinal later.
17581756
struct LLVMRustCOFFShortExport {
17591757
const char* name;
1758+
bool ordinal_present;
1759+
// The value of `ordinal` is only meaningful if `ordinal_present` is true.
1760+
uint16_t ordinal;
17601761
};
17611762

17621763
// Machine must be a COFF machine type, as defined in PE specs.
@@ -1772,13 +1773,15 @@ extern "C" LLVMRustResult LLVMRustWriteImportLibrary(
17721773
ConvertedExports.reserve(NumExports);
17731774

17741775
for (size_t i = 0; i < NumExports; ++i) {
1776+
bool ordinal_present = Exports[i].ordinal_present;
1777+
uint16_t ordinal = ordinal_present ? Exports[i].ordinal : 0;
17751778
ConvertedExports.push_back(llvm::object::COFFShortExport{
17761779
Exports[i].name, // Name
17771780
std::string{}, // ExtName
17781781
std::string{}, // SymbolName
17791782
std::string{}, // AliasTarget
1780-
0, // Ordinal
1781-
false, // Noname
1783+
ordinal, // Ordinal
1784+
ordinal_present, // Noname
17821785
false, // Data
17831786
false, // Private
17841787
false // Constant

compiler/rustc_metadata/src/native_libs.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,12 @@ impl Collector<'tcx> {
433433
}
434434
}
435435
};
436-
DllImport { name: item.ident.name, ordinal: None, calling_convention, span: item.span }
436+
437+
DllImport {
438+
name: item.ident.name,
439+
ordinal: self.tcx.codegen_fn_attrs(item.id.def_id).link_ordinal,
440+
calling_convention,
441+
span: item.span,
442+
}
437443
}
438444
}

compiler/rustc_middle/src/middle/codegen_fn_attrs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub struct CodegenFnAttrs {
2222
/// imported function has in the dynamic library. Note that this must not
2323
/// be set when `link_name` is set. This is for foreign items with the
2424
/// "raw-dylib" kind.
25-
pub link_ordinal: Option<usize>,
25+
pub link_ordinal: Option<u16>,
2626
/// The `#[target_feature(enable = "...")]` attribute and the enabled
2727
/// features (only enabled features are supported right now).
2828
pub target_features: Vec<Symbol>,

compiler/rustc_typeck/src/collect.rs

+31-4
Original file line numberDiff line numberDiff line change
@@ -2861,6 +2861,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
28612861
} else if attr.has_name(sym::link_name) {
28622862
codegen_fn_attrs.link_name = attr.value_str();
28632863
} else if attr.has_name(sym::link_ordinal) {
2864+
if link_ordinal_span.is_some() {
2865+
tcx.sess
2866+
.struct_span_err(
2867+
attr.span,
2868+
"multiple `link_ordinal` attributes on a single definition",
2869+
)
2870+
.emit();
2871+
}
28642872
link_ordinal_span = Some(attr.span);
28652873
if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
28662874
codegen_fn_attrs.link_ordinal = ordinal;
@@ -3156,22 +3164,41 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
31563164
false
31573165
}
31583166

3159-
fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<usize> {
3167+
fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
31603168
use rustc_ast::{Lit, LitIntType, LitKind};
31613169
let meta_item_list = attr.meta_item_list();
31623170
let meta_item_list: Option<&[ast::NestedMetaItem]> = meta_item_list.as_ref().map(Vec::as_ref);
31633171
let sole_meta_list = match meta_item_list {
31643172
Some([item]) => item.literal(),
3173+
Some(_) => {
3174+
tcx.sess
3175+
.struct_span_err(attr.span, "incorrect number of arguments to `#[link_ordinal]`")
3176+
.note("the attribute requires exactly one argument")
3177+
.emit();
3178+
return None;
3179+
}
31653180
_ => None,
31663181
};
31673182
if let Some(Lit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = sole_meta_list {
3168-
if *ordinal <= usize::MAX as u128 {
3169-
Some(*ordinal as usize)
3183+
// According to the table at https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header,
3184+
// the ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
3185+
// in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import information
3186+
// to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
3187+
//
3188+
// FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for this:
3189+
// both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that specifies
3190+
// a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import library
3191+
// for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an import
3192+
// library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I don't know yet
3193+
// if the resulting EXE runs, as I haven't yet built the necessary DLL -- see earlier comment
3194+
// about LINK.EXE failing.)
3195+
if *ordinal <= u16::MAX as u128 {
3196+
Some(*ordinal as u16)
31703197
} else {
31713198
let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal);
31723199
tcx.sess
31733200
.struct_span_err(attr.span, &msg)
3174-
.note("the value may not exceed `usize::MAX`")
3201+
.note("the value may not exceed `u16::MAX`")
31753202
.emit();
31763203
None
31773204
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Test the behavior of #[link(.., kind = "raw-dylib")] and #[link_ordinal] on windows-msvc
2+
3+
# only-windows-msvc
4+
5+
-include ../../run-make-fulldeps/tools.mk
6+
7+
all:
8+
$(call COMPILE_OBJ,"$(TMPDIR)"/exporter.obj,exporter.c)
9+
$(CC) "$(TMPDIR)"/exporter.obj exporter.def -link -dll -out:"$(TMPDIR)"/exporter.dll
10+
$(RUSTC) --crate-type lib --crate-name raw_dylib_test lib.rs
11+
$(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)"
12+
"$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt
13+
14+
ifdef RUSTC_BLESS_TEST
15+
cp "$(TMPDIR)"/output.txt output.txt
16+
else
17+
$(DIFF) output.txt "$(TMPDIR)"/output.txt
18+
endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
extern crate raw_dylib_test;
2+
3+
fn main() {
4+
raw_dylib_test::library_function();
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#include <stdio.h>
2+
3+
void exported_function() {
4+
printf("exported_function\n");
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
LIBRARY exporter
2+
EXPORTS
3+
exported_function @13 NONAME
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![feature(raw_dylib)]
2+
3+
#[link(name = "exporter", kind = "raw-dylib")]
4+
extern {
5+
#[link_ordinal(13)]
6+
fn imported_function();
7+
}
8+
9+
pub fn library_function() {
10+
unsafe {
11+
imported_function();
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
exported_function
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![feature(raw_dylib)]
2+
//~^ WARN the feature `raw_dylib` is incomplete
3+
4+
#[link(name = "foo")]
5+
extern "C" {
6+
#[link_ordinal()]
7+
//~^ ERROR incorrect number of arguments to `#[link_ordinal]`
8+
fn foo();
9+
}
10+
11+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/link-ordinal-missing-argument.rs:1:12
3+
|
4+
LL | #![feature(raw_dylib)]
5+
| ^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+
= note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information
9+
10+
error: incorrect number of arguments to `#[link_ordinal]`
11+
--> $DIR/link-ordinal-missing-argument.rs:6:5
12+
|
13+
LL | #[link_ordinal()]
14+
| ^^^^^^^^^^^^^^^^^
15+
|
16+
= note: the attribute requires exactly one argument
17+
18+
error: aborting due to previous error; 1 warning emitted
19+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// only-windows-msvc
2+
#![feature(raw_dylib)]
3+
//~^ WARN the feature `raw_dylib` is incomplete
4+
5+
#[link(name = "foo", kind = "raw-dylib")]
6+
extern "C" {
7+
#[link_ordinal(1)]
8+
#[link_ordinal(2)]
9+
//~^ ERROR multiple `link_ordinal` attributes on a single definition
10+
fn foo();
11+
}
12+
13+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/link-ordinal-multiple.rs:2:12
3+
|
4+
LL | #![feature(raw_dylib)]
5+
| ^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+
= note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information
9+
10+
error: multiple `link_ordinal` attributes on a single definition
11+
--> $DIR/link-ordinal-multiple.rs:8:5
12+
|
13+
LL | #[link_ordinal(2)]
14+
| ^^^^^^^^^^^^^^^^^^
15+
16+
error: aborting due to previous error; 1 warning emitted
17+

src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-large.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
#[link(name = "foo")]
55
extern "C" {
6-
#[link_ordinal(18446744073709551616)]
7-
//~^ ERROR ordinal value in `link_ordinal` is too large: `18446744073709551616`
6+
#[link_ordinal(72436)]
7+
//~^ ERROR ordinal value in `link_ordinal` is too large: `72436`
88
fn foo();
99
}
1010

src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-large.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ LL | #![feature(raw_dylib)]
77
= note: `#[warn(incomplete_features)]` on by default
88
= note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information
99

10-
error: ordinal value in `link_ordinal` is too large: `18446744073709551616`
10+
error: ordinal value in `link_ordinal` is too large: `72436`
1111
--> $DIR/link-ordinal-too-large.rs:6:5
1212
|
13-
LL | #[link_ordinal(18446744073709551616)]
14-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13+
LL | #[link_ordinal(72436)]
14+
| ^^^^^^^^^^^^^^^^^^^^^^
1515
|
16-
= note: the value may not exceed `usize::MAX`
16+
= note: the value may not exceed `u16::MAX`
1717

1818
error: aborting due to previous error; 1 warning emitted
1919

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![feature(raw_dylib)]
2+
//~^ WARN the feature `raw_dylib` is incomplete
3+
4+
#[link(name = "foo")]
5+
extern "C" {
6+
#[link_ordinal(3, 4)]
7+
//~^ ERROR incorrect number of arguments to `#[link_ordinal]`
8+
fn foo();
9+
}
10+
11+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/link-ordinal-too-many-arguments.rs:1:12
3+
|
4+
LL | #![feature(raw_dylib)]
5+
| ^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+
= note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information
9+
10+
error: incorrect number of arguments to `#[link_ordinal]`
11+
--> $DIR/link-ordinal-too-many-arguments.rs:6:5
12+
|
13+
LL | #[link_ordinal(3, 4)]
14+
| ^^^^^^^^^^^^^^^^^^^^^
15+
|
16+
= note: the attribute requires exactly one argument
17+
18+
error: aborting due to previous error; 1 warning emitted
19+

0 commit comments

Comments
 (0)