Skip to content

Commit

Permalink
Update DwarfWalker subrange handling (#1369)
Browse files Browse the repository at this point in the history
* Remove redundant include of libdw.h

* Add subrange parsing to dynDwarf

* Remove decipherBound

This implementation was neither correct nor complete. Moreover, we
shouldn't be parsing FORM types. Looking for DW_TAG* is sufficient.

* Remove legacy comments and commented-out code from parseArray

* Improve error message in parseArray

* Rename parseSubrangeAUX to parseSubrange and change interface

* Replace bound calculations in parseSubrange

* Update some comments and whitespace

* Update construction of the result type

The LONG_MIN/MAX aren't great, but they are propagated to preserve
behavior.

* Update debugging messages in parseSubrange

* Have parseSubrange() call parseSubrange(Dwarf_Die*)

* Use updated parseSubrange in parseMultidimensionalArray

* Use std::tostring instead of snprintf

* Update comments and remove dead code

* Whitespace

* parseMDA returns a typeArray instead of just a Type

* Fix bug in dwarf_result::operator bool

* Fix hi/low bound mixup in parseMDA

* Try using Dyninst's parsed CU language, if needed

* Display subrange DIE ID in pareSubrange

* Don't create subrange type in parseSubrange

This will happen in the callee

* Update parseSubrange() to use new parseSubrange(Dwarf_DIE*)

* More carefully parse the child DIE

* Iteratively parse the subranges in parseArray

* Whitespace

* Remove parseMultiDimensionalArray

* Improve debug messages

* Register each subrange type in parseArray

* Remove common/h/dyninstversion.h

This was accidentally included.

* Add copyright notices to new files

* Make dwarf_result also hold a Dwarf_Word

C++ doesn't guarantee it is convertible to `long`.

* Only offer an interface to parse both bounds simultaneously

The calculation for a range's upper bound when DW_AT_count is used
requires knowing the lower bound (if given), so these needed to be
merged.

* Use new dwarf_subrange interface in DwarfWalker::parseSubrange
  • Loading branch information
hainest committed Feb 27, 2023
1 parent 2bfa6e8 commit 9d15c0f
Show file tree
Hide file tree
Showing 5 changed files with 422 additions and 273 deletions.
2 changes: 1 addition & 1 deletion dwarf/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ include_directories(src h ${PROJECT_SOURCE_DIR}/elf/h ${PROJECT_SOURCE_DIR}/comm
add_definitions(-DDYNDWARF_LIB)

set(SRC_LIST src/dwarfResult.C src/dwarfExprParser.C src/dwarfFrameParser.C
src/dwarfHandle.C)
src/dwarfHandle.C src/dwarf_subrange.cpp)

dyninst_library(dynDwarf dynElf common ${LibDwarf_LIBRARIES})
add_dependencies(dynDwarf ElfUtils)
Expand Down
206 changes: 206 additions & 0 deletions dwarf/src/dwarf_subrange.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/*
* See the dyninst/COPYRIGHT file for copyright information.
*
* We provide the Paradyn Tools (below described as "Paradyn")
* on an AS IS basis, and do not warrant its validity or performance.
* We reserve the right to update, modify, or discontinue this
* software at any time. We shall have no obligation to supply such
* updates or modifications or any other form of support to you.
*
* By your use of Paradyn, you understand and agree that we (or any
* other person or entity with proprietary rights in Paradyn) are
* under no obligation to provide either maintenance services,
* update services, notices of latent defects, or correction of
* defects for Paradyn.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "dwarf_subrange.h"

namespace {

Dwarf_Die *get_type(Dwarf_Die *die) {
Dwarf_Attribute scratch_attr;
dwarf_attr_integrate(die, DW_AT_type, &scratch_attr);
Dwarf_Die scratch_die;
Dwarf_Die *type = dwarf_formref_die(&scratch_attr, &scratch_die);

if (!type || dwarf_peel_type(type, type) != 0)
return nullptr;

return type;
}

bool is_signed(Dwarf_Die *die) {
Dwarf_Attribute attr;
if (dwarf_attr(get_type(die), DW_AT_encoding, &attr)) {
Dwarf_Word encoding;
if (dwarf_formudata(&attr, &encoding) == 0)
return encoding == DW_ATE_signed || encoding == DW_ATE_signed_char;
}
return false;
}
} // namespace

namespace Dyninst {
namespace DwarfDyninst {

static dwarf_result upper_bound(Dwarf_Die *die) {
Dwarf_Attribute attr;
if (dwarf_attr_integrate(die, DW_AT_upper_bound, &attr)) {
if (is_signed(die)) {
Dwarf_Sword upper;
if (dwarf_formsdata(&attr, &upper) != 0)
return dwarf_error{};
return upper;
}
Dwarf_Word unsigned_upper;
if (dwarf_formudata(&attr, &unsigned_upper) != 0)
return dwarf_error{};
return unsigned_upper;
}

// Nothing was found, but there was no error
return dwarf_result{};
}

static dwarf_result lower_bound(Dwarf_Die *die) {
Dwarf_Attribute attr;
if (dwarf_attr_integrate(die, DW_AT_lower_bound, &attr)) {
if (is_signed(die)) {
Dwarf_Sword lower;
if (dwarf_formsdata(&attr, &lower) != 0)
return dwarf_error{};
return lower;
}
Dwarf_Word unsigned_lower;
if (dwarf_formudata(&attr, &unsigned_lower) != 0)
return dwarf_error{};
return unsigned_lower;
}

// Nothing was found, but there was no error
return dwarf_result{};
}

static dwarf_result lower_bound_by_language(Dwarf_Die *die) {
int lang = dwarf_srclang(die);
if (lang != -1) {
Dwarf_Sword lower;
if (dwarf_default_lower_bound(lang, &lower) != 0)
return dwarf_error{};
return lower;
}

// Nothing was found, but there was no error
return dwarf_result{};
}

static dwarf_result length_from_count(Dwarf_Die *die) {
Dwarf_Attribute attr;
if (dwarf_attr_integrate(die, DW_AT_count, &attr)) {
Dwarf_Word count;
if (dwarf_formudata(&attr, &count) != 0)
return dwarf_error{};
return count;
}

// Nothing was found, but there was no error
return dwarf_result{};
}

dwarf_bounds dwarf_subrange_bounds(Dwarf_Die *die) {
/*
* DWARF5 - Section 5.13 Subrange Type Entries
*
* The subrange entry may have the attributes DW_AT_lower_bound and
* DW_AT_upper_bound to specify, respectively, the lower and upper
* bound values of the subrange.
*/
dwarf_bounds bounds{lower_bound(die), upper_bound(die)};

// Don't continue if we encountered an error in either lookup
if (!bounds.lower || !bounds.upper) {
return bounds;
}

// If the lower bound value is missing, the value is assumed to
// be a language-dependent default constant
if (!bounds.lower.value) {
bounds.lower = lower_bound_by_language(die);

// If there was a dwarf error, bail out
if (!bounds.lower) {
return bounds;
}
}

/*
* The DW_AT_upper_bound attribute may be replaced by a DW_AT_count
* attribute, whose value describes the number of elements in the
* subrange rather than the value of the last element.
*/
auto count = length_from_count(die);

// If there was a dwarf error, explicitly mark the upper bound as bad
if (!count) {
bounds.upper = dwarf_error{};
return bounds;
}

if (count.value) {
auto const lb = [&bounds]() {
// If we have a lower bound, use it
if (bounds.lower.value) return bounds.lower.value.get();

// Otherwise, assume the array is zero-based
return static_cast<Dwarf_Word>(0);
}();
bounds.upper = dwarf_result{lb + count.value.get() - static_cast<Dwarf_Word>(1)};
}

// If the upper bound and count are missing, then the upper bound value is unknown.
return bounds;
}

dwarf_result dwarf_subrange_length_from_enum(Dwarf_Die *die) {
/* We have to find the DW_TAG_enumerator child with the
highest value to know the array's element count. */
Dwarf_Die enum_child;
int has_children = dwarf_child(die, &enum_child);
if (has_children < 0)
return dwarf_error{};
if (has_children > 0) {
Dwarf_Attribute attr;
Dwarf_Word count{};
do {
if (dwarf_tag(&enum_child) == DW_TAG_enumerator) {
dwarf_attr_integrate(&enum_child, DW_AT_const_value, &attr);
Dwarf_Word value;
if (dwarf_formudata(&attr, &value) != 0)
return dwarf_error{};
if (value >= count)
count = value + 1;
}
} while (dwarf_siblingof(&enum_child, &enum_child) > 0);
return count;
}

// Nothing was found, but there was no error
return dwarf_result{};
}
} // namespace DwarfDyninst
} // namespace Dyninst
58 changes: 58 additions & 0 deletions dwarf/src/dwarf_subrange.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* See the dyninst/COPYRIGHT file for copyright information.
*
* We provide the Paradyn Tools (below described as "Paradyn")
* on an AS IS basis, and do not warrant its validity or performance.
* We reserve the right to update, modify, or discontinue this
* software at any time. We shall have no obligation to supply such
* updates or modifications or any other form of support to you.
*
* By your use of Paradyn, you understand and agree that we (or any
* other person or entity with proprietary rights in Paradyn) are
* under no obligation to provide either maintenance services,
* update services, notices of latent defects, or correction of
* defects for Paradyn.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "util.h"
#include <boost/optional.hpp>
#include <dwarf.h>
#include <elfutils/libdw.h>

namespace Dyninst {
namespace DwarfDyninst {

struct dwarf_error {};

struct dwarf_result {
boost::optional<Dwarf_Word> value;
bool error = false;
dwarf_result() = default;
dwarf_result(Dwarf_Word t) : value{t} {}
dwarf_result(dwarf_error) : error{true} {}
explicit operator bool() const { return !error; }
};

struct dwarf_bounds {
dwarf_result lower, upper;
};

DYNDWARF_EXPORT dwarf_bounds dwarf_subrange_bounds(Dwarf_Die *die);
DYNDWARF_EXPORT dwarf_result dwarf_subrange_length_from_enum(Dwarf_Die *die);

} // namespace DwarfDyninst
} // namespace Dyninst

0 comments on commit 9d15c0f

Please sign in to comment.