mirror of
https://github.com/autc04/Retro68.git
synced 2024-12-02 03:50:17 +00:00
d21be3b4e1
(from https://sourceware.org/elfutils/, GPL/LGPL licensed)
1040 lines
30 KiB
C
1040 lines
30 KiB
C
/* Report modules by examining dynamic linker data structures.
|
||
Copyright (C) 2008-2016 Red Hat, Inc.
|
||
This file is part of elfutils.
|
||
|
||
This file is free software; you can redistribute it and/or modify
|
||
it under the terms of either
|
||
|
||
* the GNU Lesser General Public License as published by the Free
|
||
Software Foundation; either version 3 of the License, or (at
|
||
your option) any later version
|
||
|
||
or
|
||
|
||
* the GNU General Public License as published by the Free
|
||
Software Foundation; either version 2 of the License, or (at
|
||
your option) any later version
|
||
|
||
or both in parallel, as here.
|
||
|
||
elfutils is distributed in the hope that it will be useful, but
|
||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
General Public License for more details.
|
||
|
||
You should have received copies of the GNU General Public License and
|
||
the GNU Lesser General Public License along with this program. If
|
||
not, see <http://www.gnu.org/licenses/>. */
|
||
|
||
#include <config.h>
|
||
#include "libdwflP.h"
|
||
#include "../libdw/memory-access.h"
|
||
#include "system.h"
|
||
|
||
#include <byteswap.h>
|
||
#include <endian.h>
|
||
#include <fcntl.h>
|
||
|
||
/* This element is always provided and always has a constant value.
|
||
This makes it an easy thing to scan for to discern the format. */
|
||
#define PROBE_TYPE AT_PHENT
|
||
#define PROBE_VAL32 sizeof (Elf32_Phdr)
|
||
#define PROBE_VAL64 sizeof (Elf64_Phdr)
|
||
|
||
|
||
static inline bool
|
||
do_check64 (size_t i, const Elf64_auxv_t (*a64)[], uint_fast8_t *elfdata)
|
||
{
|
||
/* The AUXV pointer might not even be naturally aligned for 64-bit
|
||
data, because note payloads in a core file are not aligned. */
|
||
|
||
uint64_t type = read_8ubyte_unaligned_noncvt (&(*a64)[i].a_type);
|
||
uint64_t val = read_8ubyte_unaligned_noncvt (&(*a64)[i].a_un.a_val);
|
||
|
||
if (type == BE64 (PROBE_TYPE)
|
||
&& val == BE64 (PROBE_VAL64))
|
||
{
|
||
*elfdata = ELFDATA2MSB;
|
||
return true;
|
||
}
|
||
|
||
if (type == LE64 (PROBE_TYPE)
|
||
&& val == LE64 (PROBE_VAL64))
|
||
{
|
||
*elfdata = ELFDATA2LSB;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
#define check64(n) do_check64 (n, a64, elfdata)
|
||
|
||
static inline bool
|
||
do_check32 (size_t i, const Elf32_auxv_t (*a32)[], uint_fast8_t *elfdata)
|
||
{
|
||
/* The AUXV pointer might not even be naturally aligned for 32-bit
|
||
data, because note payloads in a core file are not aligned. */
|
||
|
||
uint32_t type = read_4ubyte_unaligned_noncvt (&(*a32)[i].a_type);
|
||
uint32_t val = read_4ubyte_unaligned_noncvt (&(*a32)[i].a_un.a_val);
|
||
|
||
if (type == BE32 (PROBE_TYPE)
|
||
&& val == BE32 (PROBE_VAL32))
|
||
{
|
||
*elfdata = ELFDATA2MSB;
|
||
return true;
|
||
}
|
||
|
||
if (type == LE32 (PROBE_TYPE)
|
||
&& val == LE32 (PROBE_VAL32))
|
||
{
|
||
*elfdata = ELFDATA2LSB;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
#define check32(n) do_check32 (n, a32, elfdata)
|
||
|
||
/* Examine an auxv data block and determine its format.
|
||
Return true iff we figured it out. */
|
||
static bool
|
||
auxv_format_probe (const void *auxv, size_t size,
|
||
uint_fast8_t *elfclass, uint_fast8_t *elfdata)
|
||
{
|
||
const Elf32_auxv_t (*a32)[size / sizeof (Elf32_auxv_t)] = (void *) auxv;
|
||
const Elf64_auxv_t (*a64)[size / sizeof (Elf64_auxv_t)] = (void *) auxv;
|
||
|
||
for (size_t i = 0; i < size / sizeof (Elf64_auxv_t); ++i)
|
||
{
|
||
if (check64 (i))
|
||
{
|
||
*elfclass = ELFCLASS64;
|
||
return true;
|
||
}
|
||
|
||
if (check32 (i * 2) || check32 (i * 2 + 1))
|
||
{
|
||
*elfclass = ELFCLASS32;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/* This is a Dwfl_Memory_Callback that wraps another memory callback.
|
||
If the underlying callback cannot fill the data, then this will
|
||
fall back to fetching data from module files. */
|
||
|
||
struct integrated_memory_callback
|
||
{
|
||
Dwfl_Memory_Callback *memory_callback;
|
||
void *memory_callback_arg;
|
||
void *buffer;
|
||
};
|
||
|
||
static bool
|
||
integrated_memory_callback (Dwfl *dwfl, int ndx,
|
||
void **buffer, size_t *buffer_available,
|
||
GElf_Addr vaddr,
|
||
size_t minread,
|
||
void *arg)
|
||
{
|
||
struct integrated_memory_callback *info = arg;
|
||
|
||
if (ndx == -1)
|
||
{
|
||
/* Called for cleanup. */
|
||
if (info->buffer != NULL)
|
||
{
|
||
/* The last probe buffer came from the underlying callback.
|
||
Let it do its cleanup. */
|
||
assert (*buffer == info->buffer); /* XXX */
|
||
*buffer = info->buffer;
|
||
info->buffer = NULL;
|
||
return (*info->memory_callback) (dwfl, ndx, buffer, buffer_available,
|
||
vaddr, minread,
|
||
info->memory_callback_arg);
|
||
}
|
||
*buffer = NULL;
|
||
*buffer_available = 0;
|
||
return false;
|
||
}
|
||
|
||
if (*buffer != NULL)
|
||
/* For a final-read request, we only use the underlying callback. */
|
||
return (*info->memory_callback) (dwfl, ndx, buffer, buffer_available,
|
||
vaddr, minread, info->memory_callback_arg);
|
||
|
||
/* Let the underlying callback try to fill this request. */
|
||
if ((*info->memory_callback) (dwfl, ndx, &info->buffer, buffer_available,
|
||
vaddr, minread, info->memory_callback_arg))
|
||
{
|
||
*buffer = info->buffer;
|
||
return true;
|
||
}
|
||
|
||
/* Now look for module text covering this address. */
|
||
|
||
Dwfl_Module *mod;
|
||
(void) INTUSE(dwfl_addrsegment) (dwfl, vaddr, &mod);
|
||
if (mod == NULL)
|
||
return false;
|
||
|
||
Dwarf_Addr bias;
|
||
Elf_Scn *scn = INTUSE(dwfl_module_address_section) (mod, &vaddr, &bias);
|
||
if (unlikely (scn == NULL))
|
||
{
|
||
#if 0 // XXX would have to handle ndx=-1 cleanup calls passed down.
|
||
/* If we have no sections we can try to fill it from the module file
|
||
based on its phdr mappings. */
|
||
if (likely (mod->e_type != ET_REL) && mod->main.elf != NULL)
|
||
return INTUSE(dwfl_elf_phdr_memory_callback)
|
||
(dwfl, 0, buffer, buffer_available,
|
||
vaddr - mod->main.bias, minread, mod->main.elf);
|
||
#endif
|
||
return false;
|
||
}
|
||
|
||
Elf_Data *data = elf_rawdata (scn, NULL);
|
||
if (unlikely (data == NULL))
|
||
// XXX throw error?
|
||
return false;
|
||
|
||
if (unlikely (data->d_size < vaddr))
|
||
return false;
|
||
|
||
/* Provide as much data as we have. */
|
||
void *contents = data->d_buf + vaddr;
|
||
size_t avail = data->d_size - vaddr;
|
||
if (unlikely (avail < minread))
|
||
return false;
|
||
|
||
/* If probing for a string, make sure it's terminated. */
|
||
if (minread == 0 && unlikely (memchr (contents, '\0', avail) == NULL))
|
||
return false;
|
||
|
||
/* We have it! */
|
||
*buffer = contents;
|
||
*buffer_available = avail;
|
||
return true;
|
||
}
|
||
|
||
static size_t
|
||
addrsize (uint_fast8_t elfclass)
|
||
{
|
||
return elfclass * 4;
|
||
}
|
||
|
||
/* Report a module for each struct link_map in the linked list at r_map
|
||
in the struct r_debug at R_DEBUG_VADDR. For r_debug_info description
|
||
see dwfl_link_map_report in libdwflP.h. If R_DEBUG_INFO is not NULL then no
|
||
modules get added to DWFL, caller has to add them from filled in
|
||
R_DEBUG_INFO.
|
||
|
||
For each link_map entry, if an existing module resides at its address,
|
||
this just modifies that module's name and suggested file name. If
|
||
no such module exists, this calls dwfl_report_elf on the l_name string.
|
||
|
||
Returns the number of modules found, or -1 for errors. */
|
||
|
||
static int
|
||
report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata,
|
||
Dwfl *dwfl, GElf_Addr r_debug_vaddr,
|
||
Dwfl_Memory_Callback *memory_callback,
|
||
void *memory_callback_arg,
|
||
struct r_debug_info *r_debug_info)
|
||
{
|
||
/* Skip r_version, to aligned r_map field. */
|
||
GElf_Addr read_vaddr = r_debug_vaddr + addrsize (elfclass);
|
||
|
||
void *buffer = NULL;
|
||
size_t buffer_available = 0;
|
||
inline int release_buffer (int result)
|
||
{
|
||
if (buffer != NULL)
|
||
(void) (*memory_callback) (dwfl, -1, &buffer, &buffer_available, 0, 0,
|
||
memory_callback_arg);
|
||
return result;
|
||
}
|
||
|
||
GElf_Addr addrs[4];
|
||
inline bool read_addrs (GElf_Addr vaddr, size_t n)
|
||
{
|
||
size_t nb = n * addrsize (elfclass); /* Address words -> bytes to read. */
|
||
|
||
/* Read a new buffer if the old one doesn't cover these words. */
|
||
if (buffer == NULL
|
||
|| vaddr < read_vaddr
|
||
|| vaddr - read_vaddr + nb > buffer_available)
|
||
{
|
||
release_buffer (0);
|
||
|
||
read_vaddr = vaddr;
|
||
int segndx = INTUSE(dwfl_addrsegment) (dwfl, vaddr, NULL);
|
||
if (unlikely (segndx < 0)
|
||
|| unlikely (! (*memory_callback) (dwfl, segndx,
|
||
&buffer, &buffer_available,
|
||
vaddr, nb, memory_callback_arg)))
|
||
return true;
|
||
}
|
||
|
||
Elf32_Addr (*a32)[n] = vaddr - read_vaddr + buffer;
|
||
Elf64_Addr (*a64)[n] = (void *) a32;
|
||
|
||
if (elfclass == ELFCLASS32)
|
||
{
|
||
if (elfdata == ELFDATA2MSB)
|
||
for (size_t i = 0; i < n; ++i)
|
||
addrs[i] = BE32 (read_4ubyte_unaligned_noncvt (&(*a32)[i]));
|
||
else
|
||
for (size_t i = 0; i < n; ++i)
|
||
addrs[i] = LE32 (read_4ubyte_unaligned_noncvt (&(*a32)[i]));
|
||
}
|
||
else
|
||
{
|
||
if (elfdata == ELFDATA2MSB)
|
||
for (size_t i = 0; i < n; ++i)
|
||
addrs[i] = BE64 (read_8ubyte_unaligned_noncvt (&(*a64)[i]));
|
||
else
|
||
for (size_t i = 0; i < n; ++i)
|
||
addrs[i] = LE64 (read_8ubyte_unaligned_noncvt (&(*a64)[i]));
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
if (unlikely (read_addrs (read_vaddr, 1)))
|
||
return release_buffer (-1);
|
||
|
||
GElf_Addr next = addrs[0];
|
||
|
||
Dwfl_Module **lastmodp = &dwfl->modulelist;
|
||
int result = 0;
|
||
|
||
/* There can't be more elements in the link_map list than there are
|
||
segments. DWFL->lookup_elts is probably twice that number, so it
|
||
is certainly above the upper bound. If we iterate too many times,
|
||
there must be a loop in the pointers due to link_map clobberation. */
|
||
size_t iterations = 0;
|
||
while (next != 0 && ++iterations < dwfl->lookup_elts)
|
||
{
|
||
if (read_addrs (next, 4))
|
||
return release_buffer (-1);
|
||
|
||
/* Unused: l_addr is the difference between the address in memory
|
||
and the ELF file when the core was created. We need to
|
||
recalculate the difference below because the ELF file we use
|
||
might be differently pre-linked. */
|
||
// GElf_Addr l_addr = addrs[0];
|
||
GElf_Addr l_name = addrs[1];
|
||
GElf_Addr l_ld = addrs[2];
|
||
next = addrs[3];
|
||
|
||
/* If a clobbered or truncated memory image has no useful pointer,
|
||
just skip this element. */
|
||
if (l_ld == 0)
|
||
continue;
|
||
|
||
/* Fetch the string at the l_name address. */
|
||
const char *name = NULL;
|
||
if (buffer != NULL
|
||
&& read_vaddr <= l_name
|
||
&& l_name + 1 - read_vaddr < buffer_available
|
||
&& memchr (l_name - read_vaddr + buffer, '\0',
|
||
buffer_available - (l_name - read_vaddr)) != NULL)
|
||
name = l_name - read_vaddr + buffer;
|
||
else
|
||
{
|
||
release_buffer (0);
|
||
read_vaddr = l_name;
|
||
int segndx = INTUSE(dwfl_addrsegment) (dwfl, l_name, NULL);
|
||
if (likely (segndx >= 0)
|
||
&& (*memory_callback) (dwfl, segndx,
|
||
&buffer, &buffer_available,
|
||
l_name, 0, memory_callback_arg))
|
||
name = buffer;
|
||
}
|
||
|
||
if (name != NULL && name[0] == '\0')
|
||
name = NULL;
|
||
|
||
if (iterations == 1
|
||
&& dwfl->user_core != NULL
|
||
&& dwfl->user_core->executable_for_core != NULL)
|
||
name = dwfl->user_core->executable_for_core;
|
||
|
||
struct r_debug_info_module *r_debug_info_module = NULL;
|
||
if (r_debug_info != NULL)
|
||
{
|
||
/* Save link map information about valid shared library (or
|
||
executable) which has not been found on disk. */
|
||
const char *name1 = name == NULL ? "" : name;
|
||
r_debug_info_module = malloc (sizeof (*r_debug_info_module)
|
||
+ strlen (name1) + 1);
|
||
if (unlikely (r_debug_info_module == NULL))
|
||
return release_buffer (result);
|
||
r_debug_info_module->fd = -1;
|
||
r_debug_info_module->elf = NULL;
|
||
r_debug_info_module->l_ld = l_ld;
|
||
r_debug_info_module->start = 0;
|
||
r_debug_info_module->end = 0;
|
||
r_debug_info_module->disk_file_has_build_id = false;
|
||
strcpy (r_debug_info_module->name, name1);
|
||
r_debug_info_module->next = r_debug_info->module;
|
||
r_debug_info->module = r_debug_info_module;
|
||
}
|
||
|
||
Dwfl_Module *mod = NULL;
|
||
if (name != NULL)
|
||
{
|
||
/* This code is mostly inlined dwfl_report_elf. */
|
||
// XXX hook for sysroot
|
||
int fd = open (name, O_RDONLY);
|
||
if (fd >= 0)
|
||
{
|
||
Elf *elf;
|
||
Dwfl_Error error = __libdw_open_file (&fd, &elf, true, false);
|
||
GElf_Addr elf_dynamic_vaddr;
|
||
if (error == DWFL_E_NOERROR
|
||
&& __libdwfl_dynamic_vaddr_get (elf, &elf_dynamic_vaddr))
|
||
{
|
||
const void *build_id_bits;
|
||
GElf_Addr build_id_elfaddr;
|
||
int build_id_len;
|
||
bool valid = true;
|
||
|
||
if (__libdwfl_find_elf_build_id (NULL, elf, &build_id_bits,
|
||
&build_id_elfaddr,
|
||
&build_id_len) > 0
|
||
&& build_id_elfaddr != 0)
|
||
{
|
||
if (r_debug_info_module != NULL)
|
||
r_debug_info_module->disk_file_has_build_id = true;
|
||
GElf_Addr build_id_vaddr = (build_id_elfaddr
|
||
- elf_dynamic_vaddr + l_ld);
|
||
|
||
release_buffer (0);
|
||
int segndx = INTUSE(dwfl_addrsegment) (dwfl,
|
||
build_id_vaddr,
|
||
NULL);
|
||
if (! (*memory_callback) (dwfl, segndx,
|
||
&buffer, &buffer_available,
|
||
build_id_vaddr, build_id_len,
|
||
memory_callback_arg))
|
||
{
|
||
/* File has valid build-id which cannot be read from
|
||
memory. This happens for core files without bit 4
|
||
(0x10) set in Linux /proc/PID/coredump_filter. */
|
||
}
|
||
else
|
||
{
|
||
if (memcmp (build_id_bits, buffer, build_id_len) != 0)
|
||
/* File has valid build-id which does not match
|
||
the one in memory. */
|
||
valid = false;
|
||
release_buffer (0);
|
||
}
|
||
}
|
||
|
||
if (valid)
|
||
{
|
||
// It is like l_addr but it handles differently prelinked
|
||
// files at core dumping vs. core loading time.
|
||
GElf_Addr base = l_ld - elf_dynamic_vaddr;
|
||
if (r_debug_info_module == NULL)
|
||
{
|
||
// XXX hook for sysroot
|
||
mod = __libdwfl_report_elf (dwfl, basename (name),
|
||
name, fd, elf, base,
|
||
true, true);
|
||
if (mod != NULL)
|
||
{
|
||
elf = NULL;
|
||
fd = -1;
|
||
}
|
||
}
|
||
else if (__libdwfl_elf_address_range (elf, base, true,
|
||
true, NULL, NULL,
|
||
&r_debug_info_module->start,
|
||
&r_debug_info_module->end,
|
||
NULL, NULL))
|
||
{
|
||
r_debug_info_module->elf = elf;
|
||
r_debug_info_module->fd = fd;
|
||
elf = NULL;
|
||
fd = -1;
|
||
}
|
||
}
|
||
if (elf != NULL)
|
||
elf_end (elf);
|
||
if (fd != -1)
|
||
close (fd);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (mod != NULL)
|
||
{
|
||
++result;
|
||
|
||
/* Move this module to the end of the list, so that we end
|
||
up with a list in the same order as the link_map chain. */
|
||
if (mod->next != NULL)
|
||
{
|
||
if (*lastmodp != mod)
|
||
{
|
||
lastmodp = &dwfl->modulelist;
|
||
while (*lastmodp != mod)
|
||
lastmodp = &(*lastmodp)->next;
|
||
}
|
||
*lastmodp = mod->next;
|
||
mod->next = NULL;
|
||
while (*lastmodp != NULL)
|
||
lastmodp = &(*lastmodp)->next;
|
||
*lastmodp = mod;
|
||
}
|
||
|
||
lastmodp = &mod->next;
|
||
}
|
||
}
|
||
|
||
return release_buffer (result);
|
||
}
|
||
|
||
static GElf_Addr
|
||
consider_executable (Dwfl_Module *mod, GElf_Addr at_phdr, GElf_Addr at_entry,
|
||
uint_fast8_t *elfclass, uint_fast8_t *elfdata,
|
||
Dwfl_Memory_Callback *memory_callback,
|
||
void *memory_callback_arg)
|
||
{
|
||
GElf_Ehdr ehdr;
|
||
if (unlikely (gelf_getehdr (mod->main.elf, &ehdr) == NULL))
|
||
return 0;
|
||
|
||
if (at_entry != 0)
|
||
{
|
||
/* If we have an AT_ENTRY value, reject this executable if
|
||
its entry point address could not have supplied that. */
|
||
|
||
if (ehdr.e_entry == 0)
|
||
return 0;
|
||
|
||
if (mod->e_type == ET_EXEC)
|
||
{
|
||
if (ehdr.e_entry != at_entry)
|
||
return 0;
|
||
}
|
||
else
|
||
{
|
||
/* It could be a PIE. */
|
||
}
|
||
}
|
||
|
||
// XXX this could be saved in the file cache: phdr vaddr, DT_DEBUG d_val vaddr
|
||
/* Find the vaddr of the DT_DEBUG's d_ptr. This is the memory
|
||
address where &r_debug was written at runtime. */
|
||
GElf_Xword align = mod->dwfl->segment_align;
|
||
GElf_Addr d_val_vaddr = 0;
|
||
size_t phnum;
|
||
if (elf_getphdrnum (mod->main.elf, &phnum) != 0)
|
||
return 0;
|
||
|
||
for (size_t i = 0; i < phnum; ++i)
|
||
{
|
||
GElf_Phdr phdr_mem;
|
||
GElf_Phdr *phdr = gelf_getphdr (mod->main.elf, i, &phdr_mem);
|
||
if (phdr == NULL)
|
||
break;
|
||
|
||
if (phdr->p_align > 1 && (align == 0 || phdr->p_align < align))
|
||
align = phdr->p_align;
|
||
|
||
if (at_phdr != 0
|
||
&& phdr->p_type == PT_LOAD
|
||
&& (phdr->p_offset & -align) == (ehdr.e_phoff & -align))
|
||
{
|
||
/* This is the segment that would map the phdrs.
|
||
If we have an AT_PHDR value, reject this executable
|
||
if its phdr mapping could not have supplied that. */
|
||
if (mod->e_type == ET_EXEC)
|
||
{
|
||
if (ehdr.e_phoff - phdr->p_offset + phdr->p_vaddr != at_phdr)
|
||
return 0;
|
||
}
|
||
else
|
||
{
|
||
/* It could be a PIE. If the AT_PHDR value and our
|
||
phdr address don't match modulo ALIGN, then this
|
||
could not have been the right PIE. */
|
||
if (((ehdr.e_phoff - phdr->p_offset + phdr->p_vaddr) & -align)
|
||
!= (at_phdr & -align))
|
||
return 0;
|
||
|
||
/* Calculate the bias applied to the PIE's p_vaddr values. */
|
||
GElf_Addr bias = (at_phdr - (ehdr.e_phoff - phdr->p_offset
|
||
+ phdr->p_vaddr));
|
||
|
||
/* Final sanity check: if we have an AT_ENTRY value,
|
||
reject this PIE unless its biased e_entry matches. */
|
||
if (at_entry != 0 && at_entry != ehdr.e_entry + bias)
|
||
return 0;
|
||
|
||
/* If we're changing the module's address range,
|
||
we've just invalidated the module lookup table. */
|
||
GElf_Addr mod_bias = dwfl_adjusted_address (mod, 0);
|
||
if (bias != mod_bias)
|
||
{
|
||
mod->low_addr -= mod_bias;
|
||
mod->high_addr -= mod_bias;
|
||
mod->low_addr += bias;
|
||
mod->high_addr += bias;
|
||
|
||
free (mod->dwfl->lookup_module);
|
||
mod->dwfl->lookup_module = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (phdr->p_type == PT_DYNAMIC)
|
||
{
|
||
Elf_Data *data = elf_getdata_rawchunk (mod->main.elf, phdr->p_offset,
|
||
phdr->p_filesz, ELF_T_DYN);
|
||
if (data == NULL)
|
||
continue;
|
||
const size_t entsize = gelf_fsize (mod->main.elf,
|
||
ELF_T_DYN, 1, EV_CURRENT);
|
||
const size_t n = data->d_size / entsize;
|
||
for (size_t j = 0; j < n; ++j)
|
||
{
|
||
GElf_Dyn dyn_mem;
|
||
GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem);
|
||
if (dyn != NULL && dyn->d_tag == DT_DEBUG)
|
||
{
|
||
d_val_vaddr = phdr->p_vaddr + entsize * j + entsize / 2;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (d_val_vaddr != 0)
|
||
{
|
||
/* Now we have the final address from which to read &r_debug. */
|
||
d_val_vaddr = dwfl_adjusted_address (mod, d_val_vaddr);
|
||
|
||
void *buffer = NULL;
|
||
size_t buffer_available = addrsize (ehdr.e_ident[EI_CLASS]);
|
||
|
||
int segndx = INTUSE(dwfl_addrsegment) (mod->dwfl, d_val_vaddr, NULL);
|
||
|
||
if ((*memory_callback) (mod->dwfl, segndx,
|
||
&buffer, &buffer_available,
|
||
d_val_vaddr, buffer_available,
|
||
memory_callback_arg))
|
||
{
|
||
const union
|
||
{
|
||
Elf32_Addr a32;
|
||
Elf64_Addr a64;
|
||
} *u = buffer;
|
||
|
||
GElf_Addr vaddr;
|
||
if (ehdr.e_ident[EI_CLASS] == ELFCLASS32)
|
||
vaddr = (ehdr.e_ident[EI_DATA] == ELFDATA2MSB
|
||
? BE32 (u->a32) : LE32 (u->a32));
|
||
else
|
||
vaddr = (ehdr.e_ident[EI_DATA] == ELFDATA2MSB
|
||
? BE64 (u->a64) : LE64 (u->a64));
|
||
|
||
(*memory_callback) (mod->dwfl, -1, &buffer, &buffer_available, 0, 0,
|
||
memory_callback_arg);
|
||
|
||
if (*elfclass == ELFCLASSNONE)
|
||
*elfclass = ehdr.e_ident[EI_CLASS];
|
||
else if (*elfclass != ehdr.e_ident[EI_CLASS])
|
||
return 0;
|
||
|
||
if (*elfdata == ELFDATANONE)
|
||
*elfdata = ehdr.e_ident[EI_DATA];
|
||
else if (*elfdata != ehdr.e_ident[EI_DATA])
|
||
return 0;
|
||
|
||
return vaddr;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Try to find an existing executable module with a DT_DEBUG. */
|
||
static GElf_Addr
|
||
find_executable (Dwfl *dwfl, GElf_Addr at_phdr, GElf_Addr at_entry,
|
||
uint_fast8_t *elfclass, uint_fast8_t *elfdata,
|
||
Dwfl_Memory_Callback *memory_callback,
|
||
void *memory_callback_arg)
|
||
{
|
||
for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
|
||
if (mod->main.elf != NULL)
|
||
{
|
||
GElf_Addr r_debug_vaddr = consider_executable (mod, at_phdr, at_entry,
|
||
elfclass, elfdata,
|
||
memory_callback,
|
||
memory_callback_arg);
|
||
if (r_debug_vaddr != 0)
|
||
return r_debug_vaddr;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
int
|
||
dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
|
||
Dwfl_Memory_Callback *memory_callback,
|
||
void *memory_callback_arg,
|
||
struct r_debug_info *r_debug_info)
|
||
{
|
||
GElf_Addr r_debug_vaddr = 0;
|
||
|
||
uint_fast8_t elfclass = ELFCLASSNONE;
|
||
uint_fast8_t elfdata = ELFDATANONE;
|
||
if (likely (auxv != NULL)
|
||
&& likely (auxv_format_probe (auxv, auxv_size, &elfclass, &elfdata)))
|
||
{
|
||
GElf_Addr entry = 0;
|
||
GElf_Addr phdr = 0;
|
||
GElf_Xword phent = 0;
|
||
GElf_Xword phnum = 0;
|
||
|
||
#define READ_AUXV32(ptr) read_4ubyte_unaligned_noncvt (ptr)
|
||
#define READ_AUXV64(ptr) read_8ubyte_unaligned_noncvt (ptr)
|
||
#define AUXV_SCAN(NN, BL) do \
|
||
{ \
|
||
const Elf##NN##_auxv_t *av = auxv; \
|
||
for (size_t i = 0; i < auxv_size / sizeof av[0]; ++i) \
|
||
{ \
|
||
uint##NN##_t type = READ_AUXV##NN (&av[i].a_type); \
|
||
uint##NN##_t val = BL##NN (READ_AUXV##NN (&av[i].a_un.a_val)); \
|
||
if (type == BL##NN (AT_ENTRY)) \
|
||
entry = val; \
|
||
else if (type == BL##NN (AT_PHDR)) \
|
||
phdr = val; \
|
||
else if (type == BL##NN (AT_PHNUM)) \
|
||
phnum = val; \
|
||
else if (type == BL##NN (AT_PHENT)) \
|
||
phent = val; \
|
||
else if (type == BL##NN (AT_PAGESZ)) \
|
||
{ \
|
||
if (val > 1 \
|
||
&& (dwfl->segment_align == 0 \
|
||
|| val < dwfl->segment_align)) \
|
||
dwfl->segment_align = val; \
|
||
} \
|
||
} \
|
||
} \
|
||
while (0)
|
||
|
||
if (elfclass == ELFCLASS32)
|
||
{
|
||
if (elfdata == ELFDATA2MSB)
|
||
AUXV_SCAN (32, BE);
|
||
else
|
||
AUXV_SCAN (32, LE);
|
||
}
|
||
else
|
||
{
|
||
if (elfdata == ELFDATA2MSB)
|
||
AUXV_SCAN (64, BE);
|
||
else
|
||
AUXV_SCAN (64, LE);
|
||
}
|
||
|
||
/* If we found the phdr dimensions, search phdrs for PT_DYNAMIC. */
|
||
GElf_Addr dyn_vaddr = 0;
|
||
GElf_Xword dyn_filesz = 0;
|
||
GElf_Addr dyn_bias = (GElf_Addr) -1;
|
||
|
||
inline bool consider_phdr (GElf_Word type,
|
||
GElf_Addr vaddr, GElf_Xword filesz)
|
||
{
|
||
switch (type)
|
||
{
|
||
case PT_PHDR:
|
||
if (dyn_bias == (GElf_Addr) -1
|
||
/* Do a sanity check on the putative address. */
|
||
&& ((vaddr & (dwfl->segment_align - 1))
|
||
== (phdr & (dwfl->segment_align - 1))))
|
||
{
|
||
dyn_bias = phdr - vaddr;
|
||
return dyn_vaddr != 0;
|
||
}
|
||
break;
|
||
|
||
case PT_DYNAMIC:
|
||
dyn_vaddr = vaddr;
|
||
dyn_filesz = filesz;
|
||
return dyn_bias != (GElf_Addr) -1;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
if (phdr != 0 && phnum != 0)
|
||
{
|
||
Dwfl_Module *phdr_mod;
|
||
int phdr_segndx = INTUSE(dwfl_addrsegment) (dwfl, phdr, &phdr_mod);
|
||
Elf_Data in =
|
||
{
|
||
.d_type = ELF_T_PHDR,
|
||
.d_version = EV_CURRENT,
|
||
.d_size = phnum * phent,
|
||
.d_buf = NULL
|
||
};
|
||
bool in_ok = (*memory_callback) (dwfl, phdr_segndx, &in.d_buf,
|
||
&in.d_size, phdr, phnum * phent,
|
||
memory_callback_arg);
|
||
bool in_from_exec = false;
|
||
if (! in_ok
|
||
&& dwfl->user_core != NULL
|
||
&& dwfl->user_core->executable_for_core != NULL)
|
||
{
|
||
/* AUXV -> PHDR -> DYNAMIC
|
||
Both AUXV and DYNAMIC should be always present in a core file.
|
||
PHDR may be missing in core file, try to read it from
|
||
EXECUTABLE_FOR_CORE to find where DYNAMIC is located in the
|
||
core file. */
|
||
|
||
int fd = open (dwfl->user_core->executable_for_core, O_RDONLY);
|
||
Elf *elf;
|
||
Dwfl_Error error = DWFL_E_ERRNO;
|
||
if (fd != -1)
|
||
error = __libdw_open_file (&fd, &elf, true, false);
|
||
if (error != DWFL_E_NOERROR)
|
||
{
|
||
__libdwfl_seterrno (error);
|
||
return false;
|
||
}
|
||
GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
|
||
if (ehdr == NULL)
|
||
{
|
||
elf_end (elf);
|
||
close (fd);
|
||
__libdwfl_seterrno (DWFL_E_LIBELF);
|
||
return false;
|
||
}
|
||
size_t e_phnum;
|
||
if (elf_getphdrnum (elf, &e_phnum) != 0)
|
||
{
|
||
elf_end (elf);
|
||
close (fd);
|
||
__libdwfl_seterrno (DWFL_E_LIBELF);
|
||
return false;
|
||
}
|
||
if (e_phnum != phnum || ehdr->e_phentsize != phent)
|
||
{
|
||
elf_end (elf);
|
||
close (fd);
|
||
__libdwfl_seterrno (DWFL_E_BADELF);
|
||
return false;
|
||
}
|
||
off_t off = ehdr->e_phoff;
|
||
assert (in.d_buf == NULL);
|
||
/* Note this in the !in_ok path. That means memory_callback
|
||
failed. But the callback might still have reset the d_size
|
||
value (to zero). So explicitly set it here again. */
|
||
in.d_size = phnum * phent;
|
||
in.d_buf = malloc (in.d_size);
|
||
if (unlikely (in.d_buf == NULL))
|
||
{
|
||
elf_end (elf);
|
||
close (fd);
|
||
__libdwfl_seterrno (DWFL_E_NOMEM);
|
||
return false;
|
||
}
|
||
ssize_t nread = pread_retry (fd, in.d_buf, in.d_size, off);
|
||
elf_end (elf);
|
||
close (fd);
|
||
if (nread != (ssize_t) in.d_size)
|
||
{
|
||
free (in.d_buf);
|
||
__libdwfl_seterrno (DWFL_E_ERRNO);
|
||
return false;
|
||
}
|
||
in_ok = true;
|
||
in_from_exec = true;
|
||
}
|
||
if (in_ok)
|
||
{
|
||
if (unlikely (phnum > SIZE_MAX / phent))
|
||
{
|
||
__libdwfl_seterrno (DWFL_E_NOMEM);
|
||
return false;
|
||
}
|
||
size_t nbytes = phnum * phent;
|
||
void *buf = malloc (nbytes);
|
||
Elf32_Phdr (*p32)[phnum] = buf;
|
||
Elf64_Phdr (*p64)[phnum] = buf;
|
||
if (unlikely (buf == NULL))
|
||
{
|
||
__libdwfl_seterrno (DWFL_E_NOMEM);
|
||
return false;
|
||
}
|
||
Elf_Data out =
|
||
{
|
||
.d_type = ELF_T_PHDR,
|
||
.d_version = EV_CURRENT,
|
||
.d_size = phnum * phent,
|
||
.d_buf = buf
|
||
};
|
||
in.d_size = out.d_size;
|
||
if (likely ((elfclass == ELFCLASS32
|
||
? elf32_xlatetom : elf64_xlatetom)
|
||
(&out, &in, elfdata) != NULL))
|
||
{
|
||
/* We are looking for PT_DYNAMIC. */
|
||
if (elfclass == ELFCLASS32)
|
||
{
|
||
for (size_t i = 0; i < phnum; ++i)
|
||
if (consider_phdr ((*p32)[i].p_type,
|
||
(*p32)[i].p_vaddr,
|
||
(*p32)[i].p_filesz))
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
for (size_t i = 0; i < phnum; ++i)
|
||
if (consider_phdr ((*p64)[i].p_type,
|
||
(*p64)[i].p_vaddr,
|
||
(*p64)[i].p_filesz))
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (in_from_exec)
|
||
free (in.d_buf);
|
||
else
|
||
(*memory_callback) (dwfl, -1, &in.d_buf, &in.d_size, 0, 0,
|
||
memory_callback_arg);
|
||
free (buf);
|
||
}
|
||
else
|
||
/* We could not read the executable's phdrs from the
|
||
memory image. If we have a presupplied executable,
|
||
we can still use the AT_PHDR and AT_ENTRY values to
|
||
verify it, and to adjust its bias if it's a PIE.
|
||
|
||
If there was an ET_EXEC module presupplied that contains
|
||
the AT_PHDR address, then we only consider that one.
|
||
We'll either accept it if its phdr location and e_entry
|
||
make sense or reject it if they don't. If there is no
|
||
presupplied ET_EXEC, then look for a presupplied module,
|
||
which might be a PIE (ET_DYN) that needs its bias adjusted. */
|
||
r_debug_vaddr = ((phdr_mod == NULL
|
||
|| phdr_mod->main.elf == NULL
|
||
|| phdr_mod->e_type != ET_EXEC)
|
||
? find_executable (dwfl, phdr, entry,
|
||
&elfclass, &elfdata,
|
||
memory_callback,
|
||
memory_callback_arg)
|
||
: consider_executable (phdr_mod, phdr, entry,
|
||
&elfclass, &elfdata,
|
||
memory_callback,
|
||
memory_callback_arg));
|
||
}
|
||
|
||
/* If we found PT_DYNAMIC, search it for DT_DEBUG. */
|
||
if (dyn_filesz != 0)
|
||
{
|
||
if (dyn_bias != (GElf_Addr) -1)
|
||
dyn_vaddr += dyn_bias;
|
||
|
||
Elf_Data in =
|
||
{
|
||
.d_type = ELF_T_DYN,
|
||
.d_version = EV_CURRENT,
|
||
.d_size = dyn_filesz,
|
||
.d_buf = NULL
|
||
};
|
||
int dyn_segndx = dwfl_addrsegment (dwfl, dyn_vaddr, NULL);
|
||
if ((*memory_callback) (dwfl, dyn_segndx, &in.d_buf, &in.d_size,
|
||
dyn_vaddr, dyn_filesz, memory_callback_arg))
|
||
{
|
||
void *buf = malloc (dyn_filesz);
|
||
Elf32_Dyn (*d32)[dyn_filesz / sizeof (Elf32_Dyn)] = buf;
|
||
Elf64_Dyn (*d64)[dyn_filesz / sizeof (Elf64_Dyn)] = buf;
|
||
if (unlikely (buf == NULL))
|
||
{
|
||
__libdwfl_seterrno (DWFL_E_NOMEM);
|
||
return false;
|
||
}
|
||
Elf_Data out =
|
||
{
|
||
.d_type = ELF_T_DYN,
|
||
.d_version = EV_CURRENT,
|
||
.d_size = dyn_filesz,
|
||
.d_buf = buf
|
||
};
|
||
in.d_size = out.d_size;
|
||
if (likely ((elfclass == ELFCLASS32
|
||
? elf32_xlatetom : elf64_xlatetom)
|
||
(&out, &in, elfdata) != NULL))
|
||
{
|
||
/* We are looking for DT_DEBUG. */
|
||
if (elfclass == ELFCLASS32)
|
||
{
|
||
size_t n = dyn_filesz / sizeof (Elf32_Dyn);
|
||
for (size_t i = 0; i < n; ++i)
|
||
if ((*d32)[i].d_tag == DT_DEBUG)
|
||
{
|
||
r_debug_vaddr = (*d32)[i].d_un.d_val;
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
size_t n = dyn_filesz / sizeof (Elf64_Dyn);
|
||
for (size_t i = 0; i < n; ++i)
|
||
if ((*d64)[i].d_tag == DT_DEBUG)
|
||
{
|
||
r_debug_vaddr = (*d64)[i].d_un.d_val;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
(*memory_callback) (dwfl, -1, &in.d_buf, &in.d_size, 0, 0,
|
||
memory_callback_arg);
|
||
free (buf);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
/* We have to look for a presupplied executable file to determine
|
||
the vaddr of its dynamic section and DT_DEBUG therein. */
|
||
r_debug_vaddr = find_executable (dwfl, 0, 0, &elfclass, &elfdata,
|
||
memory_callback, memory_callback_arg);
|
||
|
||
if (r_debug_vaddr == 0)
|
||
return 0;
|
||
|
||
/* For following pointers from struct link_map, we will use an
|
||
integrated memory access callback that can consult module text
|
||
elided from the core file. This is necessary when the l_name
|
||
pointer for the dynamic linker's own entry is a pointer into the
|
||
executable's .interp section. */
|
||
struct integrated_memory_callback mcb =
|
||
{
|
||
.memory_callback = memory_callback,
|
||
.memory_callback_arg = memory_callback_arg
|
||
};
|
||
|
||
/* Now we can follow the dynamic linker's library list. */
|
||
return report_r_debug (elfclass, elfdata, dwfl, r_debug_vaddr,
|
||
&integrated_memory_callback, &mcb, r_debug_info);
|
||
}
|
||
INTDEF (dwfl_link_map_report)
|