ELF64 Dynamic loading and DT_JMPREL

73 views
Skip to first unread message

John Leidel

unread,
Mar 16, 2022, 10:54:57 AM3/16/22
to RISC-V SW Dev
RV SW: We are in the process of porting some runtime code that
contains some dynamic symbol table magic that allows higher level
runtimes to overlay specific libc functionality using dynamically
loaded libraries. We're building/running on a RV64G platform running
Ubuntu 21.04 (just for reference) and GCC 10.3.0.

The code in question looks up the symbol table and strtab entries
using the `dl_phdr_info->phdr_info` structure. We appear to resolve
these fine. We then attempt to grab a reference to the DT_JMPREL
table using the same lookup method. We get what appears to be a valid
address back for DT_JMPREL from the loaded library. However, when we
attempt to walk the set of `r_info` blocks from the address specified
by DT_JMPREL, we start wandering off into bad memory. The library is
dynamically loaded with (RTLD_NOW | RTLD_LOCAL) enabled.

I'm curious whether the DT_JMPREL is resolving the correct address per
the RISC-V ELF spec. It should resolve to the address of relocation
entries associated solely with the PLT. DT_JMPREL is technically an
optional component, but it shows up in the ELF headers:

JMPREL 0x0000000000011ba8

The code is wrapped in a bunch of library specific functions, but I'll
try to paraphrase as follows. I know this is a rather obscure
question....

static uintptr_t
my_reloc_get_entry(ElfW(Addr) base, const ElfW(Phdr) *dphdr, ElfW(Sxword) tag)
{
ElfW(Dyn) *entry;
for (entry = (void*)(base + dphdr->p_vaddr); entry->d_tag != 0; ++entry) {
if (entry->d_tag == tag) {
return entry->d_un.d_val;
}
}
return 0;
}

static unsigned
my_dl_populate_symbols(ucm_dl_info_t *dl_info, uintptr_t dlpi_addr, void *table,
size_t table_size, void *strtab, ElfW(Sym) *symtab,
const char *dl_name)
{
ElfW(Rela) *reloc;
khiter_t khiter;
unsigned count;
char *elf_sym;
int ret;

count = 0;
for (reloc = table; (void*)reloc < UCS_PTR_BYTE_OFFSET(table, table_size);
++reloc) {
// the seg fault occurs below when trying to access r_info
elf_sym = (char*)strtab + symtab[ELF64_R_SYM(reloc->r_info)].st_name;

// the rest is omitted for brevity...
}
}

ElfW(Sym) *symtab = (void*)my_reloc_get_entry(dlpi_addr, dphdr, DT_SYMTAB);
void *strtab = (void*)my_reloc_get_entry(dlpi_addr, dphdr, DT_STRTAB);
void *jmprel = (void*)my_reloc_get_entry(dlpi_addr, dphdr, DT_JMPREL);
size_t pltrelsz = my_reloc_get_entry(dlpi_addr, dphdr, DT_PLTRELSZ);
unsigned num_symbols = my_dl_populate_symbols(dl_info, dlpi_addr, jmprel,
pltrelsz, strtab,
symtab, dl_name);

Palmer Dabbelt

unread,
Mar 17, 2022, 2:41:00 PM3/17/22
to RISC-V SW Dev, john.leidel
[I'm replying from the web interface, as I don't usually watch sw-dev any more but got pointed here.]

On Wednesday, March 16, 2022 at 7:54:57 AM UTC-7 john.leidel wrote:
RV SW: We are in the process of porting some runtime code that
contains some dynamic symbol table magic that allows higher level
runtimes to overlay specific libc functionality using dynamically 
loaded libraries. We're building/running on a RV64G platform running
Ubuntu 21.04 (just for reference) and GCC 10.3.0.

The code in question looks up the symbol table and strtab entries
using the `dl_phdr_info->phdr_info` structure. We appear to resolve
these fine. We then attempt to grab a reference to the DT_JMPREL
table using the same lookup method. We get what appears to be a valid
address back for DT_JMPREL from the loaded library. However, when we
attempt to walk the set of `r_info` blocks from the address specified
by DT_JMPREL, we start wandering off into bad memory. The library is
dynamically loaded with (RTLD_NOW | RTLD_LOCAL) enabled.

I guess this sort of an orthogonal issue, but this seems like the sort of thing that glibc's rtld-audid stuff could be used for.  That comes with a whole bunch of other constraints, but might save you from having to dig this deep into ELF.
 
I'm curious whether the DT_JMPREL is resolving the correct address per
the RISC-V ELF spec. It should resolve to the address of relocation
entries associated solely with the PLT. DT_JMPREL is technically an
optional component, but it shows up in the ELF headers:

It's correct enough that dynamic linking works between binutils and glibc, but that's not to say we meet every bit of the ELF spec.  This is one of those areas where there tends to be some unwritten architecture-specific ABI surface, just because it's pretty tricky stuff and once it's in binaries it's in the ABI.

The code below is really hard to read, possibly because of the web interface but also just because it's fragments.  IMO the best way to move forward here is to get a test case that we can put into either binutils or glibc, so we can have a more concrete discussion about whether the behavior in question is a bug or a surprise feature.

John Leidel

unread,
Mar 17, 2022, 2:48:50 PM3/17/22
to Palmer Dabbelt, RISC-V SW Dev
Palmer, thanks for replying. We're working on getting a functional
test case put together. We're also working on testing a slightly
newer version of binutils + glibc + gcc 11.2.0. The latest GCC
variants appear to have slightly different options with respect to the
ELF attributes. As soon as I get something together, I'll post a more
concrete example here.

Palmer Dabbelt

unread,
Mar 17, 2022, 2:54:09 PM3/17/22
to John Leidel, RISC-V SW Dev


On Thu, Mar 17, 2022, 11:48 AM John Leidel <john....@gmail.com> wrote:
Palmer, thanks for replying.  We're working on getting a functional
test case put together.  We're also working on testing a slightly
newer version of binutils + glibc + gcc 11.2.0.  The latest GCC
variants appear to have slightly different options with respect to the
ELF attributes.  As soon as I get something together, I'll post a more
concrete example here.

Probably best to just open a binutils or glibc bug with the test.  Most of the folks who do this sort of thing aren't all that active on sw-dev.

John Leidel

unread,
Mar 21, 2022, 10:54:15 AM3/21/22
to Palmer Dabbelt, RISC-V SW Dev
All, quick update on the issue. We've been able to reproduce the bug
in a relatively small test case (see attached). We're in the process
of requesting a Sourceware account so we can send the bug upstream to
Binutils. The attached code reproduces the bug in short order. This
was run natively using binutils 2.36.1 and binutils 2.37 on a SiFive
Umatched running Ubuntu 21.04 and 21.10, respectively.

best
john
experiments.tar.gz

John Leidel

unread,
Mar 21, 2022, 11:51:22 AM3/21/22
to Palmer Dabbelt, RISC-V SW Dev
All, the bug has been posted to Bintuils here:
https://sourceware.org/bugzilla/show_bug.cgi?id=28986

Palmer Dabbelt

unread,
Mar 21, 2022, 1:45:39 PM3/21/22
to John Leidel, RISC-V SW Dev
Thanks!  Let's move the discussion over there, it'll probably be more likely to be seen by the relevant parties.
Reply all
Reply to author
Forward
0 new messages