[PATCH] libdw: Fix __libdw_form_val_len endp pointer.
Mark Wielaard
mjw at redhat.com
Sun Dec 14 14:53:54 UTC 2014
On Sat, Dec 13, 2014 at 02:36:08PM -0800, Josh Stone wrote:
> On 12/12/2014 07:54 AM, Mark Wielaard wrote:
> > @@ -513,6 +515,7 @@ __libdw_form_val_len (Dwarf *dbg, struct Dwarf_CU *cu,
> > uint8_t len = form_lengths[form];
> > if (len != 0)
> > {
> > + const const unsigned char *endp = cu->endp;
> > len &= 0x7f; /* Mask to allow 0x80 -> 0. */
> > if (unlikely (len > (size_t) (endp - valp)))
> > {
>
> "const const", for when you're *really* sure it won't ever change!
:) Removed the extra const.
I also made two additional changes to harden the code a bit more when
using "fake" cus. The fake empty cu isn't associated with a DWARF dbg,
which isn't a problem because it is empty, so attributes pointing to it
won't use it to resolve anything. But we might refer to it when interning
the block Dwarf_Ops. Since the block is empty no ops need to be interned
anyway. So short-circuit that path. Also in dwarf_formref_die we would
refer to the data buffer of the form with cu_data (). But that might
be the wrong buffer. Use the cu startp and endp directly and remove
cu_data () completely to prevent any future mistake like that. I haven't
actually found any such a case, because none of the attributes associated
with the fake CUs use DW_FORM_addr. So that is just a "future hardening"
change.
Update patch attached.
Cheers,
Mark
-------------- next part --------------
>From 41d33ebccb7573b6ce277c05db72eb6c4e0f0a41 Mon Sep 17 00:00:00 2001
From: Mark Wielaard <mjw at redhat.com>
Date: Fri, 12 Dec 2014 16:43:04 +0100
Subject: [PATCH] libdw: Make sure all attributes come with a (fake) CU for
bound checks.
All attributes now have a reference to a (fake) CU that has startp and
endp set to the data section where the form data comes from. Use that
for bounds checking in __libdw_form_val_len and dwarf_formblock to make
sure data read doesn't overflow any data section. Remove libdwP.h cu_data
and use cu startp and endp directly where appropriate.
Signed-off-by: Mark Wielaard <mjw at redhat.com>
---
libdw/ChangeLog | 28 +++++++++++++++++++++++++++
libdw/dwarf_begin_elf.c | 22 +++++++++++++++++++++
libdw/dwarf_child.c | 4 +---
libdw/dwarf_end.c | 3 +++
libdw/dwarf_formblock.c | 21 +++++++++++++-------
libdw/dwarf_formref_die.c | 13 ++++++++-----
libdw/dwarf_getattrs.c | 4 +---
libdw/dwarf_getlocation.c | 8 ++++++++
libdw/dwarf_getlocation_attr.c | 29 +++++++++++++++++++++++-----
libdw/dwarf_getlocation_implicit_pointer.c | 8 +++++---
libdw/dwarf_getmacros.c | 5 +++--
libdw/libdwP.h | 31 ++++++++++++++----------------
libdw/libdw_findcu.c | 5 ++---
libdw/libdw_form.c | 12 ++++++------
14 files changed, 139 insertions(+), 54 deletions(-)
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 922cc6b..6bb326d 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,31 @@
+2014-12-12 Mark Wielaard <mjw at redhat.com>
+
+ * libdwP.h (struct Dwarf): Add fake_loc_cu.
+ (cu_data): Removed.
+ (DIE_OFFSET_FROM_CU_OFFSET): Don't use cu_data, use cu_sec_idx.
+ (__libdw_form_val_compute_len): Drop dbg and endp arguments.
+ (__libdw_form_val_len): Likewise.
+ * libdw_form.c (__libdw_form_val_compute_len): Likewise.
+ * libdw_findcu.c (__libdw_intern_next_unit): Don't use cu_data, use
+ the already found data buffer directly.
+ * dwarf_begin_elf.c (valid_p): Setup fake_loc_cu.
+ * dwarf_end.c (dwarf_end): Free fake_loc_cu.
+ * dwarf_child.c (__libdw_find_attr): Call __libdw_form_val_len with
+ just cu.
+ * dwarf_getattrs.c (dwarf_getattrs): Likewise.
+ * dwarf_formblock.c (dwarf_formblock): Add bounds checking.
+ * dwarf_getlocation_attr.c (attr_form_cu): New function.
+ (dwarf_getlocation_attr): Use attr_form_cu to set result->cu.
+ (getlocation): Handle empty blocks immediately.
+ * dwarf_getlocation_implicit_pointer.c (empty_cu): New static var.
+ (__libdw_empty_loc_attr): Drop cu argument, use empty_cu.
+ (dwarf_getlocation_implicit_pointer): Call __libdw_empty_loc_attr with
+ one argument.
+ * dwarf_getmacros.c (read_macros): Also setup startp and endp for
+ fake_cu. Call __libdw_form_val_len with just fake_cu.
+ * dwarf_formref_die.c (dwarf_formref_die): Don't use cu_data, get
+ datap and size directly from cu startp and endp.
+
2014-12-13 Mark Wielaard <mjw at redhat.com>
* dwarf_getaranges.c (compare_aranges): Make sure Dwarf_Addr
diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c
index 4c6346a..4c49ce2 100644
--- a/libdw/dwarf_begin_elf.c
+++ b/libdw/dwarf_begin_elf.c
@@ -235,6 +235,28 @@ valid_p (Dwarf *result)
result = NULL;
}
+ if (result != NULL && result->sectiondata[IDX_debug_loc] != NULL)
+ {
+ result->fake_loc_cu = (Dwarf_CU *) calloc (1, sizeof (Dwarf_CU));
+ if (unlikely (result->fake_loc_cu == NULL))
+ {
+ __libdw_free_zdata (result);
+ Dwarf_Sig8_Hash_free (&result->sig8_hash);
+ __libdw_seterrno (DWARF_E_NOMEM);
+ free (result);
+ result = NULL;
+ }
+ else
+ {
+ result->fake_loc_cu->dbg = result;
+ result->fake_loc_cu->startp
+ = result->sectiondata[IDX_debug_loc]->d_buf;
+ result->fake_loc_cu->endp
+ = (result->sectiondata[IDX_debug_loc]->d_buf
+ + result->sectiondata[IDX_debug_loc]->d_size);
+ }
+ }
+
return result;
}
diff --git a/libdw/dwarf_child.c b/libdw/dwarf_child.c
index 2a5d379..96d8e23 100644
--- a/libdw/dwarf_child.c
+++ b/libdw/dwarf_child.c
@@ -94,9 +94,7 @@ __libdw_find_attr (Dwarf_Die *die, unsigned int search_name,
/* Skip over the rest of this attribute (if there is any). */
if (attr_form != 0)
{
- size_t len = __libdw_form_val_len (dbg, die->cu, attr_form, readp,
- endp);
-
+ size_t len = __libdw_form_val_len (die->cu, attr_form, readp);
if (unlikely (len == (size_t) -1l))
{
readp = NULL;
diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c
index 647a1b8..922dc8f 100644
--- a/libdw/dwarf_end.c
+++ b/libdw/dwarf_end.c
@@ -117,6 +117,9 @@ dwarf_end (dwarf)
if (dwarf->free_elf)
elf_end (dwarf->elf);
+ /* Free the fake location list CU. */
+ free (dwarf->fake_loc_cu);
+
/* Free the context descriptor. */
free (dwarf);
}
diff --git a/libdw/dwarf_formblock.c b/libdw/dwarf_formblock.c
index 799d877..980bc10 100644
--- a/libdw/dwarf_formblock.c
+++ b/libdw/dwarf_formblock.c
@@ -1,5 +1,5 @@
/* Return block represented by attribute.
- Copyright (C) 2004-2010 Red Hat, Inc.
+ Copyright (C) 2004-2010, 2014 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper at redhat.com>, 2004.
@@ -43,28 +43,37 @@ dwarf_formblock (attr, return_block)
if (attr == NULL)
return -1;
- const unsigned char *datap;
+ const unsigned char *datap = attr->valp;
+ const unsigned char *endp = attr->cu->endp;
switch (attr->form)
{
case DW_FORM_block1:
+ if (unlikely (endp - datap < 1))
+ goto invalid;
return_block->length = *(uint8_t *) attr->valp;
return_block->data = attr->valp + 1;
break;
case DW_FORM_block2:
+ if (unlikely (endp - datap < 2))
+ goto invalid;
return_block->length = read_2ubyte_unaligned (attr->cu->dbg, attr->valp);
return_block->data = attr->valp + 2;
break;
case DW_FORM_block4:
+ if (unlikely (endp - datap < 4))
+ goto invalid;
return_block->length = read_4ubyte_unaligned (attr->cu->dbg, attr->valp);
return_block->data = attr->valp + 4;
break;
case DW_FORM_block:
case DW_FORM_exprloc:
- datap = attr->valp;
+ if (unlikely (endp - datap < 1))
+ goto invalid;
+ // XXX bounds check
get_uleb128 (return_block->length, datap);
return_block->data = (unsigned char *) datap;
break;
@@ -74,12 +83,10 @@ dwarf_formblock (attr, return_block)
return -1;
}
- if (unlikely (cu_data (attr->cu)->d_size
- - (return_block->data
- - (unsigned char *) cu_data (attr->cu)->d_buf)
- < return_block->length))
+ if (unlikely (return_block->length > (size_t) (endp - return_block->data)))
{
/* Block does not fit. */
+ invalid:
__libdw_seterrno (DWARF_E_INVALID_DWARF);
return -1;
}
diff --git a/libdw/dwarf_formref_die.c b/libdw/dwarf_formref_die.c
index b54e216..63f6697 100644
--- a/libdw/dwarf_formref_die.c
+++ b/libdw/dwarf_formref_die.c
@@ -70,7 +70,8 @@ dwarf_formref_die (attr, result)
return INTUSE(dwarf_offdie) (dbg_ret, offset, result);
}
- Elf_Data *data;
+ const unsigned char *datap;
+ size_t size;
if (attr->form == DW_FORM_ref_sig8)
{
/* This doesn't have an offset, but instead a value we
@@ -92,7 +93,8 @@ dwarf_formref_die (attr, result)
}
while (cu->type_sig8 != sig);
- data = cu->dbg->sectiondata[IDX_debug_types];
+ datap = cu->dbg->sectiondata[IDX_debug_types]->d_buf;
+ size = cu->dbg->sectiondata[IDX_debug_types]->d_size;
offset = cu->type_offset;
}
else
@@ -101,17 +103,18 @@ dwarf_formref_die (attr, result)
if (unlikely (__libdw_formref (attr, &offset) != 0))
return NULL;
- data = cu_data (cu);
+ datap = cu->startp;
+ size = cu->endp - cu->startp;
}
- if (unlikely (data->d_size - cu->start <= offset))
+ if (unlikely (offset >= size))
{
__libdw_seterrno (DWARF_E_INVALID_DWARF);
return NULL;
}
memset (result, '\0', sizeof (Dwarf_Die));
- result->addr = (char *) data->d_buf + cu->start + offset;
+ result->addr = (char *) datap + offset;
result->cu = cu;
return result;
}
diff --git a/libdw/dwarf_getattrs.c b/libdw/dwarf_getattrs.c
index 9ea70fc..0c54e5d 100644
--- a/libdw/dwarf_getattrs.c
+++ b/libdw/dwarf_getattrs.c
@@ -106,9 +106,7 @@ dwarf_getattrs (Dwarf_Die *die, int (*callback) (Dwarf_Attribute *, void *),
/* Skip over the rest of this attribute (if there is any). */
if (attr.form != 0)
{
- size_t len = __libdw_form_val_len (dbg, die->cu, attr.form,
- die_addr, endp);
-
+ size_t len = __libdw_form_val_len (die->cu, attr.form, die_addr);
if (unlikely (len == (size_t) -1l))
/* Something wrong with the file. */
return -1l;
diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c
index 2a4c890..38e93e6 100644
--- a/libdw/dwarf_getlocation.c
+++ b/libdw/dwarf_getlocation.c
@@ -555,6 +555,14 @@ static int
getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
Dwarf_Op **llbuf, size_t *listlen, int sec_index)
{
+ /* Empty location expressions don't have any ops to intern.
+ Note that synthetic empty_cu doesn't have an associated DWARF dbg. */
+ if (block->length == 0)
+ {
+ *listlen = 0;
+ return 0;
+ }
+
return __libdw_intern_expression (cu->dbg, cu->dbg->other_byte_order,
cu->address_size, (cu->version == 2
? cu->address_size
diff --git a/libdw/dwarf_getlocation_attr.c b/libdw/dwarf_getlocation_attr.c
index cb29045..3229baf 100644
--- a/libdw/dwarf_getlocation_attr.c
+++ b/libdw/dwarf_getlocation_attr.c
@@ -1,5 +1,5 @@
/* Return DWARF attribute associated with a location expression op.
- Copyright (C) 2013 Red Hat, Inc.
+ Copyright (C) 2013, 2014 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -33,6 +33,24 @@
#include <dwarf.h>
#include <libdwP.h>
+static Dwarf_CU *
+attr_form_cu (Dwarf_Attribute *attr)
+{
+ /* If the attribute has block/expr form the data comes from the
+ .debug_info from the same cu as the attr. Otherwise it comes from
+ the .debug_loc data section. */
+ switch (attr->form)
+ {
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ case DW_FORM_block:
+ case DW_FORM_exprloc:
+ return attr->cu;
+ default:
+ return attr->cu->dbg->fake_loc_cu;
+ }
+}
int
dwarf_getlocation_attr (attr, op, result)
@@ -43,26 +61,27 @@ dwarf_getlocation_attr (attr, op, result)
if (attr == NULL)
return -1;
- result->cu = attr->cu;
-
switch (op->atom)
{
case DW_OP_implicit_value:
result->code = DW_AT_const_value;
result->form = DW_FORM_block;
result->valp = (unsigned char *) (uintptr_t) op->number2;
+ result->cu = attr_form_cu (attr);
break;
case DW_OP_GNU_entry_value:
result->code = DW_AT_location;
result->form = DW_FORM_exprloc;
result->valp = (unsigned char *) (uintptr_t) op->number2;
+ result->cu = attr_form_cu (attr);
break;
case DW_OP_GNU_const_type:
result->code = DW_AT_const_value;
result->form = DW_FORM_block1;
result->valp = (unsigned char *) (uintptr_t) op->number2;
+ result->cu = attr_form_cu (attr);
break;
case DW_OP_call2:
@@ -74,7 +93,7 @@ dwarf_getlocation_attr (attr, op, result)
return -1;
if (INTUSE(dwarf_attr) (&die, DW_AT_location, result) == NULL)
{
- __libdw_empty_loc_attr (result, attr->cu);
+ __libdw_empty_loc_attr (result);
return 0;
}
}
@@ -88,7 +107,7 @@ dwarf_getlocation_attr (attr, op, result)
if (INTUSE(dwarf_attr) (&die, DW_AT_location, result) == NULL
&& INTUSE(dwarf_attr) (&die, DW_AT_const_value, result) == NULL)
{
- __libdw_empty_loc_attr (result, attr->cu);
+ __libdw_empty_loc_attr (result);
return 0;
}
}
diff --git a/libdw/dwarf_getlocation_implicit_pointer.c b/libdw/dwarf_getlocation_implicit_pointer.c
index f93d17e..f1c16be 100644
--- a/libdw/dwarf_getlocation_implicit_pointer.c
+++ b/libdw/dwarf_getlocation_implicit_pointer.c
@@ -35,15 +35,17 @@
static unsigned char empty_exprloc = 0;
+static Dwarf_CU empty_cu = { .startp = &empty_exprloc,
+ .endp = &empty_exprloc + 1 };
void
internal_function
-__libdw_empty_loc_attr (Dwarf_Attribute *attr, struct Dwarf_CU *cu )
+__libdw_empty_loc_attr (Dwarf_Attribute *attr)
{
attr->code = DW_AT_location;
attr->form = DW_FORM_exprloc;
attr->valp = &empty_exprloc;
- attr->cu = cu;
+ attr->cu = &empty_cu;
}
int
@@ -69,7 +71,7 @@ dwarf_getlocation_implicit_pointer (attr, op, result)
if (INTUSE(dwarf_attr) (&die, DW_AT_location, result) == NULL
&& INTUSE(dwarf_attr) (&die, DW_AT_const_value, result) == NULL)
{
- __libdw_empty_loc_attr (result, attr->cu);
+ __libdw_empty_loc_attr (result);
return 0;
}
diff --git a/libdw/dwarf_getmacros.c b/libdw/dwarf_getmacros.c
index 737dc5d..848128e 100644
--- a/libdw/dwarf_getmacros.c
+++ b/libdw/dwarf_getmacros.c
@@ -354,6 +354,8 @@ read_macros (Dwarf *dbg, int sec_index,
.dbg = dbg,
.version = 4,
.offset_size = table->is_64bit ? 8 : 4,
+ .startp = (void *) startp + offset,
+ .endp = (void *) endp,
};
Dwarf_Attribute attributes[proto->nforms];
@@ -367,8 +369,7 @@ read_macros (Dwarf *dbg, int sec_index,
attributes[i].valp = (void *) readp;
attributes[i].cu = &fake_cu;
- size_t len = __libdw_form_val_len (dbg, &fake_cu,
- proto->forms[i], readp, endp);
+ size_t len = __libdw_form_val_len (&fake_cu, proto->forms[i], readp);
if (len == (size_t) -1)
return -1;
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index edceb59..eda61a9 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -188,6 +188,10 @@ struct Dwarf
/* Cached info from the CFI section. */
struct Dwarf_CFI_s *cfi;
+ /* Fake loc CU. Used when synthesizing attributes for Dwarf_Ops that
+ came from a location list entry in dwarf_getlocation_attr. */
+ struct Dwarf_CU *fake_loc_cu;
+
/* Internal memory handling. This is basically a simplified
reimplementation of obstacks. Unfortunately the standard obstack
implementation is not usable in libraries. */
@@ -337,7 +341,7 @@ struct Dwarf_CU
((Dwarf_Die) \
{ \
.cu = (fromcu), \
- .addr = ((char *) cu_data (fromcu)->d_buf \
+ .addr = ((char *) fromcu->dbg->sectiondata[cu_sec_idx (fromcu)]->d_buf \
+ DIE_OFFSET_FROM_CU_OFFSET ((fromcu)->start, \
(fromcu)->offset_size, \
(fromcu)->type_offset != 0)) \
@@ -483,18 +487,16 @@ __libdw_dieabbrev (Dwarf_Die *die, const unsigned char **readp)
}
/* Helper functions for form handling. */
-extern size_t __libdw_form_val_compute_len (Dwarf *dbg, struct Dwarf_CU *cu,
+extern size_t __libdw_form_val_compute_len (struct Dwarf_CU *cu,
unsigned int form,
- const unsigned char *valp,
- const unsigned char *endp)
- __nonnull_attribute__ (1, 2, 4, 5) internal_function;
+ const unsigned char *valp)
+ __nonnull_attribute__ (1, 3) internal_function;
/* Find the length of a form attribute. */
static inline size_t
-__nonnull_attribute__ (1, 2, 4, 5)
-__libdw_form_val_len (Dwarf *dbg, struct Dwarf_CU *cu,
- unsigned int form, const unsigned char *valp,
- const unsigned char *endp)
+__nonnull_attribute__ (1, 3)
+__libdw_form_val_len (struct Dwarf_CU *cu, unsigned int form,
+ const unsigned char *valp)
{
/* Small lookup table of forms with fixed lengths. Absent indexes are
initialized 0, so any truly desired 0 is set to 0x80 and masked. */
@@ -513,6 +515,7 @@ __libdw_form_val_len (Dwarf *dbg, struct Dwarf_CU *cu,
uint8_t len = form_lengths[form];
if (len != 0)
{
+ const unsigned char *endp = cu->endp;
len &= 0x7f; /* Mask to allow 0x80 -> 0. */
if (unlikely (len > (size_t) (endp - valp)))
{
@@ -524,7 +527,7 @@ __libdw_form_val_len (Dwarf *dbg, struct Dwarf_CU *cu,
}
/* Other forms require some computation. */
- return __libdw_form_val_compute_len (dbg, cu, form, valp, endp);
+ return __libdw_form_val_compute_len (cu, form, valp);
}
/* Helper function for DW_FORM_ref* handling. */
@@ -721,12 +724,6 @@ cu_sec_idx (struct Dwarf_CU *cu)
return cu->type_offset == 0 ? IDX_debug_info : IDX_debug_types;
}
-static inline Elf_Data *
-cu_data (struct Dwarf_CU *cu)
-{
- return cu->dbg->sectiondata[cu_sec_idx (cu)];
-}
-
/* Read up begin/end pair and increment read pointer.
- If it's normal range record, set up *BEGINP and *ENDP and return 0.
- If it's base address selection record, set up *BASEP and return 1.
@@ -744,7 +741,7 @@ unsigned char * __libdw_formptr (Dwarf_Attribute *attr, int sec_index,
internal_function;
/* Fills in the given attribute to point at an empty location expression. */
-void __libdw_empty_loc_attr (Dwarf_Attribute *attr, struct Dwarf_CU *cu)
+void __libdw_empty_loc_attr (Dwarf_Attribute *attr)
internal_function;
/* Load .debug_line unit at DEBUG_LINE_OFFSET. COMP_DIR is a value of
diff --git a/libdw/libdw_findcu.c b/libdw/libdw_findcu.c
index c783ff8..d8da2e3 100644
--- a/libdw/libdw_findcu.c
+++ b/libdw/libdw_findcu.c
@@ -119,9 +119,8 @@ __libdw_intern_next_unit (dbg, debug_types)
if (debug_types)
Dwarf_Sig8_Hash_insert (&dbg->sig8_hash, type_sig8, newp);
- void *buf = cu_data (newp)->d_buf;
- newp->startp = buf + newp->start;
- newp->endp = buf + newp->end;
+ newp->startp = data->d_buf + newp->start;
+ newp->endp = data->d_buf + newp->end;
/* Add the new entry to the search tree. */
if (tsearch (newp, tree, findcu_cb) == NULL)
diff --git a/libdw/libdw_form.c b/libdw/libdw_form.c
index 65ac7de..379ede5 100644
--- a/libdw/libdw_form.c
+++ b/libdw/libdw_form.c
@@ -39,11 +39,11 @@
size_t
internal_function
-__libdw_form_val_compute_len (Dwarf *dbg, struct Dwarf_CU *cu,
- unsigned int form, const unsigned char *valp,
- const unsigned char *endp)
+__libdw_form_val_compute_len (struct Dwarf_CU *cu, unsigned int form,
+ const unsigned char *valp)
{
const unsigned char *startp = valp;
+ const unsigned char *endp = cu->endp;
Dwarf_Word u128;
size_t result;
@@ -75,13 +75,13 @@ __libdw_form_val_compute_len (Dwarf *dbg, struct Dwarf_CU *cu,
case DW_FORM_block2:
if (unlikely ((size_t) (endp - startp) < 2))
goto invalid;
- result = read_2ubyte_unaligned (dbg, valp) + 2;
+ result = read_2ubyte_unaligned (cu->dbg, valp) + 2;
break;
case DW_FORM_block4:
if (unlikely ((size_t) (endp - startp) < 4))
goto invalid;
- result = read_4ubyte_unaligned (dbg, valp) + 4;
+ result = read_4ubyte_unaligned (cu->dbg, valp) + 4;
break;
case DW_FORM_block:
@@ -112,7 +112,7 @@ __libdw_form_val_compute_len (Dwarf *dbg, struct Dwarf_CU *cu,
case DW_FORM_indirect:
get_uleb128 (u128, valp);
// XXX Is this really correct?
- result = __libdw_form_val_len (dbg, cu, u128, valp, endp);
+ result = __libdw_form_val_len (cu, u128, valp);
if (result != (size_t) -1)
result += valp - startp;
else
--
2.1.0
More information about the elfutils-devel
mailing list