Skip to content
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

LLVM: add LLVM support on Windows #36753

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cmake/compiler/clang/target.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ if(NOT "${ARCH}" STREQUAL "posix")
list(APPEND TOOLCHAIN_LIBS gcc)
endif()

set(CMAKE_REQUIRED_FLAGS -nostartfiles -nostdlib ${isystem_include_flags})
# CMake needs a valid linker to check compiler flag, but clang
# won't use LLVM built-in linker by default, so use this option
# to force clang to use LLVM built-in linker
set(CMAKE_REQUIRED_FLAGS -nostartfiles -nostdlib -fuse-ld=lld ${isystem_include_flags})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the "-fuse-ld=lld" option is added to force to use lld,
which is the built-in linker of llvm.

It looks like Clang defaults to -fuse-ld=bfd (i.e. binutils linker) for x86_64-pc-none-elf and i686-pc-none-elf targets; so, if any, we should be specifying -fuse-ld=bfd here.

Note that -fuse-ld=bfd depends on the GNU binutils being available on the host and, to my knowledge, there is currently no readily available Windows binutils/gcc distribution supporting the x86_64-pc-none-elf and i686-pc-none-elf targets.

For now, users shall be responsible for providing their own Windows binutils/gcc build that supports x86_64-pc-none-elf and i686-pc-none-elf targets. In the future, the Windows Zephyr SDK distribution shall provide the binutils/gcc for use in this case.

As a proof of concept, you can try using the following Windows Zephyr SDK test build:
https://github.com/zephyrproject-rtos/crosstool-ng-old/releases/download/zephyr-crosstool-ng-1.24.0.4/zephyr-crosstool-ng-1.24.0.4_windows-x86_64_x86_64-zephyr-elf.zip

>path
PATH = ...;D:\zephyr-crosstool-ng-1.24.0.4\x86_64-zephyr-elf\bin;C:\Program Files\LLVM\bin

>clang dummy.c -o dummy -target x86_64-zephyr-elf -fuse-ld=bfd -nostdlib
d:/zephyr-crosstool-ng-1.24.0.4/x86_64-zephyr-elf/bin/../lib/gcc/x86_64-zephyr-elf/9.2.0/../../../../x86_64-zephyr-elf/bin/ld.bfd.exe: warning: cannot find entry symbol _start; defaulting to 0000000000400078

>x86_64-zephyr-elf-objdump -x dummy
dummy:     file format elf64-x86-64
dummy
architecture: i386:x86-64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0000000000400078

Program Header:
   STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
         filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .comment      00000016  0000000000000000  0000000000000000  00000078  2**0
                  CONTENTS, READONLY
SYMBOL TABLE:
0000000000000000 l    d  .comment       0000000000000000 .comment
0000000000000000 l    df *ABS*  0000000000000000 dummy.c
0000000000000000         *UND*  0000000000000000 _start
0000000000601000 g       *ABS*  0000000000000000 __bss_start
0000000000601000 g       *ABS*  0000000000000000 _edata
0000000000601000 g       *ABS*  0000000000000000 _end

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we don't use i686-pc-linux-elf, this option -fuse-ld=lld is not necessary too.
because this is more about environment issues, we need to ensure there is a gnu linker which supports i686-pc-none-elf, then it will invoke this linker by default and won't report error "program not executable".
I installed a cross compiler i486-elf on windows, it supports i686-pc-none-elf, just need to add it into system path, then it's ok to build zephyr binary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stephanosio I used -fuse-ld=lld because I wanted to use the llvm built-in linker to complete link process firstly, but I found that both on linux and windows, it reported the same error as below, then I used another i486-elf-ld on windows to build binary.

ld.lld: error: zephyr/linker_zephyr_prebuilt.cmd:29: malformed number: "__start"
epoint = (("__start") - ((0x0 + 0x100000) - (0x0 + 0x100000)));
^
collect2: error: ld returned 1 exit status

string(REPLACE ";" " " CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")

endif()
Expand Down
6 changes: 4 additions & 2 deletions cmake/toolchain/llvm/generic.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ if("${ARCH}" STREQUAL "arm")
set(CMAKE_EXE_LINKER_FLAGS_INIT "--specs=nosys.specs")
elseif("${ARCH}" STREQUAL "x86")
if(CONFIG_64BIT)
set(triple x86_64-pc-none-elf)
# The LLVM built-in linker lld is a generic driver, needs to use triple os filed
# to distinguish which linker will be used.
set(triple x86_64-pc-linux-elf)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it force lld to generate an elf binary on Windows?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this is a target name for cross compiler, but on windows, the older name "i686-pc-none-elf" couldn't be recognized and used to find a valid linker, we need to use "i686-pc-linux-elf".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is this coming from? Why does llvm on windows require a triple with linux in it? Is this in LLVM?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nashif as I understood, we need to build a zephyr elf file which could be executed on linux, it's a cross compilation from windows to linux, and llvm on windows has four linker based on os type: ld.lld (Unix), ld64.lld (macOS), lld-link (Windows), wasm-ld (WebAssembly), so I added linux in triple name, then it will use this ld.lld.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i686-pc-none-elf refers to the x86 (i686) ELF bare metal target, while x86_64-pc-linux-elf refers to the x86 (i686) ELF Linux target.

I do not know how LLVM internally handles this; but, at least in terms of GNU toolchains, they are not the same thing and we must use i686-pc-none-elf to ensure correct behaviour.

If the Windows LLVM distribution does not support i686-pc-none-elf (i.e. bare metal target) while the Linux one does, we should be filing an LLVM-side bug instead to make it support that -- unless, of course, there is a good reason why it should not support bare metal targets on Windows hosts (I really cannot see any).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I installed LLVM 10.0.0 on Windows (same version as the one I have on my Linux machine) and tested this.

I was able to reproduce clang: error: unable to execute command: program not executable on Windows host with non-Linux targets, but that is only because Clang internally calls the GCC front-end (supposedly to invoke binutils in order to link the targets that it thinks are not supported by LLD) and I did not have GCC installed/available in the PATH. Note that the same behaviour is observed on Linux hosts as well, except that GCC is installed there, so no such error message is displayed.

In theory, if we have gcc/binutils that supports the x86_64-none-elf target installed on the Windows host, it will work just fine (note that MSYS2/MinGW-w64 GCC does not support x86_64-none-elf target, so using that is not an option; this will not be a problem if we distribute Windows version of Zephyr SDK though, since it supports *-none-elf targets).

Now the question is:

  1. Does LLD not support bare metal targets (e.g. *-*-elf)? (i.e. should Clang be calling GCC front-end/binutils for these targets?)
  2. If LLD supports bare metal targets, is there a way to force Clang to directly invoke ld.lld for these targets as it does for the Linux targets?

The main problem here is that, on both Linux and Windows, Clang is calling GCC front-end with -fuse-ld=lld, and the GCC front-end calls the binutils linker (collect2), supposedly ignoring -fuse-ld=lld -- at this point, we might as well be just using -fuse-ld=bfd.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @tejlmand
It looks like there is something funny going on with the invocation of LLD, or rather lack thereof.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stephanosio yes, actually I used a cross compiler i486-elf to complete the link process on windows, rather than the llvm built-in linker.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both i686-pc-none-elf and i686-none-elf as well as x86_64-pc-none-elf and x86_64-none-elf work for me:

it seems that it's related to gcc version, I found a comment "gcc 8.x doesn't support -fuse-ld=lld option at all, it has been only added in http://gcc.gnu.org/r265940 for gcc 9"
then I installed a gcc 9.4 on my fedora machine, it worked with x86_64-none-elf.

"/usr/lib64/ccache/gcc" -nostdlib -fuse-ld=lld -m32 -o dummy /tmp/dummy-675e45.o
ld.lld: warning: cannot find entry symbol _start; defaulting to 0x401120

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used gcc 8.3 before.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I agree this.

else()
set(triple i686-pc-none-elf)
set(triple i686-pc-linux-elf)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

endif()
endif()

Expand Down