#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "libelfP.h"
#include "libdwflP.h"
#if !USE_BZLIB
# define __libdw_bunzip2(...) DWFL_E_BADELF
#endif
#if !USE_LZMA
# define __libdw_unlzma(...) DWFL_E_BADELF
#endif
#if !USE_ZSTD
# define __libdw_unzstd(...) DWFL_E_BADELF
#endif
static Dwfl_Error
decompress (int fd __attribute__ ((unused)), Elf **elf)
{
Dwfl_Error error = DWFL_E_BADELF;
if (fd == -1)
return error;
void *buffer = NULL;
size_t size = 0;
const off_t offset = (*elf)->start_offset;
void *const mapped = ((*elf)->map_address == NULL ? NULL
: (*elf)->map_address + offset);
const size_t mapped_size = (*elf)->maximum_size;
if (mapped_size == 0)
return error;
error = __libdw_gunzip (fd, offset, mapped, mapped_size, &buffer, &size);
if (error == DWFL_E_BADELF)
error = __libdw_bunzip2 (fd, offset, mapped, mapped_size, &buffer, &size);
if (error == DWFL_E_BADELF)
error = __libdw_unlzma (fd, offset, mapped, mapped_size, &buffer, &size);
if (error == DWFL_E_BADELF)
error = __libdw_unzstd (fd, offset, mapped, mapped_size, &buffer, &size);
if (error == DWFL_E_NOERROR)
{
if (unlikely (size == 0))
{
error = DWFL_E_BADELF;
free (buffer);
}
else
{
Elf *memelf = elf_memory (buffer, size);
if (memelf == NULL)
{
error = DWFL_E_LIBELF;
free (buffer);
}
else
{
memelf->flags |= ELF_F_MALLOCED;
elf_end (*elf);
*elf = memelf;
}
}
}
else
free (buffer);
return error;
}
static Dwfl_Error
what_kind (int fd, Elf **elfp, Elf_Kind *kind, bool *may_close_fd)
{
Dwfl_Error error = DWFL_E_NOERROR;
*kind = elf_kind (*elfp);
if (unlikely (*kind == ELF_K_NONE))
{
if (unlikely (*elfp == NULL))
error = DWFL_E_LIBELF;
else
{
error = decompress (fd, elfp);
if (error == DWFL_E_NOERROR)
{
*may_close_fd = true;
*kind = elf_kind (*elfp);
}
}
}
return error;
}
static Dwfl_Error
libdw_open_elf (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok,
bool never_close_fd, bool bad_elf_ok, bool use_elfp)
{
bool may_close_fd = false;
Elf *elf =
use_elfp ? *elfp : elf_begin (*fdp, ELF_C_READ_MMAP_PRIVATE, NULL);
Elf_Kind kind;
Dwfl_Error error = what_kind (*fdp, &elf, &kind, &may_close_fd);
if (error == DWFL_E_BADELF)
{
off_t offset = elf->start_offset;
error = __libdw_image_header (*fdp, &offset,
(elf->map_address == NULL ? NULL
: elf->map_address + offset),
elf->maximum_size);
if (error == DWFL_E_NOERROR)
{
elf->kind = ELF_K_AR;
elf->state.ar.elf_ar_hdr.ar_name = "libdwfl is faking you out";
elf->state.ar.elf_ar_hdr.ar_size = elf->maximum_size - offset;
elf->state.ar.offset = offset - sizeof (struct ar_hdr);
Elf *subelf = elf_begin (-1, ELF_C_READ_MMAP_PRIVATE, elf);
elf->kind = ELF_K_NONE;
if (unlikely (subelf == NULL))
error = DWFL_E_LIBELF;
else
{
subelf->parent = NULL;
subelf->flags |= elf->flags & (ELF_F_MMAPPED | ELF_F_MALLOCED);
elf->flags &= ~(ELF_F_MMAPPED | ELF_F_MALLOCED);
elf_end (elf);
elf = subelf;
error = what_kind (*fdp, &elf, &kind, &may_close_fd);
}
}
}
if (error == DWFL_E_NOERROR
&& kind != ELF_K_ELF
&& !(archive_ok && kind == ELF_K_AR))
error = DWFL_E_BADELF;
if (bad_elf_ok && error == DWFL_E_BADELF)
error = DWFL_E_NOERROR;
if (error != DWFL_E_NOERROR)
{
elf_end (elf);
elf = NULL;
}
if (! never_close_fd
&& error == DWFL_E_NOERROR ? may_close_fd : close_on_fail)
{
close (*fdp);
*fdp = -1;
}
*elfp = elf;
return error;
}
Dwfl_Error internal_function
__libdw_open_file (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok)
{
return libdw_open_elf (fdp, elfp, close_on_fail, archive_ok, false, false,
false);
}
Dwfl_Error internal_function
__libdw_open_elf_memory (char *data, size_t size, Elf **elfp, bool archive_ok)
{
int fd = -1;
*elfp = elf_memory (data, size);
if (unlikely(*elfp == NULL))
{
return DWFL_E_LIBELF;
}
(*elfp)->cmd = ELF_C_READ_MMAP_PRIVATE;
return libdw_open_elf (&fd, elfp, false, archive_ok, true, false, true);
}
Dwfl_Error internal_function
__libdw_open_elf (int fd, Elf **elfp)
{
return libdw_open_elf (&fd, elfp, false, true, true, true, false);
}