Why -pie option force LLD to output shared obj file type, not executable?

Hello Rui,

I’m enabling the LLD in the Uefi firmware edk2 build. I meet a problem about the -pie option and cannot output the executable type obj file correctly. I need your advice.

The Uefi firmware executable binary is the position independent + small code mode in 64bits. So we always add the options “-Wl,-pie -mcmodel=small” in our clang build toolchain. These options work well with binutils LD, but cannot work with LLD. I see the LLD uses the below code to force the output obj file as shared obj type if link with -pie. Is there any way to let LLD output executable type obj with -pie option? I’m OK if I have to link twice, first output DYN and then covert to EXEC in some way. Could you give some link command examples on it?


static uint16_t getELFType() {

if (Config->Pic)

return ET_DYN;

if (Config->Relocatable)

return ET_REL;

return ET_EXEC;




Hello Steven,

I'm not Rui, but I may be able to help. We recently ran into something
like this on AArch64 with pr39810 lld would not accept --pie and
--shared simultaneously.

In general even on ld.bfd use of the --pie linker option sets the file
type to ET_DYN, ld.bfd has a special case when the start of the text
section is non 0 (I think). This was described in
H.J. Lu - RFC: binutils PATCH: Set e_type to ET_EXEC for -pie -Ttext-segment= . As far as I
know this was for non-position independent executables that had been
compiled -fPIE just to get the small code model.

In the AArch64 case the problem was that --shared was being used to
work around ld.bfd using ET_EXEC, unfortunately it seems like you need
the ET_EXEC behaviour? If so I don't think that there are any flags
that you can give to lld to change this.

My understanding is that the only difference in the file ld.bfd
generates is the e_type field in the ELF header. It would be trivial
to write a quick tool to change the field from ET_DYN to ET_EXEC.

Hope this is of some use.


lld sets ET_DYN instead of ET_EXEC if -pic is specified, which is basically the same behavior except the case that Peter explained. Unfortunately, there’s no way to set ET_EXEC using lld, but the “type” field is 2 byte long and at offset 16, so you can edit it using a binary editor to change it from ET_DYN to ET_EXEC (although it’s super hacky).

If you have GNU sed, you can do with the following command to change the field value:

sed -E -i -e ‘1s/^(.{16})…/\1\x2\x0/’ your-executable-file

Does that work for you?

Hi Peter, Rui

Thank you for the info.