I am compiling a piece of UEFI C code with Visual Studio 2015 C/C++ compiler.
The compiler is targeting IA32, not X64.
When turning on the optimization with "/O1", the build is OK.
When turning off the optimization with "/Od", the build gives below error:
error LNK2001: unresolved external symbol __aullshr
According to here, there's an explanation why this kind of functions can be called implicitly by the compiler:
It turns out that this function is one of several compiler support functions that are invoked explicitly by the Microsoft C/C++ compiler. In this case, this function is called whenever the 32-bit compiler needs to multiply two 64-bit integers together. The EDK does not link with Microsoft's libraries and does not provide this function.
Are there other functions like this one? Sure, several more for 64-bit division, remainder and shifting.
But according to here:
...Compilers that implement intrinsic functions generally enable them only when a program requests optimization...
So how could such functions still be called when I explicitly turned off the optimization with /Od
??
ADD 1 - 2:32 PM 2/16/2019
It seems I am wrong about the __aullshr
function.
It is not a compiler intrinsic function. According to here, it turns out to be a runtime library function, whose implementation can be found in: C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\crt\src\intel\ullshr.asm
or C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\crt\src\i386\ullshr.asm
Such VC runtime functions are brought in by the compiler for 32-bit applications to do 64-bit operations.
But I still don't know why /O1
can build pass while /Od
failed?
It seems optimization switch can affect the usage of VC runtime library.
ADD 2 - 4:59 PM 2/17/2019
I found the code that cause the build failure.
It turns out to be some C struct bit field operation. There's a 64-bit C struct which has a lot of bit fields backed by a single UINT64 variable. When I comment out the single line of code that access these bit fields, the build is pass. It seems _aullshr()
function is used to access these bit fields when /Od
is specified.
Since this is part of the firmware code, I am wondering if it is a good practice to turn-off the optimization with /Od
?
ADD 3 - 9:33 AM 2/18/2019
I created below minimal reproducible example for VS2015.
First, there's a static lib project:
(test.c)
typedef unsigned __int64 UINT64;
typedef union {
struct {
UINT64 field1 : 16;
UINT64 field2 : 16;
UINT64 field3 : 6;
UINT64 field4 : 15;
UINT64 field5 : 2;
UINT64 field6 : 1;
UINT64 field7 : 1;
UINT64 field8 : 1; //<=========
UINT64 field9 : 1;
UINT64 field10 : 1;
UINT64 field11 : 1;
UINT64 field12 : 1; //<=========
UINT64 field13 : 1;
UINT64 field14 : 1;
} Bits;
UINT64 Data;
} ISSUE_STRUCT;
int
Method1
(
UINT64 Data
)
{
ISSUE_STRUCT IssueStruct;
IssueStruct.Data = Data;
if (IssueStruct.Bits.field8 == 1 && IssueStruct.Bits.field12 == 1) { // <==== HERE
return 1;
}
else
{
return 0;
}
}
Then a Windows DLL project:
(DllMain.c)
#include <Windows.h>
typedef unsigned __int64 UINT64;
int
Method1
(
UINT64 Data
);
int __stdcall DllMethod1
(
HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpReserved
)
{
if (Method1(1234)) //<===== Use the Method1 from the test.obj
{
return 1;
}
return 2;
}
Build process:
First, compile the test.obj:
cl.exe /nologo /arch:IA32 /c /GS- /W4 /Gs32768 /D UNICODE /O1b2 /GL /EHs-c- /GR- /GF /Gy /Zi /Gm /Gw /Od /Zl test.c
(add: VC++ 2015 compiler gives below warning for test.obj
:
warning C4214: nonstandard extension used: bit field types other than int
)
Then compile the DllMain.obj:
cl /nologo /arch:IA32 /c /GS- /W4 /Gs32768 /D UNICODE /O1b2 /GL /EHs-c- /GR- /GF /Gy /Zi /Gm /Gw /Od /Zl DllMain.c
Then link the DllMain.obj to the test.obj
link DllMain.obj ..\aullshr\test.obj /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /MACHINE:X86 /LTCG /SAFESEH:NO /DLL /ENTRY:DllMethod1 /DRIVER
It will give below error:
Generating code Finished generating code test.obj : error LNK2001: unresolved external symbol __aullshr DllMain.dll : fatal error LNK1120: 1 unresolved externals
If I remove the bit field manipulation code at HERE in the test.c, the link error will disappear.
If I only remove the /Od from the compile options for the test.c, the link error will disappear.
ADD 4 - 12:40 PM 2/18/2019
Thanks to @PeterCordes in his comment, there's an even simpler way to reproduce this issue. Just invoke below method:
uint64_t shr(uint64_t a, unsigned c) { return a >> c; }
Then compile the source code with below command:
cl /nologo /arch:IA32 /c /GS- /W4 /Gs32768 /D UNICODE /O1b2 /GL /EHs-c- /GR- /GF /Gy /Zi /Gm /Gw /Od /Zl DllMain.c
link DllMain.obj /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /MACHINE:X86 /LTCG /SAFESEH:NO /DLL /ENTRY:DllMethod1 /DRIVER
This issue can be reproduced for:
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.40629 for x86 (VS2013)
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24210 for x86 (VS2015)
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24215.1 for x86 (VS2015)
As mandated in the UEFI coding standard 5.6.3.4 Bit Fields :
Bit fields may only be of type INT32, signed INT32, UINT32, or a typedef name defined as one of the three INT32 variants.
So my final solution is to modify the UEFI code to use UINT32
instead of UINT64
.