mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2024-11-24 08:55:41 +01:00
Add support for dwarf5 line tables.
Change-Id: I2c0cd0e7163502e52fbf0745b611befb2e219071 Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/2276193 Reviewed-by: Sterling Augustine <saugustine@google.com>
This commit is contained in:
parent
b6f36dd775
commit
a741027533
@ -316,6 +316,14 @@ enum DwarfAttribute {
|
|||||||
DW_AT_PGI_lstride = 0x3a02
|
DW_AT_PGI_lstride = 0x3a02
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Line number content type codes (DWARF 5).
|
||||||
|
enum DwarfLineNumberContentType {
|
||||||
|
DW_LNCT_path = 1,
|
||||||
|
DW_LNCT_directory_index = 2,
|
||||||
|
DW_LNCT_timestamp = 3,
|
||||||
|
DW_LNCT_size = 4,
|
||||||
|
DW_LNCT_MD5 = 5,
|
||||||
|
};
|
||||||
|
|
||||||
// Line number opcodes.
|
// Line number opcodes.
|
||||||
enum DwarfLineNumberOps {
|
enum DwarfLineNumberOps {
|
||||||
|
@ -1039,10 +1039,17 @@ uint32_t DwpReader::LookupCUv2(uint64_t dwo_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LineInfo::LineInfo(const uint8_t *buffer, uint64_t buffer_length,
|
LineInfo::LineInfo(const uint8_t *buffer, uint64_t buffer_length,
|
||||||
ByteReader* reader, LineInfoHandler* handler):
|
ByteReader* reader, const uint8_t* string_buffer,
|
||||||
handler_(handler), reader_(reader), buffer_(buffer) {
|
size_t string_buffer_length,
|
||||||
|
const uint8_t* line_string_buffer,
|
||||||
|
size_t line_string_buffer_length, LineInfoHandler* handler):
|
||||||
|
handler_(handler), reader_(reader), buffer_(buffer),
|
||||||
|
string_buffer_(string_buffer),
|
||||||
|
line_string_buffer_(line_string_buffer) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
buffer_length_ = buffer_length;
|
buffer_length_ = buffer_length;
|
||||||
|
string_buffer_length_ = string_buffer_length;
|
||||||
|
line_string_buffer_length_ = line_string_buffer_length;
|
||||||
#endif
|
#endif
|
||||||
header_.std_opcode_lengths = NULL;
|
header_.std_opcode_lengths = NULL;
|
||||||
}
|
}
|
||||||
@ -1053,6 +1060,128 @@ uint64_t LineInfo::Start() {
|
|||||||
return after_header_ - buffer_;
|
return after_header_ - buffer_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LineInfo::ReadTypesAndForms(const uint8_t** lineptr,
|
||||||
|
uint32_t* content_types,
|
||||||
|
uint32_t* content_forms,
|
||||||
|
uint32_t max_types,
|
||||||
|
uint32_t* format_count) {
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
uint32_t count = reader_->ReadUnsignedLEB128(*lineptr, &len);
|
||||||
|
*lineptr += len;
|
||||||
|
if (count < 1 || count > max_types) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (uint32_t col = 0; col < count; ++col) {
|
||||||
|
content_types[col] = reader_->ReadUnsignedLEB128(*lineptr, &len);
|
||||||
|
*lineptr += len;
|
||||||
|
content_forms[col] = reader_->ReadUnsignedLEB128(*lineptr, &len);
|
||||||
|
*lineptr += len;
|
||||||
|
}
|
||||||
|
*format_count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* LineInfo::ReadStringForm(uint32_t form, const uint8_t** lineptr) {
|
||||||
|
const char* name = nullptr;
|
||||||
|
if (form == DW_FORM_string) {
|
||||||
|
name = reinterpret_cast<const char*>(*lineptr);
|
||||||
|
*lineptr += strlen(name) + 1;
|
||||||
|
return name;
|
||||||
|
} else if (form == DW_FORM_strp) {
|
||||||
|
uint64_t offset = reader_->ReadOffset(*lineptr);
|
||||||
|
assert(offset < string_buffer_length_);
|
||||||
|
*lineptr += reader_->OffsetSize();
|
||||||
|
if (string_buffer_ != nullptr) {
|
||||||
|
name = reinterpret_cast<const char*>(string_buffer_) + offset;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
} else if (form == DW_FORM_line_strp) {
|
||||||
|
uint64_t offset = reader_->ReadOffset(*lineptr);
|
||||||
|
assert(offset < line_string_buffer_length_);
|
||||||
|
*lineptr += reader_->OffsetSize();
|
||||||
|
if (line_string_buffer_ != nullptr) {
|
||||||
|
name = reinterpret_cast<const char*>(line_string_buffer_) + offset;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Shouldn't be called with a non-string-form, and
|
||||||
|
// if there is a string form but no string buffer,
|
||||||
|
// that is a problem too.
|
||||||
|
assert(0);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t LineInfo::ReadUnsignedData(uint32_t form, const uint8_t** lineptr) {
|
||||||
|
size_t len;
|
||||||
|
uint64_t value;
|
||||||
|
|
||||||
|
switch (form) {
|
||||||
|
case DW_FORM_data1:
|
||||||
|
value = reader_->ReadOneByte(*lineptr);
|
||||||
|
*lineptr += 1;
|
||||||
|
return value;
|
||||||
|
case DW_FORM_data2:
|
||||||
|
value = reader_->ReadTwoBytes(*lineptr);
|
||||||
|
*lineptr += 2;
|
||||||
|
return value;
|
||||||
|
case DW_FORM_data4:
|
||||||
|
value = reader_->ReadFourBytes(*lineptr);
|
||||||
|
*lineptr += 4;
|
||||||
|
return value;
|
||||||
|
case DW_FORM_data8:
|
||||||
|
value = reader_->ReadEightBytes(*lineptr);
|
||||||
|
*lineptr += 8;
|
||||||
|
return value;
|
||||||
|
case DW_FORM_udata:
|
||||||
|
value = reader_->ReadUnsignedLEB128(*lineptr, &len);
|
||||||
|
*lineptr += len;
|
||||||
|
return value;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unrecognized data form.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LineInfo::ReadFileRow(const uint8_t** lineptr,
|
||||||
|
const uint32_t* content_types,
|
||||||
|
const uint32_t* content_forms, uint32_t row,
|
||||||
|
uint32_t format_count) {
|
||||||
|
const char* filename = nullptr;
|
||||||
|
uint64_t dirindex = 0;
|
||||||
|
uint64_t mod_time = 0;
|
||||||
|
uint64_t filelength = 0;
|
||||||
|
|
||||||
|
for (uint32_t col = 0; col < format_count; ++col) {
|
||||||
|
switch (content_types[col]) {
|
||||||
|
case DW_LNCT_path:
|
||||||
|
filename = ReadStringForm(content_forms[col], lineptr);
|
||||||
|
break;
|
||||||
|
case DW_LNCT_directory_index:
|
||||||
|
dirindex = ReadUnsignedData(content_forms[col], lineptr);
|
||||||
|
break;
|
||||||
|
case DW_LNCT_timestamp:
|
||||||
|
mod_time = ReadUnsignedData(content_forms[col], lineptr);
|
||||||
|
break;
|
||||||
|
case DW_LNCT_size:
|
||||||
|
filelength = ReadUnsignedData(content_forms[col], lineptr);
|
||||||
|
break;
|
||||||
|
case DW_LNCT_MD5:
|
||||||
|
// MD5 entries help a debugger sort different versions of files with
|
||||||
|
// the same name. It is always paired with a DW_FORM_data16 and is
|
||||||
|
// unused in this case.
|
||||||
|
lineptr += 16;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unrecognized form in line table header. %d\n",
|
||||||
|
content_types[col]);
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(filename != nullptr);
|
||||||
|
handler_->DefineFile(filename, row, dirindex, mod_time, filelength);
|
||||||
|
}
|
||||||
|
|
||||||
// The header for a debug_line section is mildly complicated, because
|
// The header for a debug_line section is mildly complicated, because
|
||||||
// the line info is very tightly encoded.
|
// the line info is very tightly encoded.
|
||||||
void LineInfo::ReadHeader() {
|
void LineInfo::ReadHeader() {
|
||||||
@ -1067,12 +1196,24 @@ void LineInfo::ReadHeader() {
|
|||||||
assert(buffer_ + initial_length_size + header_.total_length <=
|
assert(buffer_ + initial_length_size + header_.total_length <=
|
||||||
buffer_ + buffer_length_);
|
buffer_ + buffer_length_);
|
||||||
|
|
||||||
// Address size *must* be set by CU ahead of time.
|
|
||||||
assert(reader_->AddressSize() != 0);
|
|
||||||
|
|
||||||
header_.version = reader_->ReadTwoBytes(lineptr);
|
header_.version = reader_->ReadTwoBytes(lineptr);
|
||||||
lineptr += 2;
|
lineptr += 2;
|
||||||
|
|
||||||
|
if (header_.version >= 5) {
|
||||||
|
uint8_t address_size = reader_->ReadOneByte(lineptr);
|
||||||
|
reader_->SetAddressSize(address_size);
|
||||||
|
lineptr += 1;
|
||||||
|
uint8_t segment_selector_size = reader_->ReadOneByte(lineptr);
|
||||||
|
if (segment_selector_size != 0) {
|
||||||
|
fprintf(stderr,"No support for segmented memory.");
|
||||||
|
}
|
||||||
|
lineptr += 1;
|
||||||
|
} else {
|
||||||
|
// Address size *must* be set by CU ahead of time.
|
||||||
|
assert(reader_->AddressSize() != 0);
|
||||||
|
}
|
||||||
|
|
||||||
header_.prologue_length = reader_->ReadOffset(lineptr);
|
header_.prologue_length = reader_->ReadOffset(lineptr);
|
||||||
lineptr += reader_->OffsetSize();
|
lineptr += reader_->OffsetSize();
|
||||||
|
|
||||||
@ -1106,41 +1247,84 @@ void LineInfo::ReadHeader() {
|
|||||||
lineptr += 1;
|
lineptr += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It is legal for the directory entry table to be empty.
|
if (header_.version <= 4) {
|
||||||
if (*lineptr) {
|
// Directory zero is assumed to be the compilation directory and special
|
||||||
uint32_t dirindex = 1;
|
// cased where used. It is not actually stored in the dwarf data. But an
|
||||||
while (*lineptr) {
|
// empty entry here avoids off-by-one errors elsewhere in the code.
|
||||||
const char *dirname = reinterpret_cast<const char *>(lineptr);
|
handler_->DefineDir("", 0);
|
||||||
handler_->DefineDir(dirname, dirindex);
|
// It is legal for the directory entry table to be empty.
|
||||||
lineptr += strlen(dirname) + 1;
|
if (*lineptr) {
|
||||||
dirindex++;
|
uint32_t dirindex = 1;
|
||||||
|
while (*lineptr) {
|
||||||
|
const char* dirname = reinterpret_cast<const char*>(lineptr);
|
||||||
|
handler_->DefineDir(dirname, dirindex);
|
||||||
|
lineptr += strlen(dirname) + 1;
|
||||||
|
dirindex++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
lineptr++;
|
||||||
lineptr++;
|
// It is also legal for the file entry table to be empty.
|
||||||
|
|
||||||
// It is also legal for the file entry table to be empty.
|
// Similarly for file zero.
|
||||||
if (*lineptr) {
|
handler_->DefineFile("", 0, 0, 0, 0);
|
||||||
uint32_t fileindex = 1;
|
if (*lineptr) {
|
||||||
|
uint32_t fileindex = 1;
|
||||||
|
size_t len;
|
||||||
|
while (*lineptr) {
|
||||||
|
const char* filename = ReadStringForm(DW_FORM_string, &lineptr);
|
||||||
|
|
||||||
|
uint64_t dirindex = reader_->ReadUnsignedLEB128(lineptr, &len);
|
||||||
|
lineptr += len;
|
||||||
|
|
||||||
|
uint64_t mod_time = reader_->ReadUnsignedLEB128(lineptr, &len);
|
||||||
|
lineptr += len;
|
||||||
|
|
||||||
|
uint64_t filelength = reader_->ReadUnsignedLEB128(lineptr, &len);
|
||||||
|
lineptr += len;
|
||||||
|
handler_->DefineFile(filename, fileindex,
|
||||||
|
static_cast<uint32_t>(dirindex), mod_time,
|
||||||
|
filelength);
|
||||||
|
fileindex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lineptr++;
|
||||||
|
} else {
|
||||||
|
// Read the DWARF-5 directory table.
|
||||||
|
|
||||||
|
// Dwarf5 supports five different types and forms per directory- and
|
||||||
|
// file-table entry. Theoretically, there could be duplicate entries
|
||||||
|
// in this table, but that would be quite unusual.
|
||||||
|
static const uint32_t kMaxTypesAndForms = 5;
|
||||||
|
uint32_t content_types[kMaxTypesAndForms];
|
||||||
|
uint32_t content_forms[kMaxTypesAndForms];
|
||||||
|
uint32_t format_count;
|
||||||
size_t len;
|
size_t len;
|
||||||
while (*lineptr) {
|
|
||||||
const char *filename = reinterpret_cast<const char *>(lineptr);
|
|
||||||
lineptr += strlen(filename) + 1;
|
|
||||||
|
|
||||||
uint64_t dirindex = reader_->ReadUnsignedLEB128(lineptr, &len);
|
ReadTypesAndForms(&lineptr, content_types, content_forms, kMaxTypesAndForms,
|
||||||
lineptr += len;
|
&format_count);
|
||||||
|
uint32_t entry_count = reader_->ReadUnsignedLEB128(lineptr, &len);
|
||||||
|
lineptr += len;
|
||||||
|
for (uint32_t row = 0; row < entry_count; ++row) {
|
||||||
|
const char* dirname = nullptr;
|
||||||
|
for (uint32_t col = 0; col < format_count; ++col) {
|
||||||
|
// The path is the only relevant content type for this implementation.
|
||||||
|
if (content_types[col] == DW_LNCT_path) {
|
||||||
|
dirname = ReadStringForm(content_forms[col], &lineptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handler_->DefineDir(dirname, row);
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t mod_time = reader_->ReadUnsignedLEB128(lineptr, &len);
|
// Read the DWARF-5 filename table.
|
||||||
lineptr += len;
|
ReadTypesAndForms(&lineptr, content_types, content_forms, kMaxTypesAndForms,
|
||||||
|
&format_count);
|
||||||
|
entry_count = reader_->ReadUnsignedLEB128(lineptr, &len);
|
||||||
|
lineptr += len;
|
||||||
|
|
||||||
uint64_t filelength = reader_->ReadUnsignedLEB128(lineptr, &len);
|
for (uint32_t row = 0; row < entry_count; ++row) {
|
||||||
lineptr += len;
|
ReadFileRow(&lineptr, content_types, content_forms, row, format_count);
|
||||||
handler_->DefineFile(filename, fileindex, static_cast<uint32_t>(dirindex),
|
|
||||||
mod_time, filelength);
|
|
||||||
fileindex++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lineptr++;
|
|
||||||
|
|
||||||
after_header_ = lineptr;
|
after_header_ = lineptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,8 +98,10 @@ class LineInfo {
|
|||||||
// to the beginning and length of the line information to read.
|
// to the beginning and length of the line information to read.
|
||||||
// Reader is a ByteReader class that has the endianness set
|
// Reader is a ByteReader class that has the endianness set
|
||||||
// properly.
|
// properly.
|
||||||
LineInfo(const uint8_t *buffer_, uint64_t buffer_length,
|
LineInfo(const uint8_t* buffer, uint64_t buffer_length,
|
||||||
ByteReader* reader, LineInfoHandler* handler);
|
ByteReader* reader, const uint8_t* string_buffer,
|
||||||
|
size_t string_buffer_length, const uint8_t* line_string_buffer,
|
||||||
|
size_t line_string_buffer_length, LineInfoHandler* handler);
|
||||||
|
|
||||||
virtual ~LineInfo() {
|
virtual ~LineInfo() {
|
||||||
if (header_.std_opcode_lengths) {
|
if (header_.std_opcode_lengths) {
|
||||||
@ -137,15 +139,32 @@ class LineInfo {
|
|||||||
// Reads the DWARF2/3 line information
|
// Reads the DWARF2/3 line information
|
||||||
void ReadLines();
|
void ReadLines();
|
||||||
|
|
||||||
|
// Read the DWARF5 types and forms for the file and directory tables.
|
||||||
|
void ReadTypesAndForms(const uint8_t** lineptr, uint32_t* content_types,
|
||||||
|
uint32_t* content_forms, uint32_t max_types,
|
||||||
|
uint32_t* format_count);
|
||||||
|
|
||||||
|
// Read a row from the dwarf5 LineInfo file table.
|
||||||
|
void ReadFileRow(const uint8_t** lineptr, const uint32_t* content_types,
|
||||||
|
const uint32_t* content_forms, uint32_t row,
|
||||||
|
uint32_t format_count);
|
||||||
|
|
||||||
|
// Read and return the data at *lineptr according to form. Advance
|
||||||
|
// *lineptr appropriately.
|
||||||
|
uint64_t ReadUnsignedData(uint32_t form, const uint8_t** lineptr);
|
||||||
|
|
||||||
|
// Read and return the data at *lineptr according to form. Advance
|
||||||
|
// *lineptr appropriately.
|
||||||
|
const char* ReadStringForm(uint32_t form, const uint8_t** lineptr);
|
||||||
|
|
||||||
// The associated handler to call processing functions in
|
// The associated handler to call processing functions in
|
||||||
LineInfoHandler* handler_;
|
LineInfoHandler* handler_;
|
||||||
|
|
||||||
// The associated ByteReader that handles endianness issues for us
|
// The associated ByteReader that handles endianness issues for us
|
||||||
ByteReader* reader_;
|
ByteReader* reader_;
|
||||||
|
|
||||||
// A DWARF2/3 line info header. This is not the same size as
|
// A DWARF line info header. This is not the same size as in the actual file,
|
||||||
// in the actual file, as the one in the file may have a 32 bit or
|
// as the one in the file may have a 32 bit or 64 bit lengths
|
||||||
// 64 bit lengths
|
|
||||||
|
|
||||||
struct LineInfoHeader header_;
|
struct LineInfoHeader header_;
|
||||||
|
|
||||||
@ -156,6 +175,13 @@ class LineInfo {
|
|||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
uint64_t buffer_length_;
|
uint64_t buffer_length_;
|
||||||
#endif
|
#endif
|
||||||
|
// Convenience pointers into .debug_str and .debug_line_str. These exactly
|
||||||
|
// correspond to those in the compilation unit.
|
||||||
|
const uint8_t* string_buffer_;
|
||||||
|
uint64_t string_buffer_length_;
|
||||||
|
const uint8_t* line_string_buffer_;
|
||||||
|
uint64_t line_string_buffer_length_;
|
||||||
|
|
||||||
const uint8_t *after_header_;
|
const uint8_t *after_header_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
187
src/common/dwarf/dwarf2reader_lineinfo_unittest.cc
Normal file
187
src/common/dwarf/dwarf2reader_lineinfo_unittest.cc
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
// Copyright (c) 2020, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// Original author: Sterling Augustine <saugustine@google.com>
|
||||||
|
|
||||||
|
// dwarf2reader_lineinfo_unittest.cc: Unit tests for dwarf2reader::LineInfo
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "breakpad_googletest_includes.h"
|
||||||
|
#include "common/dwarf/bytereader.h"
|
||||||
|
#include "common/dwarf/dwarf2reader.h"
|
||||||
|
#include "google_breakpad/common/breakpad_types.h"
|
||||||
|
|
||||||
|
using std::vector;
|
||||||
|
using testing::InSequence;
|
||||||
|
using testing::Return;
|
||||||
|
using testing::Sequence;
|
||||||
|
using testing::Test;
|
||||||
|
using testing::_;
|
||||||
|
|
||||||
|
using namespace dwarf2reader;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const uint8_t dwarf5_line_program[] = {
|
||||||
|
0x40, 0x0, 0x0, 0x0, // unit_length (end - begin)
|
||||||
|
// begin
|
||||||
|
0x05, 0x0, // version
|
||||||
|
0x8, // address_size
|
||||||
|
0x0, // segment_selector_size
|
||||||
|
0x26, 0x0, 0x0, 0x0, // header_length (end_header_end - begin_header)
|
||||||
|
// begin_header:
|
||||||
|
0x1, // minimum_instruction_length
|
||||||
|
0x1, // maximum_operations_per_instruction
|
||||||
|
0x1, // default_is_stmt
|
||||||
|
0xfb, // line_base
|
||||||
|
0xe, // line_range
|
||||||
|
0xd, // opcode_base and lengths
|
||||||
|
0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1,
|
||||||
|
0x1, // directory entry format count
|
||||||
|
DW_LNCT_path, DW_FORM_strp,
|
||||||
|
0x1, // directories count
|
||||||
|
0x1, 0x0, 0x0, 0x0, // offset into .debug_line_str
|
||||||
|
0x2, // file_name_entry_format_count
|
||||||
|
DW_LNCT_directory_index, DW_FORM_data1,
|
||||||
|
DW_LNCT_path, DW_FORM_line_strp,
|
||||||
|
0x1, // filename count
|
||||||
|
0x0, // directory index
|
||||||
|
0x1, 0x0, 0x0, 0x0, // offset into .debug_str
|
||||||
|
// end_header
|
||||||
|
DW_LNS_set_file, 0x0,
|
||||||
|
// set address to 0x0
|
||||||
|
0x0, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
// Advance Address by 0 and line by 3
|
||||||
|
0x15,
|
||||||
|
// Advance PC by 1
|
||||||
|
0x2, 0x1,
|
||||||
|
0x0,
|
||||||
|
DW_LNE_end_sequence,
|
||||||
|
DW_LNE_end_sequence,
|
||||||
|
// end
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t dwarf4_line_program[] = {
|
||||||
|
0x37, 0x0, 0x0, 0x0, // unit_length (end - begin)
|
||||||
|
// begin
|
||||||
|
0x04, 0x0, // version
|
||||||
|
0x1d, 0x0, 0x0, 0x0, // header_length (end_header - begin_header)
|
||||||
|
// begin_header:
|
||||||
|
0x1, // minimum_instruction_length
|
||||||
|
0x1, // maximum_operations_per_instruction
|
||||||
|
0x1, // default_is_stmt
|
||||||
|
0xfb, // line_base
|
||||||
|
0xe, // line_range
|
||||||
|
0xd, // opcode_base and lengths
|
||||||
|
0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1,
|
||||||
|
'/', 'a', '\0', // directory entry 1 (zeroth entry implied)
|
||||||
|
'\0', // end of directory table
|
||||||
|
'b', '/', 'c', '\0', // file entry 1 (zeroth entry implied)
|
||||||
|
0, // file 1 directory
|
||||||
|
0, // file 1 modification time
|
||||||
|
0, // file 1 length
|
||||||
|
'\0', // end of file table
|
||||||
|
// end_header
|
||||||
|
DW_LNS_set_file, 0x0,
|
||||||
|
// set address to 0x0
|
||||||
|
0x0, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
// Advance Address by 0 and line by 3
|
||||||
|
0x15,
|
||||||
|
// Advance PC by 1
|
||||||
|
0x2, 0x1,
|
||||||
|
0x0,
|
||||||
|
DW_LNE_end_sequence,
|
||||||
|
DW_LNE_end_sequence,
|
||||||
|
// end
|
||||||
|
};
|
||||||
|
|
||||||
|
class MockLineInfoHandler: public LineInfoHandler {
|
||||||
|
public:
|
||||||
|
MOCK_METHOD(void, DefineDir, (const string&, uint32_t dir_num), (override));
|
||||||
|
MOCK_METHOD(void, DefineFile, (const string& name, int32_t file_num,
|
||||||
|
uint32_t dir_num, uint64_t mod_time,
|
||||||
|
uint64_t length), (override));
|
||||||
|
MOCK_METHOD(void, AddLine, (uint64_t address, uint64_t length,
|
||||||
|
uint32_t file_num, uint32_t line_num,
|
||||||
|
uint32_t column_num), (override));
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t string_section[] = {'x', '/', 'a', '\0'};
|
||||||
|
const uint8_t line_string_section[] = {'x', 'b', '/', 'c', '\0' };
|
||||||
|
|
||||||
|
struct LineProgram: public Test {
|
||||||
|
MockLineInfoHandler handler_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(LineProgram, ReadLinesDwarf5) {
|
||||||
|
ByteReader byte_reader(ENDIANNESS_LITTLE);
|
||||||
|
// LineTables don't specify the offset size like Compilation Units do.
|
||||||
|
byte_reader.SetOffsetSize(4);
|
||||||
|
LineInfo line_reader(dwarf5_line_program,
|
||||||
|
sizeof(dwarf5_line_program),
|
||||||
|
&byte_reader,
|
||||||
|
string_section,
|
||||||
|
sizeof(string_section),
|
||||||
|
line_string_section,
|
||||||
|
sizeof(line_string_section),
|
||||||
|
&handler_);
|
||||||
|
EXPECT_CALL(handler_, DefineDir("/a", 0)).Times(1);
|
||||||
|
EXPECT_CALL(handler_, DefineFile("b/c", 0, 0, 0, 0)).Times(1);
|
||||||
|
EXPECT_CALL(handler_, AddLine(0, 1, 0, 4, 0)).Times(1);
|
||||||
|
EXPECT_EQ(line_reader.Start(), sizeof(dwarf5_line_program));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LineProgram, ReadLinesDwarf4) {
|
||||||
|
ByteReader byte_reader(ENDIANNESS_LITTLE);
|
||||||
|
// LineTables don't specify the offset size like Compilation Units do.
|
||||||
|
byte_reader.SetOffsetSize(4);
|
||||||
|
// dwarf4 line info headers don't encode the address size.
|
||||||
|
byte_reader.SetAddressSize(8);
|
||||||
|
LineInfo line_reader(dwarf4_line_program,
|
||||||
|
sizeof(dwarf5_line_program),
|
||||||
|
&byte_reader,
|
||||||
|
// dwarf4 line tables can't access the string sections
|
||||||
|
// so pass values likely to make assertions fail if
|
||||||
|
// the code uses them improperly.
|
||||||
|
nullptr, 0, nullptr, 0,
|
||||||
|
&handler_);
|
||||||
|
EXPECT_CALL(handler_, DefineDir("", 0)).Times(1);
|
||||||
|
EXPECT_CALL(handler_, DefineDir("/a", 1)).Times(1);
|
||||||
|
EXPECT_CALL(handler_, DefineFile("", 0, 0, 0, 0)).Times(1);
|
||||||
|
EXPECT_CALL(handler_, DefineFile("b/c", 1, 0, 0, 0)).Times(1);
|
||||||
|
EXPECT_CALL(handler_, AddLine(0, 1, 0, 4, 0)).Times(1);
|
||||||
|
EXPECT_EQ(line_reader.Start(), sizeof(dwarf4_line_program));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
@ -51,15 +51,9 @@ CULineInfoHandler::CULineInfoHandler(std::vector<SourceFileInfo>* files,
|
|||||||
LineMap* linemap):linemap_(linemap),
|
LineMap* linemap):linemap_(linemap),
|
||||||
files_(files),
|
files_(files),
|
||||||
dirs_(dirs) {
|
dirs_(dirs) {
|
||||||
// The dirs and files are 1 indexed, so just make sure we put
|
// In dwarf4, the dirs and files are 1 indexed, and in dwarf5 they are zero
|
||||||
// nothing in the 0 vector.
|
// indexed. This is handled in the LineInfo reader, so empty files are not
|
||||||
assert(dirs->size() == 0);
|
// needed here.
|
||||||
assert(files->size() == 0);
|
|
||||||
dirs->push_back("");
|
|
||||||
SourceFileInfo s;
|
|
||||||
s.name = "";
|
|
||||||
s.lowpc = ULLONG_MAX;
|
|
||||||
files->push_back(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CULineInfoHandler::DefineDir(const string& name, uint32_t dir_num) {
|
void CULineInfoHandler::DefineDir(const string& name, uint32_t dir_num) {
|
||||||
|
@ -940,14 +940,35 @@ void DwarfCUToModule::ReadSourceLines(uint64_t offset) {
|
|||||||
cu_context_->reporter->MissingSection(".debug_line");
|
cu_context_->reporter->MissingSection(".debug_line");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const uint8_t *section_start = map_entry->second.first;
|
const uint8_t* line_section_start = map_entry->second.first + offset;
|
||||||
uint64_t section_length = map_entry->second.second;
|
uint64_t line_section_length = map_entry->second.second;
|
||||||
if (offset >= section_length) {
|
if (offset >= line_section_length) {
|
||||||
cu_context_->reporter->BadLineInfoOffset(offset);
|
cu_context_->reporter->BadLineInfoOffset(offset);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
line_reader_->ReadProgram(section_start + offset, section_length - offset,
|
line_section_length -= offset;
|
||||||
cu_context_->file_context->module_, &lines_);
|
// When reading line tables, string sections are never needed for dwarf4, and
|
||||||
|
// may or may not be needed by dwarf5, so no error if they are missing.
|
||||||
|
const uint8_t* string_section_start = nullptr;
|
||||||
|
uint64_t string_section_length = 0;
|
||||||
|
map_entry = dwarf2reader::GetSectionByName(section_map, ".debug_str");
|
||||||
|
if (map_entry != section_map.end()) {
|
||||||
|
string_section_start = map_entry->second.first + offset;
|
||||||
|
string_section_length = map_entry->second.second - offset;
|
||||||
|
}
|
||||||
|
const uint8_t* line_string_section_start = nullptr;
|
||||||
|
uint64_t line_string_section_length = 0;
|
||||||
|
map_entry = dwarf2reader::GetSectionByName(section_map, ".debug_line_str");
|
||||||
|
if (map_entry != section_map.end()) {
|
||||||
|
line_string_section_start = map_entry->second.first + offset;
|
||||||
|
line_string_section_length = map_entry->second.second - offset;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
line_reader_->ReadProgram(
|
||||||
|
line_section_start, line_section_length,
|
||||||
|
string_section_start, string_section_length,
|
||||||
|
line_string_section_start, line_string_section_length,
|
||||||
|
cu_context_->file_context->module_, &lines_);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -158,7 +158,11 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
|
|||||||
// mappings, given a pointer to some DWARF line number data
|
// mappings, given a pointer to some DWARF line number data
|
||||||
// PROGRAM, and an overestimate of its size. Add no zero-length
|
// PROGRAM, and an overestimate of its size. Add no zero-length
|
||||||
// lines to LINES.
|
// lines to LINES.
|
||||||
virtual void ReadProgram(const uint8_t *program, uint64_t length,
|
virtual void ReadProgram(const uint8_t* program, uint64_t length,
|
||||||
|
const uint8_t* string_section,
|
||||||
|
uint64_t string_section_length,
|
||||||
|
const uint8_t* line_string_section,
|
||||||
|
uint64_t line_string_length,
|
||||||
Module *module, vector<Module::Line> *lines) = 0;
|
Module *module, vector<Module::Line> *lines) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -67,8 +67,12 @@ using ::testing::ValuesIn;
|
|||||||
class MockLineToModuleHandler: public DwarfCUToModule::LineToModuleHandler {
|
class MockLineToModuleHandler: public DwarfCUToModule::LineToModuleHandler {
|
||||||
public:
|
public:
|
||||||
MOCK_METHOD1(StartCompilationUnit, void(const string& compilation_dir));
|
MOCK_METHOD1(StartCompilationUnit, void(const string& compilation_dir));
|
||||||
MOCK_METHOD4(ReadProgram, void(const uint8_t *program, uint64_t length,
|
MOCK_METHOD8(ReadProgram, void(const uint8_t* program, uint64_t length,
|
||||||
Module *module, vector<Module::Line> *lines));
|
const uint8_t* string_section,
|
||||||
|
uint64_t string_section_length,
|
||||||
|
const uint8_t* line_string_section,
|
||||||
|
uint64_t line_string_section_length,
|
||||||
|
Module* module, vector<Module::Line>* lines));
|
||||||
};
|
};
|
||||||
|
|
||||||
class MockWarningReporter: public DwarfCUToModule::WarningReporter {
|
class MockWarningReporter: public DwarfCUToModule::WarningReporter {
|
||||||
@ -113,8 +117,12 @@ class CUFixtureBase {
|
|||||||
public:
|
public:
|
||||||
explicit AppendLinesFunctor(
|
explicit AppendLinesFunctor(
|
||||||
const vector<Module::Line> *lines) : lines_(lines) { }
|
const vector<Module::Line> *lines) : lines_(lines) { }
|
||||||
void operator()(const uint8_t *program, uint64_t length,
|
void operator()(const uint8_t* program, uint64_t length,
|
||||||
Module *module, vector<Module::Line> *lines) {
|
const uint8_t* string_section,
|
||||||
|
uint64_t string_section_length,
|
||||||
|
const uint8_t* line_string_section,
|
||||||
|
uint64_t line_string_section_length,
|
||||||
|
Module *module, vector<Module::Line>* lines) {
|
||||||
lines->insert(lines->end(), lines_->begin(), lines_->end());
|
lines->insert(lines->end(), lines_->begin(), lines_->end());
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
@ -147,7 +155,7 @@ class CUFixtureBase {
|
|||||||
// By default, expect the line program reader not to be invoked. We
|
// By default, expect the line program reader not to be invoked. We
|
||||||
// may override this in StartCU.
|
// may override this in StartCU.
|
||||||
EXPECT_CALL(line_reader_, StartCompilationUnit(_)).Times(0);
|
EXPECT_CALL(line_reader_, StartCompilationUnit(_)).Times(0);
|
||||||
EXPECT_CALL(line_reader_, ReadProgram(_,_,_,_)).Times(0);
|
EXPECT_CALL(line_reader_, ReadProgram(_,_,_,_,_,_,_,_)).Times(0);
|
||||||
|
|
||||||
// The handler will consult this section map to decide what to
|
// The handler will consult this section map to decide what to
|
||||||
// pass to our line reader.
|
// pass to our line reader.
|
||||||
@ -332,10 +340,10 @@ void CUFixtureBase::StartCU() {
|
|||||||
if (!lines_.empty())
|
if (!lines_.empty())
|
||||||
EXPECT_CALL(line_reader_,
|
EXPECT_CALL(line_reader_,
|
||||||
ReadProgram(&dummy_line_program_[0], dummy_line_size_,
|
ReadProgram(&dummy_line_program_[0], dummy_line_size_,
|
||||||
|
_,_,_,_,
|
||||||
&module_, _))
|
&module_, _))
|
||||||
.Times(AtMost(1))
|
.Times(AtMost(1))
|
||||||
.WillOnce(DoAll(Invoke(appender_), Return()));
|
.WillOnce(DoAll(Invoke(appender_), Return()));
|
||||||
|
|
||||||
ASSERT_TRUE(root_handler_
|
ASSERT_TRUE(root_handler_
|
||||||
.StartCompilationUnit(0x51182ec307610b51ULL, 0x81, 0x44,
|
.StartCompilationUnit(0x51182ec307610b51ULL, 0x81, 0x44,
|
||||||
0x4241b4f33720dd5cULL, 3));
|
0x4241b4f33720dd5cULL, 3));
|
||||||
@ -1509,7 +1517,7 @@ TEST_F(Specifications, InterCU) {
|
|||||||
DwarfCUToModule::FileContext fc("dwarf-filename", &m, true);
|
DwarfCUToModule::FileContext fc("dwarf-filename", &m, true);
|
||||||
EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return());
|
EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return());
|
||||||
MockLineToModuleHandler lr;
|
MockLineToModuleHandler lr;
|
||||||
EXPECT_CALL(lr, ReadProgram(_,_,_,_)).Times(0);
|
EXPECT_CALL(lr, ReadProgram(_,_,_,_,_,_,_,_)).Times(0);
|
||||||
|
|
||||||
// Kludge: satisfy reporter_'s expectation.
|
// Kludge: satisfy reporter_'s expectation.
|
||||||
reporter_.SetCUName("compilation-unit-name");
|
reporter_.SetCUName("compilation-unit-name");
|
||||||
@ -1568,7 +1576,7 @@ TEST_F(Specifications, UnhandledInterCU) {
|
|||||||
DwarfCUToModule::FileContext fc("dwarf-filename", &m, false);
|
DwarfCUToModule::FileContext fc("dwarf-filename", &m, false);
|
||||||
EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return());
|
EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return());
|
||||||
MockLineToModuleHandler lr;
|
MockLineToModuleHandler lr;
|
||||||
EXPECT_CALL(lr, ReadProgram(_,_,_,_)).Times(0);
|
EXPECT_CALL(lr, ReadProgram(_,_,_,_,_,_,_,_)).Times(0);
|
||||||
|
|
||||||
// Kludge: satisfy reporter_'s expectation.
|
// Kludge: satisfy reporter_'s expectation.
|
||||||
reporter_.SetCUName("compilation-unit-name");
|
reporter_.SetCUName("compilation-unit-name");
|
||||||
|
@ -262,10 +262,18 @@ class DumperLineToModule: public DwarfCUToModule::LineToModuleHandler {
|
|||||||
void StartCompilationUnit(const string& compilation_dir) {
|
void StartCompilationUnit(const string& compilation_dir) {
|
||||||
compilation_dir_ = compilation_dir;
|
compilation_dir_ = compilation_dir;
|
||||||
}
|
}
|
||||||
void ReadProgram(const uint8_t *program, uint64_t length,
|
void ReadProgram(const uint8_t* program, uint64_t length,
|
||||||
|
const uint8_t* string_section,
|
||||||
|
uint64_t string_section_length,
|
||||||
|
const uint8_t* line_string_section,
|
||||||
|
uint64_t line_string_section_length,
|
||||||
Module* module, std::vector<Module::Line>* lines) {
|
Module* module, std::vector<Module::Line>* lines) {
|
||||||
DwarfLineToModule handler(module, compilation_dir_, lines);
|
DwarfLineToModule handler(module, compilation_dir_, lines);
|
||||||
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
|
dwarf2reader::LineInfo parser(program, length, byte_reader_,
|
||||||
|
string_section, string_section_length,
|
||||||
|
line_string_section,
|
||||||
|
line_string_section_length,
|
||||||
|
&handler);
|
||||||
parser.Start();
|
parser.Start();
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
|
@ -344,10 +344,15 @@ class DumpSymbols::DumperLineToModule:
|
|||||||
compilation_dir_ = compilation_dir;
|
compilation_dir_ = compilation_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadProgram(const uint8_t *program, uint64_t length,
|
void ReadProgram(const uint8_t* program, uint64_t length,
|
||||||
Module *module, vector<Module::Line> *lines) {
|
const uint8_t* string_section,
|
||||||
|
uint64_t string_section_length,
|
||||||
|
const uint8_t* line_string_section,
|
||||||
|
uint64_t line_string_section_length,
|
||||||
|
Module* module, vector<Module::Line>* lines) {
|
||||||
DwarfLineToModule handler(module, compilation_dir_, lines);
|
DwarfLineToModule handler(module, compilation_dir_, lines);
|
||||||
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
|
dwarf2reader::LineInfo parser(program, length, byte_reader_,
|
||||||
|
nullptr, 0, nullptr, 0, &handler);
|
||||||
parser.Start();
|
parser.Start();
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
|
Loading…
Reference in New Issue
Block a user