Open mitchblank opened 8 years ago
Here is an example of what I'm talking about on a trivial "Hello, World" executable. Before:
$ objdump -x -j .dynamic a.out | tail
Sections:
Idx Name Size VMA LMA File off Algn
20 .dynamic 000001d0 0000000000600e28 0000000000600e28 00000e28 2**3
CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
0000000000600e28 l d .dynamic 0000000000000000 .dynamic
0000000000600e28 l O .dynamic 0000000000000000 _DYNAMIC
So this shows that the .dynamic
section is at file offset 0xe28 and will end up mapped at 0x600e28. In the symbol table there is both a debugging symbol called .dynamic
and also an object symbol called _DYNAMIC
that both point to this section.
Then if I run patchelf on this binary, we'll see this change:
$ patchelf --set-rpath '$ORIGIN/../lib' a.out
$ objdump -x -j .dynamic a.out | tail
Sections:
Idx Name Size VMA LMA File off Algn
0 .dynamic 000001e0 00000000003ff270 00000000003ff270 00000270 2**3
CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
00000000003ff270 l d .dynamic 0000000000000000 .dynamic
0000000000600e28 l O .dynamic 0000000000000000 _DYNAMIC
So the section has now been relocated, both in the file and in virtual address space. The debugging symbol .dynamic
is also updated. However the object symbol _DYNAMIC
still has its old value and isn't pointing into this ELF table anymore.
I suspect this behavior is related to this "FIXME" comment in ElfFile<ElfFileParamNames>::rewriteHeaders
:
/* Rewrite st_value. FIXME: we should do this for all
types, but most don't actually change. */
if (ELF32_ST_TYPE(rdi(sym->st_info)) == STT_SECTION)
wri(sym->st_value, rdi(shdrs[newIndex].sh_addr));
Presumably _DYNAMIC
will be STT_OBJECT
instead...
This fix would probably look something a little like:
/* Rewrite st_value. */
if (ELF32_ST_TYPE(rdi(sym->st_info)) == STT_SECTION) {
wri(sym->st_value, rdi(shdrs[newIndex].sh_addr));
} else if (replacedSections.find(section) != replacedSections.end()) {
wri(sym->st_value, rdi(shdrs[newIndex].sh_addr));
}
...however, that only works for the special case that the symbol points to the beginning of the beginning of the section. What we really want is something that looks like:
Elf_Addr addr = rdi(sym->st_value);
addr -= OLD_START_ADDRESS_OF_SECTION;
addr += rdi(shdrs[newIndex].sh_addr));
wri(sym->st_value, addr);
I dug around and its not clear if the original address of the section is kept around anywhere though...
Alright, got some progress here!
As it turns out, updating the symbol is not sufficient, because at least GCC-generated code seems to also rely on the .rela.dyn
section to fill-in references to the symbol, so patchelf also needs to update that as well - but having done so, it seems that this problem is solved and the program from SO works.
Somewhat unfortunately, this doesn't actually fix the underlying issue with NodeJS (and pcloud, in my case), so on to investigating further 🔍
Wow, good job finding this old bug. Hope the info in it helped.
I can't be of much assistance to verifying your fix since I filed this in reference to a problem I hit at my day job... which I left 4 years ago. I don't have access to the codebase where I was seeing this problem occur.
I was trying to make a function that determined if a program had already the correct runpath set (don't ask...) This should be possible by inspecting the
_DYNAMIC
symbol as show here and just looking at my ownd_tag == DT_RUNPATH
entry: http://stackoverflow.com/a/2846736However, after running a binary through patchelf, it seems like the
_DYNAMIC
symbol points to garbage? I haven't investigated more, but I suspect that it just isn't updating that symbol after replacing that ELF header section... if that's the case than probably some other programs that are trying to be clever with_DYNAMIC
-introspection