New user self-registration is disabled due to spam. For an account please email bugs-admin@lists.llvm.org with your e-mail address and full name.

Bug 39104 - LLD links incorrect ELF executable if version script contains "local: *;"
Summary: LLD links incorrect ELF executable if version script contains "local: *;"
Status: RESOLVED FIXED
Alias: None
Product: lld
Classification: Unclassified
Component: ELF (show other bugs)
Version: unspecified
Hardware: PC Linux
: P enhancement
Assignee: Unassigned LLVM Bugs
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2018-09-27 14:23 PDT by Orivej Desh
Modified: 2018-10-11 07:26 PDT (History)
4 users (show)

See Also:
Fixed By Commit(s):


Attachments
reproducer (1.13 KB, application/gzip)
2018-09-28 18:15 PDT, Orivej Desh
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Orivej Desh 2018-09-27 14:23:36 PDT
Given these files:

  x.c:

    int main() { return 0; }

  x.map:

    { local: *; };

an executable linked with:

  clang x.c -o x -fuse-ld=lld -Wl,--version-script=x.map

does not run with an error:

  ./x: symbol lookup error: ./x: undefined symbol:

and "LD_DEBUG=symbols ./x" reveals that ld is looking for a symbol with zero-length name.

(Such linker scripts are useful when an executable needs to dlopen a shared object without exposing symbols that are supposed to be details of implementation.)

This was introduced in https://reviews.llvm.org/D46103. LLD before r330966 and other linkers produce an executable that runs with code 0, therefore this is a regression in LLD 7.
Comment 1 Orivej Desh 2018-09-27 17:14:40 PDT
LLD >= r330966 does something wrong with the undefined versioned symbol __libc_start_main@GLIBC_2.2.5. Good "readelf -s" output:

…
Relocation section '.rela.plt' at offset 0x420 contains 2 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000202028  000500000007 R_X86_64_JUMP_SLO 00000000002011c0 __libc_start_main@GLIBC_2.2.5 + 0
…

Bad "readelf -s" output:

…
Relocation section '.rela.plt' at offset 0x3b8 contains 2 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000202028  000000000007 R_X86_64_JUMP_SLO                    0
…

Good "readelf -V" output:

Version symbols section '.gnu.version' contains 6 entries:
 Addr: 0000000000200320  Offset: 0x000320  Link: 3 (.dynsym)
  000:   0 (*local*)       0 (*local*)       0 (*local*)       0 (*local*)    
  004:   0 (*local*)       2 (GLIBC_2.2.5)

Version needs section '.gnu.version_r' contains 1 entries:
 Addr: 0x000000000020032c  Offset: 0x00032c  Link: 7 (.dynstr)
  000000: Version: 1  File: libc.so.6  Cnt: 1
  0x0010:   Name: GLIBC_2.2.5  Flags: none  Version: 2


Bad "readelf -V" output:

No version information found in this file.
Comment 2 Rui Ueyama 2018-09-28 11:24:48 PDT
It works for me. What is your operating system?

$ cat x.c
int main() { return 0; }

$ cat x.map
{ local: *; };

$ bin/clang x.c -o x -fuse-ld=lld -Wl,--version-script=x.map

$ ./x

$
Comment 3 Orivej Desh 2018-09-28 13:08:34 PDT
I'm testing on Ubuntu 16, but this should be reproducible on any system with glibc. Is the lld used by clang recent enough? If not, put the directory with ld.lld in front of the PATH. Otherwise, what is the output of "readelf -s ./x" and "readelf -V ./x"?

$ clang -v      
clang version 8.0.0 (trunk 343356)
Target: x86_64-unknown-linux-gnu
…
$ clang x.c -o x -fuse-ld=lld -Wl,--version
LLD 8.0.0 (trunk 343356) (compatible with GNU linkers)
Comment 4 Rui Ueyama 2018-09-28 13:13:08 PDT
Yes, I tried lld 7 that I built from the released source code.

lld has a feature to gather input files to pack it to a single tar archive. Can you add `--reproduce=repro.tar` to your command line, compress a generated tar file and then share it with me?
Comment 5 Orivej Desh 2018-09-28 13:28:08 PDT
Here is repro.tar.gz: https://s3.amazonaws.com/orivej/bugs/llvm/39104/repro.tar.gz
Comment 6 Orivej Desh 2018-09-28 18:15:44 PDT
Created attachment 20941 [details]
reproducer

I have made a more self-contained reproducer that does not depend on glibc. This:

tar xf repro2.tar.gz
cd repro2
make test

should run ./x without error, but ./x fails with the undefined symbol error. This succeeds:

make clean
make test ld=ld
Comment 7 Rui Ueyama 2018-09-29 08:25:50 PDT
Thanks. I tried to fix it but looks like the easiest way of fixing it is to revert that patch. Since many patches have been submitted that patch, it is not very easy to do that, but I'll do.
Comment 8 Orivej Desh 2018-09-29 16:40:57 PDT
I have proposed a revert at https://reviews.llvm.org/D52698 (tested with check-lld).

Could you help with a test case? I don't understand the issue yet. (Is it limited to version scripts, or can it happen without version scripts? How to reduce crt1.o in repro2 and build it from source?)
Comment 9 George Rimar 2018-10-01 03:34:51 PDT
(In reply to Orivej Desh from comment #8)
> I have proposed a revert at https://reviews.llvm.org/D52698 (tested with
> check-lld).
> 
> Could you help with a test case?

I was able to use the following code to build my version of crt1.o for your test case:

.global __libc_csu_init
.global __libc_csu_fini
.global __libc_start_main
.global _start
_start:
xor    %ebp,%ebp
mov    %rdx,%r9
pop    %rsi
mov    %rsp,%rdx
and    $0xfffffffffffffff0,%rsp
push   %rax
push   %rsp
mov    $__libc_csu_fini,%r8
mov    $__libc_csu_init,%rcx
mov    $main,%rdi
call   __libc_start_main
hlt

(I used "objdump -D" and "readelf -r" to restore the original code from crt1.o).
Comment 10 George Rimar 2018-10-03 02:36:12 PDT
Fixed in: r343668
Comment 11 Orivej Desh 2018-10-03 07:14:19 PDT
Thanks! This seems appropriate to release in 7.0.1.