|
Packit Service |
97d2fb |
/* Recover relocatibility for addresses computed from debug information.
|
|
Packit Service |
97d2fb |
Copyright (C) 2005-2009, 2012 Red Hat, Inc.
|
|
Packit Service |
97d2fb |
This file is part of elfutils.
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
This file is free software; you can redistribute it and/or modify
|
|
Packit Service |
97d2fb |
it under the terms of either
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
* the GNU Lesser General Public License as published by the Free
|
|
Packit Service |
97d2fb |
Software Foundation; either version 3 of the License, or (at
|
|
Packit Service |
97d2fb |
your option) any later version
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
or
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
* the GNU General Public License as published by the Free
|
|
Packit Service |
97d2fb |
Software Foundation; either version 2 of the License, or (at
|
|
Packit Service |
97d2fb |
your option) any later version
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
or both in parallel, as here.
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
elfutils is distributed in the hope that it will be useful, but
|
|
Packit Service |
97d2fb |
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
97d2fb |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit Service |
97d2fb |
General Public License for more details.
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
You should have received copies of the GNU General Public License and
|
|
Packit Service |
97d2fb |
the GNU Lesser General Public License along with this program. If
|
|
Packit Service |
97d2fb |
not, see <http://www.gnu.org/licenses/>. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#ifdef HAVE_CONFIG_H
|
|
Packit Service |
97d2fb |
# include <config.h>
|
|
Packit Service |
97d2fb |
#endif
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#include "libdwflP.h"
|
|
Packit Service |
97d2fb |
#include <fcntl.h>
|
|
Packit Service |
97d2fb |
#include <unistd.h>
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Since dwfl_report_elf lays out the sections already, this will only be
|
|
Packit Service |
97d2fb |
called when the section headers of the debuginfo file are being
|
|
Packit Service |
97d2fb |
consulted instead, or for the section placed at 0. With binutils
|
|
Packit Service |
97d2fb |
strip-to-debug, the symbol table is in the debuginfo file and relocation
|
|
Packit Service |
97d2fb |
looks there. */
|
|
Packit Service |
97d2fb |
int
|
|
Packit Service |
97d2fb |
dwfl_offline_section_address (Dwfl_Module *mod,
|
|
Packit Service |
97d2fb |
void **userdata __attribute__ ((unused)),
|
|
Packit Service |
97d2fb |
const char *modname __attribute__ ((unused)),
|
|
Packit Service |
97d2fb |
Dwarf_Addr base __attribute__ ((unused)),
|
|
Packit Service |
97d2fb |
const char *secname __attribute__ ((unused)),
|
|
Packit Service |
97d2fb |
Elf32_Word shndx,
|
|
Packit Service |
97d2fb |
const GElf_Shdr *shdr __attribute__ ((unused)),
|
|
Packit Service |
97d2fb |
Dwarf_Addr *addr)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
assert (mod->e_type == ET_REL);
|
|
Packit Service |
97d2fb |
assert (shdr->sh_addr == 0);
|
|
Packit Service |
97d2fb |
assert (shdr->sh_flags & SHF_ALLOC);
|
|
Packit Service |
97d2fb |
assert (shndx != 0);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (mod->debug.elf == NULL)
|
|
Packit Service |
97d2fb |
/* We are only here because sh_addr is zero even though layout is complete.
|
|
Packit Service |
97d2fb |
The first section in the first file under -e is placed at 0. */
|
|
Packit Service |
97d2fb |
return 0;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* The section numbers might not match between the two files.
|
|
Packit Service |
97d2fb |
The best we can rely on is the order of SHF_ALLOC sections. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
Elf_Scn *ourscn = elf_getscn (mod->debug.elf, shndx);
|
|
Packit Service |
97d2fb |
Elf_Scn *scn = NULL;
|
|
Packit Service |
97d2fb |
uint_fast32_t skip_alloc = 0;
|
|
Packit Service |
97d2fb |
while ((scn = elf_nextscn (mod->debug.elf, scn)) != ourscn)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
assert (scn != NULL);
|
|
Packit Service |
97d2fb |
GElf_Shdr shdr_mem;
|
|
Packit Service |
97d2fb |
GElf_Shdr *sh = gelf_getshdr (scn, &shdr_mem);
|
|
Packit Service |
97d2fb |
if (unlikely (sh == NULL))
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
if (sh->sh_flags & SHF_ALLOC)
|
|
Packit Service |
97d2fb |
++skip_alloc;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
scn = NULL;
|
|
Packit Service |
97d2fb |
while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
GElf_Shdr shdr_mem;
|
|
Packit Service |
97d2fb |
GElf_Shdr *main_shdr = gelf_getshdr (scn, &shdr_mem);
|
|
Packit Service |
97d2fb |
if (unlikely (main_shdr == NULL))
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
if ((main_shdr->sh_flags & SHF_ALLOC) && skip_alloc-- == 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
assert (main_shdr->sh_flags == shdr->sh_flags);
|
|
Packit Service |
97d2fb |
*addr = main_shdr->sh_addr;
|
|
Packit Service |
97d2fb |
return 0;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* This should never happen. */
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
INTDEF (dwfl_offline_section_address)
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Forward declarations. */
|
|
Packit Service |
97d2fb |
static Dwfl_Module *process_elf (Dwfl *dwfl, const char *name,
|
|
Packit Service |
97d2fb |
const char *file_name, int fd, Elf *elf);
|
|
Packit Service |
97d2fb |
static Dwfl_Module *process_archive (Dwfl *dwfl, const char *name,
|
|
Packit Service |
97d2fb |
const char *file_name, int fd, Elf *elf,
|
|
Packit Service |
97d2fb |
int (*predicate) (const char *module,
|
|
Packit Service |
97d2fb |
const char *file));
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Report one module for an ELF file, or many for an archive.
|
|
Packit Service |
97d2fb |
Always consumes ELF and FD. */
|
|
Packit Service |
97d2fb |
static Dwfl_Module *
|
|
Packit Service |
97d2fb |
process_file (Dwfl *dwfl, const char *name, const char *file_name, int fd,
|
|
Packit Service |
97d2fb |
Elf *elf, int (*predicate) (const char *module,
|
|
Packit Service |
97d2fb |
const char *file))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
switch (elf_kind (elf))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
default:
|
|
Packit Service |
97d2fb |
case ELF_K_NONE:
|
|
Packit Service |
97d2fb |
__libdwfl_seterrno (elf == NULL ? DWFL_E_LIBELF : DWFL_E_BADELF);
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
case ELF_K_ELF:
|
|
Packit Service |
97d2fb |
return process_elf (dwfl, name, file_name, fd, elf);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
case ELF_K_AR:
|
|
Packit Service |
97d2fb |
return process_archive (dwfl, name, file_name, fd, elf, predicate);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Report the open ELF file as a module. Always consumes ELF and FD. */
|
|
Packit Service |
97d2fb |
static Dwfl_Module *
|
|
Packit Service |
97d2fb |
process_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd,
|
|
Packit Service |
97d2fb |
Elf *elf)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name, fd, elf,
|
|
Packit Service |
97d2fb |
dwfl->offline_next_address, true,
|
|
Packit Service |
97d2fb |
false);
|
|
Packit Service |
97d2fb |
if (mod != NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* If this is an ET_EXEC file with fixed addresses, the address range
|
|
Packit Service |
97d2fb |
it consumed may or may not intersect with the arbitrary range we
|
|
Packit Service |
97d2fb |
will use for relocatable modules. Make sure we always use a free
|
|
Packit Service |
97d2fb |
range for the offline allocations. If this module did use
|
|
Packit Service |
97d2fb |
offline_next_address, it may have rounded it up for the module's
|
|
Packit Service |
97d2fb |
alignment requirements. */
|
|
Packit Service |
97d2fb |
if ((dwfl->offline_next_address >= mod->low_addr
|
|
Packit Service |
97d2fb |
|| mod->low_addr - dwfl->offline_next_address < OFFLINE_REDZONE)
|
|
Packit Service |
97d2fb |
&& dwfl->offline_next_address < mod->high_addr + OFFLINE_REDZONE)
|
|
Packit Service |
97d2fb |
dwfl->offline_next_address = mod->high_addr + OFFLINE_REDZONE;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Don't keep the file descriptor around. */
|
|
Packit Service |
97d2fb |
if (mod->main.fd != -1 && elf_cntl (mod->main.elf, ELF_C_FDREAD) == 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Grab the dir path in case we want to report this file as
|
|
Packit Service |
97d2fb |
Dwarf later. */
|
|
Packit Service |
97d2fb |
mod->elfdir = __libdw_debugdir (mod->main.fd);
|
|
Packit Service |
97d2fb |
close (mod->main.fd);
|
|
Packit Service |
97d2fb |
mod->main.fd = -1;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return mod;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Always consumes MEMBER. Returns elf_next result on success.
|
|
Packit Service |
97d2fb |
For errors returns ELF_C_NULL with *MOD set to null. */
|
|
Packit Service |
97d2fb |
static Elf_Cmd
|
|
Packit Service |
97d2fb |
process_archive_member (Dwfl *dwfl, const char *name, const char *file_name,
|
|
Packit Service |
97d2fb |
int (*predicate) (const char *module, const char *file),
|
|
Packit Service |
97d2fb |
int fd, Elf *member, Dwfl_Module **mod)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
const Elf_Arhdr *h = elf_getarhdr (member);
|
|
Packit Service |
97d2fb |
if (unlikely (h == NULL))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libdwfl_seterrno (DWFL_E_LIBELF);
|
|
Packit Service |
97d2fb |
fail:
|
|
Packit Service |
97d2fb |
elf_end (member);
|
|
Packit Service |
97d2fb |
*mod = NULL;
|
|
Packit Service |
97d2fb |
return ELF_C_NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (!strcmp (h->ar_name, "/") || !strcmp (h->ar_name, "//")
|
|
Packit Service |
97d2fb |
|| !strcmp (h->ar_name, "/SYM64/"))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
skip:;
|
|
Packit Service |
97d2fb |
/* Skip this and go to the next. */
|
|
Packit Service |
97d2fb |
Elf_Cmd result = elf_next (member);
|
|
Packit Service |
97d2fb |
elf_end (member);
|
|
Packit Service |
97d2fb |
return result;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
char *member_name;
|
|
Packit Service |
97d2fb |
if (unlikely (asprintf (&member_name, "%s(%s)", file_name, h->ar_name) < 0))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
nomem:
|
|
Packit Service |
97d2fb |
__libdwfl_seterrno (DWFL_E_NOMEM);
|
|
Packit Service |
97d2fb |
elf_end (member);
|
|
Packit Service |
97d2fb |
*mod = NULL;
|
|
Packit Service |
97d2fb |
return ELF_C_NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
char *module_name = NULL;
|
|
Packit Service |
97d2fb |
if (name == NULL || name[0] == '\0')
|
|
Packit Service |
97d2fb |
name = h->ar_name;
|
|
Packit Service |
97d2fb |
else if (unlikely (asprintf (&module_name, "%s:%s", name, h->ar_name) < 0))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
free (member_name);
|
|
Packit Service |
97d2fb |
goto nomem;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
name = module_name;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (predicate != NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Let the predicate decide whether to use this one. */
|
|
Packit Service |
97d2fb |
int want = (*predicate) (name, member_name);
|
|
Packit Service |
97d2fb |
if (want <= 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
free (member_name);
|
|
Packit Service |
97d2fb |
free (module_name);
|
|
Packit Service |
97d2fb |
if (unlikely (want < 0))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libdwfl_seterrno (DWFL_E_CB);
|
|
Packit Service |
97d2fb |
goto fail;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
goto skip;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* We let __libdwfl_report_elf cache the fd in mod->main.fd,
|
|
Packit Service |
97d2fb |
though it's the same fd for all the members.
|
|
Packit Service |
97d2fb |
On module teardown we will close it only on the last Elf reference. */
|
|
Packit Service |
97d2fb |
*mod = process_file (dwfl, name, member_name, fd, member, predicate);
|
|
Packit Service |
97d2fb |
free (member_name);
|
|
Packit Service |
97d2fb |
free (module_name);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (*mod == NULL) /* process_file called elf_end. */
|
|
Packit Service |
97d2fb |
return ELF_C_NULL;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Advance the archive-reading offset for the next iteration. */
|
|
Packit Service |
97d2fb |
return elf_next (member);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Report each member of the archive as its own module. */
|
|
Packit Service |
97d2fb |
static Dwfl_Module *
|
|
Packit Service |
97d2fb |
process_archive (Dwfl *dwfl, const char *name, const char *file_name, int fd,
|
|
Packit Service |
97d2fb |
Elf *archive,
|
|
Packit Service |
97d2fb |
int (*predicate) (const char *module, const char *file))
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
Dwfl_Module *mod = NULL;
|
|
Packit Service |
97d2fb |
Elf *member = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, archive);
|
|
Packit Service |
97d2fb |
if (unlikely (member == NULL)) /* Empty archive. */
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libdwfl_seterrno (DWFL_E_BADELF);
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
while (process_archive_member (dwfl, name, file_name, predicate,
|
|
Packit Service |
97d2fb |
fd, member, &mod) != ELF_C_NULL)
|
|
Packit Service |
97d2fb |
member = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, archive);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* We can drop the archive Elf handle even if we're still using members
|
|
Packit Service |
97d2fb |
in live modules. When the last module's elf_end on a member returns
|
|
Packit Service |
97d2fb |
zero, that module will close FD. If no modules survived the predicate,
|
|
Packit Service |
97d2fb |
we are all done with the file right here. */
|
|
Packit Service |
97d2fb |
if (mod != NULL /* If no modules, caller will clean up. */
|
|
Packit Service |
97d2fb |
&& elf_end (archive) == 0)
|
|
Packit Service |
97d2fb |
close (fd);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return mod;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
Dwfl_Module *
|
|
Packit Service |
97d2fb |
internal_function
|
|
Packit Service |
97d2fb |
__libdwfl_report_offline (Dwfl *dwfl, const char *name,
|
|
Packit Service |
97d2fb |
const char *file_name, int fd, bool closefd,
|
|
Packit Service |
97d2fb |
int (*predicate) (const char *module,
|
|
Packit Service |
97d2fb |
const char *file))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
Elf *elf;
|
|
Packit Service |
97d2fb |
Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, true);
|
|
Packit Service |
97d2fb |
if (error != DWFL_E_NOERROR)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libdwfl_seterrno (error);
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
Dwfl_Module *mod = process_file (dwfl, name, file_name, fd, elf, predicate);
|
|
Packit Service |
97d2fb |
if (mod == NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
elf_end (elf);
|
|
Packit Service |
97d2fb |
if (closefd)
|
|
Packit Service |
97d2fb |
close (fd);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
return mod;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
Dwfl_Module *
|
|
Packit Service |
97d2fb |
dwfl_report_offline (Dwfl *dwfl, const char *name,
|
|
Packit Service |
97d2fb |
const char *file_name, int fd)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (dwfl == NULL)
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
bool closefd = false;
|
|
Packit Service |
97d2fb |
if (fd < 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
closefd = true;
|
|
Packit Service |
97d2fb |
fd = open (file_name, O_RDONLY);
|
|
Packit Service |
97d2fb |
if (fd < 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libdwfl_seterrno (DWFL_E_ERRNO);
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return __libdwfl_report_offline (dwfl, name, file_name, fd, closefd, NULL);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
INTDEF (dwfl_report_offline)
|