error LNK2019: unresolved external symbol __imp_yourexternFunc
I have a C DLL function that is external called "output" which is similar to printf:
output( format , va_args);
In *.h files its declared:
__declspec( dllexport ) void output( LPCTSTR format, ... );
or
__declspec( dllimport ) void output( LPCTSTR format, ... );
(for *.h includes) there is a MACRO that selects between export/import base on usage
In my rust module I declare it extern as:
#[link(name="aDLL", kind="dylib")]
extern {
fn output( format:LPCTSTR, ...);
}
The dumpbin for this function is as follows (from dumpbin)
31 ?output##YAXPEBDZZ (void __cdecl output(char const *,...))
But when I attempt to link this the rustc linker is prepending _imp to the function name:
second_rust_lib_v0.second_rust_lib_v0.ay01u8ua-cgu.6.rcgu.o : error LNK2019: unresolved external symbol __imp_output referenced in function print_something
On windows linking DLLs goes through a trampoline library (.lib file) which generates the right bindings. The convention for these is to prefix the function names with __imp__ (there is a related C++ answer).
There is an open issue that explains some of the difficulties creating and linking rust dlls under windows.
Here are the relevant bits:
If you start developing on Windows, Rust will produce a mylib.dll and mylib.dll.lib. To use this lib again from Rust you will have to specify #[link(name = "mylib.dll")], thus giving the impression that the full file name has to be specified. On Mac, however, #[link(name = "libmylib.dylib"] will fail (likewise Linux).
If you start developing on Mac and Linux, #[link(name = "mylib")] just works, giving you the impression Rust handles the name resolution (fully) automatically like other platforms that just require the base name.
In fact, the correct way to cross platform link against a dylib produced by Rust seems to be:
#[cfg_attr(all(target_os = "windows", target_env = "msvc"), link(name = "dylib.dll"))]
#[cfg_attr(not(all(target_os = "windows", target_env = "msvc")), link(name = "dylib"))]
extern "C" {}
Like your previous question you continue to ignore how compilers and linkers work. The two concepts you need to wrap your head around are these:
LPCTSTR is not a type. It is a preprocessor macro that expands to char const*, wchar_t const*, or __wchar_t const* if you are particularly unlucky. Either way, once the compiler is done, LPCTSTR is gone. Forever. It will not ever show up as a type even when using C++ name decoration.
It is not a type, don't use it in places where only types are allowed.
Compilers support different types of language linkage for external symbols. While you insist to have a C DLL, you are in fact using C++ linkage. This is evidenced by the symbol assigned to the exported function. While C++ linkage is great in that it allows type information to be encoded in the decorated names, the name decoration scheme isn't standardized in any way, and varies widely across compilers and platforms. As such, it is useless when the goal is cross language interoperability (or any interoperability).
As explained in my previous answer, you will need to get rid of the LPCTSTR in your C (or C++) interface. That's non-negotiable. It must go, and unwittingly you have done that already. Since DUMPBIN understands MSVC's C++ name decoration scheme, it was able to turn this symbol
?output##YAXPEBDZZ
into this code
void __cdecl output(char const *,...)
All type information is encoded in the decorated name, including the calling convention used. Take special note that the first formal parameter is of type char const *. That's fixed, set in stone, compiled into the DLL. There is no going back and changing your mind, so make sure your clients can't either.
You MUST change the signature of your C or C++ function. Pick either char const* or wchar_t const*. When it comes to strings in Rust on Windows there is no good option. Picking either one is the best you have.
The other issue you are going up against is insisting on having Rust come to terms with C++' language linkage. That isn't going to be an option until Standard C++ has formally standardized C++ language linkage. In statistics, this is called the "Impossible Event", so don't sink any more time into something that's not going to get you anywhere.
Instead, instruct your C or C++ library to export symbols using C language linkage by prepending an extern "C" specifier. While not formally specified either, most tools agree on a sufficiently large set of rules to be usable. Whether you like it or not, extern "C" is the only option we have when making compiled C or C++ code available to other languages (or C and C++, for that matter).
If for whatever reason you cannot use C language linkage (and frankly, since you are compiling C code I don't see a valid reason for that being the case) you could export from a DLL using a DEF file, giving you control over the names of the exported symbols. I don't see much benefit in using C++ language linkage, then throwing out all the benefits and pretend to the linker that this were C language linkage. I mean, why not just have the compiler do all that work instead?
Of course, if you are this desperately trying to avoid the solution, you could also follow the approach from your proposed answer, so long as you understand, why it works, when it stops working, and which new error mode you've introduced.
It works, in part by tricking the compiler, and in part by coincidence. The link_name = "?output##YAXPEBDZZ" attribute tells the compiler to stop massaging the import symbol and instead use the provided name when requesting the linker to resolve symbols. This works by coincidence because Rust defaults to __cdecl which happens to be the calling convention for all variadic functions in C. Most functions in the Windows API use __stdcall, though. Now ironically, had you used C linkage instead, you would have lost all type information, but retained the calling convention in the name decoration. A mismatch between calling conventions would have thus been caught during linking. Another opportunity missed, oh well.
It stops working when you recompile your C DLL and define UNICODE or _UNICODE, because now the symbol has a different name, due to different types. It will also stop working when Microsoft ever decide to change their (undocumented) name decoration scheme. And it will certainly stop working when using a different C++ compiler.
The Rust implementation introduced a new error mode. Presumably, LPCTSTR is a type alias, gated by some sort of configuration. This allows clients to select, whether they want an output that accepts a *const u8 or *const u16. The library, though, is compiled to accept char const* only. Another mismatch opportunity introduced needlessly. There is no place for generic-text mappings in Windows code, and hasn't been for decades.
As always, a few words of caution: Trying to introduce Rust into a business that's squarely footed on C and C++ requires careful consideration. Someone doing that will need to be intimately familiar with C++ compilers, linkers, and Rust. I feel that you are struggling with all three of those, and fear that you are ultimately going to provide a disservice.
Consider whether you should be bringing someone in that is sufficiently experienced. You can either thank me later for the advice, or pay me to fill in that role.
This is not my ideal answer, but it is how I solve the problem.
What I'm still looking for is a way to get the Microsoft Linker (I believe) to output full verbosity in the rust build as it can do when doing C++ builds. There are options to the build that might trigger this but I haven't found them yet. That plus this name munging in maybe 80% less text than I write here would be an ideal answer I think.
The users.rust-lang.org user chrefr helped by asking some clarifying questiongs which jogged my brain. He mentioned that "name mangling schema is unspecified in C++" which was my aha moment.
I was trying to force RUST to make the RUST linker look for my external output() API function, expecting it to look for the mangled name, as the native API call I am accessing was not declared with "cdecl" to prevent name mangling.
I simply forced RUST to use the mangled name I found with dumpbin.hex (code below)
What I was hoping for as an answer was a way to get linker.exe to output all the symbols it is looking for. Which would have been "output" which was what the compiler error was stating. I was thinking it was looking for a mangled name and wanted to compare the two mangled names by getting the microsoft linker to output what it was attempting to match.
So my solution was to use the dumpbin munged name in my #[link] directive:
//#[link(name="myNativeLib")]
//#[link(name="myNativeLib", kind="dylib")] // prepends _imp to symbol below
#[link(name="myNativeLib", kind="static")] // I'm linking with a DLL
extern {
//#[link_name = "output"]
#[link_name = "?output##YAXPEBDZZ"] // Name found via DUMPBIN.exe /Exports
fn output( format:LPCTSTR, ...);
}
Although I have access to sources of myNativeLib, these are not distributed, and not going to change. The *.lib and *.exp are only available internally, so long term I will need a solution to bind to these modules that only relys on the *.dll being present. That suggests I might need to dynamically load the DLL instead of doing what I consider "implicit" linking of the DLL. As I suspect rust is looking just at the *.lib module to resolve the symbols. I need a kind="dylibOnly" for Windows DLLS that are distributed without *.lib and *.exp modules.
But for the moment I was able to get all my link issues resolved.
I can now call my RUST DLL from a VS2019 Platform Toolset V142 "main" and the RUST DLL can call a 'C' DLL function "output" and the data goes to the propriatary stream that the native "output" function was designed to send data to.
There were several hoops involved but generally cargo/rustc/cbindgen worked well for this newbie. Now I'm trying to condsider any compute intensive task where multithreading is being avoided in 'C' that could be safely implemented in RUST which could be bencmarked to illustrate all this pain is worthwhile.
I'm trying to call the STM32 Cube Programmer C libraries from Rust.
The entire code, and branches showing various attempts, are available here:
https://github.com/becky112358/rust_c_linking_stm32_cube_programmer
Attempt 1 (in my GitHub repository, branch main)
Following the Rust Bindgen tutorial: https://rust-lang.github.io/rust-bindgen/
This is my preferred method. A Rust crate wraps the C library. Other Rust crates can then include the Rust wrapper crate, and not have to worry about any C libraries.
... in theory.
The Rust crate wrapping the C library (libstm32_cube_programmer_sys) builds ok. Its tests run ok.
The Rust crate calling the Rust crate which wraps the C library (caller) does not build, but reports:
= note: LINK : fatal error LNK1181: cannot open input file '.\drivers\CubeProgrammer_API.lib'
Why is caller even trying to look for the C library? I expected libstm32_cube_programmer_sys to handle all C library to Rust conversion, and that any Rust crates then calling libstm32_cube_programmer_sys could be purely Rusty (with maybe some unsafeness).
In build.rs I initially mis-wrote the C library name, and libstm32_cube_programmer_sys did not build. Correcting the library name allowed libstm32_cube_programmer_sys to build successfully. So it seems like libstm32_cube_programmer_sys does open the C library.
I tried adding the path to the drivers folder to my PATH.
I tried listing the absolute path to the C library:
println!("cargo:rustc-link-lib=C:/[blah blah]/drivers/CubeProgrammer_API");
I could not find how to feed in the path correctly, without Rust reporting:
error: renaming of the library `C` was specified, however this crate contains no `#[link(...)]` attributes referencing this library.
Attempt 2 (branch all_in_one)
In the main branch it seemed like maybe the problem was that libstm32_cube_programmer_sys could find the C library but caller could not. So I tried discarding the separate Rust crate, and having a single Rust crate which both wraps the C library and calls the C functions.
This time I get the following error, plus a bonus warning:
= note: caller.59pofysds2mkvvjr.rcgu.o : error LNK2019: unresolved external symbol disconnect referenced in function _ZN6caller4main17ha79648c0a9e86ed0E
.\drivers\CubeProgrammer_API.lib : warning LNK4272: library machine type 'x86' conflicts with target machine type 'x64'
Attempt 3 (branch link_search)
I searched a lot on the internet and found lots of different ways to call a C library from Rust. One way is to use link-search rather than link-lib. This surely only makes things harder for the compiler because you make it do more work. But I am stuck and need to try different things!
This time I get the following error, plus the bonus warning:
= note: caller.59pofysds2mkvvjr.rcgu.o : error LNK2019: unresolved external symbol __imp_disconnect referenced in function _ZN6caller4main17ha79648c0a9e86ed0E
.\drivers\CubeProgrammer_API.lib : warning LNK4272: library machine type 'x86' conflicts with target machine type 'x64'
Question
How do I make this work? Ideally from Attempt 1, but I'll take anything!
When we have:
C library <- Rust library <- Rust code
It seems that
when compiling, the Rust code needs to be able to see the C library, even though it is also calling the Rust library
when running, there may be C dlls which you need to store alongside the Rust exe
That was my main misunderstanding when I posted my original question.
Some other tips / reminders:
Make sure to use the correct lib file! (x64, x86 etc)
Resolve warnings too!
I took Linux kernel version 4.9.30, added a new directory /fsac whose files include headers from other directory (/include/fsac) and that is almost all I changed. Trying to compile I get a lot of errors in /kernel/sched/core.c that look like this:
kernel/sched/core.c:2326:20: error: invalid storage class for function ‘set_schedstats’
kernel/sched/core.c:2342:19: error: invalid storage class for function ‘setup_schedstats’
(...)
Searching on-line I saw that the recommendation was to simply remove the 'static' from the functions. Doing that effectively avoided that errors, but instead I started getting a lot of warnings from objtool like "frame pointer state mismatch" for each of the functions whose static was removed.
I discovered these warnings can be avoided by changing in the configuration file CONFIG_STACK_VALIDATION=n. That worked but even though there is no warning, compilation fails (Error 2).
Hence, I am wondering:
1 - Is the removal of "static" from the offended functions the proper solution?
2 - If so, how do I deal with the objtool warnings? Can those warnings alone constitute a compilation error, or there must be something else?
3 - Do you think I should degrade GCC?
This is the source code of the project: https://github.com/Zildj1an/FSAC_Kernel
This is the compilation log w/o removing the statics: https://github.com/Zildj1an/FSAC_Kernel/blob/master/build_err
I will happily provide any other information you might need.
Cheers.
Problem found, I had a function inside a function... Probably was moved copying something else.
I'm new to all of this and am trying to compile a program on a virtual machine with kernel version 3.10.0-957.el7.x86_64. But I get this error:
/home/../../../isr_demux.c: In function 'demux0_isr':
/home/../../../ isr_demux.c: 782:3: error: implicit declaration of function 'rdtscl' [-Werror=implicit-function-declaration]`
I pulled up isr_demux.c and saw that the snippet of code it said was causing the error is this:
The third to last line is line 782. What's interesting is that this same file compiles fine on a virtual machine with an older kernel version 3.10.0-327.el7.x86_64.
I searched online a lot but am truly stuck. I saw another post and checked my header files, and included linux/proc_fs.h along with many other ones. Someone said I could be missing a function prototype or definition, but adding a prototype did not fix this, and defining the function led to a redefinition error.
Any ideas about whether I'm still missing a header file somewhere? Or if the problem is with the newer kernel itself and I might need to find some sort of patch? Thanks!
The website elixir.bootlin.com is pretty useful for these things. The following link is a search across the entire kernel sources tree for version 3.10.108 for the symbol rdtscl.
https://elixir.bootlin.com/linux/v3.10.108/ident/rdtscl
rdtscl is only defined as a macro, so looks like the macro is not defined where you are getting your error about implicit declaration of rdtscl.
The two places where rdtscl is defined as a macro are:
https://elixir.bootlin.com/linux/v3.10.108/source/arch/x86/include/asm/msr.h#L182
https://elixir.bootlin.com/linux/v3.10.108/source/arch/x86/include/asm/paravirt.h#L182
So the fix is to include the correct headers files if you are building for x86 (the headers defining those macros are in arch/x86), or replace the call to rdtscl with something else.
Using VS2012 C/C++:
I created and linked a static lib called "libtools" to my project.
Calls to functions in the libtools lib worked as expected.
I created and linked a second static lib called "shunt" to my project.
But when I incorporate a call to a function in shunt, I am getting a c3861 "Identifier not found"
I added both libs to my project in the same way. I added a ref to each one in the Framework and References, and added the full path in the C/C++ Additional directories.
How can I fix this?
C++ uses something called name mangling when it creates symbol names. It's needed because the symbol names must contain the complete function signature.
When you use extern "C" the names will not be mangled, and can be used from other programming languages, like C.
You clearly make the shunt library in C++, which means that the shuntfunc function isn't actually named that way once it passed the compiler. As the actual application using the library is made in C (guessing based on the tag and information) it can't find the shuntfunc symbol, not without telling the C++ compiler to not mangle the symbol name.
That it works for the other library is probably because it's made in C as well.