mirror of
https://git.suyu.dev/suyu/breakpad.git
synced 2025-12-24 08:15:01 +01:00
If there is an address index reference to .debug_addr section before DW_AT_addr_base, addr_base_ will be 0. It will retrieve wrong address. An example could be DW_AT_loc_pc occurs before DW_AT_addr_base. Change-Id: Id2b337f5235470cc9beaf05a62efebbde797dacf Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/2900806 Reviewed-by: Joshua Peraza <jperaza@chromium.org>
1494 lines
62 KiB
C++
1494 lines
62 KiB
C++
// -*- mode: C++ -*-
|
|
|
|
// Copyright (c) 2010 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.
|
|
|
|
// CFI reader author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
|
|
|
// This file contains definitions related to the DWARF2/3 reader and
|
|
// it's handler interfaces.
|
|
// The DWARF2/3 specification can be found at
|
|
// http://dwarf.freestandards.org and should be considered required
|
|
// reading if you wish to modify the implementation.
|
|
// Only a cursory attempt is made to explain terminology that is
|
|
// used here, as it is much better explained in the standard documents
|
|
#ifndef COMMON_DWARF_DWARF2READER_H__
|
|
#define COMMON_DWARF_DWARF2READER_H__
|
|
|
|
#include <assert.h>
|
|
#include <stdint.h>
|
|
|
|
#include <list>
|
|
#include <map>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
#include <memory>
|
|
|
|
#include "common/dwarf/bytereader.h"
|
|
#include "common/dwarf/dwarf2enums.h"
|
|
#include "common/dwarf/types.h"
|
|
#include "common/using_std_string.h"
|
|
#include "common/dwarf/elf_reader.h"
|
|
|
|
namespace dwarf2reader {
|
|
struct LineStateMachine;
|
|
class Dwarf2Handler;
|
|
class LineInfoHandler;
|
|
class DwpReader;
|
|
|
|
// This maps from a string naming a section to a pair containing a
|
|
// the data for the section, and the size of the section.
|
|
typedef std::map<string, std::pair<const uint8_t*, uint64_t> > SectionMap;
|
|
|
|
// Abstract away the difference between elf and mach-o section names.
|
|
// Elf-names use ".section_name, mach-o uses "__section_name". Pass "name" in
|
|
// the elf form, ".section_name".
|
|
const SectionMap::const_iterator GetSectionByName(const SectionMap&
|
|
sections, const char* name);
|
|
|
|
// Most of the time, this struct functions as a simple attribute and form pair.
|
|
// However, Dwarf5 DW_FORM_implicit_const means that a form may have its value
|
|
// in line in the abbrev table, and that value must be associated with the
|
|
// pair until the attr's value is needed.
|
|
struct AttrForm {
|
|
AttrForm(enum DwarfAttribute attr, enum DwarfForm form, uint64_t value) :
|
|
attr_(attr), form_(form), value_(value) { }
|
|
|
|
enum DwarfAttribute attr_;
|
|
enum DwarfForm form_;
|
|
uint64_t value_;
|
|
};
|
|
typedef std::list<AttrForm> AttributeList;
|
|
typedef AttributeList::iterator AttributeIterator;
|
|
typedef AttributeList::const_iterator ConstAttributeIterator;
|
|
|
|
struct LineInfoHeader {
|
|
uint64_t total_length;
|
|
uint16_t version;
|
|
uint64_t prologue_length;
|
|
uint8_t min_insn_length; // insn stands for instructin
|
|
bool default_is_stmt; // stmt stands for statement
|
|
int8_t line_base;
|
|
uint8_t line_range;
|
|
uint8_t opcode_base;
|
|
// Use a pointer so that signalsafe_addr2line is able to use this structure
|
|
// without heap allocation problem.
|
|
std::vector<unsigned char>* std_opcode_lengths;
|
|
};
|
|
|
|
class LineInfo {
|
|
public:
|
|
|
|
// Initializes a .debug_line reader. Buffer and buffer length point
|
|
// to the beginning and length of the line information to read.
|
|
// Reader is a ByteReader class that has the endianness set
|
|
// properly.
|
|
LineInfo(const uint8_t* buffer, uint64_t buffer_length,
|
|
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() {
|
|
if (header_.std_opcode_lengths) {
|
|
delete header_.std_opcode_lengths;
|
|
}
|
|
}
|
|
|
|
// Start processing line info, and calling callbacks in the handler.
|
|
// Consumes the line number information for a single compilation unit.
|
|
// Returns the number of bytes processed.
|
|
uint64_t Start();
|
|
|
|
// Process a single line info opcode at START using the state
|
|
// machine at LSM. Return true if we should define a line using the
|
|
// current state of the line state machine. Place the length of the
|
|
// opcode in LEN.
|
|
// If LSM_PASSES_PC is non-NULL, this function also checks if the lsm
|
|
// passes the address of PC. In other words, LSM_PASSES_PC will be
|
|
// set to true, if the following condition is met.
|
|
//
|
|
// lsm's old address < PC <= lsm's new address
|
|
static bool ProcessOneOpcode(ByteReader* reader,
|
|
LineInfoHandler* handler,
|
|
const struct LineInfoHeader& header,
|
|
const uint8_t* start,
|
|
struct LineStateMachine* lsm,
|
|
size_t* len,
|
|
uintptr pc,
|
|
bool* lsm_passes_pc);
|
|
|
|
private:
|
|
// Reads the DWARF2/3 header for this line info.
|
|
void ReadHeader();
|
|
|
|
// Reads the DWARF2/3 line information
|
|
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
|
|
LineInfoHandler* handler_;
|
|
|
|
// The associated ByteReader that handles endianness issues for us
|
|
ByteReader* reader_;
|
|
|
|
// A DWARF line info header. This is not the same size as in the actual file,
|
|
// as the one in the file may have a 32 bit or 64 bit lengths
|
|
|
|
struct LineInfoHeader header_;
|
|
|
|
// buffer is the buffer for our line info, starting at exactly where
|
|
// the line info to read is. after_header is the place right after
|
|
// the end of the line information header.
|
|
const uint8_t* buffer_;
|
|
#ifndef NDEBUG
|
|
uint64_t buffer_length_;
|
|
#endif
|
|
// Convenience pointers into .debug_str and .debug_line_str. These exactly
|
|
// correspond to those in the compilation unit.
|
|
const uint8_t* string_buffer_;
|
|
#ifndef NDEBUG
|
|
uint64_t string_buffer_length_;
|
|
#endif
|
|
const uint8_t* line_string_buffer_;
|
|
#ifndef NDEBUG
|
|
uint64_t line_string_buffer_length_;
|
|
#endif
|
|
|
|
const uint8_t* after_header_;
|
|
};
|
|
|
|
// This class is the main interface between the line info reader and
|
|
// the client. The virtual functions inside this get called for
|
|
// interesting events that happen during line info reading. The
|
|
// default implementation does nothing
|
|
|
|
class LineInfoHandler {
|
|
public:
|
|
LineInfoHandler() { }
|
|
|
|
virtual ~LineInfoHandler() { }
|
|
|
|
// Called when we define a directory. NAME is the directory name,
|
|
// DIR_NUM is the directory number
|
|
virtual void DefineDir(const string& name, uint32_t dir_num) { }
|
|
|
|
// Called when we define a filename. NAME is the filename, FILE_NUM
|
|
// is the file number which is -1 if the file index is the next
|
|
// index after the last numbered index (this happens when files are
|
|
// dynamically defined by the line program), DIR_NUM is the
|
|
// directory index for the directory name of this file, MOD_TIME is
|
|
// the modification time of the file, and LENGTH is the length of
|
|
// the file
|
|
virtual void DefineFile(const string& name, int32_t file_num,
|
|
uint32_t dir_num, uint64_t mod_time,
|
|
uint64_t length) { }
|
|
|
|
// Called when the line info reader has a new line, address pair
|
|
// ready for us. ADDRESS is the address of the code, LENGTH is the
|
|
// length of its machine code in bytes, FILE_NUM is the file number
|
|
// containing the code, LINE_NUM is the line number in that file for
|
|
// the code, and COLUMN_NUM is the column number the code starts at,
|
|
// if we know it (0 otherwise).
|
|
virtual void AddLine(uint64_t address, uint64_t length,
|
|
uint32_t file_num, uint32_t line_num, uint32_t column_num) { }
|
|
};
|
|
|
|
class RangeListHandler {
|
|
public:
|
|
RangeListHandler() { }
|
|
|
|
virtual ~RangeListHandler() { }
|
|
|
|
// Add a range.
|
|
virtual void AddRange(uint64_t begin, uint64_t end) { };
|
|
|
|
// Finish processing the range list.
|
|
virtual void Finish() { };
|
|
};
|
|
|
|
class RangeListReader {
|
|
public:
|
|
// Reading a range list requires quite a bit of information
|
|
// from the compilation unit. Package it conveniently.
|
|
struct CURangesInfo {
|
|
CURangesInfo() :
|
|
version_(0), base_address_(0), ranges_base_(0),
|
|
buffer_(nullptr), size_(0), addr_buffer_(nullptr),
|
|
addr_buffer_size_(0), addr_base_(0) { }
|
|
|
|
uint16_t version_;
|
|
// Ranges base address. Ordinarily the CU's low_pc.
|
|
uint64_t base_address_;
|
|
// Offset into .debug_rnglists for this CU's rangelists.
|
|
uint64_t ranges_base_;
|
|
// Contents of either .debug_ranges or .debug_rnglists.
|
|
const uint8_t* buffer_;
|
|
uint64_t size_;
|
|
// Contents of .debug_addr. This cu's contribution starts at
|
|
// addr_base_
|
|
const uint8_t* addr_buffer_;
|
|
uint64_t addr_buffer_size_;
|
|
uint64_t addr_base_;
|
|
};
|
|
|
|
RangeListReader(ByteReader* reader, CURangesInfo* cu_info,
|
|
RangeListHandler* handler) :
|
|
reader_(reader), cu_info_(cu_info), handler_(handler),
|
|
offset_array_(0) { }
|
|
|
|
// Read ranges from cu_info as specified by form and data.
|
|
bool ReadRanges(enum DwarfForm form, uint64_t data);
|
|
|
|
private:
|
|
// Read dwarf4 .debug_ranges at offset.
|
|
bool ReadDebugRanges(uint64_t offset);
|
|
// Read dwarf5 .debug_rngslist at offset.
|
|
bool ReadDebugRngList(uint64_t offset);
|
|
|
|
// Convenience functions to handle the mechanics of reading entries in the
|
|
// ranges section.
|
|
uint64_t ReadULEB(uint64_t offset, uint64_t* value) {
|
|
size_t len;
|
|
*value = reader_->ReadUnsignedLEB128(cu_info_->buffer_ + offset, &len);
|
|
return len;
|
|
}
|
|
|
|
uint64_t ReadAddress(uint64_t offset, uint64_t* value) {
|
|
*value = reader_->ReadAddress(cu_info_->buffer_ + offset);
|
|
return reader_->AddressSize();
|
|
}
|
|
|
|
// Read the address at this CU's addr_index in the .debug_addr section.
|
|
uint64_t GetAddressAtIndex(uint64_t addr_index) {
|
|
assert(cu_info_->addr_buffer_ != nullptr);
|
|
uint64_t offset =
|
|
cu_info_->addr_base_ + addr_index * reader_->AddressSize();
|
|
assert(offset < cu_info_->addr_buffer_size_);
|
|
return reader_->ReadAddress(cu_info_->addr_buffer_ + offset);
|
|
}
|
|
|
|
ByteReader* reader_;
|
|
CURangesInfo* cu_info_;
|
|
RangeListHandler* handler_;
|
|
uint64_t offset_array_;
|
|
};
|
|
|
|
// This class is the main interface between the reader and the
|
|
// client. The virtual functions inside this get called for
|
|
// interesting events that happen during DWARF2 reading.
|
|
// The default implementation skips everything.
|
|
class Dwarf2Handler {
|
|
public:
|
|
Dwarf2Handler() { }
|
|
|
|
virtual ~Dwarf2Handler() { }
|
|
|
|
// Start to process a compilation unit at OFFSET from the beginning of the
|
|
// .debug_info section. Return false if you would like to skip this
|
|
// compilation unit.
|
|
virtual bool StartCompilationUnit(uint64_t offset, uint8_t address_size,
|
|
uint8_t offset_size, uint64_t cu_length,
|
|
uint8_t dwarf_version) { return false; }
|
|
|
|
// When processing a skeleton compilation unit, resulting from a split
|
|
// DWARF compilation, once the skeleton debug info has been read,
|
|
// the reader will call this function to ask the client if it needs
|
|
// the full debug info from the .dwo or .dwp file. Return true if
|
|
// you need it, or false to skip processing the split debug info.
|
|
virtual bool NeedSplitDebugInfo() { return true; }
|
|
|
|
// Start to process a split compilation unit at OFFSET from the beginning of
|
|
// the debug_info section in the .dwp/.dwo file. Return false if you would
|
|
// like to skip this compilation unit.
|
|
virtual bool StartSplitCompilationUnit(uint64_t offset,
|
|
uint64_t cu_length) { return false; }
|
|
|
|
// Start to process a DIE at OFFSET from the beginning of the .debug_info
|
|
// section. Return false if you would like to skip this DIE.
|
|
virtual bool StartDIE(uint64_t offset, enum DwarfTag tag) { return false; }
|
|
|
|
// Called when we have an attribute with unsigned data to give to our
|
|
// handler. The attribute is for the DIE at OFFSET from the beginning of the
|
|
// .debug_info section. Its name is ATTR, its form is FORM, and its value is
|
|
// DATA.
|
|
virtual void ProcessAttributeUnsigned(uint64_t offset,
|
|
enum DwarfAttribute attr,
|
|
enum DwarfForm form,
|
|
uint64_t data) { }
|
|
|
|
// Called when we have an attribute with signed data to give to our handler.
|
|
// The attribute is for the DIE at OFFSET from the beginning of the
|
|
// .debug_info section. Its name is ATTR, its form is FORM, and its value is
|
|
// DATA.
|
|
virtual void ProcessAttributeSigned(uint64_t offset,
|
|
enum DwarfAttribute attr,
|
|
enum DwarfForm form,
|
|
int64_t data) { }
|
|
|
|
// Called when we have an attribute whose value is a reference to
|
|
// another DIE. The attribute belongs to the DIE at OFFSET from the
|
|
// beginning of the .debug_info section. Its name is ATTR, its form
|
|
// is FORM, and the offset of the DIE being referred to from the
|
|
// beginning of the .debug_info section is DATA.
|
|
virtual void ProcessAttributeReference(uint64_t offset,
|
|
enum DwarfAttribute attr,
|
|
enum DwarfForm form,
|
|
uint64_t data) { }
|
|
|
|
// Called when we have an attribute with a buffer of data to give to our
|
|
// handler. The attribute is for the DIE at OFFSET from the beginning of the
|
|
// .debug_info section. Its name is ATTR, its form is FORM, DATA points to
|
|
// the buffer's contents, and its length in bytes is LENGTH. The buffer is
|
|
// owned by the caller, not the callee, and may not persist for very long.
|
|
// If you want the data to be available later, it needs to be copied.
|
|
virtual void ProcessAttributeBuffer(uint64_t offset,
|
|
enum DwarfAttribute attr,
|
|
enum DwarfForm form,
|
|
const uint8_t* data,
|
|
uint64_t len) { }
|
|
|
|
// Called when we have an attribute with string data to give to our handler.
|
|
// The attribute is for the DIE at OFFSET from the beginning of the
|
|
// .debug_info section. Its name is ATTR, its form is FORM, and its value is
|
|
// DATA.
|
|
virtual void ProcessAttributeString(uint64_t offset,
|
|
enum DwarfAttribute attr,
|
|
enum DwarfForm form,
|
|
const string& data) { }
|
|
|
|
// Called when we have an attribute whose value is the 64-bit signature
|
|
// of a type unit in the .debug_types section. OFFSET is the offset of
|
|
// the DIE whose attribute we're reporting. ATTR and FORM are the
|
|
// attribute's name and form. SIGNATURE is the type unit's signature.
|
|
virtual void ProcessAttributeSignature(uint64_t offset,
|
|
enum DwarfAttribute attr,
|
|
enum DwarfForm form,
|
|
uint64_t signature) { }
|
|
|
|
// Called when finished processing the DIE at OFFSET.
|
|
// Because DWARF2/3 specifies a tree of DIEs, you may get starts
|
|
// before ends of the previous DIE, as we process children before
|
|
// ending the parent.
|
|
virtual void EndDIE(uint64_t offset) { }
|
|
|
|
};
|
|
|
|
// The base of DWARF2/3 debug info is a DIE (Debugging Information
|
|
// Entry.
|
|
// DWARF groups DIE's into a tree and calls the root of this tree a
|
|
// "compilation unit". Most of the time, there is one compilation
|
|
// unit in the .debug_info section for each file that had debug info
|
|
// generated.
|
|
// Each DIE consists of
|
|
|
|
// 1. a tag specifying a thing that is being described (ie
|
|
// DW_TAG_subprogram for functions, DW_TAG_variable for variables, etc
|
|
// 2. attributes (such as DW_AT_location for location in memory,
|
|
// DW_AT_name for name), and data for each attribute.
|
|
// 3. A flag saying whether the DIE has children or not
|
|
|
|
// In order to gain some amount of compression, the format of
|
|
// each DIE (tag name, attributes and data forms for the attributes)
|
|
// are stored in a separate table called the "abbreviation table".
|
|
// This is done because a large number of DIEs have the exact same tag
|
|
// and list of attributes, but different data for those attributes.
|
|
// As a result, the .debug_info section is just a stream of data, and
|
|
// requires reading of the .debug_abbrev section to say what the data
|
|
// means.
|
|
|
|
// As a warning to the user, it should be noted that the reason for
|
|
// using absolute offsets from the beginning of .debug_info is that
|
|
// DWARF2/3 supports referencing DIE's from other DIE's by their offset
|
|
// from either the current compilation unit start, *or* the beginning
|
|
// of the .debug_info section. This means it is possible to reference
|
|
// a DIE in one compilation unit from a DIE in another compilation
|
|
// unit. This style of reference is usually used to eliminate
|
|
// duplicated information that occurs across compilation
|
|
// units, such as base types, etc. GCC 3.4+ support this with
|
|
// -feliminate-dwarf2-dups. Other toolchains will sometimes do
|
|
// duplicate elimination in the linker.
|
|
|
|
class CompilationUnit {
|
|
public:
|
|
|
|
// Initialize a compilation unit. This requires a map of sections,
|
|
// the offset of this compilation unit in the .debug_info section, a
|
|
// ByteReader, and a Dwarf2Handler class to call callbacks in.
|
|
CompilationUnit(const string& path, const SectionMap& sections,
|
|
uint64_t offset, ByteReader* reader, Dwarf2Handler* handler);
|
|
virtual ~CompilationUnit() {
|
|
if (abbrevs_) delete abbrevs_;
|
|
}
|
|
|
|
// Initialize a compilation unit from a .dwo or .dwp file.
|
|
// In this case, we need the .debug_addr section from the
|
|
// executable file that contains the corresponding skeleton
|
|
// compilation unit. We also inherit the Dwarf2Handler from
|
|
// the executable file, and call it as if we were still
|
|
// processing the original compilation unit.
|
|
void SetSplitDwarf(const uint8_t* addr_buffer, uint64_t addr_buffer_length,
|
|
uint64_t addr_base, uint64_t ranges_base, uint64_t dwo_id);
|
|
|
|
// Begin reading a Dwarf2 compilation unit, and calling the
|
|
// callbacks in the Dwarf2Handler
|
|
|
|
// Return the full length of the compilation unit, including
|
|
// headers. This plus the starting offset passed to the constructor
|
|
// is the offset of the end of the compilation unit --- and the
|
|
// start of the next compilation unit, if there is one.
|
|
uint64_t Start();
|
|
|
|
private:
|
|
|
|
// This struct represents a single DWARF2/3 abbreviation
|
|
// The abbreviation tells how to read a DWARF2/3 DIE, and consist of a
|
|
// tag and a list of attributes, as well as the data form of each attribute.
|
|
struct Abbrev {
|
|
uint64_t number;
|
|
enum DwarfTag tag;
|
|
bool has_children;
|
|
AttributeList attributes;
|
|
};
|
|
|
|
// A DWARF2/3 compilation unit header. This is not the same size as
|
|
// in the actual file, as the one in the file may have a 32 bit or
|
|
// 64 bit length.
|
|
struct CompilationUnitHeader {
|
|
uint64_t length;
|
|
uint16_t version;
|
|
uint64_t abbrev_offset;
|
|
uint8_t address_size;
|
|
} header_;
|
|
|
|
// Reads the DWARF2/3 header for this compilation unit.
|
|
void ReadHeader();
|
|
|
|
// Reads the DWARF2/3 abbreviations for this compilation unit
|
|
void ReadAbbrevs();
|
|
|
|
// Read the abbreviation offset for this compilation unit
|
|
size_t ReadAbbrevOffset(const uint8_t* headerptr);
|
|
|
|
// Read the address size for this compilation unit
|
|
size_t ReadAddressSize(const uint8_t* headerptr);
|
|
|
|
// Read the DWO id from a split or skeleton compilation unit header
|
|
size_t ReadDwoId(const uint8_t* headerptr);
|
|
|
|
// Read the type signature from a type or split type compilation unit header
|
|
size_t ReadTypeSignature(const uint8_t* headerptr);
|
|
|
|
// Read the DWO id from a split or skeleton compilation unit header
|
|
size_t ReadTypeOffset(const uint8_t* headerptr);
|
|
|
|
// Processes a single DIE for this compilation unit and return a new
|
|
// pointer just past the end of it
|
|
const uint8_t* ProcessDIE(uint64_t dieoffset,
|
|
const uint8_t* start,
|
|
const Abbrev& abbrev);
|
|
|
|
// Processes a single attribute and return a new pointer just past the
|
|
// end of it
|
|
const uint8_t* ProcessAttribute(uint64_t dieoffset,
|
|
const uint8_t* start,
|
|
enum DwarfAttribute attr,
|
|
enum DwarfForm form,
|
|
uint64_t implicit_const);
|
|
|
|
// Special version of ProcessAttribute, for finding str_offsets_base and
|
|
// DW_AT_addr_base in DW_TAG_compile_unit, for DWARF v5.
|
|
const uint8_t* ProcessOffsetBaseAttribute(uint64_t dieoffset,
|
|
const uint8_t* start,
|
|
enum DwarfAttribute attr,
|
|
enum DwarfForm form,
|
|
uint64_t implicit_const);
|
|
|
|
// Called when we have an attribute with unsigned data to give to
|
|
// our handler. The attribute is for the DIE at OFFSET from the
|
|
// beginning of compilation unit, has a name of ATTR, a form of
|
|
// FORM, and the actual data of the attribute is in DATA.
|
|
// If we see a DW_AT_GNU_dwo_id attribute, save the value so that
|
|
// we can find the debug info in a .dwo or .dwp file.
|
|
void ProcessAttributeUnsigned(uint64_t offset,
|
|
enum DwarfAttribute attr,
|
|
enum DwarfForm form,
|
|
uint64_t data) {
|
|
if (attr == DW_AT_GNU_dwo_id) {
|
|
dwo_id_ = data;
|
|
}
|
|
else if (attr == DW_AT_GNU_addr_base || attr == DW_AT_addr_base) {
|
|
addr_base_ = data;
|
|
}
|
|
else if (attr == DW_AT_str_offsets_base) {
|
|
str_offsets_base_ = data;
|
|
}
|
|
else if (attr == DW_AT_GNU_ranges_base || attr == DW_AT_rnglists_base) {
|
|
ranges_base_ = data;
|
|
}
|
|
// TODO(yunlian): When we add DW_AT_ranges_base from DWARF-5,
|
|
// that base will apply to DW_AT_ranges attributes in the
|
|
// skeleton CU as well as in the .dwo/.dwp files.
|
|
else if (attr == DW_AT_ranges && is_split_dwarf_) {
|
|
data += ranges_base_;
|
|
}
|
|
handler_->ProcessAttributeUnsigned(offset, attr, form, data);
|
|
}
|
|
|
|
// Called when we have an attribute with signed data to give to
|
|
// our handler. The attribute is for the DIE at OFFSET from the
|
|
// beginning of compilation unit, has a name of ATTR, a form of
|
|
// FORM, and the actual data of the attribute is in DATA.
|
|
void ProcessAttributeSigned(uint64_t offset,
|
|
enum DwarfAttribute attr,
|
|
enum DwarfForm form,
|
|
int64_t data) {
|
|
handler_->ProcessAttributeSigned(offset, attr, form, data);
|
|
}
|
|
|
|
// Called when we have an attribute with a buffer of data to give to
|
|
// our handler. The attribute is for the DIE at OFFSET from the
|
|
// beginning of compilation unit, has a name of ATTR, a form of
|
|
// FORM, and the actual data of the attribute is in DATA, and the
|
|
// length of the buffer is LENGTH.
|
|
void ProcessAttributeBuffer(uint64_t offset,
|
|
enum DwarfAttribute attr,
|
|
enum DwarfForm form,
|
|
const uint8_t* data,
|
|
uint64_t len) {
|
|
handler_->ProcessAttributeBuffer(offset, attr, form, data, len);
|
|
}
|
|
|
|
// Handles the common parts of DW_FORM_GNU_str_index, DW_FORM_strx,
|
|
// DW_FORM_strx1, DW_FORM_strx2, DW_FORM_strx3, and DW_FORM_strx4.
|
|
// Retrieves the data and calls through to ProcessAttributeString.
|
|
void ProcessFormStringIndex(uint64_t offset,
|
|
enum DwarfAttribute attr,
|
|
enum DwarfForm form,
|
|
uint64_t str_index);
|
|
|
|
// Called when we have an attribute with string data to give to
|
|
// our handler. The attribute is for the DIE at OFFSET from the
|
|
// beginning of compilation unit, has a name of ATTR, a form of
|
|
// FORM, and the actual data of the attribute is in DATA.
|
|
// If we see a DW_AT_GNU_dwo_name attribute, save the value so
|
|
// that we can find the debug info in a .dwo or .dwp file.
|
|
void ProcessAttributeString(uint64_t offset,
|
|
enum DwarfAttribute attr,
|
|
enum DwarfForm form,
|
|
const char* data) {
|
|
if (attr == DW_AT_GNU_dwo_name || attr == DW_AT_dwo_name)
|
|
dwo_name_ = data;
|
|
handler_->ProcessAttributeString(offset, attr, form, data);
|
|
}
|
|
|
|
// Called to handle common portions of DW_FORM_addrx and variations, as well
|
|
// as DW_FORM_GNU_addr_index.
|
|
void ProcessAttributeAddrIndex(uint64_t offset,
|
|
enum DwarfAttribute attr,
|
|
enum DwarfForm form,
|
|
uint64_t addr_index) {
|
|
const uint8_t* addr_ptr =
|
|
addr_buffer_ + addr_base_ + addr_index * reader_->AddressSize();
|
|
ProcessAttributeUnsigned(
|
|
offset, attr, form, reader_->ReadAddress(addr_ptr));
|
|
}
|
|
|
|
// Processes all DIEs for this compilation unit
|
|
void ProcessDIEs();
|
|
|
|
// Skips the die with attributes specified in ABBREV starting at
|
|
// START, and return the new place to position the stream to.
|
|
const uint8_t* SkipDIE(const uint8_t* start, const Abbrev& abbrev);
|
|
|
|
// Skips the attribute starting at START, with FORM, and return the
|
|
// new place to position the stream to.
|
|
const uint8_t* SkipAttribute(const uint8_t* start, enum DwarfForm form);
|
|
|
|
// Process the actual debug information in a split DWARF file.
|
|
void ProcessSplitDwarf();
|
|
|
|
// Read the debug sections from a .dwo file.
|
|
void ReadDebugSectionsFromDwo(ElfReader* elf_reader,
|
|
SectionMap* sections);
|
|
|
|
// Path of the file containing the debug information.
|
|
const string path_;
|
|
|
|
// Offset from section start is the offset of this compilation unit
|
|
// from the beginning of the .debug_info section.
|
|
uint64_t offset_from_section_start_;
|
|
|
|
// buffer is the buffer for our CU, starting at .debug_info + offset
|
|
// passed in from constructor.
|
|
// after_header points to right after the compilation unit header.
|
|
const uint8_t* buffer_;
|
|
uint64_t buffer_length_;
|
|
const uint8_t* after_header_;
|
|
|
|
// The associated ByteReader that handles endianness issues for us
|
|
ByteReader* reader_;
|
|
|
|
// The map of sections in our file to buffers containing their data
|
|
const SectionMap& sections_;
|
|
|
|
// The associated handler to call processing functions in
|
|
Dwarf2Handler* handler_;
|
|
|
|
// Set of DWARF2/3 abbreviations for this compilation unit. Indexed
|
|
// by abbreviation number, which means that abbrevs_[0] is not
|
|
// valid.
|
|
std::vector<Abbrev>* abbrevs_;
|
|
|
|
// String section buffer and length, if we have a string section.
|
|
// This is here to avoid doing a section lookup for strings in
|
|
// ProcessAttribute, which is in the hot path for DWARF2 reading.
|
|
const uint8_t* string_buffer_;
|
|
uint64_t string_buffer_length_;
|
|
|
|
// Similarly for .debug_line_string.
|
|
const uint8_t* line_string_buffer_;
|
|
uint64_t line_string_buffer_length_;
|
|
|
|
// String offsets section buffer and length, if we have a string offsets
|
|
// section (.debug_str_offsets or .debug_str_offsets.dwo).
|
|
const uint8_t* str_offsets_buffer_;
|
|
uint64_t str_offsets_buffer_length_;
|
|
|
|
// Address section buffer and length, if we have an address section
|
|
// (.debug_addr).
|
|
const uint8_t* addr_buffer_;
|
|
uint64_t addr_buffer_length_;
|
|
|
|
// Flag indicating whether this compilation unit is part of a .dwo
|
|
// or .dwp file. If true, we are reading this unit because a
|
|
// skeleton compilation unit in an executable file had a
|
|
// DW_AT_GNU_dwo_name or DW_AT_GNU_dwo_id attribute.
|
|
// In a .dwo file, we expect the string offsets section to
|
|
// have a ".dwo" suffix, and we will use the ".debug_addr" section
|
|
// associated with the skeleton compilation unit.
|
|
bool is_split_dwarf_;
|
|
|
|
// Flag indicating if it's a Type Unit (only applicable to DWARF v5).
|
|
bool is_type_unit_;
|
|
|
|
// The value of the DW_AT_GNU_dwo_id attribute, if any.
|
|
uint64_t dwo_id_;
|
|
|
|
// The value of the DW_AT_GNU_type_signature attribute, if any.
|
|
uint64_t type_signature_;
|
|
|
|
// The value of the DW_AT_GNU_type_offset attribute, if any.
|
|
size_t type_offset_;
|
|
|
|
// The value of the DW_AT_GNU_dwo_name attribute, if any.
|
|
const char* dwo_name_;
|
|
|
|
// If this is a split DWARF CU, the value of the DW_AT_GNU_dwo_id attribute
|
|
// from the skeleton CU.
|
|
uint64_t skeleton_dwo_id_;
|
|
|
|
// The value of the DW_AT_GNU_ranges_base or DW_AT_rnglists_base attribute,
|
|
// if any.
|
|
uint64_t ranges_base_;
|
|
|
|
// The value of the DW_AT_GNU_addr_base attribute, if any.
|
|
uint64_t addr_base_;
|
|
|
|
// The value of DW_AT_str_offsets_base attribute, if any.
|
|
uint64_t str_offsets_base_;
|
|
|
|
// True if we have already looked for a .dwp file.
|
|
bool have_checked_for_dwp_;
|
|
|
|
// Path to the .dwp file.
|
|
string dwp_path_;
|
|
|
|
// ByteReader for the DWP file.
|
|
std::unique_ptr<ByteReader> dwp_byte_reader_;
|
|
|
|
// DWP reader.
|
|
std::unique_ptr<DwpReader> dwp_reader_;
|
|
};
|
|
|
|
// A Reader for a .dwp file. Supports the fetching of DWARF debug
|
|
// info for a given dwo_id.
|
|
//
|
|
// There are two versions of .dwp files. In both versions, the
|
|
// .dwp file is an ELF file containing only debug sections.
|
|
// In Version 1, the file contains many copies of each debug
|
|
// section, one for each .dwo file that is packaged in the .dwp
|
|
// file, and the .debug_cu_index section maps from the dwo_id
|
|
// to a set of section indexes. In Version 2, the file contains
|
|
// one of each debug section, and the .debug_cu_index section
|
|
// maps from the dwo_id to a set of offsets and lengths that
|
|
// identify each .dwo file's contribution to the larger sections.
|
|
|
|
class DwpReader {
|
|
public:
|
|
DwpReader(const ByteReader& byte_reader, ElfReader* elf_reader);
|
|
|
|
~DwpReader();
|
|
|
|
// Read the CU index and initialize data members.
|
|
void Initialize();
|
|
|
|
// Read the debug sections for the given dwo_id.
|
|
void ReadDebugSectionsForCU(uint64_t dwo_id, SectionMap* sections);
|
|
|
|
private:
|
|
// Search a v1 hash table for "dwo_id". Returns the slot index
|
|
// where the dwo_id was found, or -1 if it was not found.
|
|
int LookupCU(uint64_t dwo_id);
|
|
|
|
// Search a v2 hash table for "dwo_id". Returns the row index
|
|
// in the offsets and sizes tables, or 0 if it was not found.
|
|
uint32_t LookupCUv2(uint64_t dwo_id);
|
|
|
|
// The ELF reader for the .dwp file.
|
|
ElfReader* elf_reader_;
|
|
|
|
// The ByteReader for the .dwp file.
|
|
const ByteReader& byte_reader_;
|
|
|
|
// Pointer to the .debug_cu_index section.
|
|
const char* cu_index_;
|
|
|
|
// Size of the .debug_cu_index section.
|
|
size_t cu_index_size_;
|
|
|
|
// Pointer to the .debug_str.dwo section.
|
|
const char* string_buffer_;
|
|
|
|
// Size of the .debug_str.dwo section.
|
|
size_t string_buffer_size_;
|
|
|
|
// Version of the .dwp file. We support versions 1 and 2 currently.
|
|
int version_;
|
|
|
|
// Number of columns in the section tables (version 2).
|
|
unsigned int ncolumns_;
|
|
|
|
// Number of units in the section tables (version 2).
|
|
unsigned int nunits_;
|
|
|
|
// Number of slots in the hash table.
|
|
unsigned int nslots_;
|
|
|
|
// Pointer to the beginning of the hash table.
|
|
const char* phash_;
|
|
|
|
// Pointer to the beginning of the index table.
|
|
const char* pindex_;
|
|
|
|
// Pointer to the beginning of the section index pool (version 1).
|
|
const char* shndx_pool_;
|
|
|
|
// Pointer to the beginning of the section offset table (version 2).
|
|
const char* offset_table_;
|
|
|
|
// Pointer to the beginning of the section size table (version 2).
|
|
const char* size_table_;
|
|
|
|
// Contents of the sections of interest (version 2).
|
|
const char* abbrev_data_;
|
|
size_t abbrev_size_;
|
|
const char* info_data_;
|
|
size_t info_size_;
|
|
const char* str_offsets_data_;
|
|
size_t str_offsets_size_;
|
|
};
|
|
|
|
// This class is a reader for DWARF's Call Frame Information. CFI
|
|
// describes how to unwind stack frames --- even for functions that do
|
|
// not follow fixed conventions for saving registers, whose frame size
|
|
// varies as they execute, etc.
|
|
//
|
|
// CFI describes, at each machine instruction, how to compute the
|
|
// stack frame's base address, how to find the return address, and
|
|
// where to find the saved values of the caller's registers (if the
|
|
// callee has stashed them somewhere to free up the registers for its
|
|
// own use).
|
|
//
|
|
// For example, suppose we have a function whose machine code looks
|
|
// like this (imagine an assembly language that looks like C, for a
|
|
// machine with 32-bit registers, and a stack that grows towards lower
|
|
// addresses):
|
|
//
|
|
// func: ; entry point; return address at sp
|
|
// func+0: sp = sp - 16 ; allocate space for stack frame
|
|
// func+1: sp[12] = r0 ; save r0 at sp+12
|
|
// ... ; other code, not frame-related
|
|
// func+10: sp -= 4; *sp = x ; push some x on the stack
|
|
// ... ; other code, not frame-related
|
|
// func+20: r0 = sp[16] ; restore saved r0
|
|
// func+21: sp += 20 ; pop whole stack frame
|
|
// func+22: pc = *sp; sp += 4 ; pop return address and jump to it
|
|
//
|
|
// DWARF CFI is (a very compressed representation of) a table with a
|
|
// row for each machine instruction address and a column for each
|
|
// register showing how to restore it, if possible.
|
|
//
|
|
// A special column named "CFA", for "Canonical Frame Address", tells how
|
|
// to compute the base address of the frame; registers' entries may
|
|
// refer to the CFA in describing where the registers are saved.
|
|
//
|
|
// Another special column, named "RA", represents the return address.
|
|
//
|
|
// For example, here is a complete (uncompressed) table describing the
|
|
// function above:
|
|
//
|
|
// insn cfa r0 r1 ... ra
|
|
// =======================================
|
|
// func+0: sp cfa[0]
|
|
// func+1: sp+16 cfa[0]
|
|
// func+2: sp+16 cfa[-4] cfa[0]
|
|
// func+11: sp+20 cfa[-4] cfa[0]
|
|
// func+21: sp+20 cfa[0]
|
|
// func+22: sp cfa[0]
|
|
//
|
|
// Some things to note here:
|
|
//
|
|
// - Each row describes the state of affairs *before* executing the
|
|
// instruction at the given address. Thus, the row for func+0
|
|
// describes the state before we allocate the stack frame. In the
|
|
// next row, the formula for computing the CFA has changed,
|
|
// reflecting that allocation.
|
|
//
|
|
// - The other entries are written in terms of the CFA; this allows
|
|
// them to remain unchanged as the stack pointer gets bumped around.
|
|
// For example, the rule for recovering the return address (the "ra"
|
|
// column) remains unchanged throughout the function, even as the
|
|
// stack pointer takes on three different offsets from the return
|
|
// address.
|
|
//
|
|
// - Although we haven't shown it, most calling conventions designate
|
|
// "callee-saves" and "caller-saves" registers. The callee must
|
|
// preserve the values of callee-saves registers; if it uses them,
|
|
// it must save their original values somewhere, and restore them
|
|
// before it returns. In contrast, the callee is free to trash
|
|
// caller-saves registers; if the callee uses these, it will
|
|
// probably not bother to save them anywhere, and the CFI will
|
|
// probably mark their values as "unrecoverable".
|
|
//
|
|
// (However, since the caller cannot assume the callee was going to
|
|
// save them, caller-saves registers are probably dead in the caller
|
|
// anyway, so compilers usually don't generate CFA for caller-saves
|
|
// registers.)
|
|
//
|
|
// - Exactly where the CFA points is a matter of convention that
|
|
// depends on the architecture and ABI in use. In the example, the
|
|
// CFA is the value the stack pointer had upon entry to the
|
|
// function, pointing at the saved return address. But on the x86,
|
|
// the call frame information generated by GCC follows the
|
|
// convention that the CFA is the address *after* the saved return
|
|
// address.
|
|
//
|
|
// But by definition, the CFA remains constant throughout the
|
|
// lifetime of the frame. This makes it a useful value for other
|
|
// columns to refer to. It is also gives debuggers a useful handle
|
|
// for identifying a frame.
|
|
//
|
|
// If you look at the table above, you'll notice that a given entry is
|
|
// often the same as the one immediately above it: most instructions
|
|
// change only one or two aspects of the stack frame, if they affect
|
|
// it at all. The DWARF format takes advantage of this fact, and
|
|
// reduces the size of the data by mentioning only the addresses and
|
|
// columns at which changes take place. So for the above, DWARF CFI
|
|
// data would only actually mention the following:
|
|
//
|
|
// insn cfa r0 r1 ... ra
|
|
// =======================================
|
|
// func+0: sp cfa[0]
|
|
// func+1: sp+16
|
|
// func+2: cfa[-4]
|
|
// func+11: sp+20
|
|
// func+21: r0
|
|
// func+22: sp
|
|
//
|
|
// In fact, this is the way the parser reports CFI to the consumer: as
|
|
// a series of statements of the form, "At address X, column Y changed
|
|
// to Z," and related conventions for describing the initial state.
|
|
//
|
|
// Naturally, it would be impractical to have to scan the entire
|
|
// program's CFI, noting changes as we go, just to recover the
|
|
// unwinding rules in effect at one particular instruction. To avoid
|
|
// this, CFI data is grouped into "entries", each of which covers a
|
|
// specified range of addresses and begins with a complete statement
|
|
// of the rules for all recoverable registers at that starting
|
|
// address. Each entry typically covers a single function.
|
|
//
|
|
// Thus, to compute the contents of a given row of the table --- that
|
|
// is, rules for recovering the CFA, RA, and registers at a given
|
|
// instruction --- the consumer should find the entry that covers that
|
|
// instruction's address, start with the initial state supplied at the
|
|
// beginning of the entry, and work forward until it has processed all
|
|
// the changes up to and including those for the present instruction.
|
|
//
|
|
// There are seven kinds of rules that can appear in an entry of the
|
|
// table:
|
|
//
|
|
// - "undefined": The given register is not preserved by the callee;
|
|
// its value cannot be recovered.
|
|
//
|
|
// - "same value": This register has the same value it did in the callee.
|
|
//
|
|
// - offset(N): The register is saved at offset N from the CFA.
|
|
//
|
|
// - val_offset(N): The value the register had in the caller is the
|
|
// CFA plus offset N. (This is usually only useful for describing
|
|
// the stack pointer.)
|
|
//
|
|
// - register(R): The register's value was saved in another register R.
|
|
//
|
|
// - expression(E): Evaluating the DWARF expression E using the
|
|
// current frame's registers' values yields the address at which the
|
|
// register was saved.
|
|
//
|
|
// - val_expression(E): Evaluating the DWARF expression E using the
|
|
// current frame's registers' values yields the value the register
|
|
// had in the caller.
|
|
|
|
class CallFrameInfo {
|
|
public:
|
|
// The different kinds of entries one finds in CFI. Used internally,
|
|
// and for error reporting.
|
|
enum EntryKind { kUnknown, kCIE, kFDE, kTerminator };
|
|
|
|
// The handler class to which the parser hands the parsed call frame
|
|
// information. Defined below.
|
|
class Handler;
|
|
|
|
// A reporter class, which CallFrameInfo uses to report errors
|
|
// encountered while parsing call frame information. Defined below.
|
|
class Reporter;
|
|
|
|
// Create a DWARF CFI parser. BUFFER points to the contents of the
|
|
// .debug_frame section to parse; BUFFER_LENGTH is its length in bytes.
|
|
// REPORTER is an error reporter the parser should use to report
|
|
// problems. READER is a ByteReader instance that has the endianness and
|
|
// address size set properly. Report the data we find to HANDLER.
|
|
//
|
|
// This class can also parse Linux C++ exception handling data, as found
|
|
// in '.eh_frame' sections. This data is a variant of DWARF CFI that is
|
|
// placed in loadable segments so that it is present in the program's
|
|
// address space, and is interpreted by the C++ runtime to search the
|
|
// call stack for a handler interested in the exception being thrown,
|
|
// actually pop the frames, and find cleanup code to run.
|
|
//
|
|
// There are two differences between the call frame information described
|
|
// in the DWARF standard and the exception handling data Linux places in
|
|
// the .eh_frame section:
|
|
//
|
|
// - Exception handling data uses uses a different format for call frame
|
|
// information entry headers. The distinguished CIE id, the way FDEs
|
|
// refer to their CIEs, and the way the end of the series of entries is
|
|
// determined are all slightly different.
|
|
//
|
|
// If the constructor's EH_FRAME argument is true, then the
|
|
// CallFrameInfo parses the entry headers as Linux C++ exception
|
|
// handling data. If EH_FRAME is false or omitted, the CallFrameInfo
|
|
// parses standard DWARF call frame information.
|
|
//
|
|
// - Linux C++ exception handling data uses CIE augmentation strings
|
|
// beginning with 'z' to specify the presence of additional data after
|
|
// the CIE and FDE headers and special encodings used for addresses in
|
|
// frame description entries.
|
|
//
|
|
// CallFrameInfo can handle 'z' augmentations in either DWARF CFI or
|
|
// exception handling data if you have supplied READER with the base
|
|
// addresses needed to interpret the pointer encodings that 'z'
|
|
// augmentations can specify. See the ByteReader interface for details
|
|
// about the base addresses. See the CallFrameInfo::Handler interface
|
|
// for details about the additional information one might find in
|
|
// 'z'-augmented data.
|
|
//
|
|
// Thus:
|
|
//
|
|
// - If you are parsing standard DWARF CFI, as found in a .debug_frame
|
|
// section, you should pass false for the EH_FRAME argument, or omit
|
|
// it, and you need not worry about providing READER with the
|
|
// additional base addresses.
|
|
//
|
|
// - If you want to parse Linux C++ exception handling data from a
|
|
// .eh_frame section, you should pass EH_FRAME as true, and call
|
|
// READER's Set*Base member functions before calling our Start method.
|
|
//
|
|
// - If you want to parse DWARF CFI that uses the 'z' augmentations
|
|
// (although I don't think any toolchain ever emits such data), you
|
|
// could pass false for EH_FRAME, but call READER's Set*Base members.
|
|
//
|
|
// The extensions the Linux C++ ABI makes to DWARF for exception
|
|
// handling are described here, rather poorly:
|
|
// http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
|
|
// http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
|
|
//
|
|
// The mechanics of C++ exception handling, personality routines,
|
|
// and language-specific data areas are described here, rather nicely:
|
|
// http://www.codesourcery.com/public/cxx-abi/abi-eh.html
|
|
CallFrameInfo(const uint8_t* buffer, size_t buffer_length,
|
|
ByteReader* reader, Handler* handler, Reporter* reporter,
|
|
bool eh_frame = false)
|
|
: buffer_(buffer), buffer_length_(buffer_length),
|
|
reader_(reader), handler_(handler), reporter_(reporter),
|
|
eh_frame_(eh_frame) { }
|
|
|
|
~CallFrameInfo() { }
|
|
|
|
// Parse the entries in BUFFER, reporting what we find to HANDLER.
|
|
// Return true if we reach the end of the section successfully, or
|
|
// false if we encounter an error.
|
|
bool Start();
|
|
|
|
// Return the textual name of KIND. For error reporting.
|
|
static const char* KindName(EntryKind kind);
|
|
|
|
private:
|
|
|
|
struct CIE;
|
|
|
|
// A CFI entry, either an FDE or a CIE.
|
|
struct Entry {
|
|
// The starting offset of the entry in the section, for error
|
|
// reporting.
|
|
size_t offset;
|
|
|
|
// The start of this entry in the buffer.
|
|
const uint8_t* start;
|
|
|
|
// Which kind of entry this is.
|
|
//
|
|
// We want to be able to use this for error reporting even while we're
|
|
// in the midst of parsing. Error reporting code may assume that kind,
|
|
// offset, and start fields are valid, although kind may be kUnknown.
|
|
EntryKind kind;
|
|
|
|
// The end of this entry's common prologue (initial length and id), and
|
|
// the start of this entry's kind-specific fields.
|
|
const uint8_t* fields;
|
|
|
|
// The start of this entry's instructions.
|
|
const uint8_t* instructions;
|
|
|
|
// The address past the entry's last byte in the buffer. (Note that
|
|
// since offset points to the entry's initial length field, and the
|
|
// length field is the number of bytes after that field, this is not
|
|
// simply buffer_ + offset + length.)
|
|
const uint8_t* end;
|
|
|
|
// For both DWARF CFI and .eh_frame sections, this is the CIE id in a
|
|
// CIE, and the offset of the associated CIE in an FDE.
|
|
uint64_t id;
|
|
|
|
// The CIE that applies to this entry, if we've parsed it. If this is a
|
|
// CIE, then this field points to this structure.
|
|
CIE* cie;
|
|
};
|
|
|
|
// A common information entry (CIE).
|
|
struct CIE: public Entry {
|
|
uint8_t version; // CFI data version number
|
|
string augmentation; // vendor format extension markers
|
|
uint64_t code_alignment_factor; // scale for code address adjustments
|
|
int data_alignment_factor; // scale for stack pointer adjustments
|
|
unsigned return_address_register; // which register holds the return addr
|
|
|
|
// True if this CIE includes Linux C++ ABI 'z' augmentation data.
|
|
bool has_z_augmentation;
|
|
|
|
// Parsed 'z' augmentation data. These are meaningful only if
|
|
// has_z_augmentation is true.
|
|
bool has_z_lsda; // The 'z' augmentation included 'L'.
|
|
bool has_z_personality; // The 'z' augmentation included 'P'.
|
|
bool has_z_signal_frame; // The 'z' augmentation included 'S'.
|
|
|
|
// If has_z_lsda is true, this is the encoding to be used for language-
|
|
// specific data area pointers in FDEs.
|
|
DwarfPointerEncoding lsda_encoding;
|
|
|
|
// If has_z_personality is true, this is the encoding used for the
|
|
// personality routine pointer in the augmentation data.
|
|
DwarfPointerEncoding personality_encoding;
|
|
|
|
// If has_z_personality is true, this is the address of the personality
|
|
// routine --- or, if personality_encoding & DW_EH_PE_indirect, the
|
|
// address where the personality routine's address is stored.
|
|
uint64_t personality_address;
|
|
|
|
// This is the encoding used for addresses in the FDE header and
|
|
// in DW_CFA_set_loc instructions. This is always valid, whether
|
|
// or not we saw a 'z' augmentation string; its default value is
|
|
// DW_EH_PE_absptr, which is what normal DWARF CFI uses.
|
|
DwarfPointerEncoding pointer_encoding;
|
|
|
|
// These were only introduced in DWARF4, so will not be set in older
|
|
// versions.
|
|
uint8_t address_size;
|
|
uint8_t segment_size;
|
|
};
|
|
|
|
// A frame description entry (FDE).
|
|
struct FDE: public Entry {
|
|
uint64_t address; // start address of described code
|
|
uint64_t size; // size of described code, in bytes
|
|
|
|
// If cie->has_z_lsda is true, then this is the language-specific data
|
|
// area's address --- or its address's address, if cie->lsda_encoding
|
|
// has the DW_EH_PE_indirect bit set.
|
|
uint64_t lsda_address;
|
|
};
|
|
|
|
// Internal use.
|
|
class Rule;
|
|
class UndefinedRule;
|
|
class SameValueRule;
|
|
class OffsetRule;
|
|
class ValOffsetRule;
|
|
class RegisterRule;
|
|
class ExpressionRule;
|
|
class ValExpressionRule;
|
|
class RuleMap;
|
|
class State;
|
|
|
|
// Parse the initial length and id of a CFI entry, either a CIE, an FDE,
|
|
// or a .eh_frame end-of-data mark. CURSOR points to the beginning of the
|
|
// data to parse. On success, populate ENTRY as appropriate, and return
|
|
// true. On failure, report the problem, and return false. Even if we
|
|
// return false, set ENTRY->end to the first byte after the entry if we
|
|
// were able to figure that out, or NULL if we weren't.
|
|
bool ReadEntryPrologue(const uint8_t* cursor, Entry* entry);
|
|
|
|
// Parse the fields of a CIE after the entry prologue, including any 'z'
|
|
// augmentation data. Assume that the 'Entry' fields of CIE are
|
|
// populated; use CIE->fields and CIE->end as the start and limit for
|
|
// parsing. On success, populate the rest of *CIE, and return true; on
|
|
// failure, report the problem and return false.
|
|
bool ReadCIEFields(CIE* cie);
|
|
|
|
// Parse the fields of an FDE after the entry prologue, including any 'z'
|
|
// augmentation data. Assume that the 'Entry' fields of *FDE are
|
|
// initialized; use FDE->fields and FDE->end as the start and limit for
|
|
// parsing. Assume that FDE->cie is fully initialized. On success,
|
|
// populate the rest of *FDE, and return true; on failure, report the
|
|
// problem and return false.
|
|
bool ReadFDEFields(FDE* fde);
|
|
|
|
// Report that ENTRY is incomplete, and return false. This is just a
|
|
// trivial wrapper for invoking reporter_->Incomplete; it provides a
|
|
// little brevity.
|
|
bool ReportIncomplete(Entry* entry);
|
|
|
|
// Return true if ENCODING has the DW_EH_PE_indirect bit set.
|
|
static bool IsIndirectEncoding(DwarfPointerEncoding encoding) {
|
|
return encoding & DW_EH_PE_indirect;
|
|
}
|
|
|
|
// The contents of the DWARF .debug_info section we're parsing.
|
|
const uint8_t* buffer_;
|
|
size_t buffer_length_;
|
|
|
|
// For reading multi-byte values with the appropriate endianness.
|
|
ByteReader* reader_;
|
|
|
|
// The handler to which we should report the data we find.
|
|
Handler* handler_;
|
|
|
|
// For reporting problems in the info we're parsing.
|
|
Reporter* reporter_;
|
|
|
|
// True if we are processing .eh_frame-format data.
|
|
bool eh_frame_;
|
|
};
|
|
|
|
// The handler class for CallFrameInfo. The a CFI parser calls the
|
|
// member functions of a handler object to report the data it finds.
|
|
class CallFrameInfo::Handler {
|
|
public:
|
|
// The pseudo-register number for the canonical frame address.
|
|
enum { kCFARegister = -1 };
|
|
|
|
Handler() { }
|
|
virtual ~Handler() { }
|
|
|
|
// The parser has found CFI for the machine code at ADDRESS,
|
|
// extending for LENGTH bytes. OFFSET is the offset of the frame
|
|
// description entry in the section, for use in error messages.
|
|
// VERSION is the version number of the CFI format. AUGMENTATION is
|
|
// a string describing any producer-specific extensions present in
|
|
// the data. RETURN_ADDRESS is the number of the register that holds
|
|
// the address to which the function should return.
|
|
//
|
|
// Entry should return true to process this CFI, or false to skip to
|
|
// the next entry.
|
|
//
|
|
// The parser invokes Entry for each Frame Description Entry (FDE)
|
|
// it finds. The parser doesn't report Common Information Entries
|
|
// to the handler explicitly; instead, if the handler elects to
|
|
// process a given FDE, the parser reiterates the appropriate CIE's
|
|
// contents at the beginning of the FDE's rules.
|
|
virtual bool Entry(size_t offset, uint64_t address, uint64_t length,
|
|
uint8_t version, const string& augmentation,
|
|
unsigned return_address) = 0;
|
|
|
|
// When the Entry function returns true, the parser calls these
|
|
// handler functions repeatedly to describe the rules for recovering
|
|
// registers at each instruction in the given range of machine code.
|
|
// Immediately after a call to Entry, the handler should assume that
|
|
// the rule for each callee-saves register is "unchanged" --- that
|
|
// is, that the register still has the value it had in the caller.
|
|
//
|
|
// If a *Rule function returns true, we continue processing this entry's
|
|
// instructions. If a *Rule function returns false, we stop evaluating
|
|
// instructions, and skip to the next entry. Either way, we call End
|
|
// before going on to the next entry.
|
|
//
|
|
// In all of these functions, if the REG parameter is kCFARegister, then
|
|
// the rule describes how to find the canonical frame address.
|
|
// kCFARegister may be passed as a BASE_REGISTER argument, meaning that
|
|
// the canonical frame address should be used as the base address for the
|
|
// computation. All other REG values will be positive.
|
|
|
|
// At ADDRESS, register REG's value is not recoverable.
|
|
virtual bool UndefinedRule(uint64_t address, int reg) = 0;
|
|
|
|
// At ADDRESS, register REG's value is the same as that it had in
|
|
// the caller.
|
|
virtual bool SameValueRule(uint64_t address, int reg) = 0;
|
|
|
|
// At ADDRESS, register REG has been saved at offset OFFSET from
|
|
// BASE_REGISTER.
|
|
virtual bool OffsetRule(uint64_t address, int reg,
|
|
int base_register, long offset) = 0;
|
|
|
|
// At ADDRESS, the caller's value of register REG is the current
|
|
// value of BASE_REGISTER plus OFFSET. (This rule doesn't provide an
|
|
// address at which the register's value is saved.)
|
|
virtual bool ValOffsetRule(uint64_t address, int reg,
|
|
int base_register, long offset) = 0;
|
|
|
|
// At ADDRESS, register REG has been saved in BASE_REGISTER. This differs
|
|
// from ValOffsetRule(ADDRESS, REG, BASE_REGISTER, 0), in that
|
|
// BASE_REGISTER is the "home" for REG's saved value: if you want to
|
|
// assign to a variable whose home is REG in the calling frame, you
|
|
// should put the value in BASE_REGISTER.
|
|
virtual bool RegisterRule(uint64_t address, int reg, int base_register) = 0;
|
|
|
|
// At ADDRESS, the DWARF expression EXPRESSION yields the address at
|
|
// which REG was saved.
|
|
virtual bool ExpressionRule(uint64_t address, int reg,
|
|
const string& expression) = 0;
|
|
|
|
// At ADDRESS, the DWARF expression EXPRESSION yields the caller's
|
|
// value for REG. (This rule doesn't provide an address at which the
|
|
// register's value is saved.)
|
|
virtual bool ValExpressionRule(uint64_t address, int reg,
|
|
const string& expression) = 0;
|
|
|
|
// Indicate that the rules for the address range reported by the
|
|
// last call to Entry are complete. End should return true if
|
|
// everything is okay, or false if an error has occurred and parsing
|
|
// should stop.
|
|
virtual bool End() = 0;
|
|
|
|
// Handler functions for Linux C++ exception handling data. These are
|
|
// only called if the data includes 'z' augmentation strings.
|
|
|
|
// The Linux C++ ABI uses an extension of the DWARF CFI format to
|
|
// walk the stack to propagate exceptions from the throw to the
|
|
// appropriate catch, and do the appropriate cleanups along the way.
|
|
// CFI entries used for exception handling have two additional data
|
|
// associated with them:
|
|
//
|
|
// - The "language-specific data area" describes which exception
|
|
// types the function has 'catch' clauses for, and indicates how
|
|
// to go about re-entering the function at the appropriate catch
|
|
// clause. If the exception is not caught, it describes the
|
|
// destructors that must run before the frame is popped.
|
|
//
|
|
// - The "personality routine" is responsible for interpreting the
|
|
// language-specific data area's contents, and deciding whether
|
|
// the exception should continue to propagate down the stack,
|
|
// perhaps after doing some cleanup for this frame, or whether the
|
|
// exception will be caught here.
|
|
//
|
|
// In principle, the language-specific data area is opaque to
|
|
// everybody but the personality routine. In practice, these values
|
|
// may be useful or interesting to readers with extra context, and
|
|
// we have to at least skip them anyway, so we might as well report
|
|
// them to the handler.
|
|
|
|
// This entry's exception handling personality routine's address is
|
|
// ADDRESS. If INDIRECT is true, then ADDRESS is the address at
|
|
// which the routine's address is stored. The default definition for
|
|
// this handler function simply returns true, allowing parsing of
|
|
// the entry to continue.
|
|
virtual bool PersonalityRoutine(uint64_t address, bool indirect) {
|
|
return true;
|
|
}
|
|
|
|
// This entry's language-specific data area (LSDA) is located at
|
|
// ADDRESS. If INDIRECT is true, then ADDRESS is the address at
|
|
// which the area's address is stored. The default definition for
|
|
// this handler function simply returns true, allowing parsing of
|
|
// the entry to continue.
|
|
virtual bool LanguageSpecificDataArea(uint64_t address, bool indirect) {
|
|
return true;
|
|
}
|
|
|
|
// This entry describes a signal trampoline --- this frame is the
|
|
// caller of a signal handler. The default definition for this
|
|
// handler function simply returns true, allowing parsing of the
|
|
// entry to continue.
|
|
//
|
|
// The best description of the rationale for and meaning of signal
|
|
// trampoline CFI entries seems to be in the GCC bug database:
|
|
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=26208
|
|
virtual bool SignalHandler() { return true; }
|
|
};
|
|
|
|
// The CallFrameInfo class makes calls on an instance of this class to
|
|
// report errors or warn about problems in the data it is parsing. The
|
|
// default definitions of these methods print a message to stderr, but
|
|
// you can make a derived class that overrides them.
|
|
class CallFrameInfo::Reporter {
|
|
public:
|
|
// Create an error reporter which attributes troubles to the section
|
|
// named SECTION in FILENAME.
|
|
//
|
|
// Normally SECTION would be .debug_frame, but the Mac puts CFI data
|
|
// in a Mach-O section named __debug_frame. If we support
|
|
// Linux-style exception handling data, we could be reading an
|
|
// .eh_frame section.
|
|
Reporter(const string& filename,
|
|
const string& section = ".debug_frame")
|
|
: filename_(filename), section_(section) { }
|
|
virtual ~Reporter() { }
|
|
|
|
// The CFI entry at OFFSET ends too early to be well-formed. KIND
|
|
// indicates what kind of entry it is; KIND can be kUnknown if we
|
|
// haven't parsed enough of the entry to tell yet.
|
|
virtual void Incomplete(uint64_t offset, CallFrameInfo::EntryKind kind);
|
|
|
|
// The .eh_frame data has a four-byte zero at OFFSET where the next
|
|
// entry's length would be; this is a terminator. However, the buffer
|
|
// length as given to the CallFrameInfo constructor says there should be
|
|
// more data.
|
|
virtual void EarlyEHTerminator(uint64_t offset);
|
|
|
|
// The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the
|
|
// section is not that large.
|
|
virtual void CIEPointerOutOfRange(uint64_t offset, uint64_t cie_offset);
|
|
|
|
// The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the entry
|
|
// there is not a CIE.
|
|
virtual void BadCIEId(uint64_t offset, uint64_t cie_offset);
|
|
|
|
// The FDE at OFFSET refers to a CIE with an address size we don't know how
|
|
// to handle.
|
|
virtual void UnexpectedAddressSize(uint64_t offset, uint8_t address_size);
|
|
|
|
// The FDE at OFFSET refers to a CIE with an segment descriptor size we
|
|
// don't know how to handle.
|
|
virtual void UnexpectedSegmentSize(uint64_t offset, uint8_t segment_size);
|
|
|
|
// The FDE at OFFSET refers to a CIE with version number VERSION,
|
|
// which we don't recognize. We cannot parse DWARF CFI if it uses
|
|
// a version number we don't recognize.
|
|
virtual void UnrecognizedVersion(uint64_t offset, int version);
|
|
|
|
// The FDE at OFFSET refers to a CIE with augmentation AUGMENTATION,
|
|
// which we don't recognize. We cannot parse DWARF CFI if it uses
|
|
// augmentations we don't recognize.
|
|
virtual void UnrecognizedAugmentation(uint64_t offset,
|
|
const string& augmentation);
|
|
|
|
// The pointer encoding ENCODING, specified by the CIE at OFFSET, is not
|
|
// a valid encoding.
|
|
virtual void InvalidPointerEncoding(uint64_t offset, uint8_t encoding);
|
|
|
|
// The pointer encoding ENCODING, specified by the CIE at OFFSET, depends
|
|
// on a base address which has not been supplied.
|
|
virtual void UnusablePointerEncoding(uint64_t offset, uint8_t encoding);
|
|
|
|
// The CIE at OFFSET contains a DW_CFA_restore instruction at
|
|
// INSN_OFFSET, which may not appear in a CIE.
|
|
virtual void RestoreInCIE(uint64_t offset, uint64_t insn_offset);
|
|
|
|
// The entry at OFFSET, of kind KIND, has an unrecognized
|
|
// instruction at INSN_OFFSET.
|
|
virtual void BadInstruction(uint64_t offset, CallFrameInfo::EntryKind kind,
|
|
uint64_t insn_offset);
|
|
|
|
// The instruction at INSN_OFFSET in the entry at OFFSET, of kind
|
|
// KIND, establishes a rule that cites the CFA, but we have not
|
|
// established a CFA rule yet.
|
|
virtual void NoCFARule(uint64_t offset, CallFrameInfo::EntryKind kind,
|
|
uint64_t insn_offset);
|
|
|
|
// The instruction at INSN_OFFSET in the entry at OFFSET, of kind
|
|
// KIND, is a DW_CFA_restore_state instruction, but the stack of
|
|
// saved states is empty.
|
|
virtual void EmptyStateStack(uint64_t offset, CallFrameInfo::EntryKind kind,
|
|
uint64_t insn_offset);
|
|
|
|
// The DW_CFA_remember_state instruction at INSN_OFFSET in the entry
|
|
// at OFFSET, of kind KIND, would restore a state that has no CFA
|
|
// rule, whereas the current state does have a CFA rule. This is
|
|
// bogus input, which the CallFrameInfo::Handler interface doesn't
|
|
// (and shouldn't) have any way to report.
|
|
virtual void ClearingCFARule(uint64_t offset, CallFrameInfo::EntryKind kind,
|
|
uint64_t insn_offset);
|
|
|
|
protected:
|
|
// The name of the file whose CFI we're reading.
|
|
string filename_;
|
|
|
|
// The name of the CFI section in that file.
|
|
string section_;
|
|
};
|
|
|
|
} // namespace dwarf2reader
|
|
|
|
#endif // UTIL_DEBUGINFO_DWARF2READER_H__
|