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
Warn on .lib files with the wrong arch #38313
Comments
This isn't a problem for C++ symbols, since they "usually" have the same mangling in 32-bit and 64-bit. So there you get a nice error already: $ cat test.cc This is true for normal C++ functions, but not e.g for __stdcall annotated functions, which get mangled as __cdecl in 64-bit. But the main use of __stdcall is for Windows SDK functions, which are C functions, so I'm not sure how important that is. So another approach would be to do:
This is a bit of a heuristic that would be incorrect e.g. for function names that start with an underscore, and the CRT has many functions starting with an underscore (_free_locale, __iscsym, _isatty, _fwrite_nolock, etc etc etc). But as long as at least one of the referenced functions doesn't start with an underscore, we'd still emit a decent diag, and mainCRTStartup doesn't have a leading underscore, so this approach would fix the main motivation for this bug. It has the advantage of putting the actionable bit at the error diagnostic instead of adding a warning, and it only requires work in the error path, while "read first obj file in lib" always requires (minor) work. STDC (/Za) |
Yet another idea would be to do a lookup for WinMainCRTStartup / wWinMainCRTStartup / mainCRTStartup / wmainCRTStartup (and I suppose _DllMainCRTStartup for dlls). Driver.cpp already special-cases those, and it would also fix the main motivation here (linking for the wrong bitness). This would allow us to emit the nicest diagnostic of the approaches so far. It wouldn't fire with /noentry or custom /entry: flags, but that's maybe not a common use case. Or we could do this approach as a first line of defense to get the nice diagnostic in the common case, and then do more complicated things later. |
Here's the original report for this. The third suggested approach wouldn't work for it. d:\src\chromium\src>ninja -v -C out\debug_component embedder_switches.dll
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: __imp__GetCurrentProcessId@0
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: __imp__GetCurrentThreadId@0
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: __imp__GetSystemTimeAsFileTime@4
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: __imp__DisableThreadLibraryCalls@4
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: __imp__InitializeSListHead@4
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: __imp__IsDebuggerPresent@0
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: __imp__UnhandledExceptionFilter@4
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: __imp__SetUnhandledExceptionFilter@4
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: __imp__GetStartupInfoW@4
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: _IsProcessorFeaturePresent@4
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: __imp__GetModuleHandleW@4
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: _IsProcessorFeaturePresent@4
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: __imp__UnhandledExceptionFilter@4
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: __imp__SetUnhandledExceptionFilter@4
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: __imp__GetCurrentProcess@0
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: __imp__TerminateProcess@8
d:\src\chromium\src\third_party\llvm-build\Release+Asserts\bin\lld-link.exe: error: undefined symbol: _IsProcessorFeaturePresent@4
ninja: build stopped: subcommand failed. d:\src\chromium\src>gn args out\debug_component |
half a patch i wonder if we could make this an error, not a warning |
symbol-table based patch Here's a slightly different approach: It gets the .lib's arch off the .obj file belonging to the first symbol in the .lib's symbol table instead of walking .obj files in the .lib until one with an arch is hit. |
...and link.exe seems to use the symbol table too: C:\src\llvm-project>type one.cc C:\src\llvm-project>type empty.cc C:\src\llvm-project>type test.cc C:\src\llvm-project>out\gn\bin\clang-cl /c -m32 empty.cc /Foempty32.obj C:\src\llvm-project>out\gn\bin\clang-cl /c -m64 test.cc C:\src\llvm-project>out\gn\bin\lld-link.exe /lib /out:mixed.lib empty32.obj one64.obj /allowMixedArchForTesting C:\src\llvm-project>link test.obj mixed.lib C:\src\llvm-project>out\gn\bin\lld-link.exe /lib /out:mixed.lib one32.obj empty64.obj /allowMixedArchForTesting C:\src\llvm-project>link test.obj mixed.lib C:\src\llvm-project>out\gn\bin\lld-link.exe /lib /out:mixed.lib empty64.obj one32.obj /allowMixedArchForTesting C:\src\llvm-project>link test.obj mixed.lib |
better symbol-table based patch Before: C:\src\llvm-project>out\gn\bin\clang-cl -m32 main.cc -fuse-ld=lld After: C:\src\llvm-project>out\gn\bin\clang-cl -m32 main.cc -fuse-ld=lld So it's good that we get the machine type diag for libcmt.lib. That does seem much more understandable than the _mainCRTStartup diag. However, oldnames.lib exclusively contains object files with IMAGE_FILE_MACHINE_UNKNOWN that are all in the symbol table. So maybe we should just ignore static libraries where the first symbol is in such an obj file. XXX: Check what link.exe does if the first symbol is such a symbol but the next one isn't. These obj files are a bit mysterious: C:\src\llvm-project>out\gn\bin\llvm-nm.exe "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.25.28610\lib\x64\oldnames.lib" C:\src\llvm-project>lib /extract:d:\agent_work\4\s\Intermediate\vctools\oldnames.nativeproj__391811071\objr\amd64\oldnames\cgets.obj "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.25.28610\lib\x64\oldnames.lib" C:\src\llvm-project>out\gn\bin\obj2yaml.exe cgets.obj
I don't know if cl.exe can create these IMAGE_SYM_CLASS_WEAK_EXTERNAL symbols (but lld handles them, see isWeakExternal calls in lld\COFF). (See also eg https://reviews.llvm.org/D33520) This is for the case where no .obj file is pulled in from a .obj file (the C case). If an .obj file from such a .lib is loaded (the C++ case), this currently happens: C:\src\llvm-project>"c:\src\llvm-project\out\gn\bin\lld-link.exe" "C:\src\llvm-project\out\gn\obj\lld\test\COFF\Output\mixed-arch-lib.s.tmp-main-64.obj" "C:\src\llvm-project\out\gn\obj\lld\test\COFF\Output\mixed-arch-lib.s.tmp-f-32.lib" That is, both the .lib and the .obj emit a diag. That's kind of lame, and with C++ lib files it's not a super contrived example either. Two ways to think about a fix here:
|
Also, the diag in comment 3 looks like it might be against an import library, which in lld is an ImportFile, not an ArchiveFile. |
mentioned in issue #38315 |
Extended Description
clang-cl defaults to 64-bit builds. If you accidentally have your env set up to point to the 32-bit .lib paths, you get these cryptic diags:
$ ./clang-cl.sh foo.cc -fuse-ld=lld
lld-link: error: : undefined symbol: mainCRTStartup
$ ./clang-cl.sh -m32 foo.cc -fuse-ld=lld # works
link.exe is much better, it has this warning telling you what the actual issue is:
C:\Program Files (x86)\Windows Kits\10\lib\10.0.17134.0\um\x64\advapi32.lib : warning LNK4272: library machine type 'x64' conflicts with target machine type 'x86'
(copied from a different bug; it probably complains about a different .lib for a trivial foo.cc with just main() -- but the warning is the same otherwise)
lld-link should warn on this too.
Hans says: """
.lib files themselves don't have a machine type.
However, the import lib pseudo-files inside them do. lld currently
doesn't check the machine type of those (it would if ImportFile
overrode getMachineType; it probably should).
But, lld will only look at the members of a .lib file lazily, i.e. if
it seems something that it needs in the .lib file's symbol table.
Because of this, I'm not sure that implementing
ImportFile::getMachineType is enough to produce a warning in Bruce's
case. Probably no symbols from advapi32.lib's symbol table are being
referenced, and so we would never parse the .lib file members :-/
"""
Zach says: """
It's pretty rare for a .lib file to have mixed arch object files. You would have to go out of your way to make it happen. Given that, can we just open the first object file, parse just enough of it to get the object file, and early-warn if it mismatches on the assumption that all other objects in the archive will have the same architecture?
"""
We should find some way to make this work.
The text was updated successfully, but these errors were encountered: