|
|
4c2ad1 |
From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
|
|
|
4c2ad1 |
From: Sergio Durigan Junior <sergiodj@redhat.com>
|
|
|
4c2ad1 |
Date: Fri, 30 Nov 2018 15:20:27 -0500
|
|
|
4c2ad1 |
Subject:
|
|
|
4c2ad1 |
gdb-rhbz1639242-fix-dwarf2_find_containing_comp_unit-binary-search.patch
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
;; Fix for 'py-bt is broken, results in exception'.
|
|
|
4c2ad1 |
;; RHBZ 1639242
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
Fix dwarf2read.c:dwarf2_find_containing_comp_unit's binary search
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
First of all, I would like to express my gratitude to Keith Seitz, Jan
|
|
|
4c2ad1 |
Kratochvil and Tom Tromey, who were really kind and helped a lot with
|
|
|
4c2ad1 |
this bug. The patch itself was authored by Jan.
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
This all began with:
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
https://bugzilla.redhat.com/show_bug.cgi?id=1639242
|
|
|
4c2ad1 |
py-bt is broken, results in exception
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
In summary, the error reported by the bug above is:
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
$ gdb -args python3
|
|
|
4c2ad1 |
GNU gdb (GDB) Fedora 8.1.1-3.fc28
|
|
|
4c2ad1 |
(...)
|
|
|
4c2ad1 |
Reading symbols from python3...Reading symbols from /usr/lib/debug/usr/bin/python3.6-3.6.6-1.fc28.x86_64.debug...done.
|
|
|
4c2ad1 |
done.
|
|
|
4c2ad1 |
Dwarf Error: could not find partial DIE containing offset 0x316 [in module /usr/lib/debug/usr/bin/python3.6-3.6.6-1.fc28.x86_64.debug]
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
After a long investigation, and after thinking that the problem might
|
|
|
4c2ad1 |
actually be on DWZ's side, we were able to determine that there's
|
|
|
4c2ad1 |
something wrong going on when
|
|
|
4c2ad1 |
dwarf2read.c:dwarf2_find_containing_comp_unit performs a binary search
|
|
|
4c2ad1 |
over all of the CUs belonging to an objfile in order to find the CU
|
|
|
4c2ad1 |
which contains a DIE at an specific offset. The current algorithm is:
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
static struct dwarf2_per_cu_data *
|
|
|
4c2ad1 |
dwarf2_find_containing_comp_unit (sect_offset sect_off,
|
|
|
4c2ad1 |
unsigned int offset_in_dwz,
|
|
|
4c2ad1 |
struct dwarf2_per_objfile *dwarf2_per_objfile)
|
|
|
4c2ad1 |
{
|
|
|
4c2ad1 |
struct dwarf2_per_cu_data *this_cu;
|
|
|
4c2ad1 |
int low, high;
|
|
|
4c2ad1 |
const sect_offset *cu_off;
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
low = 0;
|
|
|
4c2ad1 |
high = dwarf2_per_objfile->all_comp_units.size () - 1;
|
|
|
4c2ad1 |
while (high > low)
|
|
|
4c2ad1 |
{
|
|
|
4c2ad1 |
struct dwarf2_per_cu_data *mid_cu;
|
|
|
4c2ad1 |
int mid = low + (high - low) / 2;
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
mid_cu = dwarf2_per_objfile->all_comp_units[mid];
|
|
|
4c2ad1 |
cu_off = &mid_cu->sect_off;
|
|
|
4c2ad1 |
if (mid_cu->is_dwz > offset_in_dwz
|
|
|
4c2ad1 |
|| (mid_cu->is_dwz == offset_in_dwz && *cu_off >= sect_off))
|
|
|
4c2ad1 |
high = mid;
|
|
|
4c2ad1 |
else
|
|
|
4c2ad1 |
low = mid + 1;
|
|
|
4c2ad1 |
}
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
For the sake of this example, let's consider that "sect_off =
|
|
|
4c2ad1 |
0x7d".
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
There are a few important things going on here. First,
|
|
|
4c2ad1 |
"dwarf2_per_objfile->all_comp_units ()" will be sorted first by
|
|
|
4c2ad1 |
whether the CU is a DWZ CU, and then by cu->sect_off. In this
|
|
|
4c2ad1 |
specific bug, "offset_in_dwz" is false, which means that, for the most
|
|
|
4c2ad1 |
part of the loop, we're going to do "high = mid" (i.e, we'll work with
|
|
|
4c2ad1 |
the lower part of the vector).
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
In our particular case, when we reach the part where "mid_cu->is_dwz
|
|
|
4c2ad1 |
== offset_in_dwz" (i.e, both are false), we end up with "high = 2" and
|
|
|
4c2ad1 |
"mid = 1". I.e., there are only 2 elements in the vector who are not
|
|
|
4c2ad1 |
DWZ. The vector looks like this:
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
#0: cu->sect_off = 0; length = 114; is_dwz = false <-- low
|
|
|
4c2ad1 |
#1: cu->sect_off = 114; length = 7796; is_dwz = false <-- mid
|
|
|
4c2ad1 |
#2: cu->sect_off = 0; length = 28; is_dwz = true <-- high
|
|
|
4c2ad1 |
...
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
The CU we want is #1, which is exactly where "mid" is. Also, #1 is
|
|
|
4c2ad1 |
not DWZ, which is also exactly what we want. So we perform the second
|
|
|
4c2ad1 |
comparison:
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
(mid_cu->is_dwz == offset_in_dwz && *cu_off >= sect_off)
|
|
|
4c2ad1 |
^^^^^^^^^^^^^^^^^^^
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
Because "*cu_off = 114" and "sect_off = 0x7d", this evaluates to
|
|
|
4c2ad1 |
false, so we end up with "low = mid + 1 = 2", which actually gives us
|
|
|
4c2ad1 |
the wrong CU (i.e., a CU that is DWZ). Next in the code, GDB does:
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
gdb_assert (low == high);
|
|
|
4c2ad1 |
this_cu = dwarf2_per_objfile->all_comp_units[low];
|
|
|
4c2ad1 |
cu_off = &this_cu->sect_off;
|
|
|
4c2ad1 |
if (this_cu->is_dwz != offset_in_dwz || *cu_off > sect_off)
|
|
|
4c2ad1 |
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
4c2ad1 |
{
|
|
|
4c2ad1 |
if (low == 0 || this_cu->is_dwz != offset_in_dwz)
|
|
|
4c2ad1 |
error (_("Dwarf Error: could not find partial DIE containing "
|
|
|
4c2ad1 |
"offset %s [in module %s]"),
|
|
|
4c2ad1 |
sect_offset_str (sect_off),
|
|
|
4c2ad1 |
bfd_get_filename (dwarf2_per_objfile->objfile->obfd));
|
|
|
4c2ad1 |
...
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
Triggering the error we saw in the original bug report.
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
It's important to notice that we see the error message because the
|
|
|
4c2ad1 |
selected CU is a DWZ one, but we're looking for a non-DWZ CU here.
|
|
|
4c2ad1 |
However, even when the selected CU is *not* a DWZ (and we don't see
|
|
|
4c2ad1 |
any error message), we still end up with the wrong CU. For example,
|
|
|
4c2ad1 |
suppose that the vector had:
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
#0: cu->sect_off = 0; length = 114; is_dwz = false
|
|
|
4c2ad1 |
#1: cu->sect_off = 114; length = 7796; is_dwz = false
|
|
|
4c2ad1 |
#2: cu->sect_off = 7910; length = 28; is_dwz = false
|
|
|
4c2ad1 |
...
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
I.e., #2's "is_dwz" is false instead of true. In this case, we still
|
|
|
4c2ad1 |
want #1, because that's where the DIE is located. After the loop ends
|
|
|
4c2ad1 |
up in #2, we have "is_dwz" as false, which is what we wanted, so we
|
|
|
4c2ad1 |
compare offsets. In this case, "7910 >= 0x7d", so we set "mid = high
|
|
|
4c2ad1 |
= 2". Next iteration, we have "mid = 0 + (2 - 0) / 2 = 1", and thus
|
|
|
4c2ad1 |
we examining #1. "is_dwz" is still false, but "114 >= 0x7d" also
|
|
|
4c2ad1 |
evaluates to false, so "low = mid + 1 = 2", which makes the loop stop.
|
|
|
4c2ad1 |
Therefore, we end up choosing #2 as our CU, even though #1 is the
|
|
|
4c2ad1 |
right one.
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
The problem here is happening because we're comparing "sect_off"
|
|
|
4c2ad1 |
directly against "*cu_off", while we should actually be comparing
|
|
|
4c2ad1 |
against "*cu_off + mid_cu->length" (i.e., the end offset):
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
...
|
|
|
4c2ad1 |
|| (mid_cu->is_dwz == offset_in_dwz
|
|
|
4c2ad1 |
&& *cu_off + mid_cu->length >= sect_off))
|
|
|
4c2ad1 |
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
4c2ad1 |
...
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
And this is what the patch does. The idea is that if GDB is searching
|
|
|
4c2ad1 |
for an offset that falls above the *end* of the CU being
|
|
|
4c2ad1 |
analyzed (i.e., "mid"), then the next iteration should try a
|
|
|
4c2ad1 |
higher-offset CU next. The previous algorithm was using
|
|
|
4c2ad1 |
the *beginning* of the CU.
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
Unfortunately, I could not devise a testcase for this problem, so I am
|
|
|
4c2ad1 |
proposing a fix with this huge explanation attached to it in the hope
|
|
|
4c2ad1 |
that it is sufficient. After talking a bit to Keith (our testcase
|
|
|
4c2ad1 |
guru), it seems that one would have to create an objfile with both DWZ
|
|
|
4c2ad1 |
and non-DWZ sections, which may prove very hard to do, I think.
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
I ran this patch on our BuildBot, and no regressions were detected.
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
gdb/ChangeLog:
|
|
|
4c2ad1 |
2018-11-30 Jan Kratochvil <jan.kratochvil@redhat.com>
|
|
|
4c2ad1 |
Keith Seitz <keiths@redhat.com>
|
|
|
4c2ad1 |
Tom Tromey <tom@tromey.com>
|
|
|
4c2ad1 |
Sergio Durigan Junior <sergiodj@redhat.com>
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
https://bugzilla.redhat.com/show_bug.cgi?id=1613614
|
|
|
4c2ad1 |
* dwarf2read.c (dwarf2_find_containing_comp_unit): Add
|
|
|
4c2ad1 |
'mid_cu->length' to '*cu_off' when checking if 'sect_off' is
|
|
|
4c2ad1 |
inside the CU.
|
|
|
4c2ad1 |
|
|
|
4c2ad1 |
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
|
|
|
4c2ad1 |
--- a/gdb/dwarf2read.c
|
|
|
4c2ad1 |
+++ b/gdb/dwarf2read.c
|
|
|
4c2ad1 |
@@ -25039,7 +25039,8 @@ dwarf2_find_containing_comp_unit (sect_offset sect_off,
|
|
|
4c2ad1 |
mid_cu = dwarf2_per_objfile->all_comp_units[mid];
|
|
|
4c2ad1 |
cu_off = &mid_cu->sect_off;
|
|
|
4c2ad1 |
if (mid_cu->is_dwz > offset_in_dwz
|
|
|
4c2ad1 |
- || (mid_cu->is_dwz == offset_in_dwz && *cu_off >= sect_off))
|
|
|
4c2ad1 |
+ || (mid_cu->is_dwz == offset_in_dwz
|
|
|
4c2ad1 |
+ && *cu_off + mid_cu->length >= sect_off))
|
|
|
4c2ad1 |
high = mid;
|
|
|
4c2ad1 |
else
|
|
|
4c2ad1 |
low = mid + 1;
|