lld-link fails to link 32bits assembly functions but 64bits pass

Hi Rui, Peter,

You know I’m enabling the “clang-cl + lld-link” toolchain for Uefi firmware. I meet a problem that the lld-link fails to link 32bits assembly functions, but can link 64bits assembly functions successfully. I need your suggestion.

Below is an example to show my problem in linux. The example has two only source files: main.c and foo.nasm.

$ cat main.c

void Foo (void);

int main()

{

Foo();

return 0;

}

$ cat foo.nasm

SECTION .text

global Foo

Foo:

Ret

64bits compiling and linking is successful:

$ nasm foo.nasm -Ox -f win64 -g -o foo.obj

$ ~/llvm/releaseinstall/bin/clang-cl main.c /nologo /c /WX /GS- /W4 /Gs32768 /D UNICODE /O1b2s /EHs-c- /GR- /GF /Gy /Zi /Gw -m64

$ ~/llvm/releaseinstall/bin/lld-link main.obj foo.obj /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /DLL /ENTRY:main /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER: /DEBUG:GHASH /Machine:X64

But 32bits linking fails with error of Foo assembly function is a undefined symbol:

$ nasm foo.nasm -Ox -f win32 -g -o foo.obj

$ ~/llvm/releaseinstall/bin/clang-cl main.c /nologo /c /WX /GS- /W4 /Gs32768 /D UNICODE /O1b2 /EHs-c- /GR- /GF /Gy /Zi /Gw -m32

$ ~/llvm/releaseinstall/bin/lld-link main.obj foo.obj /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /DLL /ENTRY:main /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER: /DEBUG:GHASH /MACHINE:X86

lld-link: error: undefined symbol: _Foo

referenced by /home/jshi19/llvm/wrongcode/lld-link/main.c:4

main.obj:(_main)

BTW, I find the lld-link does not suppor the /MAP option to generate mapfile. If I hope to output the linking mapfile info, what option should I use?

Thanks

Steven

Hi Rui, Peter,

You know I’m enabling the “clang-cl + lld-link” toolchain for Uefi firmware.
I meet a problem that the lld-link fails to link 32bits assembly functions,
but can link 64bits assembly functions successfully. I need your suggestion.

Below is an example to show my problem in linux. The example has two only
source files: main.c and foo.nasm.

$ cat main.c

void Foo (void);

int main()

{

Foo();

return 0;

}

$ cat foo.nasm

SECTION \.text

global Foo

Foo:

Ret

64bits compiling and linking is successful:

$ nasm foo.nasm -Ox -f win64 -g -o foo.obj

$ ~/llvm/releaseinstall/bin/clang-cl main.c /nologo /c /WX /GS- /W4 /Gs32768
/D UNICODE /O1b2s /EHs-c- /GR- /GF /Gy /Zi /Gw -m64

$ ~/llvm/releaseinstall/bin/lld-link main.obj foo.obj /NOLOGO /NODEFAULTLIB
/IGNORE:4001 /OPT:REF /OPT:ICF=10 /ALIGN:32 /SECTION:.xdata,D
/SECTION:.pdata,D /DLL /ENTRY:main /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER
/SAFESEH:NO /BASE:0 /DRIVER: /DEBUG:GHASH /Machine:X64

But 32bits linking fails with error of Foo assembly function is a undefined
symbol:

$ nasm foo.nasm -Ox -f win32 -g -o foo.obj

$ ~/llvm/releaseinstall/bin/clang-cl main.c /nologo /c /WX /GS- /W4 /Gs32768
/D UNICODE /O1b2 /EHs-c- /GR- /GF /Gy /Zi /Gw -m32

$ ~/llvm/releaseinstall/bin/lld-link main.obj foo.obj /NOLOGO /NODEFAULTLIB
/IGNORE:4001 /OPT:REF /OPT:ICF=10 /ALIGN:32 /SECTION:.xdata,D
/SECTION:.pdata,D /DLL /ENTRY:main /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER
/SAFESEH:NO /BASE:0 /DRIVER: /DEBUG:GHASH /MACHINE:X86

lld-link: error: undefined symbol: _Foo

>>> referenced by /home/jshi19/llvm/wrongcode/lld-link/main.c:4

>>> main.obj:(_main)

For 32 bit windows, functions with cdecl calling convention (the default in C) are decorated with an underscore prefix. So you'd need to update your nasm source file to define the symbol _Foo instead of Foo. (Most assembly files use some sort of macro for wrapping this detail.)

BTW, I find the lld-link does not suppor the /MAP option to generate
mapfile. If I hope to output the linking mapfile info, what option should I
use?

lld-link has got a private option /lldmap:<filename> which does output some sort of filename. I'm not sure what the reasons are for providing it under a private name instead of supporting the link.exe option /map. Maybe the format of the generated mapfile differs so that it might not work for automatic tools that operate on the map file at least.

// Martin

Hi Martin,
Thank you for the hint.

For 32 bit windows, functions with cdecl calling convention (the default
in C) are decorated with an underscore prefix. So you'd need to update
your nasm source file to define the symbol _Foo instead of Foo. (Most
assembly files use some sort of macro for wrapping this detail.)

Yes, my assembly files do use prefix macro (ASM_PFX) for wrapping the underscore. We use the __USER_LABEL_PREFIX__ to control the prefix macro expand to be "_" as below. I find the __USER_LABEL_PREFIX__ has been predefined by clang-cl, but it is defined as nothing. That is why my prefix macro expands to be nothing. Do you know why the clang-cl predefines the __USER_LABEL_PREFIX__ as nothing?

#ifndef __USER_LABEL_PREFIX__
#define __USER_LABEL_PREFIX__ _
#endif

#define _CONCATENATE(a, b) __CONCATENATE(a, b)
#define __CONCATENATE(a, b) a ## b
#define ASM_PFX(name) _CONCATENATE (__USER_LABEL_PREFIX__, name)

Thanks
Steven

It looks to me like __USER_LABEL_PREFIX__ is properly defined to an underscore when preprocessing with clang-cl:

$ cat userprefix.c
__USER_LABEL_PREFIX__
$ bin/clang-cl -E userprefix.c -m32
# 1 "userprefix.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 331 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "userprefix.c" 2

Hi Martin,
OK, It was my fault. I didn't set correct target when run the preprocess. Thank you very much!

Thanks
Steven