Skip to content

Commit

Permalink
Merge pull request #262 from chaoticgd/dwarf_bugs
Browse files Browse the repository at this point in the history
Improve DWARF printing, fix some bugs
  • Loading branch information
chaoticgd authored Oct 31, 2024
2 parents c550a6e + 27928f0 commit 6327ba5
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 51 deletions.
202 changes: 154 additions & 48 deletions src/ccc/dwarf_section.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ Value& Value::operator=(const Value& rhs)
return *this;
}

Form Value::form() const
{
return static_cast<Form>(m_form);
}

bool Value::valid() const
{
return form_to_string(m_form) != nullptr;
Expand All @@ -43,15 +48,40 @@ Value Value::from_reference(u32 reference)
return result;
}

Value Value::from_constant(u64 constant)
Value Value::from_constant_2(u16 constant)
{
Value result;
result.m_form = FORM_DATA2;
result.m_value.constant = constant;
return result;
}

Value Value::from_constant_4(u32 constant)
{
Value result;
result.m_form = FORM_DATA4;
result.m_value.constant = constant;
return result;
}

Value Value::from_constant_8(u64 constant)
{
Value result;
result.m_form = FORM_DATA8;
result.m_value.constant = constant;
return result;
}

Value Value::from_block(std::span<const u8> block)
Value Value::from_block_2(std::span<const u8> block)
{
Value result;
result.m_form = FORM_BLOCK2;
result.m_value.block.begin = block.data();
result.m_value.block.end = block.data() + block.size();
return result;
}

Value Value::from_block_4(std::span<const u8> block)
{
Value result;
result.m_form = FORM_BLOCK4;
Expand Down Expand Up @@ -82,13 +112,13 @@ u32 Value::reference() const

u64 Value::constant() const
{
CCC_ASSERT(m_form == FORM_DATA8);
CCC_ASSERT(m_form == FORM_DATA2 || m_form == FORM_DATA4 || m_form == FORM_DATA8);
return m_value.constant;
}

std::span<const u8> Value::block() const
{
CCC_ASSERT(m_form == FORM_BLOCK4);
CCC_ASSERT(m_form == FORM_BLOCK2 || m_form == FORM_BLOCK4);
return std::span<const u8>(m_value.block.begin, m_value.block.end);
}

Expand All @@ -100,6 +130,11 @@ const char* Value::string() const

// *****************************************************************************

#define DIE_CHECK(condition, message) \
CCC_CHECK(condition, message " at 0x%x inside DIE at 0x%x.", offset, m_offset);
#define DIE_CHECK_ARGS(condition, message, ...) \
CCC_CHECK(condition, message " at 0x%x inside DIE at 0x%x.", __VA_ARGS__, offset, m_offset);

Result<std::optional<DIE>> DIE::parse(std::span<const u8> debug, u32 offset, u32 importer_flags)
{
DIE die;
Expand Down Expand Up @@ -146,7 +181,7 @@ Result<std::optional<DIE>> DIE::first_child() const
Result<AttributeTuple> attribute = parse_attribute(offset);
CCC_RETURN_IF_ERROR(attribute);

if (attribute->attribute == AT_sibling && attribute->form == FORM_REF) {
if (attribute->attribute == AT_sibling && attribute->value.form() == FORM_REF) {
sibling_offset = attribute->value.reference();
}
}
Expand All @@ -165,14 +200,22 @@ Result<std::optional<DIE>> DIE::sibling() const
Result<AttributeTuple> attribute = parse_attribute(offset);
CCC_RETURN_IF_ERROR(attribute);

if (attribute->attribute == AT_sibling && attribute->form == FORM_REF) {
if (attribute->attribute == AT_sibling && attribute->value.form() == FORM_REF) {
// Prevent infinite recursion if the file contains a cycle.
CCC_CHECK(attribute->value.reference() > m_offset,
"Sibling attribute of DIE at 0x%x points backwards.", m_offset);
return DIE::parse(m_debug, attribute->value.reference(), m_importer_flags);
}
}

return std::optional<DIE>(std::nullopt);
}

u32 DIE::offset() const
{
return m_offset;
}

Tag DIE::tag() const
{
return m_tag;
Expand All @@ -190,10 +233,8 @@ Result<void> DIE::attributes(std::span<Value*> output, const RequiredAttributes&
continue;
}

CCC_CHECK(iterator->second.valid_forms & 1 << (attribute->form),
"Attribute %s has an unexpected form %s.",
form_to_string(attribute->form),
attribute_to_string(attribute->attribute));
DIE_CHECK_ARGS(iterator->second.valid_forms & 1 << (attribute->value.form()),
"Attribute %x has an unexpected form %s", attribute->attribute, form_to_string(attribute->value.form()));

*output[iterator->second.index] = std::move(attribute->value);
}
Expand All @@ -216,98 +257,94 @@ Result<std::vector<AttributeTuple>> DIE::all_attributes() const
return result;
}

#define ATTRIBUTE_PARSER_CHECK(condition, message) \
CCC_CHECK(condition, message " at 0x%x inside DIE at 0x%x.", offset, m_offset);
#define ATTRIBUTE_PARSER_CHECK_ARG(condition, message, arg) \
CCC_CHECK(condition, message " at 0x%x inside DIE at 0x%x.", arg, offset, m_offset);

Result<AttributeTuple> DIE::parse_attribute(u32& offset) const
{
AttributeTuple result;

result.offset = offset;

const std::optional<u16> name = copy_unaligned<u16>(m_debug, offset);
ATTRIBUTE_PARSER_CHECK(name.has_value(), "Cannot read attribute name");
DIE_CHECK(name.has_value(), "Cannot read attribute name");
offset += sizeof(u16);

u8 form = *name & 0xf;
ATTRIBUTE_PARSER_CHECK_ARG(form_to_string(form) != nullptr, "Unknown attribute form 0x%hhx", form);
DIE_CHECK_ARGS(form_to_string(form) != nullptr, "Unknown attribute form 0x%hhx", form);

u16 attribute = *name >> 4;
bool known_attribute = attribute_to_string(attribute);
if (!known_attribute) {
const char* uknown_attribute_error_message =
"Unknown user attribute name 0x%03hx at 0x%x inside DIE at 0x%x.";
"Unknown attribute name 0x%03hx at 0x%x inside DIE at 0x%x.";
if ((m_importer_flags & STRICT_PARSING) == 0 && attribute >= AT_lo_user && attribute <= AT_hi_user) {
CCC_WARN(uknown_attribute_error_message, *name, offset, m_offset);
} else {
return CCC_FAILURE(uknown_attribute_error_message, *name, offset, m_offset);
}
}

result.form = static_cast<Form>(form);
result.attribute = static_cast<Attribute>(attribute);

switch (form) {
case FORM_ADDR: {
std::optional<u32> address = copy_unaligned<u32>(m_debug, offset);
ATTRIBUTE_PARSER_CHECK(address.has_value(), "Cannot read address attribute");
DIE_CHECK(address.has_value(), "Cannot read address attribute");
result.value = Value::from_address(*address);
offset += sizeof(u32);
break;
}
case FORM_REF: {
std::optional<u32> reference = copy_unaligned<u32>(m_debug, offset);
ATTRIBUTE_PARSER_CHECK(reference.has_value(), "Cannot read reference attribute");
DIE_CHECK(reference.has_value(), "Cannot read reference attribute");
result.value = Value::from_reference(*reference);
offset += sizeof(u32);
break;
}
case FORM_BLOCK2: {
std::optional<u16> size = copy_unaligned<u16>(m_debug, offset);
ATTRIBUTE_PARSER_CHECK(size.has_value(), "Cannot read block attribute size");
DIE_CHECK(size.has_value(), "Cannot read block attribute size");
offset += sizeof(u16);

ATTRIBUTE_PARSER_CHECK((u64) offset + *size <= m_debug.size(), "Cannot read block attribute data");
result.value = Value::from_block(m_debug.subspan(offset, *size));
DIE_CHECK((u64) offset + *size <= m_debug.size(), "Cannot read block attribute data");
result.value = Value::from_block_2(m_debug.subspan(offset, *size));
offset += *size;

break;
}
case FORM_BLOCK4: {
std::optional<u32> size = copy_unaligned<u32>(m_debug, offset);
ATTRIBUTE_PARSER_CHECK(size.has_value(), "Cannot read block attribute size");
DIE_CHECK(size.has_value(), "Cannot read block attribute size");
offset += sizeof(u32);

ATTRIBUTE_PARSER_CHECK((u64) offset + *size <= m_debug.size(), "Cannot read block attribute data");
result.value = Value::from_block(m_debug.subspan(offset, *size));
DIE_CHECK((u64) offset + *size <= m_debug.size(), "Cannot read block attribute data");
result.value = Value::from_block_4(m_debug.subspan(offset, *size));
offset += *size;

break;
}
case FORM_DATA2: {
std::optional<u16> constant = copy_unaligned<u16>(m_debug, offset);
ATTRIBUTE_PARSER_CHECK(constant.has_value(), "Cannot read constant attribute");
result.value = Value::from_constant(*constant);
DIE_CHECK(constant.has_value(), "Cannot read constant attribute");
result.value = Value::from_constant_2(*constant);
offset += sizeof(u16);
break;
}
case FORM_DATA4: {
std::optional<u32> constant = copy_unaligned<u32>(m_debug, offset);
ATTRIBUTE_PARSER_CHECK(constant.has_value(), "Cannot read constant attribute");
result.value = Value::from_constant(*constant);
DIE_CHECK(constant.has_value(), "Cannot read constant attribute");
result.value = Value::from_constant_4(*constant);
offset += sizeof(u32);
break;
}
case FORM_DATA8: {
std::optional<u64> constant = copy_unaligned<u64>(m_debug, offset);
ATTRIBUTE_PARSER_CHECK(constant.has_value(), "Cannot read constant attribute");
result.value = Value::from_constant(*constant);
DIE_CHECK(constant.has_value(), "Cannot read constant attribute");
result.value = Value::from_constant_8(*constant);
offset += sizeof(u64);
break;
}
case FORM_STRING: {
const char* string = get_string(m_debug, offset);
ATTRIBUTE_PARSER_CHECK(string, "Cannot read string attribute");
DIE_CHECK(string, "Cannot read string attribute");
result.value = Value::from_string(string);
offset += strlen(string) + 1;
break;
Expand Down Expand Up @@ -343,8 +380,15 @@ Result<void> SectionReader::print_dies(FILE* out, DIE die, s32 depth) const
std::optional<DIE> current_die = std::move(die);

while (current_die.has_value()) {
indent(out, depth);
fprintf(out, "%s ", tag_to_string(current_die->tag()));
fprintf(out, "%8x:", current_die->offset());
indent(out, depth + 1);

const char* tag = tag_to_string(current_die->tag());
if (tag) {
fprintf(out, "%s", tag);
} else {
fprintf(out, "unknown(%hx)", current_die->tag());
}

Result<void> result = print_attributes(out, *current_die);
CCC_RETURN_IF_ERROR(result);
Expand All @@ -370,19 +414,81 @@ Result<void> SectionReader::print_attributes(FILE* out, const DIE& die) const
Result<std::vector<AttributeTuple>> attributes = die.all_attributes();
CCC_RETURN_IF_ERROR(attributes);

for (const auto& [attribute, form, value] : *attributes) {
fprintf(out, "%s=", attribute_to_string(attribute));
switch (form) {
case FORM_ADDR: fprintf(out, "0x%x", value.address()); break;
case FORM_REF: fprintf(out, "DIE@0x%x", value.reference()); break;
case FORM_BLOCK2: fprintf(out, "(block2)"); break;
case FORM_BLOCK4: fprintf(out, "(block4)"); break;
case FORM_DATA2: fprintf(out, "0x%hx", (short) value.constant()); break;
case FORM_DATA4: fprintf(out, "0x%x", (int) value.constant()); break;
case FORM_DATA8: fprintf(out, "0x%llx", (long long) value.constant()); break;
case FORM_STRING: fprintf(out, "\"%s\"", value.string()); break;
for (const auto& [offset, attribute, value] : *attributes) {
// The sibling attributes are just used to represent the structure of
// the graph, which is displayed anyway, so skip over them for the sake
// of readability.
if (attribute == AT_sibling) {
continue;
}

const char* name = attribute_to_string(attribute);
if (name) {
fprintf(out, " %s=", name);
} else {
fprintf(out, " unknown(%x)=", attribute);
}

switch (value.form()) {
case FORM_ADDR: {
fprintf(out, "0x%x", value.address());
break;
}
case FORM_REF: {
Result<std::optional<DIE>> referenced_die = DIE::parse(m_debug, value.reference(), NO_IMPORTER_FLAGS);
if (referenced_die.success()) {
if (referenced_die->has_value()) {
const char* referenced_die_tag = tag_to_string((*referenced_die)->tag());
if (referenced_die_tag) {
fprintf(out, "%s", referenced_die_tag);
} else {
fprintf(out, "unknown(%hx)", (*referenced_die)->tag());
}
} else {
// The DIE was less than 8 bytes in size.
fprintf(out, "null");
}
} else {
// The DIE could not be read.
fprintf(out, "???");
}

fprintf(out, "@%x", value.reference());
break;
}
case FORM_BLOCK2:
case FORM_BLOCK4: {
fprintf(out, "{");

size_t max_bytes_to_display = 3;
std::span<const u8> block = value.block();

for (size_t i = 0; i < std::min(block.size(), max_bytes_to_display); i++) {
if (i != 0) {
fprintf(out, ",");
}
fprintf(out, "%02hhx", block[i]);
}

if (block.size() > max_bytes_to_display) {
fprintf(out, ",...");
}

fprintf(out, "}@%x", offset);

break;
}
case FORM_DATA2:
case FORM_DATA4:
case FORM_DATA8: {
fprintf(out, "0x%llx", (long long) value.constant());
break;
}
case FORM_STRING: {
fprintf(out, "\"%s\"", value.string());
break;
}
}
fprintf(out, " ");
}
fprintf(out, "\n");

Expand Down
11 changes: 8 additions & 3 deletions src/ccc/dwarf_section.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,16 @@ class Value {
~Value();
Value& operator=(const Value& rhs);

Form form() const;
bool valid() const;

static Value from_address(u32 address);
static Value from_reference(u32 reference);
static Value from_constant(u64 constant);
static Value from_block(std::span<const u8> block);
static Value from_constant_2(u16 constant);
static Value from_constant_4(u32 constant);
static Value from_constant_8(u64 constant);
static Value from_block_2(std::span<const u8> block);
static Value from_block_4(std::span<const u8> block);
static Value from_string(const char* string);

u32 address() const;
Expand All @@ -147,8 +151,8 @@ class Value {
};

struct AttributeTuple {
u32 offset;
Attribute attribute;
Form form;
Value value;
};

Expand All @@ -174,6 +178,7 @@ class DIE {
Result<std::optional<DIE>> first_child() const;
Result<std::optional<DIE>> sibling() const;

u32 offset() const;
Tag tag() const;

// Parse the attributes, and output the ones specified by the required parameter.
Expand Down

0 comments on commit 6327ba5

Please sign in to comment.