Description
$ clang++ --version
Alpine clang version 15.0.6
Target: i586-alpine-linux-musl
Thread model: posix
InstalledDir: /usr/bin
$ ld.lld --version
LLD 15.0.6 (compatible with GNU linkers)
I'm in the process of chasing the segfault in firefox in alpine 3.17 x86.
I've found that an object file mozglue/baseprofiler/Unified_cpp_mozglue_baseprofiler0.o
built with clang++
(build command line)
/usr/bin/clang++ -Qunused-arguments -std=gnu++17 -o Unified_cpp_mozglue_baseprofiler0.o -c -I/home/rakslice/src/aports/community/firefox/src/firefox-108.0.1/obj/dist/stl_wrappers -I/home/rakslice/src/aports/community/firefox/src/firefox-108.0.1/obj/dist/system_wrappers -include /home/rakslice/src/aports/community/firefox/src/firefox-108.0.1/config/gcc_hidden.h -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fstack-clash-protection -DNDEBUG=1 -DTRIMMED=1 -DIMPL_MFBT -DIMPL_MFBT -DMOZ_HAS_MOZGLUE -I/home/rakslice/src/aports/community/firefox/src/firefox-108.0.1/mozglue/baseprofiler -I/home/rakslice/src/aports/community/firefox/src/firefox-108.0.1/obj/mozglue/baseprofiler -I/home/rakslice/src/aports/community/firefox/src/firefox-108.0.1/mozglue/baseprofiler/core -I/home/rakslice/src/aports/community/firefox/src/firefox-108.0.1/mozglue/linker -I/home/rakslice/src/aports/community/firefox/src/firefox-108.0.1/obj/dist/include -I/usr/include/nspr -I/home/rakslice/src/aports/community/firefox/src/firefox-108.0.1/obj/dist/include/nss -I/usr/include/libpng16 -I/usr/include/pixman-1 -DMOZILLA_CLIENT -include /home/rakslice/src/aports/community/firefox/src/firefox-108.0.1/obj/mozilla-config.h -fno-sized-deallocation -fno-aligned-new -fno-exceptions -fPIC -fno-rtti -ffunction-sections -fdata-sections -fno-exceptions -fno-math-errno -pthread -pipe -Os -fomit-frame-pointer -O2 -fno-plt -fomit-frame-pointer -funwind-tables -Wall -Wbitfield-enum-conversion -Wdeprecated-this-capture -Wempty-body -Wformat-type-confusion -Wignored-qualifiers -Wpointer-arith -Wshadow-field-in-constructor-modified -Wsign-compare -Wtype-limits -Wno-error=tautological-type-limit-compare -Wunreachable-code -Wunreachable-code-return -Wunused-but-set-parameter -Wno-invalid-offsetof -Wclass-varargs -Wempty-init-stmt -Wfloat-overflow-conversion -Wfloat-zero-conversion -Wloop-analysis -Wno-range-loop-analysis -Wc++2a-compat -Wenum-compare-conditional -Wenum-float-conversion -Wno-ambiguous-reversed-operator -Wno-error=deprecated -Wno-error=depecated-anon-enum-enum-conversion -Wno-error=deprecated-enum-enum-conversion -Wno-error=deprecated-pragma -Wno-error=deprecated-this-capture -Wno-error=deprecated-volatile -Wcomma -Wimplicit-fallthrough -Wstring-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wformat -Wformat-security -Wno-psabi -Wthread-safety -Wno-unknown-warning-option -Wno-ignored-qualifiers -fno-strict-aliasing -ffp-contract=off -MD -MP -MF .deps/Unified_cpp_mozglue_baseprofiler0.o.pp Unified_cpp_mozglue_baseprofiler0.cpp
has TLS lea
-call
sequences like
175: f0 ff 46 28 lock incl 0x28(%esi)
179: 8d 83 00 00 00 00 lea 0x0(%ebx),%eax
17f: ff 93 00 00 00 00 call *0x0(%ebx)
185: 89 c1 mov %eax,%ecx
187: 89 a8 00 00 00 00 mov %ebp,0x0(%eax)
with relevant relocs
OFFSET TYPE VALUE
0000017b R_386_TLS_LDM _ZN7mozilla12baseprofiler19TLSRegisteredThread17sRegisteredThreadE
00000181 R_386_GOT32 ___tls_get_addr
when this is linked with lld
(build command line)
/usr/bin/clang++ -Qunused-arguments -std=gnu++17 -o ../../dist/bin/firefox -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fstack-clash-protection -fno-sized-deallocation -fno-aligned-new -fno-exceptions -fPIC -fno-rtti -ffunction-sections -fdata-sections -fno-exceptions -fno-math-errno -pthread -pipe -Os -fomit-frame-pointer -O2 -fno-plt -fomit-frame-pointer -funwind-tables /home/rakslice/src/aports/community/firefox/src/firefox-108.0.1/obj/browser/app/firefox.list -lpthread -fuse-ld=lld -Wl,-z,noexecstack -Wl,-z,text -Wl,-z,relro -Wl,-z,nocopyreloc -Wl,-Bsymbolic-functions -Wl,--build-id=sha1 -fstack-protector-strong -fstack-clash-protection -rdynamic -Wl,-rpath-link,/home/rakslice/src/aports/community/firefox/src/firefox-108.0.1/obj/dist/bin -Wl,-rpath-link,/usr/lib -pie -latomic
(resulting lld
command line)
`"/usr/bin/ld.lld" -pie -export-dynamic -z now -z relro --hash-style=gnu --build-id --eh-frame-hdr -m elf_i386 -export-dynamic -dynamic-linker /lib/ld-musl-i386.so.1 -o ../../dist/bin/firefox /usr/lib/Scrt1.o /usr/lib/crti.o /usr/bin/../lib/gcc/i586-alpine-linux-musl/12.2.1/crtbeginS.o -L/usr/bin/../lib/gcc/i586-alpine-linux-musl/12.2.1 -L/usr/bin/../lib/gcc/i586-alpine-linux-musl/12.2.1/../../../../i586-alpine-linux-musl/lib -L/lib -L/usr/lib /home/rakslice/src/aports/community/firefox/src/firefox-108.0.1/obj/browser/app/firefox.list -lpthread -z noexecstack -z text -z relro -Bsymbolic-functions --build-id=sha1 -rpath-link /home/rakslice/src/aports/community/firefox/src/firefox-108.0.1/obj/dist/bin -rpath-link /usr/lib -latomic -lssp_nonshared -lstdc++ -lm -lgcc_s -lgcc -lpthread -lc -lgcc_s -lgcc /usr/bin/../lib/gcc/i586-alpine-linux-musl/12.2.1/crtendS.o /usr/lib/crtn.o`
it generates broken output like
444d5: f0 ff 46 28 lock incl 0x28(%esi)
444d9: 65 a1 00 00 00 00 mov %gs:0x0,%eax
444df: 90 nop
444e0: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
444e4: 00 89 c1 89 a8 f4 add %cl,-0xb57763f(%ecx)
444ea: ff (bad)
444eb: ff (bad)
444ec: ff 89 a8 f8 ff ff decl -0x758(%ecx)
this mov
-nop
-lea
-no-op is the canned sequence from X86::relaxTlsLdToLe()
in lld/ELF/Arch/X86.cpp
https://github.com/llvm/llvm-project/blob/llvmorg-15.0.6/lld/ELF/Arch/X86.cpp#L462,
// Convert
// leal foo(%reg),%eax
// call ___tls_get_addr
// to
// movl %gs:0,%eax
// nop
// leal 0(%esi,1),%esi
const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0,%eax
0x90, // nop
0x8d, 0x74, 0x26, 0x00, // leal 0(%esi,1),%esi
};
which is basically the LD->LE transition right out of "ELF Handling for Thread-Local Storage" v0.21 (2013-08-22) starting on p.54 (https://lrita.github.io/images/posts/cplusplus/ELF_Handling_For_Thread-Local_Storage.pdf#page=54), edit: actually it's the second one, "The GNU variant" on the next page.
But that particular rewrite is clearly inappropriate; the call
here in the GOT ___tls_get_addr
case is 6 bytes rather than presumably 5 the canned sequence is expecting so it is a byte short, and leaves us with garbage output.
How lld
got to this:
lld/ELF/Arch/X86.cpp:92
: useexpr
R_TLSLD_GOTPLT
forR_386_TLS_LDM
https://github.com/llvm/llvm-project/blob/llvmorg-15.0.6/lld/ELF/Arch/X86.cpp#L92lld/ELF/Relocations.cpp:1218
: handling of theR_TLSLD_GOTPLT
relocation figures that this LD->LE "relaxation" applies https://github.com/llvm/llvm-project/blob/llvmorg-15.0.6/lld/ELF/Relocations.cpp#L1218lld/ELF/Arch/X86.cpp:462
: as notedX86::relaxTlsLdToLe()
applies canned code block presumably designed for a differentcall
than the one we have https://github.com/llvm/llvm-project/blob/llvmorg-15.0.6/lld/ELF/Arch/X86.cpp#L462
Anyway, there you go. I don't know if this .o
is ABI compliant, I don't know if this is a valid candidate for some other LD->LE transition, I don't know what a minimal example of TLS-using code that produces the lea
-call
with these relocs would look like, I'm just an end user chasing after a broken binary I got shipped.