Reputation: 173
I was debugging an ELF shared library and noticed that in its
dynamic section the d_ptr
values, which are stored as relative addresses on disk, are converted into absolute addresses when the library is loaded into memory by ld-linux.
Looking at the glibc source, the code responsible for these relocations is:
// file: sysdeps/generic/ldsodefs.h
/* Return true if dynamic section in the shared library L should be
relocated. */
static inline bool
dl_relocate_ld (const struct link_map *l)
{
/* Don't relocate dynamic section if it is readonly */
return !(l->l_ld_readonly || DL_RO_DYN_SECTION);
}
// file: elf/get-dynamic-info.h
/* Don't adjust .dynamic unnecessarily. */
if (l->l_addr != 0 && dl_relocate_ld (l))
{
ElfW(Addr) l_addr = l->l_addr;
# define ADJUST_DYN_INFO(tag) \
do \
{ \
if (info[tag] != NULL) \
info[tag]->d_un.d_ptr += l_addr; \
} \
while (0)
ADJUST_DYN_INFO (DT_HASH);
ADJUST_DYN_INFO (DT_PLTGOT);
ADJUST_DYN_INFO (DT_STRTAB);
ADJUST_DYN_INFO (DT_SYMTAB);
ADJUST_DYN_INFO (DT_RELR);
ADJUST_DYN_INFO (DT_JMPREL);
ADJUST_DYN_INFO (VERSYMIDX (DT_VERSYM));
ADJUST_DYN_INFO (ADDRIDX (DT_GNU_HASH));
# undef ADJUST_DYN_INFO
/* DT_RELA/DT_REL are mandatory. But they may have zero value if
there is DT_RELR. Don't relocate them if they are zero. */
# define ADJUST_DYN_INFO(tag) \
do \
if (info[tag] != NULL && info[tag]->d_un.d_ptr != 0) \
info[tag]->d_un.d_ptr += l_addr; \
while (0)
...
}
which adds the base address where the object was loaded to certain entries in the dynamic section.
What I'm curious about is why these relocations are needed at all. To compare with another dynamic linker, it seems like FreeBSD's rtld doesn't do them, regardless of whether the dynamic section is writable or not.
To test, I changed the dynamic segment of my shared library to be readonly using a hex editor, and the library behaved normally. The dynamic section in memory was identical to what it was on disk.
My best guess is that maybe it's to make debugging core dumps easier.
Upvotes: 0
Views: 80