Ah sorry, I should have been more clear. So ET_EXEC or ET_DYN is just the kind of binary in the ELF header struct (the e_type field), or if you run readelf -e or objdump -f it’ll have similar output.
So readelf -e /bin/ls will show the type as EXEC, whereas if you run it on a rustc binary compiled with the standard toolchain on linux (i.e. just rustc main.rs), readelf -e main should report DYN.
A PIE is just a shared object (DLL, dynamic library, or ET_DYN) with an entry point, so it can be loaded anywhere in memory (and thus can take advantage of ASLR) and executed. For example, this is a requirement on the android platform now iirc.
So grepping through liblibc-<hash>.rlib and my libc.a from compiling musl the rustlib has the problematic R_X86_64_32S relocation, but the libc.a does not.
After spending a bunch of time on this I just realized that I originally had this problem a while back, and I added this line into the musl ./configure in my make_static.sh script which was adopted from the chapter on advanced linking, and which essentially mirrors your build setup:
CFLAGS=-fPIC ./configure --disable-shared --prefix=$PREFIX
I’ve just tested, and if I build musl without the CFLAGS=-fPIC environment variable given to configure (i.e., ./configure --disabled-shared and not CFLAGS=-fPIC ./configure --disable-shared) then it will have those problematic relocations.
So, your initial intuition was correct!
Additionally if you look at the musl Makefile, you’ll see that if passes -fPIC to the shared object (which we disable):
$(LOBJS) $(LDSO_OBJS): CFLAGS_ALL += -fPIC
but not to the static libc version :]
The above CFLAG=-fPIC is an ugly hack, but it seems to work for me.
I believe you can also add this line in the musl Makefile (but it’s more invasive):
$(AOBJ): CFLAGS_ALL += -fPIC
How is the liblibc-<hash>.rlib generated? I’d like to create the artifact with the -fPIC'd libc.a to verify it works correctly, etc.