You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
531 lines
16 KiB
531 lines
16 KiB
// ehframe.h -- handle exception frame sections for gold -*- C++ -*- |
|
|
|
// Copyright (C) 2006-2014 Free Software Foundation, Inc. |
|
// Written by Ian Lance Taylor <iant@google.com>. |
|
|
|
// This file is part of gold. |
|
|
|
// This program is free software; you can redistribute it and/or modify |
|
// it under the terms of the GNU General Public License as published by |
|
// the Free Software Foundation; either version 3 of the License, or |
|
// (at your option) any later version. |
|
|
|
// This program is distributed in the hope that it will be useful, |
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
// GNU General Public License for more details. |
|
|
|
// You should have received a copy of the GNU General Public License |
|
// along with this program; if not, write to the Free Software |
|
// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
|
// MA 02110-1301, USA. |
|
|
|
#ifndef GOLD_EHFRAME_H |
|
#define GOLD_EHFRAME_H |
|
|
|
#include <map> |
|
#include <set> |
|
#include <vector> |
|
|
|
#include "output.h" |
|
#include "merge.h" |
|
|
|
namespace gold |
|
{ |
|
|
|
template<int size, bool big_endian> |
|
class Track_relocs; |
|
|
|
class Eh_frame; |
|
|
|
// This class manages the .eh_frame_hdr section, which holds the data |
|
// for the PT_GNU_EH_FRAME segment. gcc's unwind support code uses |
|
// the PT_GNU_EH_FRAME segment to find the list of FDEs. This saves |
|
// the time required to register the exception handlers at startup |
|
// time and when a shared object is loaded, and the time required to |
|
// deregister the exception handlers when a shared object is unloaded. |
|
|
|
class Eh_frame_hdr : public Output_section_data |
|
{ |
|
public: |
|
Eh_frame_hdr(Output_section* eh_frame_section, const Eh_frame*); |
|
|
|
// Record that we found an unrecognized .eh_frame section. |
|
void |
|
found_unrecognized_eh_frame_section() |
|
{ this->any_unrecognized_eh_frame_sections_ = true; } |
|
|
|
// Record an FDE. |
|
void |
|
record_fde(section_offset_type fde_offset, unsigned char fde_encoding) |
|
{ |
|
if (!this->any_unrecognized_eh_frame_sections_) |
|
this->fde_offsets_.push_back(std::make_pair(fde_offset, fde_encoding)); |
|
} |
|
|
|
protected: |
|
// Set the final data size. |
|
void |
|
set_final_data_size(); |
|
|
|
// Write the data to the file. |
|
void |
|
do_write(Output_file*); |
|
|
|
// Write to a map file. |
|
void |
|
do_print_to_mapfile(Mapfile* mapfile) const |
|
{ mapfile->print_output_data(this, _("** eh_frame_hdr")); } |
|
|
|
private: |
|
// Write the data to the file with the right endianness. |
|
template<int size, bool big_endian> |
|
void |
|
do_sized_write(Output_file*); |
|
|
|
// The data we record for one FDE: the offset of the FDE within the |
|
// .eh_frame section, and the FDE encoding. |
|
typedef std::pair<section_offset_type, unsigned char> Fde_offset; |
|
|
|
// The list of information we record for an FDE. |
|
typedef std::vector<Fde_offset> Fde_offsets; |
|
|
|
// When writing out the header, we convert the FDE offsets into FDE |
|
// addresses. This is a list of pairs of the offset from the header |
|
// to the FDE PC and to the FDE itself. |
|
template<int size> |
|
class Fde_addresses |
|
{ |
|
public: |
|
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; |
|
typedef typename std::pair<Address, Address> Fde_address; |
|
typedef typename std::vector<Fde_address> Fde_address_list; |
|
typedef typename Fde_address_list::iterator iterator; |
|
|
|
Fde_addresses(unsigned int reserve) |
|
: fde_addresses_() |
|
{ this->fde_addresses_.reserve(reserve); } |
|
|
|
void |
|
push_back(Address pc_address, Address fde_address) |
|
{ |
|
this->fde_addresses_.push_back(std::make_pair(pc_address, fde_address)); |
|
} |
|
|
|
iterator |
|
begin() |
|
{ return this->fde_addresses_.begin(); } |
|
|
|
iterator |
|
end() |
|
{ return this->fde_addresses_.end(); } |
|
|
|
private: |
|
Fde_address_list fde_addresses_; |
|
}; |
|
|
|
// Compare Fde_address objects. |
|
template<int size> |
|
struct Fde_address_compare |
|
{ |
|
bool |
|
operator()(const typename Fde_addresses<size>::Fde_address& f1, |
|
const typename Fde_addresses<size>::Fde_address& f2) const |
|
{ return f1.first < f2.first; } |
|
}; |
|
|
|
// Return the PC to which an FDE refers. |
|
template<int size, bool big_endian> |
|
typename elfcpp::Elf_types<size>::Elf_Addr |
|
get_fde_pc(typename elfcpp::Elf_types<size>::Elf_Addr eh_frame_address, |
|
const unsigned char* eh_frame_contents, |
|
section_offset_type fde_offset, unsigned char fde_encoding); |
|
|
|
// Convert Fde_offsets to Fde_addresses. |
|
template<int size, bool big_endian> |
|
void |
|
get_fde_addresses(Output_file* of, |
|
const Fde_offsets* fde_offsets, |
|
Fde_addresses<size>* fde_addresses); |
|
|
|
// The .eh_frame section. |
|
Output_section* eh_frame_section_; |
|
// The .eh_frame section data. |
|
const Eh_frame* eh_frame_data_; |
|
// Data from the FDEs in the .eh_frame sections. |
|
Fde_offsets fde_offsets_; |
|
// Whether we found any .eh_frame sections which we could not |
|
// process. |
|
bool any_unrecognized_eh_frame_sections_; |
|
}; |
|
|
|
// This class holds an FDE. |
|
|
|
class Fde |
|
{ |
|
public: |
|
Fde(Relobj* object, unsigned int shndx, section_offset_type input_offset, |
|
const unsigned char* contents, size_t length) |
|
: object_(object), |
|
contents_(reinterpret_cast<const char*>(contents), length) |
|
{ |
|
this->u_.from_object.shndx = shndx; |
|
this->u_.from_object.input_offset = input_offset; |
|
} |
|
|
|
// Create an FDE associated with a PLT. |
|
Fde(Output_data* plt, const unsigned char* contents, size_t length, |
|
bool post_map) |
|
: object_(NULL), |
|
contents_(reinterpret_cast<const char*>(contents), length) |
|
{ |
|
this->u_.from_linker.plt = plt; |
|
this->u_.from_linker.post_map = post_map; |
|
} |
|
|
|
// Return the length of this FDE. Add 4 for the length and 4 for |
|
// the offset to the CIE. |
|
size_t |
|
length() const |
|
{ return this->contents_.length() + 8; } |
|
|
|
// Add a mapping for this FDE to MERGE_MAP, so that relocations |
|
// against the FDE are applied to right part of the output file. |
|
void |
|
add_mapping(section_offset_type output_offset, Merge_map* merge_map) const |
|
{ |
|
if (this->object_ != NULL) |
|
merge_map->add_mapping(this->object_, this->u_.from_object.shndx, |
|
this->u_.from_object.input_offset, this->length(), |
|
output_offset); |
|
} |
|
|
|
// Return whether this FDE was added after merge mapping. |
|
bool |
|
post_map() |
|
{ return this->object_ == NULL && this->u_.from_linker.post_map; } |
|
|
|
// Write the FDE to OVIEW starting at OFFSET. FDE_ENCODING is the |
|
// encoding, from the CIE. Round up the bytes to ADDRALIGN if |
|
// necessary. ADDRESS is the virtual address of OVIEW. Record the |
|
// FDE in EH_FRAME_HDR. Return the new offset. |
|
template<int size, bool big_endian> |
|
section_offset_type |
|
write(unsigned char* oview, section_offset_type output_section_offset, |
|
section_offset_type offset, uint64_t address, unsigned int addralign, |
|
section_offset_type cie_offset, unsigned char fde_encoding, |
|
Eh_frame_hdr* eh_frame_hdr); |
|
|
|
private: |
|
// The object in which this FDE was seen. This will be NULL for a |
|
// linker generated FDE. |
|
Relobj* object_; |
|
union |
|
{ |
|
// These fields are used if the FDE is from an input object (the |
|
// object_ field is not NULL). |
|
struct |
|
{ |
|
// Input section index for this FDE. |
|
unsigned int shndx; |
|
// Offset within the input section for this FDE. |
|
section_offset_type input_offset; |
|
} from_object; |
|
// This field is used if the FDE is generated by the linker (the |
|
// object_ field is NULL). |
|
struct |
|
{ |
|
// The only linker generated FDEs are for PLT sections, and this |
|
// points to the PLT section. |
|
Output_data* plt; |
|
// Set if the FDE was added after merge mapping. |
|
bool post_map; |
|
} from_linker; |
|
} u_; |
|
// FDE data. |
|
std::string contents_; |
|
}; |
|
|
|
// A FDE plus some info from a CIE to allow later writing of the FDE. |
|
|
|
struct Post_fde |
|
{ |
|
Post_fde(Fde* f, section_offset_type cie_off, unsigned char encoding) |
|
: fde(f), cie_offset(cie_off), fde_encoding(encoding) |
|
{ } |
|
|
|
Fde* fde; |
|
section_offset_type cie_offset; |
|
unsigned char fde_encoding; |
|
}; |
|
|
|
typedef std::vector<Post_fde> Post_fdes; |
|
|
|
// This class holds a CIE. |
|
|
|
class Cie |
|
{ |
|
public: |
|
Cie(Relobj* object, unsigned int shndx, section_offset_type input_offset, |
|
unsigned char fde_encoding, const char* personality_name, |
|
const unsigned char* contents, size_t length) |
|
: object_(object), |
|
shndx_(shndx), |
|
input_offset_(input_offset), |
|
fde_encoding_(fde_encoding), |
|
personality_name_(personality_name), |
|
fdes_(), |
|
contents_(reinterpret_cast<const char*>(contents), length) |
|
{ } |
|
|
|
~Cie(); |
|
|
|
// We permit copying a CIE when there are no FDEs. This is |
|
// convenient in the code which creates them. |
|
Cie(const Cie& cie) |
|
: object_(cie.object_), |
|
shndx_(cie.shndx_), |
|
input_offset_(cie.input_offset_), |
|
fde_encoding_(cie.fde_encoding_), |
|
personality_name_(cie.personality_name_), |
|
fdes_(), |
|
contents_(cie.contents_) |
|
{ gold_assert(cie.fdes_.empty()); } |
|
|
|
// Add an FDE associated with this CIE. |
|
void |
|
add_fde(Fde* fde) |
|
{ this->fdes_.push_back(fde); } |
|
|
|
// Return the number of FDEs. |
|
unsigned int |
|
fde_count() const |
|
{ return this->fdes_.size(); } |
|
|
|
// Set the output offset of this CIE to OUTPUT_OFFSET. It will be |
|
// followed by all its FDEs. ADDRALIGN is the required address |
|
// alignment, typically 4 or 8. This updates MERGE_MAP with the |
|
// mapping. It returns the new output offset. |
|
section_offset_type |
|
set_output_offset(section_offset_type output_offset, unsigned int addralign, |
|
Merge_map*); |
|
|
|
// Write the CIE to OVIEW starting at OFFSET. Round up the bytes to |
|
// ADDRALIGN. ADDRESS is the virtual address of OVIEW. |
|
// EH_FRAME_HDR is the exception frame header for FDE recording. |
|
// POST_FDES stashes FDEs created after mappings were done, for later |
|
// writing. Return the new offset. |
|
template<int size, bool big_endian> |
|
section_offset_type |
|
write(unsigned char* oview, section_offset_type output_section_offset, |
|
section_offset_type offset, uint64_t address, |
|
unsigned int addralign, Eh_frame_hdr* eh_frame_hdr, |
|
Post_fdes* post_fdes); |
|
|
|
// Return the FDE encoding. |
|
unsigned char |
|
fde_encoding() const |
|
{ return this->fde_encoding_; } |
|
|
|
friend bool operator<(const Cie&, const Cie&); |
|
friend bool operator==(const Cie&, const Cie&); |
|
|
|
private: |
|
// The class is not assignable. |
|
Cie& operator=(const Cie&); |
|
|
|
// The object in which this CIE was first seen. This will be NULL |
|
// for a linker generated CIE. |
|
Relobj* object_; |
|
// Input section index for this CIE. This will be 0 for a linker |
|
// generated CIE. |
|
unsigned int shndx_; |
|
// Offset within the input section for this CIE. This will be 0 for |
|
// a linker generated CIE. |
|
section_offset_type input_offset_; |
|
// The encoding of the FDE. This is a DW_EH_PE code. |
|
unsigned char fde_encoding_; |
|
// The name of the personality routine. This will be the name of a |
|
// global symbol, or will be the empty string. |
|
std::string personality_name_; |
|
// List of FDEs. |
|
std::vector<Fde*> fdes_; |
|
// CIE data. |
|
std::string contents_; |
|
}; |
|
|
|
extern bool operator<(const Cie&, const Cie&); |
|
extern bool operator==(const Cie&, const Cie&); |
|
|
|
// This class manages .eh_frame sections. It discards duplicate |
|
// exception information. |
|
|
|
class Eh_frame : public Output_section_data |
|
{ |
|
public: |
|
enum Eh_frame_section_disposition |
|
{ |
|
EH_EMPTY_SECTION, |
|
EH_UNRECOGNIZED_SECTION, |
|
EH_OPTIMIZABLE_SECTION, |
|
EH_END_MARKER_SECTION |
|
}; |
|
|
|
Eh_frame(); |
|
|
|
// Record the associated Eh_frame_hdr, if any. |
|
void |
|
set_eh_frame_hdr(Eh_frame_hdr* hdr) |
|
{ this->eh_frame_hdr_ = hdr; } |
|
|
|
// Add the input section SHNDX in OBJECT. SYMBOLS is the contents |
|
// of the symbol table section (size SYMBOLS_SIZE), SYMBOL_NAMES is |
|
// the symbol names section (size SYMBOL_NAMES_SIZE). RELOC_SHNDX |
|
// is the relocation section if any (0 for none, -1U for multiple). |
|
// RELOC_TYPE is the type of the relocation section if any. This |
|
// returns whether the section was incorporated into the .eh_frame |
|
// data. |
|
template<int size, bool big_endian> |
|
Eh_frame_section_disposition |
|
add_ehframe_input_section(Sized_relobj_file<size, big_endian>* object, |
|
const unsigned char* symbols, |
|
section_size_type symbols_size, |
|
const unsigned char* symbol_names, |
|
section_size_type symbol_names_size, |
|
unsigned int shndx, unsigned int reloc_shndx, |
|
unsigned int reloc_type); |
|
|
|
// Add a CIE and an FDE for a PLT section, to permit unwinding |
|
// through a PLT. The FDE data should start with 8 bytes of zero, |
|
// which will be replaced by a 4 byte PC relative reference to the |
|
// address of PLT and a 4 byte size of PLT. |
|
void |
|
add_ehframe_for_plt(Output_data* plt, const unsigned char* cie_data, |
|
size_t cie_length, const unsigned char* fde_data, |
|
size_t fde_length); |
|
|
|
// Return the number of FDEs. |
|
unsigned int |
|
fde_count() const; |
|
|
|
protected: |
|
// Set the final data size. |
|
void |
|
set_final_data_size(); |
|
|
|
// Return the output address for an input address. |
|
bool |
|
do_output_offset(const Relobj*, unsigned int shndx, |
|
section_offset_type offset, |
|
section_offset_type* poutput) const; |
|
|
|
// Return whether this is the merge section for an input section. |
|
bool |
|
do_is_merge_section_for(const Relobj*, unsigned int shndx) const; |
|
|
|
// Write the data to the file. |
|
void |
|
do_write(Output_file*); |
|
|
|
// Write to a map file. |
|
void |
|
do_print_to_mapfile(Mapfile* mapfile) const |
|
{ mapfile->print_output_data(this, _("** eh_frame")); } |
|
|
|
private: |
|
// The comparison routine for the CIE map. |
|
struct Cie_less |
|
{ |
|
bool |
|
operator()(const Cie* cie1, const Cie* cie2) const |
|
{ return *cie1 < *cie2; } |
|
}; |
|
|
|
// A set of unique CIEs. |
|
typedef std::set<Cie*, Cie_less> Cie_offsets; |
|
|
|
// A list of unmergeable CIEs. |
|
typedef std::vector<Cie*> Unmergeable_cie_offsets; |
|
|
|
// A mapping from offsets to CIEs. This is used while reading an |
|
// input section. |
|
typedef std::map<uint64_t, Cie*> Offsets_to_cie; |
|
|
|
// A list of CIEs, and a bool indicating whether the CIE is |
|
// mergeable. |
|
typedef std::vector<std::pair<Cie*, bool> > New_cies; |
|
|
|
// Skip an LEB128. |
|
static bool |
|
skip_leb128(const unsigned char**, const unsigned char*); |
|
|
|
// The implementation of add_ehframe_input_section. |
|
template<int size, bool big_endian> |
|
bool |
|
do_add_ehframe_input_section(Sized_relobj_file<size, big_endian>* object, |
|
const unsigned char* symbols, |
|
section_size_type symbols_size, |
|
const unsigned char* symbol_names, |
|
section_size_type symbol_names_size, |
|
unsigned int shndx, |
|
unsigned int reloc_shndx, |
|
unsigned int reloc_type, |
|
const unsigned char* pcontents, |
|
section_size_type contents_len, |
|
New_cies*); |
|
|
|
// Read a CIE. |
|
template<int size, bool big_endian> |
|
bool |
|
read_cie(Sized_relobj_file<size, big_endian>* object, |
|
unsigned int shndx, |
|
const unsigned char* symbols, |
|
section_size_type symbols_size, |
|
const unsigned char* symbol_names, |
|
section_size_type symbol_names_size, |
|
const unsigned char* pcontents, |
|
const unsigned char* pcie, |
|
const unsigned char* pcieend, |
|
Track_relocs<size, big_endian>* relocs, |
|
Offsets_to_cie* cies, |
|
New_cies* new_cies); |
|
|
|
// Read an FDE. |
|
template<int size, bool big_endian> |
|
bool |
|
read_fde(Sized_relobj_file<size, big_endian>* object, |
|
unsigned int shndx, |
|
const unsigned char* symbols, |
|
section_size_type symbols_size, |
|
const unsigned char* pcontents, |
|
unsigned int offset, |
|
const unsigned char* pfde, |
|
const unsigned char* pfdeend, |
|
Track_relocs<size, big_endian>* relocs, |
|
Offsets_to_cie* cies); |
|
|
|
// Template version of write function. |
|
template<int size, bool big_endian> |
|
void |
|
do_sized_write(unsigned char* oview); |
|
|
|
// The exception frame header, if any. |
|
Eh_frame_hdr* eh_frame_hdr_; |
|
// A mapping from all unique CIEs to their offset in the output |
|
// file. |
|
Cie_offsets cie_offsets_; |
|
// A mapping from unmergeable CIEs to their offset in the output |
|
// file. |
|
Unmergeable_cie_offsets unmergeable_cie_offsets_; |
|
// A mapping from input sections to the output section. |
|
Merge_map merge_map_; |
|
// Whether we have created the mappings to the output section. |
|
bool mappings_are_done_; |
|
// The final data size. This is only set if mappings_are_done_ is |
|
// true. |
|
section_size_type final_data_size_; |
|
}; |
|
|
|
} // End namespace gold. |
|
|
|
#endif // !defined(GOLD_EHFRAME_H)
|
|
|