lld write wrong symbol value in .data section if enable -pie

Hi Rui,

I still fail to enable the lld in my Uefi firmware build to replace ld, and I found it is related to the wrong symbol values in the .data section, which are pointed by R_X86_64_64 relocation entries. I need your advices.

My firmware uses a linker script https://github.com/tianocore/edk2/blob/master/BaseTools/Scripts/GccBase.lds to do the linking. We use position independent code with hidden visibility to inform the compiler that symbol references are never resolved at runtime. My problem is I found after the lld linking with –pie enabled, the symbol values in .data section, which have the R_X86_64_64 relocation entries, are all 0. In other word, I found the S in below R_X86_64_64 calculation is 0.

Hi Steven,

This is not a direct answer or suggestion for your problem, but why don’t you use lld-link (lld for Windows target) instead of ld.lld (lld for Unix target) to create UEFI applications? A quick google search showed me that UEFI applications are in PE/COFF format, and I can even find people who successfully created UEFI applications using lld-link. Looks like that’s much more straightforward way than hacking ld.lld with linker scripts.

Hello Steven,

One difference between ld.lld and ld.bfd is that ld.lld defaults to --no-apply-dynamic-relocs (do not write the relocation addend) and ld.bfd defaults to --apply-dynamic-relocs (write the relocation addend). Can you try again with --apply-dynamic-relocs ?

Note that for RELA relocations a dynamic loader is supposed to resolve the relocation using the Addend in the relocation, ignoring the value in the place (.data section in your case). When linking -pie it is generally assumed that you will have a dynamic loader to resolve any relocations resulting from it so I don’t think LLD is incorrect to put 0 in the .data section in this case.

There are a couple of things I don’t understand about the output though:

  • If the symbol we are relocating against has hidden visibility I’d expect a R_X86_64_RELATIVE relocation without a symbol for a -pie link.
  • I thought R_X86_64_64 wasn’t a dynamic relocation? I’m not that familiar with X86 so I could be missing something glaringly obvious here.

If --apply-dynamic-relocs doesn’t work, would you consider making a small example and perhaps raise a PR? If it turns out not to be a problem we can always close it.

Peter

Hi Rui,

but why don’t you use lld-link (lld for Windows target) instead of ld.lld (lld for Unix target) to create UEFI applications?

I need support both PE/COFF and ELF format tools. I’m also working on the lld-link enabling (clang-cl + lld-link) in both Linux and windows. The ld.lld enabling (clang + ld.lld) is for ELF format native users. E.g. https://ci.linaro.org/view/leg-ci/job/leg-virt-tianocore-edk2-upstream/configure. Uefi firmware have many open source developers who like ELF format toolchains …

Yes, Uefi firmware application/driver are PE/COFF format. So for ELF format object file, we have a tool to convert ELF to COFF: https://github.com/tianocore/edk2/tree/master/BaseTools/Source/C/GenFw. Our convert tool cannot handle complex relocation types, like GOT based. That’s why we use the symbol hidden visibility, LTO and other options to let gcc/clang not emit complex relocation.

Thanks

Steven Shi

Intel\SSG\FID\Firmware Infrastructure

Hi Peter,

Thanks, I will try your suggestion and let you know my result.

Thanks

Steven

I know nothing about your project, so bear with me if my reply is pointless…

I’m still wondering if just using lld-link is (at least in theory) the best option for you guys. Before lld, I believe creating UEFI applications was tricky; I’ve heard of a story of creating it by transplanting a ELF text segment to an empty PE file using objcopy.

But now lld is there. It is always a cross-linker, so however you built lld, it is always capable of creating UEFI applications on any host. Doesn’t that mean you could remove all hacks such as converting ELF to COFF, creating PE/COFF executables using linker scripts, or doing LTO just to not emit certain types of relocations that the existing tools cannot handle, no? It seems to me that it is a great opportunity to simplify build process, but maybe I’m missing something…

It seems to me that it is a great opportunity to simplify build process

I agree and we’re investigating the various options with lld. Uefi build has been existed for ~20 years and support multiple compilers, arches and OSes. If the lld can really cover all build features and meet Uefi various requirements, I believe we use it as default linker to make build simple.

Thanks

Steven Shi

Intel\SSG\FID\Firmware Infrastructure

Hi Peter,

Good news. The --apply-dynamic-relocs works for me. I finally enable the clang + ld.lld for Uefi firmware build. Thank you very much!

Also many thanks to Rui.

My next target will be the lld-link enabling. Will let you two know my progress.

Hi Rui,

A quick question: Does lld-link only work with clang-cl with windows-msvc option? Can lld-link work with clang with linux-gnu option?

lld-link is command-line compatible with MSVC link.exe, so it takes Windows-style linker options. It doesn’t take Unix-style linker options. You are also expected to invoke lld-link directly instead of using the compiler driver (i.e. clang or cc) because that’s how the linker is supposed to be used on Windows.

I believe you should use clang-cl to create COFF object files and then link them using the following command

$ lld-link -subsystem:efi_application -nodefaultlib -dll -WX -entry:efi_main -out:yourapp.exe *.obj

but I may be wrong because I’ve never tested this myself.

(By the way, what is linux-gnu option?)

I believe you should use clang-cl to create COFF object files and then link them using the following command

$ lld-link -subsystem:efi_application -nodefaultlib -dll -WX -entry:efi_main -out:yourapp.exe *.obj

Will try the clang-cl as your suggestion.

Actually, I already tried the clang + lld-link in windows. I directly used Linux-style CC options for clang in windows, but set target as ‘x86_64-pc-windows-msvc’ and used the Windows-style option for lld-link. Everything works for 32bits uefi code build, but for 64bits code the clang cannot support –pie option with ‘x86_64-pc-windows-msvc’ target and build fail as below:

clang: error: unsupported option ‘-fpie’ for target ‘x86_64-pc-windows-msvc’

So, it looks only the clang-cl + lld-link is the correct toolchain for COFF format native build. Then, I have a question: if I can only use the clang-cl for COFF format native build, does it mean I cannot use Linux debugging tools for dwarf debug info, e.g. GDB?

By the way, what is linux-gnu option?

I mean the option which is fit for *-pc-linux-gnu target.

Thanks

Steven Shi

Intel\SSG\FID\Firmware Infrastructure

I believe you should use clang-cl to create COFF object files and then link them using the following command

$ lld-link -subsystem:efi_application -nodefaultlib -dll -WX -entry:efi_main -out:yourapp.exe *.obj

Will try the clang-cl as your suggestion.

Actually, I already tried the clang + lld-link in windows. I directly used Linux-style CC options for clang in windows, but set target as ‘x86_64-pc-windows-msvc’ and used the Windows-style option for lld-link. Everything works for 32bits uefi code build, but for 64bits code the clang cannot support –pie option with ‘x86_64-pc-windows-msvc’ target and build fail as below:

clang: error: unsupported option ‘-fpie’ for target ‘x86_64-pc-windows-msvc’

So, it looks only the clang-cl + lld-link is the correct toolchain for COFF format native build. Then, I have a question: if I can only use the clang-cl for COFF format native build, does it mean I cannot use Linux debugging tools for dwarf debug info, e.g. GDB?

You can use DWARF with COFF. To make clang-cl to emit DWARF debug info, add -gdwarf to clang-cl command line.