summaryrefslogtreecommitdiffstats
path: root/libdwfl/open.c
diff options
context:
space:
mode:
authorRoland McGrath <[email protected]>2009-08-27 12:36:47 -0700
committerRoland McGrath <[email protected]>2009-08-27 12:36:47 -0700
commit6bb90710916169e242ce39b12831c5a22a57fcd4 (patch)
tree25ee634803d32660d4351dd34cd28e2db49265de /libdwfl/open.c
parente5a1a8123c4ad0e0df8a66e0d737aafb5ed9d693 (diff)
Rewrite kernel image support: use calculation instead of brute force, support uncompressed payloads.
Diffstat (limited to 'libdwfl/open.c')
-rw-r--r--libdwfl/open.c85
1 files changed, 65 insertions, 20 deletions
diff --git a/libdwfl/open.c b/libdwfl/open.c
index e78eb21f..397af358 100644
--- a/libdwfl/open.c
+++ b/libdwfl/open.c
@@ -65,8 +65,7 @@
# define __libdw_unlzma(...) false
#endif
-/* Always consumes *ELF, never consumes FD.
- Replaces *ELF on success. */
+/* Consumes and replaces *ELF only on success. */
static Dwfl_Error
decompress (int fd __attribute__ ((unused)), Elf **elf)
{
@@ -77,7 +76,7 @@ decompress (int fd __attribute__ ((unused)), Elf **elf)
#if USE_ZLIB || USE_BZLIB || USE_LZMA
const off64_t offset = (*elf)->start_offset;
void *const mapped = ((*elf)->map_address == NULL ? NULL
- : (*elf)->map_address + (*elf)->start_offset);
+ : (*elf)->map_address + offset);
const size_t mapped_size = (*elf)->maximum_size;
if (mapped_size == 0)
return error;
@@ -89,9 +88,6 @@ decompress (int fd __attribute__ ((unused)), Elf **elf)
error = __libdw_unlzma (fd, offset, mapped, mapped_size, &buffer, &size);
#endif
- elf_end (*elf);
- *elf = NULL;
-
if (error == DWFL_E_NOERROR)
{
if (unlikely (size == 0))
@@ -101,39 +97,86 @@ decompress (int fd __attribute__ ((unused)), Elf **elf)
}
else
{
- *elf = elf_memory (buffer, size);
- if (*elf == NULL)
+ Elf *memelf = elf_memory (buffer, size);
+ if (memelf == NULL)
{
error = DWFL_E_LIBELF;
free (buffer);
}
else
- (*elf)->flags |= ELF_F_MALLOCED;
+ {
+ 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 *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)
+ {
+ *close_fd = true;
+ *kind = elf_kind (*elfp);
+ }
+ }
+ }
+ return error;
+}
+
Dwfl_Error internal_function
__libdw_open_file (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok)
{
bool close_fd = false;
- Dwfl_Error error = DWFL_E_NOERROR;
Elf *elf = elf_begin (*fdp, ELF_C_READ_MMAP_PRIVATE, NULL);
- Elf_Kind kind = elf_kind (elf);
- if (unlikely (kind == ELF_K_NONE))
+
+ Elf_Kind kind;
+ Dwfl_Error error = what_kind (*fdp, &elf, &kind, &close_fd);
+ if (error == DWFL_E_BADELF)
{
- if (unlikely (elf == NULL))
- error = DWFL_E_LIBELF;
- else
+ /* It's not an ELF file or a compressed file.
+ See if it's an image with a header preceding the real file. */
+
+ off64_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)
{
- error = decompress (*fdp, &elf);
- if (error == DWFL_E_NOERROR)
+ /* Pure evil. libelf needs some better interfaces. */
+ 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
{
- close_fd = true;
- kind = elf_kind (elf);
+ 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, &close_fd);
}
}
}
@@ -141,10 +184,12 @@ __libdw_open_file (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok)
if (error == DWFL_E_NOERROR
&& kind != ELF_K_ELF
&& !(archive_ok && kind == ELF_K_AR))
+ error = DWFL_E_BADELF;
+
+ if (error != DWFL_E_NOERROR)
{
elf_end (elf);
elf = NULL;
- error = DWFL_E_BADELF;
}
if (error == DWFL_E_NOERROR ? close_fd : close_on_fail)