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.
Details
Diff Detail
Event Timeline
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
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)
test/ELF/empty-relaplt-dyntags.s | ||
---|---|---|
18 ↗ | (On Diff #206817) |
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 |
- 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.
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. |
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).
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 |
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). |