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

Implement #[link_ordinal(n)] #89025

Merged
merged 1 commit into from
Oct 8, 2021
Merged
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
10 changes: 5 additions & 5 deletions compiler/rustc_codegen_llvm/src/back/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,13 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
// All import names are Rust identifiers and therefore cannot contain \0 characters.
// FIXME: when support for #[link_name] implemented, ensure that import.name values don't
// have any \0 characters
let import_name_vector: Vec<CString> = dll_imports
let import_name_and_ordinal_vector: Vec<(CString, Option<u16>)> = dll_imports
.iter()
.map(|import: &DllImport| {
if self.config.sess.target.arch == "x86" {
LlvmArchiveBuilder::i686_decorated_name(import)
(LlvmArchiveBuilder::i686_decorated_name(import), import.ordinal)
} else {
CString::new(import.name.to_string()).unwrap()
(CString::new(import.name.to_string()).unwrap(), import.ordinal)
}
})
.collect();
Expand All @@ -184,9 +184,9 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
dll_imports.iter().map(|import| import.name.to_string()).collect::<Vec<_>>().join(", "),
);

let ffi_exports: Vec<LLVMRustCOFFShortExport> = import_name_vector
let ffi_exports: Vec<LLVMRustCOFFShortExport> = import_name_and_ordinal_vector
.iter()
.map(|name_z| LLVMRustCOFFShortExport::from_name(name_z.as_ptr()))
.map(|(name_z, ordinal)| LLVMRustCOFFShortExport::new(name_z.as_ptr(), *ordinal))
.collect();
let result = unsafe {
crate::llvm::LLVMRustWriteImportLibrary(
Expand Down
11 changes: 9 additions & 2 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,18 @@ pub enum LLVMRustResult {
#[repr(C)]
pub struct LLVMRustCOFFShortExport {
pub name: *const c_char,
pub ordinal_present: bool,
// value of `ordinal` only important when `ordinal_present` is true
pub ordinal: u16,
}

impl LLVMRustCOFFShortExport {
pub fn from_name(name: *const c_char) -> LLVMRustCOFFShortExport {
LLVMRustCOFFShortExport { name }
pub fn new(name: *const c_char, ordinal: Option<u16>) -> LLVMRustCOFFShortExport {
LLVMRustCOFFShortExport {
name,
ordinal_present: ordinal.is_some(),
ordinal: ordinal.unwrap_or(0),
}
}
}

Expand Down
11 changes: 7 additions & 4 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1749,10 +1749,11 @@ LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) {
}

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

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

for (size_t i = 0; i < NumExports; ++i) {
bool ordinal_present = Exports[i].ordinal_present;
uint16_t ordinal = ordinal_present ? Exports[i].ordinal : 0;
ConvertedExports.push_back(llvm::object::COFFShortExport{
Exports[i].name, // Name
std::string{}, // ExtName
std::string{}, // SymbolName
std::string{}, // AliasTarget
0, // Ordinal
false, // Noname
ordinal, // Ordinal
ordinal_present, // Noname
false, // Data
false, // Private
false // Constant
Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_metadata/src/native_libs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,12 @@ impl Collector<'tcx> {
}
}
};
DllImport { name: item.ident.name, ordinal: None, calling_convention, span: item.span }

DllImport {
name: item.ident.name,
ordinal: self.tcx.codegen_fn_attrs(item.id.def_id).link_ordinal,
calling_convention,
span: item.span,
}
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub struct CodegenFnAttrs {
/// imported function has in the dynamic library. Note that this must not
/// be set when `link_name` is set. This is for foreign items with the
/// "raw-dylib" kind.
pub link_ordinal: Option<usize>,
pub link_ordinal: Option<u16>,
/// The `#[target_feature(enable = "...")]` attribute and the enabled
/// features (only enabled features are supported right now).
pub target_features: Vec<Symbol>,
Expand Down
35 changes: 31 additions & 4 deletions compiler/rustc_typeck/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2858,6 +2858,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
} else if attr.has_name(sym::link_name) {
codegen_fn_attrs.link_name = attr.value_str();
} else if attr.has_name(sym::link_ordinal) {
if link_ordinal_span.is_some() {
tcx.sess
.struct_span_err(
attr.span,
"multiple `link_ordinal` attributes on a single definition",
)
.emit();
}
link_ordinal_span = Some(attr.span);
if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
codegen_fn_attrs.link_ordinal = ordinal;
Expand Down Expand Up @@ -3153,22 +3161,41 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
false
}

fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<usize> {
fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
use rustc_ast::{Lit, LitIntType, LitKind};
let meta_item_list = attr.meta_item_list();
let meta_item_list: Option<&[ast::NestedMetaItem]> = meta_item_list.as_ref().map(Vec::as_ref);
let sole_meta_list = match meta_item_list {
Some([item]) => item.literal(),
Some(_) => {
tcx.sess
.struct_span_err(attr.span, "incorrect number of arguments to `#[link_ordinal]`")
.note("the attribute requires exactly one argument")
.emit();
return None;
}
_ => None,
};
if let Some(Lit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = sole_meta_list {
if *ordinal <= usize::MAX as u128 {
Some(*ordinal as usize)
// According to the table at https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header,
// the ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
// in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import information
// to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
//
// FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for this:
// both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that specifies
// a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import library
// for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an import
// library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I don't know yet
// if the resulting EXE runs, as I haven't yet built the necessary DLL -- see earlier comment
// about LINK.EXE failing.)
if *ordinal <= u16::MAX as u128 {
Some(*ordinal as u16)
} else {
let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal);
tcx.sess
.struct_span_err(attr.span, &msg)
.note("the value may not exceed `usize::MAX`")
.note("the value may not exceed `u16::MAX`")
.emit();
None
}
Expand Down
18 changes: 18 additions & 0 deletions src/test/run-make/raw-dylib-link-ordinal/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Test the behavior of #[link(.., kind = "raw-dylib")] and #[link_ordinal] on windows-msvc

# only-windows-msvc

-include ../../run-make-fulldeps/tools.mk

all:
$(call COMPILE_OBJ,"$(TMPDIR)"/exporter.obj,exporter.c)
$(CC) "$(TMPDIR)"/exporter.obj exporter.def -link -dll -out:"$(TMPDIR)"/exporter.dll
$(RUSTC) --crate-type lib --crate-name raw_dylib_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
5 changes: 5 additions & 0 deletions src/test/run-make/raw-dylib-link-ordinal/driver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
extern crate raw_dylib_test;

fn main() {
raw_dylib_test::library_function();
}
5 changes: 5 additions & 0 deletions src/test/run-make/raw-dylib-link-ordinal/exporter.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include <stdio.h>

void exported_function() {
printf("exported_function\n");
}
3 changes: 3 additions & 0 deletions src/test/run-make/raw-dylib-link-ordinal/exporter.def
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
LIBRARY exporter
EXPORTS
exported_function @13 NONAME
13 changes: 13 additions & 0 deletions src/test/run-make/raw-dylib-link-ordinal/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#![feature(raw_dylib)]

#[link(name = "exporter", kind = "raw-dylib")]
extern {
#[link_ordinal(13)]
fn imported_function();
}

pub fn library_function() {
unsafe {
imported_function();
}
}
1 change: 1 addition & 0 deletions src/test/run-make/raw-dylib-link-ordinal/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exported_function
11 changes: 11 additions & 0 deletions src/test/ui/rfc-2627-raw-dylib/link-ordinal-missing-argument.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![feature(raw_dylib)]
//~^ WARN the feature `raw_dylib` is incomplete

#[link(name = "foo")]
extern "C" {
#[link_ordinal()]
//~^ ERROR incorrect number of arguments to `#[link_ordinal]`
fn foo();
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/link-ordinal-missing-argument.rs:1: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: incorrect number of arguments to `#[link_ordinal]`
--> $DIR/link-ordinal-missing-argument.rs:6:5
|
LL | #[link_ordinal()]
| ^^^^^^^^^^^^^^^^^
|
= note: the attribute requires exactly one argument

error: aborting due to previous error; 1 warning emitted

13 changes: 13 additions & 0 deletions src/test/ui/rfc-2627-raw-dylib/link-ordinal-multiple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// only-windows-msvc
#![feature(raw_dylib)]
//~^ WARN the feature `raw_dylib` is incomplete

#[link(name = "foo", kind = "raw-dylib")]
extern "C" {
#[link_ordinal(1)]
#[link_ordinal(2)]
//~^ ERROR multiple `link_ordinal` attributes on a single definition
fn foo();
}

fn main() {}
17 changes: 17 additions & 0 deletions src/test/ui/rfc-2627-raw-dylib/link-ordinal-multiple.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/link-ordinal-multiple.rs:2: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 `link_ordinal` attributes on a single definition
--> $DIR/link-ordinal-multiple.rs:8:5
|
LL | #[link_ordinal(2)]
| ^^^^^^^^^^^^^^^^^^

error: aborting due to previous error; 1 warning emitted

4 changes: 2 additions & 2 deletions src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-large.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

#[link(name = "foo")]
extern "C" {
#[link_ordinal(18446744073709551616)]
//~^ ERROR ordinal value in `link_ordinal` is too large: `18446744073709551616`
#[link_ordinal(72436)]
//~^ ERROR ordinal value in `link_ordinal` is too large: `72436`
fn foo();
}

Expand Down
8 changes: 4 additions & 4 deletions src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-large.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ 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: ordinal value in `link_ordinal` is too large: `18446744073709551616`
error: ordinal value in `link_ordinal` is too large: `72436`
--> $DIR/link-ordinal-too-large.rs:6:5
|
LL | #[link_ordinal(18446744073709551616)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | #[link_ordinal(72436)]
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: the value may not exceed `usize::MAX`
= note: the value may not exceed `u16::MAX`

error: aborting due to previous error; 1 warning emitted

11 changes: 11 additions & 0 deletions src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![feature(raw_dylib)]
//~^ WARN the feature `raw_dylib` is incomplete

#[link(name = "foo")]
extern "C" {
#[link_ordinal(3, 4)]
//~^ ERROR incorrect number of arguments to `#[link_ordinal]`
fn foo();
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/link-ordinal-too-many-arguments.rs:1: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: incorrect number of arguments to `#[link_ordinal]`
--> $DIR/link-ordinal-too-many-arguments.rs:6:5
|
LL | #[link_ordinal(3, 4)]
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: the attribute requires exactly one argument

error: aborting due to previous error; 1 warning emitted