Skip to content

Commit

Permalink
Parse bitfields from DWARF
Browse files Browse the repository at this point in the history
Re-using the existing bitfield parsing algorithm.

The problem is that DWARF versions >=4 use a different approach to
represent bitfield offsets from the one used in versions <4 (in
addition, some systems, such as the one in the CI, use the old approach
even for DWARF 4). Hence, we support both approaches. See code comments
for implementation details.

A new unit test for bitfield parsing is added to FieldAnalyser,
supporting both variants of bitfield representation.
  • Loading branch information
viktormalik committed Mar 29, 2023
1 parent e5eb9ae commit 919fe34
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 14 deletions.
76 changes: 63 additions & 13 deletions src/dwarf_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,36 @@ SizedType Dwarf::get_stype(const std::string &type_name) const
return get_stype(type_die.value());
}

std::optional<Bitfield> Dwarf::resolve_bitfield(Dwarf_Die &field_die) const
{
ssize_t bitfield_width = get_bitfield_size(field_die);
if (bitfield_width == 0)
return std::nullopt;

ssize_t bit_offset = get_field_bit_offset(field_die);
if (dwarf_hasattr(&field_die, DW_AT_data_bit_offset))
{
// DWARF >= 4
// DW_AT_data_bit_offset is the offset in bits from the beginning of the
// containing entity to the beginning of field. In this case, the byte
// offset of the field is determined by (bit_offset / 8) so the bit offset
// within the byte is given by (bit_offset % 8).
return Bitfield(bit_offset % 8, bitfield_width);
}
else
{
// DWARF < 4 (some implementations of DWARF 4 use this, too)
// Bit offset is given by DW_AT_bit_offset which is the offset in bits of
// the high order bit of the container type to the high order bit of the
// storage unit actually containing the bitfield. This representation was
// designed for big-endian systems, so we must use the same approach to
// determine the actual bit offset:
// (size of the container field - DW_AT_bit_offset - bitfield size)
auto field_size = dwarf_bytesize(&field_die) * 8;
return Bitfield(field_size - bit_offset - bitfield_width, bitfield_width);
}
}

void Dwarf::resolve_fields(const SizedType &type) const
{
if (!type.IsRecordTy())
Expand All @@ -243,18 +273,11 @@ void Dwarf::resolve_fields(const SizedType &type) const
for (auto &field_die :
get_all_children_with_tag(&type_die.value(), DW_TAG_member))
{
if (dwarf_hasattr(&field_die, DW_AT_bit_size))
{
// Parsing bitfields from DWARF is not supported, yet -> clear the struct
// and let Clang parser resolve the fields.
str->ClearFields();
break;
}
Dwarf_Die field_type = type_of(field_die);
str->AddField(dwarf_diename(&field_die),
get_stype(field_type),
get_field_offset(field_die),
std::nullopt,
get_field_byte_offset(field_die),
resolve_bitfield(field_die),
false);
}
}
Expand Down Expand Up @@ -357,25 +380,52 @@ ssize_t Dwarf::get_array_size(Dwarf_Die &subrange_die)
return 0;
}

ssize_t Dwarf::get_field_offset(Dwarf_Die &field_die)
ssize_t Dwarf::get_field_byte_offset(Dwarf_Die &field_die)
{
Dwarf_Attribute attr;
Dwarf_Word value;
if (dwarf_hasattr(&field_die, DW_AT_data_member_location))
{
Dwarf_Attribute attr;
Dwarf_Word value;
if (dwarf_formudata(
dwarf_attr_integrate(&field_die, DW_AT_data_member_location, &attr),
&value) >= 0)
return (ssize_t)value;
}
return get_field_bit_offset(field_die) / 8;
}

ssize_t Dwarf::get_field_bit_offset(Dwarf_Die &field_die)
{
Dwarf_Attribute attr;
Dwarf_Word value;
if (dwarf_hasattr(&field_die, DW_AT_data_bit_offset))
{
{ // DWARF >= 4
if (dwarf_formudata(
dwarf_attr_integrate(&field_die, DW_AT_data_bit_offset, &attr),
&value) >= 0)
return (ssize_t)value;
}
if (dwarf_hasattr(&field_die, DW_AT_bit_offset))
{ // DWARF < 4
if (dwarf_formudata(
dwarf_attr_integrate(&field_die, DW_AT_bit_offset, &attr),
&value) >= 0)
return (ssize_t)value;
}

return 0;
}

ssize_t Dwarf::get_bitfield_size(Dwarf_Die &field_die)
{
Dwarf_Attribute attr;
Dwarf_Word value;
if (dwarf_hasattr(&field_die, DW_AT_bit_size))
{
if (dwarf_formudata(dwarf_attr_integrate(&field_die, DW_AT_bit_size, &attr),
&value) >= 0)
return (ssize_t)value;
}
return 0;
}

Expand Down
7 changes: 6 additions & 1 deletion src/dwarf_parser.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#pragma once

#include "struct.h"
#include "types.h"

#include <memory>
#include <optional>
#include <string>
#include <vector>

Expand Down Expand Up @@ -39,7 +41,10 @@ class Dwarf
Dwarf_Word get_type_encoding(Dwarf_Die &type_die) const;
std::optional<Dwarf_Die> find_type(const std::string &name) const;
static ssize_t get_array_size(Dwarf_Die &subrange_die);
static ssize_t get_field_offset(Dwarf_Die &field_die);
static ssize_t get_field_byte_offset(Dwarf_Die &field_die);
static ssize_t get_field_bit_offset(Dwarf_Die &field_die);
static ssize_t get_bitfield_size(Dwarf_Die &field_die);
std::optional<Bitfield> resolve_bitfield(Dwarf_Die &field_die) const;

SizedType get_stype(Dwarf_Die &type_die, bool resolve_structs = true) const;

Expand Down
82 changes: 82 additions & 0 deletions tests/field_analyser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,88 @@ TEST_F(field_analyser_dwarf, parse_struct)
ASSERT_EQ(str->GetField("c").type.GetSize(), 8);
}

TEST_F(field_analyser_dwarf, dwarf_types_bitfields)
{
BPFtrace bpftrace;
std::string uprobe = "uprobe:" + std::string(bin_);
test(bpftrace,
uprobe + ":func_1 { @ = ((struct task_struct *)curtask)->pid; }",
0);

ASSERT_TRUE(bpftrace.structs.Has("struct task_struct"));
auto task_struct = bpftrace.structs.Lookup("struct task_struct").lock();

ASSERT_TRUE(task_struct->HasField("a"));
EXPECT_EQ(task_struct->GetField("a").type.type, Type::integer);
EXPECT_EQ(task_struct->GetField("a").type.GetSize(), 4U);
EXPECT_TRUE(task_struct->GetField("a").bitfield.has_value());

EXPECT_TRUE(task_struct->GetField("a").offset == 8 ||
task_struct->GetField("a").offset == 9);
if (task_struct->GetField("a").offset == 8)
{ // DWARF < 4
EXPECT_EQ(task_struct->GetField("a").bitfield->read_bytes, 0x3U);
EXPECT_EQ(task_struct->GetField("a").bitfield->access_rshift, 12U);
EXPECT_EQ(task_struct->GetField("a").bitfield->mask, 0xFFU);
}
else
{ // DWARF >= 4
EXPECT_EQ(task_struct->GetField("a").bitfield->read_bytes, 0x2U);
EXPECT_EQ(task_struct->GetField("a").bitfield->access_rshift, 4U);
EXPECT_EQ(task_struct->GetField("a").bitfield->mask, 0xFFU);
}

ASSERT_TRUE(task_struct->HasField("b"));
EXPECT_EQ(task_struct->GetField("b").type.type, Type::integer);
EXPECT_EQ(task_struct->GetField("b").type.GetSize(), 4U);
EXPECT_TRUE(task_struct->GetField("b").bitfield.has_value());

EXPECT_TRUE(task_struct->GetField("b").offset == 8 ||
task_struct->GetField("b").offset == 10);
if (task_struct->GetField("b").offset == 8)
{ // DWARF < 4
EXPECT_EQ(task_struct->GetField("b").bitfield->read_bytes, 0x3U);
EXPECT_EQ(task_struct->GetField("b").bitfield->access_rshift, 20U);
EXPECT_EQ(task_struct->GetField("b").bitfield->mask, 0x1U);
}
else
{ // DWARF >= 4
EXPECT_EQ(task_struct->GetField("b").bitfield->read_bytes, 0x1U);
EXPECT_EQ(task_struct->GetField("b").bitfield->access_rshift, 4U);
EXPECT_EQ(task_struct->GetField("b").bitfield->mask, 0x1U);
}

ASSERT_TRUE(task_struct->HasField("c"));
EXPECT_EQ(task_struct->GetField("c").type.type, Type::integer);
EXPECT_EQ(task_struct->GetField("c").type.GetSize(), 4U);
EXPECT_TRUE(task_struct->GetField("c").bitfield.has_value());

EXPECT_TRUE(task_struct->GetField("c").offset == 8 ||
task_struct->GetField("c").offset == 10);

if (task_struct->GetField("c").offset == 8)
{ // DWARF < 4
EXPECT_EQ(task_struct->GetField("c").bitfield->read_bytes, 0x3U);
EXPECT_EQ(task_struct->GetField("c").bitfield->access_rshift, 21U);
EXPECT_EQ(task_struct->GetField("c").bitfield->mask, 0x7U);
}
else
{ // DWARF >= 4
EXPECT_EQ(task_struct->GetField("c").bitfield->read_bytes, 0x1U);
EXPECT_EQ(task_struct->GetField("c").bitfield->access_rshift, 5U);
EXPECT_EQ(task_struct->GetField("c").bitfield->mask, 0x7U);
}

ASSERT_TRUE(task_struct->HasField("d"));
EXPECT_EQ(task_struct->GetField("d").type.type, Type::integer);
EXPECT_EQ(task_struct->GetField("d").type.GetSize(), 4U);
EXPECT_EQ(task_struct->GetField("d").offset, 12);
EXPECT_TRUE(task_struct->GetField("d").bitfield.has_value());
EXPECT_EQ(task_struct->GetField("d").bitfield->read_bytes, 0x3U);
EXPECT_EQ(task_struct->GetField("d").bitfield->access_rshift, 0U);
EXPECT_EQ(task_struct->GetField("d").bitfield->mask, 0xFFFFFU);
}

#endif // HAVE_LIBDW

} // namespace field_analyser
Expand Down

0 comments on commit 919fe34

Please sign in to comment.