llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.25k stars 12.08k forks source link

lld - errors out on thin archive with nested non-thin archive #98290

Open sshannin opened 4 months ago

sshannin commented 4 months ago

lld spits out an obscure error when it encounters a thin archive containing a nested, non-thin archive.

Reproducer is below. I tested the four permutations of combining thin and non-thin outer+inner nested archives. Only thin-inside-thin works.

I also tested against bfd ld and gold. They do handle this case, although they don't inherently document that they intend to; they only explicitly mention thin-inside-thin.

All 3 fail in the case where an archive (thin or otherwise) is nested in a non-thin archive.

outer nested entry success? note
thin thin success
thin non-thin fail opaque error about malformed archive
nonthin thin fail symbol resolution failure
nonthin nonthin fail symbol resolution failure

It may be that it's deliberate not to support this, but it does deviate in feature parity from the other two linkers. And the error message is quite cryptic.

This was with the 18.1.8 release built from source.

u@u:~/t17/tmp$ cat foo1.cpp
#include "foo1.hpp"
int foo1() {
    return 4;
}

u@u:~/t17/tmp$ cat foo2.cpp
#include "foo2.hpp"
int foo2() {
    return 5;
}

u@u:~/t17/tmp$ cat foo1.hpp
int foo1();

u@u:~/t17/tmp$ cat foo2.hpp
int foo2();

u@u:~/t17/tmp$ cat driver.cpp
#include "foo1.hpp"
#include "foo2.hpp"

int main(int argc, char **argv) {
    return argc + foo1() + foo2();
}
u@u:~/t17/tmp$ ./drive.sh

${PREFIX}/g++ -o foo1.o -c foo1.cpp
${PREFIX}/g++ -o foo2.o -c foo2.cpp

${PREFIX}/gcc-ar -rc nonthin1.a foo1.o
${PREFIX}/gcc-ar -rTc thin1.a foo1.o

${PREFIX}/gcc-ar -rTc thin2_thin1.a foo2.o thin1.a
${PREFIX}/gcc-ar -rTc thin2_nonthin1.a foo2.o nonthin1.a
${PREFIX}/gcc-ar -rc nonthin2_thin1.a foo2.o thin1.a
${PREFIX}/gcc-ar -rc nonthin2_nonthin1.a foo2.o nonthin1.a

${PREFIX}/g++ -o driver.o -c driver.cpp
${PREFIX}/g++ -B ${PREFIX} -o run driver.o thin2_thin1.a -fuse-ld=lld
${PREFIX}/g++ -B ${PREFIX} -o run driver.o thin2_nonthin1.a -fuse-ld=lld
ld.lld: error: thin2_nonthin1.a: could not get the buffer for a child of the archive: truncated or malformed archive (long name offset characters after the '/' are not all decimal numbers: '8:86' for archive member header at offset 252)
collect2: error: ld returned 1 exit status

${PREFIX}/g++ -B ${PREFIX} -o run driver.o nonthin2_thin1.a -fuse-ld=lld
ld.lld: warning: nonthin2_thin1.a: archive member 'thin1.a' is neither ET_REL nor LLVM bitcode
ld.lld: error: undefined symbol: foo1()
>>> referenced by driver.cpp
>>>               driver.o:(main)
>>> did you mean: foo2()
>>> defined in: nonthin2_thin1.a(foo2.o)
collect2: error: ld returned 1 exit status

${PREFIX}/g++ -B ${PREFIX} -o run driver.o nonthin2_nonthin1.a -fuse-ld=lld
ld.lld: warning: nonthin2_nonthin1.a: archive member 'nonthin1.a' is neither ET_REL nor LLVM bitcode
ld.lld: error: undefined symbol: foo1()
>>> referenced by driver.cpp
>>>               driver.o:(main)
>>> did you mean: foo2()
>>> defined in: nonthin2_nonthin1.a(foo2.o)
collect2: error: ld returned 1 exit status
u@u:~/t17/tmp$
llvmbot commented 4 months ago

@llvm/issue-subscribers-lld-elf

Author: None (sshannin)

lld spits out an obscure error when it encounters a thin archive containing a nested, non-thin archive. Reproducer is below. I tested the four permutations of combining thin and non-thin outer+inner nested archives. Only thin-inside-thin works. I also tested against bfd ld and gold. They *do* handle this case, although they don't inherently document that they intend to; they only explicitly mention thin-inside-thin. All 3 fail in the case where an archive (thin or otherwise) is nested in a non-thin archive. | outer | nested entry | success? | note | |-|-|-|-| | thin | thin | success | | | thin | non-thin | fail | opaque error about malformed archive | nonthin | thin | fail | symbol resolution failure | nonthin | nonthin | fail | symbol resolution failure It may be that it's deliberate not to support this, but it does deviate in feature parity from the other two linkers. And the error message is quite cryptic. This was with the 18.1.8 release built from source. ```c++ u@u:~/t17/tmp$ cat foo1.cpp #include "foo1.hpp" int foo1() { return 4; } u@u:~/t17/tmp$ cat foo2.cpp #include "foo2.hpp" int foo2() { return 5; } u@u:~/t17/tmp$ cat foo1.hpp int foo1(); u@u:~/t17/tmp$ cat foo2.hpp int foo2(); u@u:~/t17/tmp$ cat driver.cpp #include "foo1.hpp" #include "foo2.hpp" int main(int argc, char **argv) { return argc + foo1() + foo2(); } ``` ```bash u@u:~/t17/tmp$ ./drive.sh ${PREFIX}/g++ -o foo1.o -c foo1.cpp ${PREFIX}/g++ -o foo2.o -c foo2.cpp ${PREFIX}/gcc-ar -rc nonthin1.a foo1.o ${PREFIX}/gcc-ar -rTc thin1.a foo1.o ${PREFIX}/gcc-ar -rTc thin2_thin1.a foo2.o thin1.a ${PREFIX}/gcc-ar -rTc thin2_nonthin1.a foo2.o nonthin1.a ${PREFIX}/gcc-ar -rc nonthin2_thin1.a foo2.o thin1.a ${PREFIX}/gcc-ar -rc nonthin2_nonthin1.a foo2.o nonthin1.a ${PREFIX}/g++ -o driver.o -c driver.cpp ${PREFIX}/g++ -B ${PREFIX} -o run driver.o thin2_thin1.a -fuse-ld=lld ${PREFIX}/g++ -B ${PREFIX} -o run driver.o thin2_nonthin1.a -fuse-ld=lld ld.lld: error: thin2_nonthin1.a: could not get the buffer for a child of the archive: truncated or malformed archive (long name offset characters after the '/' are not all decimal numbers: '8:86' for archive member header at offset 252) collect2: error: ld returned 1 exit status ${PREFIX}/g++ -B ${PREFIX} -o run driver.o nonthin2_thin1.a -fuse-ld=lld ld.lld: warning: nonthin2_thin1.a: archive member 'thin1.a' is neither ET_REL nor LLVM bitcode ld.lld: error: undefined symbol: foo1() >>> referenced by driver.cpp >>> driver.o:(main) >>> did you mean: foo2() >>> defined in: nonthin2_thin1.a(foo2.o) collect2: error: ld returned 1 exit status ${PREFIX}/g++ -B ${PREFIX} -o run driver.o nonthin2_nonthin1.a -fuse-ld=lld ld.lld: warning: nonthin2_nonthin1.a: archive member 'nonthin1.a' is neither ET_REL nor LLVM bitcode ld.lld: error: undefined symbol: foo1() >>> referenced by driver.cpp >>> driver.o:(main) >>> did you mean: foo2() >>> defined in: nonthin2_nonthin1.a(foo2.o) collect2: error: ld returned 1 exit status u@u:~/t17/tmp$ ```