Skip to content

Commit 142f6c0

Browse files
committed
Implement #[link_ordinal] attribute in the context of #[link(kind = "raw-dylib")].
1 parent 60e70cc commit 142f6c0

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
@@ -1749,10 +1749,11 @@ LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) {
17491749
}
17501750

17511751
// This struct contains all necessary info about a symbol exported from a DLL.
1752-
// At the moment, it's just the symbol's name, but we use a separate struct to
1753-
// make it easier to add other information like ordinal later.
17541752
struct LLVMRustCOFFShortExport {
17551753
const char* name;
1754+
bool ordinal_present;
1755+
// The value of `ordinal` is only meaningful if `ordinal_present` is true.
1756+
uint16_t ordinal;
17561757
};
17571758

17581759
// Machine must be a COFF machine type, as defined in PE specs.
@@ -1768,13 +1769,15 @@ extern "C" LLVMRustResult LLVMRustWriteImportLibrary(
17681769
ConvertedExports.reserve(NumExports);
17691770

17701771
for (size_t i = 0; i < NumExports; ++i) {
1772+
bool ordinal_present = Exports[i].ordinal_present;
1773+
uint16_t ordinal = ordinal_present ? Exports[i].ordinal : 0;
17711774
ConvertedExports.push_back(llvm::object::COFFShortExport{
17721775
Exports[i].name, // Name
17731776
std::string{}, // ExtName
17741777
std::string{}, // SymbolName
17751778
std::string{}, // AliasTarget
1776-
0, // Ordinal
1777-
false, // Noname
1779+
ordinal, // Ordinal
1780+
ordinal_present, // Noname
17781781
false, // Data
17791782
false, // Private
17801783
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
@@ -2858,6 +2858,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
28582858
} else if attr.has_name(sym::link_name) {
28592859
codegen_fn_attrs.link_name = attr.value_str();
28602860
} else if attr.has_name(sym::link_ordinal) {
2861+
if link_ordinal_span.is_some() {
2862+
tcx.sess
2863+
.struct_span_err(
2864+
attr.span,
2865+
"multiple `link_ordinal` attributes on a single definition",
2866+
)
2867+
.emit();
2868+
}
28612869
link_ordinal_span = Some(attr.span);
28622870
if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
28632871
codegen_fn_attrs.link_ordinal = ordinal;
@@ -3153,22 +3161,41 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
31533161
false
31543162
}
31553163

3156-
fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<usize> {
3164+
fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
31573165
use rustc_ast::{Lit, LitIntType, LitKind};
31583166
let meta_item_list = attr.meta_item_list();
31593167
let meta_item_list: Option<&[ast::NestedMetaItem]> = meta_item_list.as_ref().map(Vec::as_ref);
31603168
let sole_meta_list = match meta_item_list {
31613169
Some([item]) => item.literal(),
3170+
Some(_) => {
3171+
tcx.sess
3172+
.struct_span_err(attr.span, "incorrect number of arguments to `#[link_ordinal]`")
3173+
.note("the attribute requires exactly one argument")
3174+
.emit();
3175+
return None;
3176+
}
31623177
_ => None,
31633178
};
31643179
if let Some(Lit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = sole_meta_list {
3165-
if *ordinal <= usize::MAX as u128 {
3166-
Some(*ordinal as usize)
3180+
// According to the table at https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header,
3181+
// the ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
3182+
// in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import information
3183+
// to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
3184+
//
3185+
// FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for this:
3186+
// both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that specifies
3187+
// a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import library
3188+
// for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an import
3189+
// library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I don't know yet
3190+
// if the resulting EXE runs, as I haven't yet built the necessary DLL -- see earlier comment
3191+
// about LINK.EXE failing.)
3192+
if *ordinal <= u16::MAX as u128 {
3193+
Some(*ordinal as u16)
31673194
} else {
31683195
let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal);
31693196
tcx.sess
31703197
.struct_span_err(attr.span, &msg)
3171-
.note("the value may not exceed `usize::MAX`")
3198+
.note("the value may not exceed `u16::MAX`")
31723199
.emit();
31733200
None
31743201
}
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)