main: Correct the pointer value of strtab on riscv/mips architectures
Glibc defines `d_ptr`/`d_val` to be a relocated address on most architectures, except for riscv and mips where it is just an offset from the binary load address. So we need to convert `strtab` from an offset into a pointer for riscv and mips. Internally within glibc there is the `D_PTR` macro for doing this, which relies on private data hidden in an ABI-unstable part of `link_map`. So we can't use the same conditional logic as glibc does internally. Our solution is to detect when `strtab` is unreasonably low (below the address of our binary) and so it must be an unrelocated offset. In that case we just do the relocation manually. Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6528 Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2718>
This commit is contained in:
parent
a95addd772
commit
65cde18786
20
src/main.c
20
src/main.c
@ -1,5 +1,7 @@
|
|||||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#if defined (HAVE_MALLINFO) || defined (HAVE_MALLINFO2)
|
#if defined (HAVE_MALLINFO) || defined (HAVE_MALLINFO2)
|
||||||
@ -20,6 +22,7 @@
|
|||||||
#include <link.h>
|
#include <link.h>
|
||||||
|
|
||||||
#ifdef HAVE_EXE_INTROSPECTION
|
#ifdef HAVE_EXE_INTROSPECTION
|
||||||
|
#include <dlfcn.h>
|
||||||
#include <elf.h>
|
#include <elf.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -140,6 +143,8 @@ maybe_add_rpath_introspection_paths (void)
|
|||||||
g_auto (GStrv) paths = NULL;
|
g_auto (GStrv) paths = NULL;
|
||||||
g_autofree char *exe_dir = NULL;
|
g_autofree char *exe_dir = NULL;
|
||||||
GStrv str;
|
GStrv str;
|
||||||
|
Dl_info dl_info;
|
||||||
|
struct link_map *link_map = NULL;
|
||||||
|
|
||||||
for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL; dyn++)
|
for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL; dyn++)
|
||||||
{
|
{
|
||||||
@ -154,6 +159,21 @@ maybe_add_rpath_introspection_paths (void)
|
|||||||
if ((!rpath && !runpath) || !strtab)
|
if ((!rpath && !runpath) || !strtab)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (dladdr1 (_DYNAMIC, &dl_info, (void **) &link_map, RTLD_DL_LINKMAP))
|
||||||
|
{
|
||||||
|
/* Sanity check */
|
||||||
|
g_return_if_fail ((void *) _DYNAMIC == (void *) link_map->l_ld);
|
||||||
|
|
||||||
|
/* strtab should be at an offset above our load address. If it's not
|
||||||
|
* then this is a special architecture (riscv, mips) that has a
|
||||||
|
* readonly _DYNAMIC section that's not relocated. So in that case
|
||||||
|
* strtab is currently an offset rather than an address. Let's make it
|
||||||
|
* an address...
|
||||||
|
*/
|
||||||
|
if (strtab < (const char *) link_map->l_addr)
|
||||||
|
strtab += link_map->l_addr;
|
||||||
|
}
|
||||||
|
|
||||||
if (rpath)
|
if (rpath)
|
||||||
paths = g_strsplit (strtab + rpath->d_un.d_val, ":", -1);
|
paths = g_strsplit (strtab + rpath->d_un.d_val, ":", -1);
|
||||||
else
|
else
|
||||||
|
Loading…
Reference in New Issue
Block a user