How to port 32-bit DLL definitions to 64-bit? - c

I have an Application and DLL project that I am trying to port to a 64-bit environment. I ran into an issue with the DLL module definition that would cause linker errors such as the following when compiled with a 64-bit configuration:
2>mylib.exp : error LNK2001: unresolved external symbol _func_a#4
2>mylib.exp : error LNK2001: unresolved external symbol _func_b#4
The header definition for these functions looks like:
#ifdef MYLIB_DEF
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT __declspec(dllimport)
#endif
DLLEXPORT extern int variable;
DLLEXPORT int __stdcall func_a(int value);
DLLEXPORT int __stdcall func_b(int value);
And in the C source file, it starts with this:
#define MYLIB_DEF
#include "mylib.h"
DLLEXPORT int variable;
DLLEXPORT int __stdcall func_a(int value)
{
...
Now, for the trouble part. We have something like this for the module definition file:
LIBRARY mylib.dll
EXPORTS
variable DATA
_func_a#4
func_a#4=_func_a#4
func_a=_func_a#4
_func_b#4
func_b#4=_func_a#4
func_b=_func_a#4
This triggers the aforementioned errors. I understand that MSVC decorates functions when compiled with __stdcall and 64-bit builds don't use/support __stdcall so there are no decorations. I was able to create an alternate definition file that seems to work like this:
LIBRARY mylib.dll
EXPORTS
variable DATA
func_a
func_b
But, as this file was last touched 10 years ago, I don't know if I understand all the implications or reasoning behind the use of this file. Would something like that file be adequate for 64-bit builds. While our app is the primary user of our DLL, we do have others that interface to the DLL directly so I do not want to break any compatibility, however, we have zero users of a 64-bit version at the moment.
Do we need the definition file at all if we are using __declspec(dllexport) everywhere and not using ordinal exports?
Should we just drop it for 64-bit builds or does it provide any value?
Does dropping it from 32-bit builds as unneeded noise risk breaking any external users? From local testing, it does not seem to be required, but I am doing a full rebuild of the app and DLL.
Why does every function need two additional aliases? Is this standard practice for 32-bit builds? Is there a 64-bit equivalent we should be doing?
I've looked over Microsoft's documentation on the topic, but it says nothing about 64-bit builds versus 32-bit builds:
https://learn.microsoft.com/en-us/cpp/build/reference/exports?view=msvc-170

Related

I'm trying to install the NVIDIA version of a toolkit, and I'm getting error messages like "... this declaration may not have extern "C" linkage"

I'm trying to install the NVIDIA version of an externally supplied toolkit (for the purposes of this message it doesn't matter what the toolkit is, this is a problem about how to use nvcc), and I'm getting error messages like "usr/include/c++/6/utility(329): error: this declaration may not have extern "C" linkage".
I'm not a C or C++ programmer, but I am happy enough poking around in things like Makefiles. I'm pretty sure that I've got all the paths set to point to the right places, and /usr/include/c++/6 contains all the files that are generating the error messages. But I have no idea what these error messages mean and what I should do to get round them.
I believe these errors are from C/C++ name mangling differences and the fact that NVCC compiles as a C++ compiler. I was able to compile HTK 3.5 by simply removing the extern "C" declarations from HCUDA.cu:
#ifdef __cplusplus
extern "C" {
#endif
/* ... */
#ifdef __cplusplus
}
#endif
I suspect that, because they're already declared extern "C" in HCUDA.h, they don't need to be declared such in HCUDA.cu, but I'm not sure.

Visual C++ 2017, extern "C" ignored? LNK2019 errors when linking C++ code to C libraries

A solution has a project consisting of a static library (one .h file, one .c file) written in C, and a utility program (.cxx) based on that library written in C++.
The library compiles without error. The utility compiles too, but fails in linkage with errors like:
1>abc.obj : error LNK2019: unresolved external symbol "struct DEFListen * __cdecl DEFisten(int,char *,int)" (?DEFListen##YAPAU0#HPADH#Z) referenced in function _main
The header for the library includes extern "C" guards:
#ifdef __cplusplus
extern "C" {
#endif
By putting garbage inside the ifdef, I get compile warnings when compiling (not linking) the utility as expected, so I know __cplusplus is in fact defined when the utility is compiled, and not for instance merely misspelled.
Yet, the error message shows the function signature (and the mangled name) for the function in question. dumbin /symbols on the utility's object file of course confirms the object contains mangled symbols.
In summary: extern "C" { is definitely being parsed at compile time yet ignored. Why?
Problem is so stupid: is that the closing brace code (below) had been cut and pasted to an alternate location, while re-arranging declarations, leaving several functions outside the extern block.
#ifdef __cplusplus
}
#endif
Sorry I didn't spot that before posting for help.
I leave this shameful post to found by someone equally foolish (possibly me) in the future.

Unit test C with compiler specific keywords

I am writing unit tests for some embedded C, that run on a host machine (not testing on target yet) and compile with GCC. I have been using the Ceedling build system with the Unity testing framework to test.
One of the files I would like to test includes a file (say a.h) that includes another file (say, cpu.h) that is part of the board support package from the embedded device vendor, and uses keywords specific to the target compiler (e.g. __cregister, such as in extern __cregister volatile unsigned int IER;.
Another issue, again with such a file included from the BSP, is inline assembly asm() sections such as in #define FOO_ASM asm("FOO").
These both of course throws an error when building tests as GCC does not recognise these keywords.
I had thought I could prevent these BSP headers from being added in by having Ceedling generate a mock, by adding #include "mock_a.h" to my test file, but GCC still compiles a.h, and thus b.h.
Is there a best practice way of solving such issues?
I could add something like the below to the BSP file in question, but I am loath to alter vendor code that would change or overwrite my changes with a new version release, I would rather understand how to isolate the unit properly.
// Unknown how __cregister is initially defined
#ifdef TEST
#undef __cregister // Redefine __cregister to nothing
#define __cregister
#endif
extern __cregister volatile unsigned int IER;
It ended up that I followed the method outlined in the link in my comment to the OP.
So for the example from my original post
/* foo.h */
extern __cregister volatile unsigned int IER;
#define FOO_BAR_ASM asm("BAR");
I created the following file, with the same name, within the test/support/ directory and removed the include path for the real BSP files from the test build settings:
/*foo.h - in test/support */
extern volatile unsigned int IER;
#define FOO_BAR_ASM
Then added an include like #include "mock_foo.h" to the test file.

Hide dependent DLL functions

I am struggling to understand if I can prevent exposing certain function calls from within a DLL that I am building. The function calls I want to hide are calls that are exposed by sqlite3.dll which I am building into another DLL of my own making. sqlite3.dll exposes 5 functions, one of which looks like this in the header:
SQLITE_API int SQLITE_STDCALL sqlite3_close(sqlite3*);
The macros at play here are defined earlier as:
/*
** Provide the ability to override linkage features of the interface.
*/
#ifndef SQLITE_EXTERN
# define SQLITE_EXTERN extern
#endif
#ifndef SQLITE_API
# define SQLITE_API
#endif
#ifndef SQLITE_CDECL
# define SQLITE_CDECL
#endif
#ifndef SQLITE_STDCALL
# define SQLITE_STDCALL
#endif
Now, I am building sqlite3.dll into my application by linking against sqlite3.lib and including sqlite3.h (the source of the prior code snippets).
I realize I may be able to play with those macros to achieve what I want.
I expose the functions in my own dll with:
/* module entry point */
int __declspec(dllexport) __stdcall load_properties(CAObjHandle context);
When I look at the functions available in the output of my build, I get my functions+5 functions from the sqlite library. All of the functions in sqlite that are exposed have the declaration structure similar to what I showed for close() above.
Is there a way I can hide the sqlite functions? Is it the .lib file that is causing the issue? That file was auto-generated so I am not sure what is in there.
I discovered the answer. The sqlite3.dll was incorrectly specified as a source of exports to the compiler. Removing the directive to export functions from sqlite3.dll corrected the issue.

Confused by Unresolved external symbol error

I'm trying to build a wrapper library with VC++'s compiler.
ErlDriver.c
#define __WIN32__
#define DLL_EXPORT __declspec(dllexport)
#include "erl_driver.h"
DLL_EXPORT int _driver_output(ErlDrvPort port, char *buf, int len) {
return driver_output(port, buf, len);
}
build.bat
cl /I%ERL_DRIVER_H% /LD /MD ErlDriver.c
When I attempt to build this, I get the following linker error:
ErlDriver.obj : error LNK2019: unresolved external symbol _WinDynDriverCallbacks referenced in function __driver_output
erl_win_dyn_driver.h (included in erl_driver.h)
typedef struct {
WDD_FTYPE(driver_output) *driver_output;
// a ton more of those
} TWinDynDriverCallbacks;
extern TWinDynDriverCallbacks WinDynDriverCallbacks;
#define driver_output (WinDynDriverCallbacks.driver_output)
So, as you can see, WinDynDriverCallbacks is defined declared.
What could be causing the linker error, then?
No, it's not defined (at least in what you quoted). It's declared. The "extern" keyword means "the definition for this symbol appears in another compilation unit (source file)." You need to be linking with the object file (or library) produced from compiling the source file that defines that symbol.
There is a subtle difference between "declaring" something and "defining" it in C or C++. When you declare it, it tells the compiler that a certain symbol will be defined somewhere else - this can allow the code to use that symbol without needing to see the actual definition. You still have to define the symbol somewhere in the code that is linked in, or else you will get the error message you are seeing.
For example, this is a declaration of the symbol WinDynDriverCallbacks:
extern TWinDynDriverCallbacks WinDynDriverCallbacks;
Your code has this declaration - it allows the code that uses the symbol to successfully compile (but not link).
You need to add a definition somewhere:
TWinDynDriverCallbacks WinDynDriverCallbacks;
The definition must go into a source code file somewhere (not generally in a header file). This tells the compiler to allocate space in the object code for that object and allows the program to link successfully.
I got a very similar problem building a NIF on Windows. Unresolved external symbol _WinDynNifCallbacks. Turns out this is defined by the ERL_NIF_INIT macro and in my case the entire macro needed to be enclosed in a extern C block.
ie this failed
extern "C" ERL_NIF_INIT(...)
while this succeeded
extern "C"
{
ERL_NIF_INIT(...)
}
I strongly suspect this problem is due to the same issue but with the DRIVER_INIT macro for an erlang port driver.
Driver_Init is the main loop that declares "TWinDynDriverCallbacks WinDynDriverCallbacks;" but it's properly declared in the multiple line define for driver_init. You shouldn't need to wrap it in extern "c".
since this thread came up about a million times while trying to setup my barebones erlang port driver i will say this here. I am working out of Joe Armstrong's programming erlang book, chapter 12 interfacing techniques. Using erl5.9 and vs2010.
The code in the book had a omission and a error in example1_lib.c. Though the error is most likely due to the age of the book versus erlang version changes.
Needed to set (#define WIN32) at the very top of example1_lib.c otherwise erlang defaulted to all the Linux options.
Second needed to change (int bufflen) to (ErlDrvSizeT bufflen) in example_drv_output.
After that it built clean.

Resources