pkgw / elfx86exts

Decode binaries and print out which instruction set extensions they use. This program's name is a lie: it supports not just x86/ELF but also ARM64, MachO, and possibly more.
MIT License
206 stars 13 forks source link

Problems reading binaries linked with `mold` #171

Closed HanabishiRecca closed 11 months ago

HanabishiRecca commented 11 months ago

elfx86exts has problems reading binaries linked with mold.

Somehow it is unable to detect all or almost all instruction sets. This happens with all compilers and languages, but has some correlation with compiler flags used.


E.g. lets examine elfx86exts itself linked with the default Rust linker outputs:

$ elfx86exts ./elfx86exts
File format and CPU architecture: Elf, X86_64
MODE64 (call)
CMOV (cmova)
SSE2 (movdqa)
BMI (tzcnt)
SSE1 (movups)
AVX (vmovdqa)
AVX2 (vpcmpeqb)
NOVLX (vpcmpeqb)
Instruction set extensions used: AVX, AVX2, BMI, CMOV, MODE64, NOVLX, SSE1, SSE2
CPU Generation: Unknown

Seems legit and indeed I can find say vpcmpeqb in objdump:

0000000000203500 <_ZN6memchr4arch6x86_644avx26memchr3One13find_raw_avx217he5d0877342d03fe8E>:
  203500:   48 89 d1                mov    %rdx,%rcx
  203503:   48 89 f2                mov    %rsi,%rdx
  203506:   c5 fd 6f 07             vmovdqa (%rdi),%ymm0
  20350a:   c5 fd 74 0e             vpcmpeqb (%rsi),%ymm0,%ymm1
  20350e:   c5 fd d7 f1             vpmovmskb %ymm1,%esi
  203512:   85 f6                   test   %esi,%esi
  203514:   74 16                   je     20352c

But when linked with mold (i.e. RUSTFLAGS='-C link-arg=-fuse-ld=mold' cargo build --release):

$ elfx86exts ./elfx86exts
File format and CPU architecture: Elf, X86_64
MODE64 (push)
CMOV (cmova)
SSE2 (movdqa)
BMI (tzcnt)
SSE1 (movups)
Instruction set extensions used: BMI, CMOV, MODE64, SSE1, SSE2
CPU Generation: Haswell

AVX is not detected, despite I still can find the same piece of code in objdump:

0000000000636db0 <_ZN6memchr4arch6x86_644avx26memchr3One13find_raw_avx217he5d0877342d03fe8E>:
  636db0:   48 89 d1                mov    %rdx,%rcx
  636db3:   48 89 f2                mov    %rsi,%rdx
  636db6:   c5 fd 6f 07             vmovdqa (%rdi),%ymm0
  636dba:   c5 fd 74 0e             vpcmpeqb (%rsi),%ymm0,%ymm1
  636dbe:   c5 fd d7 f1             vpmovmskb %ymm1,%esi
  636dc2:   85 f6                   test   %esi,%esi
  636dc4:   74 16                   je     636ddc

I guess mold does something unusual, which somehow confuses elfx86exts.

HanabishiRecca commented 11 months ago

A really broken example is btop.

Linked with default GCC ld:

$ elfx86exts btop
File format and CPU architecture: Elf, X86_64
MODE64 (call)
AVX (vzeroupper)
CMOV (cmova)
NOVLX (vpxor)
BWI (vmovdqu8)
BMI (tzcnt)
BMI2 (shrx)
AVX2 (vinserti128)
AVX512 (vmovdqa64)
VLX (vpbroadcastq)
SSE2 (pause)
DQI (kshiftrd)
Instruction set extensions used: AVX, AVX2, AVX512, BMI, BMI2, BWI, CMOV, DQI, MODE64, NOVLX, SSE2, VLX
CPU Generation: Unknown

Linked with mold:

$ elfx86exts btop
File format and CPU architecture: Elf, X86_64
MODE64 (ret)
Instruction set extensions used: MODE64
CPU Generation: Intel Core

Nothing is detected.

pkgw commented 11 months ago

Strange! Indeed, mold must be constructing its executables in an unusual way.

At this point, I only have the capacity to maintain this package passively, so I'm afraid that I can't look into this problem myself. But I am happy to accept pull requests and put out new releases. If you look at the source code, this program is fairly simple, and I suspect that this problem might be fixable simply by teaching the program to consider a few additional types of executable-file sections.

HanabishiRecca commented 11 months ago

Well, I started with simply printing sections and their instruction counts.

ld

$ ./elfx86exts ./elfx86exts
File format and CPU architecture: Elf, X86_64
.init: 8
MODE64 (call)
.plt: 42
.plt.got: 8
.text: 793504
BMI (tzcnt)
CMOV (cmovae)
SSE1 (movups)
SSE2 (pause)
AVX (vmovdqu)
NOVLX (vmovaps)
AVX2 (vpor)
.fini: 4
Instruction set extensions used: AVX, AVX2, BMI, CMOV, MODE64, NOVLX, SSE1, SSE2
CPU Generation: Unknown

mold

$ ./elfx86exts ./elfx86exts
File format and CPU architecture: Elf, X86_64
.plt: 90
MODE64 (push)
.plt.got: 12
.fini: 4
.init: 8
.text: 111
CMOV (cmovs)
Instruction set extensions used: CMOV, MODE64
CPU Generation: Intel Core

Instruction count on .text is obviously very underestimated for binary produced by mold.


BUT on the other hand reported sect.size() is correct. So it has troubles not to find the section, but to disassembly instructions from it.

HanabishiRecca commented 11 months ago

Inspecting the problem deeper, I found a possible reason.

I posted a detailed explanation here: https://github.com/rui314/mold/issues/1133#issuecomment-1773365655

HanabishiRecca commented 11 months ago

Hey, I actually managed to develop a fix! I will make a PR.