Intel® C++ Compiler
Community support and assistance for creating C++ code that runs on platforms based on Intel® processors.

Link Error LNK2001 when using .DEF File and __declspec(dllexport) together

mfoetsch
Beginner
1,586 Views

Dear all,

since upgrading to Intel Composer XE 2013 SP1 (Update 1) for Windows (in cooperation with Visual Studio 2008) I am experiencing the following problem when compiling my code with IPO enabled (/Qipo):

During the linker step the following error occurs:

[plain]
6>xilink: executing 'link'
6>pti_exports.def : warning LNK4022: cannot find unique match for symbol 'GetNOfAKind'
6>pti_exports.def : warning LNK4002: _GetNOfAKind@4 defined in ipo_13976obj31.obj
6>pti_exports.def : warning LNK4002: _GetNOfAKind@4.A defined in ipo_13976obj31.obj
6>pti_exports.def : warning LNK4002: _GetNOfAKind@4.P defined in ipo_13976obj31.obj
6>pti_exports.def : error LNK2001: unresolved external symbol GetNOfAKind
[/plain]

The method GetNOfAKind has the following signature:

[cpp]
extern "C" __declspec(dllexport) int __stdcall GetNOfAKind(const void* env);
[/cpp]

The only line of code in the body is a call to a member function of the object, the env parameter points to.

I have dozens of other methods with exactly the same parameters and modifiers and similar content that don't trigger that error.
When executing dumpbin /SYMBOLS on the temporary IPO .obj file, I can see, that GetNOfAKind is the only method that occurs with the additional ".P" and ".A" suffixes.

The Intel Support article at http://software.intel.com/en-us/articles/exporting-using-_declspecdllexport-on-windows states, that the IPO should not eliminate the method if __declspec(dllexport) is present, so I don't get why the error occurs.

Linkage succeeds if I omit the .DEF file completely and use __declspec(dllexport) alone. Unfortunately this is not a viable solution, as I need the .DEF File to get rid of the default name mangling introduced by the __stdcall calling convention. (I can't change that to __cdecl, as there's already client code that expects my DLL to use __stdcall).

So, has anyone had a similar problem? Is there a way to get this working? Might this be a compiler / linker bug?

Thanks in advance for your support!

Best regards,

Günther Teubl

0 Kudos
5 Replies
JenniferJ
Moderator
1,586 Views

It works for me.

You probably have /Gd or /Gr set for the file that contains "GetNOfAKind". From the project property, make sure /Gz is set.

Jennifer

0 Kudos
mfoetsch
Beginner
1,586 Views

Hello Jennifer,

thanks for your quick reply.

Your assumption was correct, the default calling convention for the project was "__cdecl" (/Gd).
Unfortunately, changing this to /Gz (on project or file-level) did not solve the problem. Instead, additional errors were introduced:

[plain]
1>Linking... (Intel C++ Environment)
1>": error: incompatible calling convention declarations for routine ___readfsdword
1>
1>": error: incompatible calling convention declarations for routine ___readfsdword
1>
1>": error: incompatible calling convention declarations for routine ___readfsdword
1>[/plain]

I guess I'll have to disable IPO for the affected sub-project for now and hope that performance degrade is within acceptable limits.

Best regards,
Günther

0 Kudos
JenniferJ
Moderator
1,586 Views

so disable IPO workaround the issue? if so, it seems a bug to me.

Could you file an issue report to Intel Premier Support or send me the .sln/.vcxproj files through "Send Author A Message"?

Thanks,

Jennifer

0 Kudos
mfoetsch
Beginner
1,586 Views

Hi again,

we investigated the issue further and found out, that the project compiles fine, if we remove the "additional code path" defines from the project (/QaxSSE3,SSE4,SSE4.2 option).

We therefore assume the following: When we compile the GetNOfAKind function with the additional code path switches, the compiler generates 3 versions of the function:

[plain]
_GetNOfAKind@4 //This is the default version
_GetNOfAKind@4.A //This is the version for processors supporting processor feature set 1 (e.g. SSE4)
_GetNOfAKind@4.P //This is the version for processors supporting processor feature set 2 (e.g. SSE4.2)
[/plain]

As all three versions are present in the ipo_ object file, the linker fails to find a distinct version, when the .DEF file only refers to "GetNOfAKind".

If the .DEF File is changed to say:

[plain]
EXPORTS
    GetNOfAKind=_GetNOfAKind@4
[/plain]

the project compiles fine.

Is it safe to assume that the unsuffixed version of the function is always the main version that will redirect to one of the suffixed versions at runtime? If so we can change the .DEF file accordingly and the problem would be fixed.
The other option would be to keep additional code paths disabled at all, but that would probably make the binary slightly slower.

Best regards,
Günther

0 Kudos
Stevens_M_
Beginner
1,586 Views

Günther,

Using "GetNOfAKind=_GetNOfAKind@4" in your module definition file will solve the LNK4022 problem, because it resolves the ambiguity. That is, you are explicitly saying you want to export the function whose decorated name is _GetNOfAKind@4. But, you are also setting an alias for that decorated name, and that alias is GetNOfAKind. This means that any client code that links to your exported function must use exactly the alias, and not the decorated name.

That may not sound so bad, since your client code probably seems like it is using GetNOfAKind when you look at your source. The problem is that, when you compile your client code, there is a good chance that the compiler will decorate that name, probably into something like _GetNOfAKind@4. When it looks at your import library to find a match for that decorated name, there won't be one, because you exported _GetNOfAKind@4 under the alias of GetNOfAKind.

To allow your client code to link to the alias, you need to force the compiler to refrain from decorating GetNOfAKind when it compiles your client code. You can do this by prefixing your declaration of GetNOfAKind in your client code with the same 'extern "C"' that you tried to use when you compiled GetNOfAKind.

In a lot of cases, using that prefix in your server code and your client code will let you export the undecorated name in your module definition file, and everything works the way you want it to. As you discovered, there are exceptions. There is also the drawback that you can't use polymorphic versions of the same symbol name, since the linker wouldn't know which you meant. In that situation, you do want to use the alias method, removing the extern "C" from your server declaration, but leaving it in your client. You can only export one polymorphic function under that alias, however. If you want your client to be able to call more than one, you must export each one under a unique alias, and have your client call the alias names, with each alias name declared with extern "C" in your client code.

Alternatively, you can export the decorated names, provided you know that your client code will compile the various calls it makes in ways that decorate the client's references to those names so they match the exported versions. Your situation seems a bit unusual, so I can't promise that's what's going to happen.

Microsoft's MSDN pages on EXPORTS say you have to use the decorated names in your module definition file. My testing indicates that this is not so. If you don't use polymorphism, and don't use extern "C", you can still use this in your definition file:

EXPORTS
    GetNOfAKind

If you do, and your code only generates one decorated version of that symbol, the decorated version will be included in your import library. When your client code compiles (assuming you use the same compiler), it will decorate that symbol the same way, which means the symbol will be resolved when the linker looks into your import library to find a match. In other words, if your server code, client code, and module definition file are all written solely with the undecorated name, the name is never declared with extern "C", and your server has only one declaration of that name, it all works as though decoration is never taking place, even though it is, because the compiler decorates the server and client names the same way, and the linker actually exports the decorated name, not the undecorated name you put into your module definition file.

I have tested this and tested this and it really does work this way, despite what it says in the MSDN pages. It is a form of undocumented behavior that seems to do just what you might want it to, when you meet the necessary conditions. Of course, undocumented behavior is also unreliable behavior. That is, it might not work in some future version of the linker. The safest play, therefore, would seem to be to avoid it and use __declspec(dllexport) and __declspec(dllimport) for your exported symbols. The only time, I think, you really need a module definition file anymore is when you want the exported symbol to be private (that is, available via GetProcAddress, but not included in the import library). In that case, I recommend prefixing with extern "C" in all server and client declarations.

Sorry I'm a couple of years late with this, but, if it's no longer of use to you, maybe someone else will find this helpful. There's more detail in a question I posted to StackOverflow, here: http://stackoverflow.com/questions/36295272/how-do-decorated-names-get-into-import-libraries-when-i-only-provide-the-undecor

Cheers!

0 Kudos
Reply