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

export_name works for msvc x86_64 toolchain, but not i686 #44282

Open
elitak opened this issue Sep 3, 2017 · 14 comments · May be fixed by #130808
Open

export_name works for msvc x86_64 toolchain, but not i686 #44282

elitak opened this issue Sep 3, 2017 · 14 comments · May be fixed by #130808
Labels
A-linkage Area: linking into static, shared libraries and binaries C-bug Category: This is a bug. O-x86_32 Target: x86 processors, 32 bit (like i686-*) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@elitak
Copy link

elitak commented Sep 3, 2017

I'm trying to build a simple dll and export a function with a pre-mangled name. I have crate-type set to cdylib and my function decorated like so:

#[allow(non_snake_case)]
#[export_name="?Hook@@YAXXZ"] // This is how MSVC mangles this name.
pub fn Hook() {

I'm using MSVC 2017 Community edition v14.10.25017; toggling between these two toolchains using rustup:
nightly-x86_64-pc-windows-msvc unchanged - rustc 1.22.0-nightly (f861b6ee4 2017-09-01)
nightly-i686-pc-windows-msvc unchanged - rustc 1.22.0-nightly (f861b6ee4 2017-09-01)

The former links fine, the latter gives this error:
mydll.dll.exp: error LNK2001: unresolved external symbol "void __cdecl Hook(void)" (?Hook@@YAXXZ)
fatal error LNK1120: 1 unresolved externals

@alexcrichton
Copy link
Member

Perhaps try using #[export_name="\x01?Hook@@YAXXZ"]? (an LLVM-ism)

@alexcrichton alexcrichton added A-linkage Area: linking into static, shared libraries and binaries C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Sep 7, 2017
@elitak
Copy link
Author

elitak commented Sep 8, 2017

Same deal. I tried a bunch of different combinations and it looks like "\x01" and "@" anywhere in the string trips it up (e.g. "a@b"). Again, this is only on the x86 and not the equivalent x64 toolchain, which seems pretty inexplicable to me, but then I know very little about LLVM.

@alexcrichton
Copy link
Member

Does making it pub extern help vs pub fn?

@elitak
Copy link
Author

elitak commented Sep 8, 2017

I already guessed all sorts of extern types, to no avail. Even if I could trick it into working, it wouldn't explain why the x64 toolchain has no problem with the simpler form. Shouldn't the function compile and link without any changes to its annotations?

Are you able to reproduce the problem? I'll make a simple example project if you can't.

@elitak
Copy link
Author

elitak commented Sep 8, 2017

Here, try building this with the two distinct toolchains mentioned in OP: https://github.com/elitak/rust-issue44282

The x86_64 will link and the i686 won't.

@alexcrichton
Copy link
Member

Sorry no I haven't been trying to reproduce locally, just throwing out some suggestions I could think of

@elitak
Copy link
Author

elitak commented Sep 8, 2017 via email

@retep998
Copy link
Member

retep998 commented Sep 9, 2017

I've experienced this issue before and as far as I can tell it occurs due to the combination of 32-bit name decoration (which 64-bit doesn't have), and the fact that Rust uses a .DEF file for exports, Specifically I believe it is interpreting @ as signifying stdcall, so it tries makes the .dll export the symbol without the @whatever suffix but then something goes wrong and it fails to resolve it. I'm not entirely sure.

@super-continent
Copy link

super-continent commented Jun 18, 2020

This is an old issue but has anyone figured out a solution? I have this issue when compiling something for i686-pc-windows-msvc and haven't been able to fix it.

It seems I'm getting decorations from MSVC on the name. I don't know if that would be fixable from a rust command or if i would need to edit something else.

Just for clarification, I've gotten this issue on using #[no_mangle], #[link_name], and the #[export_name="\x01whatever_name"] trick

@elitak
Copy link
Author

elitak commented Jun 18, 2020

I never found a workaround, but I also didn't try diving into the generated .def files or anything as was suggested at the end.

@brandonros
Copy link

brandonros commented Dec 7, 2022

I found a workaround.

ordinals.def:

LIBRARY "redacted.dll"
EXPORTS
  _Java_redacted@32

lib.rs:

#[export_name = "Java_redacted"]
pub unsafe extern "stdcall" fn Java_redacted(unk1: c_void, unk2: c_void, unk3: c_void, unk4: c_void, unk5: c_void, unk6: c_void, unk7: c_void, unk8: c_void) {
  panic!("TODO");
}

build.rs:

fn main() {
  println!("cargo:rustc-cdylib-link-arg=/DEF:./hook/ordinals.def");
}

Cargo.toml:

[package]
name = "hook"
version = "0.1.0"
edition = "2021"

[dependencies]
...

[lib]
crate-type = ["cdylib"]

@Noratrieb Noratrieb added O-x86_32 Target: x86 processors, 32 bit (like i686-*) and removed O-x86-all labels Oct 25, 2023
@ultimaweapon
Copy link

Just stumbled upon this issue on a static item. My use case is I need to define a C++ variable on Rust side like this:

extern CModeMgr *g_modeMgr;
#[export_name = "?g_modeMgr@@3PAVCModeMgr@@A"]
static mut G_MODE_MGR: usize = 0;

This will cause the following error:

symbols.o : error LNK2001: unresolved external symbol _?g_modeMgr@@3PAVCModeMgr@@A

I was tried to prefix the export_name with \x01 and the error is still the same:

#[export_name = "\x01?g_modeMgr@@3PAVCModeMgr@@A"]
static mut G_MODE_MGR: usize = 0;

@checkraisefold
Copy link

checkraisefold commented Sep 23, 2024

After doing a deep dive, the issue is pretty multifaceted:
First of all, linked symbol names are required to have a underscore decoration on 32-bit. There is no way around this.

This is normally fine, but as per this issue, problems arise whenever it's a MSVC-format C++ mangled symbol that you're trying to export with export_name.
Rust has no handling for this (probably because this is an obscure implementation detail). However, LLVM correctly mangles COFF symbols; if they are C++ mangled, they should not have a leading underscore, because MSVC doesn't emit them with a leading underscore. See:
https://github.com/llvm/llvm-project/blob/main/llvm/lib/IR/Mangler.cpp#L48-L49
https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/IR/DataLayout.h#L255-L257

So, our .rcgu.o is emitted with no leading underscores for its mangled symbols. But symbols.o in the final linking step is emitted with leading underscores for mangled symbols! Alas, the trap has been set: you can never get this to link without a custom .def file and custom linker arguments.

And in fact, the issue is even worse because C and C++ libraries in MSVC are able to completely omit the leading underscore with a #pragma comment or .def file. This would lead you to having to make a custom .def file solely to emit symbols without the leading underscore for even non-C++ mangled symbols.
(EDIT: Above is a sort of non-issue when linking actually succeeds, looks like export_name is respected with an alias in final product)

"Why does using \x01 in the export_name not fix this?"
Well, unfortunately, while it does fix the .rcgu.o codegenned file symbol name (where Test_Function is using the \x01 and OtherTest_Function is not):

DUMPBIN (https://learn.microsoft.com/en-us/cpp/build/reference/dumpbin-reference?view=msvc-170) output
1FD0 00000000 SECTA9C notype ()    External    | Test_Function
1FD3 00000000 SECTA9D notype ()    External    | _OtherTest_Function

The linker still explodes, because now the lib.def file has been malformed, because the \x01 byte is not stripped when it is written:

LIBRARY
EXPORTS
  �Test_Function ; incorrectly sorted and at the top
...
  OtherTest_Function ; correctly sorted, no 0x01 byte
= note: lib.def : error LNK2001: unresolved external symbol ␁Test_Function

"Why does this not happen in x86_64?"
Simple: there's no name mangling at all in that architecture.

Given these results, there should either be:

  1. An attribute to disable linking decoration entirely;
  2. no_mangle to disable linking decoration entirely, could lead to compatibility breakage;
  3. Keep the obscure and undocumented \x01 trick, but strip it when creating .def file. Probably a bad idea;
  4. export_name to ignore decoration entirely and export with the given name. This sounds like it would break backwards ABI compatibility and be a very bad thing;
  5. As a fix for solely C++ symbols, copy-paste the LLVM check for COFF on 32 bit only, and don't emit an underscore if there's a leading question mark. This is probably the best fix for now. Won't break any compatibility since nothing with a leading question mark in export_name can link anyway right now. In addition, this would conform to what LLVM does exactly.

@checkraisefold
Copy link

Came up with a patch for this, though it's definitely hasty. Works though.

diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index 77c35a1fe7..7de4a897ec 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -572,6 +572,7 @@ pub(crate) fn linking_symbol_name_for_instance_in_crate<'tcx>(
     }
 
     let prefix = match &target.arch[..] {
+        "x86" if target.is_like_windows && undecorated.starts_with("?") => return undecorated,
         "x86" => Some('_'),
         "x86_64" => None,
         "arm64ec" => Some('#'),

out.patch

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Jan 25, 2025
…nking, r=davidtwco

Fix linking for symbols starting with ? on i686-pc-windows-msvc

When using the `export_name` attribute to specifically export a symbol beginning with a question mark on the `i686-pc-windows-msvc` target, that symbol will fail to link and throw a linker error 100% of the time.
[Issue writeup.](rust-lang#44282 (comment))

Closes rust-lang#44282

I'm not sure if this is a proper solution, but [LLVM does the same check](https://github.com/llvm/llvm-project/blob/main/llvm/lib/IR/Mangler.cpp#L48-L49) which causes this issue, and is applied to [all 32- and 64-bit Windows COFF objects](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/IR/DataLayout.h#L255-L257) (maybe the same patch should be applied for 64 bit windows as well then?). I am *more* unsure of whether this is the proper place for such a solution (and if the exact conditions of is_like_windows are proper for this usecase), or if the underscore should be stripped elsewhere, but it seems like the most correct place.

I'm also unsure if there are any backwards compatibility ramifications here. There shouldn't be, because binaries with exported symbols starting with `?` for this target failed to link because of this issue anyway, but still.
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Jan 25, 2025
…nking, r=davidtwco

Fix linking for symbols starting with ? on i686-pc-windows-msvc

When using the `export_name` attribute to specifically export a symbol beginning with a question mark on the `i686-pc-windows-msvc` target, that symbol will fail to link and throw a linker error 100% of the time.
[Issue writeup.](rust-lang#44282 (comment))

Closes rust-lang#44282

I'm not sure if this is a proper solution, but [LLVM does the same check](https://github.com/llvm/llvm-project/blob/main/llvm/lib/IR/Mangler.cpp#L48-L49) which causes this issue, and is applied to [all 32- and 64-bit Windows COFF objects](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/IR/DataLayout.h#L255-L257) (maybe the same patch should be applied for 64 bit windows as well then?). I am *more* unsure of whether this is the proper place for such a solution (and if the exact conditions of is_like_windows are proper for this usecase), or if the underscore should be stripped elsewhere, but it seems like the most correct place.

I'm also unsure if there are any backwards compatibility ramifications here. There shouldn't be, because binaries with exported symbols starting with `?` for this target failed to link because of this issue anyway, but still.
bors added a commit to rust-lang-ci/rust that referenced this issue Jan 25, 2025
…ing, r=<try>

Fix linking for symbols starting with ? on i686-pc-windows-msvc

When using the `export_name` attribute to specifically export a symbol beginning with a question mark on the `i686-pc-windows-msvc` target, that symbol will fail to link and throw a linker error 100% of the time.
[Issue writeup.](rust-lang#44282 (comment))

Closes rust-lang#44282

I'm not sure if this is a proper solution, but [LLVM does the same check](https://github.com/llvm/llvm-project/blob/main/llvm/lib/IR/Mangler.cpp#L48-L49) which causes this issue, and is applied to [all 32- and 64-bit Windows COFF objects](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/IR/DataLayout.h#L255-L257) (maybe the same patch should be applied for 64 bit windows as well then?). I am *more* unsure of whether this is the proper place for such a solution (and if the exact conditions of is_like_windows are proper for this usecase), or if the underscore should be stripped elsewhere, but it seems like the most correct place.

I'm also unsure if there are any backwards compatibility ramifications here. There shouldn't be, because binaries with exported symbols starting with `?` for this target failed to link because of this issue anyway, but still.

 try-job: i686-mingw
bors added a commit to rust-lang-ci/rust that referenced this issue Jan 31, 2025
…ing, r=davidtwco

Fix linking for symbols starting with ? on i686-pc-windows-msvc

When using the `export_name` attribute to specifically export a symbol beginning with a question mark on the `i686-pc-windows-msvc` target, that symbol will fail to link and throw a linker error 100% of the time.
[Issue writeup.](rust-lang#44282 (comment))

Closes rust-lang#44282

I'm not sure if this is a proper solution, but [LLVM does the same check](https://github.com/llvm/llvm-project/blob/main/llvm/lib/IR/Mangler.cpp#L48-L49) which causes this issue, and is applied to [all 32- and 64-bit Windows COFF objects](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/IR/DataLayout.h#L255-L257) (maybe the same patch should be applied for 64 bit windows as well then?). I am *more* unsure of whether this is the proper place for such a solution (and if the exact conditions of is_like_windows are proper for this usecase), or if the underscore should be stripped elsewhere, but it seems like the most correct place.

I'm also unsure if there are any backwards compatibility ramifications here. There shouldn't be, because binaries with exported symbols starting with `?` for this target failed to link because of this issue anyway, but still.

 try-job: i686-mingw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-linkage Area: linking into static, shared libraries and binaries C-bug Category: This is a bug. O-x86_32 Target: x86 processors, 32 bit (like i686-*) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants