| // ehframe.h -- handle exception frame sections for gold -*- C++ -*- |
| |
| // Copyright (C) 2006-2024 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, |
| Output_section_data* output_data) const |
| { |
| if (this->object_ != NULL) |
| this->object_->add_merge_mapping(output_data, 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() const |
| { return this->object_ == NULL && this->u_.from_linker.post_map; } |
| |
| // Return whether this FDE was added for the PLT after merge mapping. |
| bool |
| post_map(const Output_data* plt) const |
| { return this->post_map() && this->u_.from_linker.plt == plt; } |
| |
| // 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); } |
| |
| // Remove the last FDE associated with this CIE. |
| void |
| remove_fde() |
| { this->fdes_.pop_back(); } |
| |
| // Access the last FDE associated with this CIE. |
| const Fde* |
| last_fde() const |
| { return this->fdes_.back(); } |
| |
| // 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, |
| Output_section_data*); |
| |
| // 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); |
| |
| // Remove all post-map unwind information for a PLT. |
| void |
| remove_ehframe_for_plt(Output_data* plt, const unsigned char* cie_data, |
| size_t cie_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; |
| |
| // 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_; |
| // 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) |