This is an archive of the discontinued LLVM Phabricator instance.

[ELF] Do not produce DT_JMPREL and DT_PLTGOT if .rela.plt is empty.
ClosedPublic

Authored by ikudrin on Jun 27 2019, 4:09 AM.

Details

Summary

If .rela.plt is mentioned in a linker script, it might be preserved even if it is empty. In that case, LLD created DT_JMPREL and DT_PLTGOT dynamic tags. When the tags exist, a dynamic loader writes values into reserved slots in .got.plt to support lazy symbol resolution. The problem is that, in fact, the linker has not reserved that space, and the writing may occur into the memory allocated for something else.

Diff Detail

Event Timeline

ikudrin created this revision.Jun 27 2019, 4:09 AM
Herald added a project: Restricted Project. · View Herald Transcript

On Linux, the issue can be reproduced in the following way:

// d.c
long val[3];
long getval(int i) {return val[i];}
// main.c
#include <stdio.h>

long getval(int i);

int main() {
  for (int i = 0; i < 3; ++i)
    printf("val[%d] = %ld\n", i, getval(i));
  return 0;
}
# d.t
PHDRS {
    all PT_LOAD;
    dyn PT_DYNAMIC;
}
SECTIONS {
    .rela.plt : {*(.rela.plt)}: all
    .dynamic : {*(.dynamic)}: all : dyn
    .got.plt : {*(.got.plt)}: all
    .bss : {*(.bss)}: all
}
$ clang d.c -nostdlib -fPIC -shared -o d.so -fuse-ld=lld -Wl,-T,d.t
$ clang main.c ./d.so -fpie -o test -fuse-ld=lld
$ ./test
val[0] = 0
val[1] = 140495953956864
val[2] = 140495951804032
ruiu accepted this revision.Jun 27 2019, 4:18 AM

LGTM

This revision is now accepted and ready to land.Jun 27 2019, 4:18 AM

I think this test case should live in test\ELF\linkerscript folder. Also it should be .test, not .s,
like our other tests that has no or little amount of asm code.
(See unused-synthetic2.test for example)

MaskRay added inline comments.Jun 27 2019, 4:32 AM
test/ELF/empty-relaplt-dyntags.s
1 ↗(On Diff #206817)

This test should probably be moved to linkerscript/

5 ↗(On Diff #206817)

Nit: indent the string literal so that S in SECTIONS does not occupy the same column of e in echo.

MaskRay added inline comments.Jun 27 2019, 4:36 AM
test/ELF/empty-relaplt-dyntags.s
18 ↗(On Diff #206817)

to write into memory it considers reserved

I don't think a dynamic linker should do this because DT_PLTRELSZ is zero.

I agree that 0 JMPREL should be omitted.

0x0000000000000017 (JMPREL) 0x0
0x0000000000000002 (PLTRELSZ) 0 (bytes)
0x0000000000000003 (PLTGOT) 0x0

ikudrin updated this revision to Diff 206829.Jun 27 2019, 4:49 AM
  • Reworked the test.

I am not sure about moving it into linkerscript, though, because it does not test the support for linker scripts per se. Moreover, there are lots of tests in the main test folder which also use linker scripts.

ikudrin marked an inline comment as done.Jun 27 2019, 4:54 AM
ikudrin added inline comments.
test/ELF/empty-relaplt-dyntags.s
18 ↗(On Diff #206817)

That is why I posted the reproducer. I tested it on Ubuntu 18.04 with the fresh built lld.

  • Reworked the test.

I am not sure about moving it into linkerscript, though, because it does not test the support for linker scripts per se.

Actually .rela.plt is kept only because it is mentioned in a linker script. It is relative.

Moreover, there are lots of tests in the main test folder which also use linker scripts.

And most of them are initially placed there by mistake it seems. Also, I found no other .test files that are based on linker script in that folder.
(.test files I see are YAML based and all linker script based test still lives in ELF/linkerscript).

MaskRay added inline comments.Jun 27 2019, 6:55 AM
test/ELF/empty-relaplt-dyntags.s
18 ↗(On Diff #206817)

You are right :) glibc writes the slots unconditionally

.got.plt[1] = _dl_runtime_resolve, .got.plt[2] = link_map

I have no more comments if you can fix the echo indentation and move the test under linkerscript

Empty .rela.plt is kept due to the isDiscardable() logic in LinkerScript.cpp

MaskRay added inline comments.Jun 27 2019, 7:18 AM
test/ELF/empty-relaplt-dyntags.test
10 ↗(On Diff #206829)

I think you need a readelf -S test to check that .rela.plt exists.

Then, for this comment, you may just say that we should not write a DT_JMPREL of 0, because otherwise the first few slots of the entries pointed to by DT_PLTGOT may be unexpected accessed at runtime.

The test doesn't add .got.plt (actually in glibc DT_PLTGOT will not be used if DT_JMPREL does not exist).

ikudrin updated this revision to Diff 207007.Jun 28 2019, 12:50 AM
ikudrin edited the summary of this revision. (Show Details)
  • Extended the test and moved it into the linkerscript folder.
grimar accepted this revision.Jun 28 2019, 1:57 AM

LGTM

This revision was automatically updated to reflect the committed changes.