// -*- 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. // Original author: Jim Blandy // cfi_assembler.h: Define CFISection, a class for creating properly // (and improperly) formatted DWARF CFI data for unit tests. #ifndef PROCESSOR_CFI_ASSEMBLER_H_ #define PROCESSOR_CFI_ASSEMBLER_H_ #include #include "common/dwarf/dwarf2enums.h" #include "common/test_assembler.h" #include "common/using_std_string.h" #include "google_breakpad/common/breakpad_types.h" namespace google_breakpad { using dwarf2reader::DwarfPointerEncoding; using google_breakpad::test_assembler::Endianness; using google_breakpad::test_assembler::Label; using google_breakpad::test_assembler::Section; class CFISection: public Section { public: // CFI augmentation strings beginning with 'z', defined by the // Linux/IA-64 C++ ABI, can specify interesting encodings for // addresses appearing in FDE headers and call frame instructions (and // for additional fields whose presence the augmentation string // specifies). In particular, pointers can be specified to be relative // to various base address: the start of the .text section, the // location holding the address itself, and so on. These allow the // frame data to be position-independent even when they live in // write-protected pages. These variants are specified at the // following two URLs: // // 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 // // CFISection leaves the production of well-formed 'z'-augmented CIEs and // FDEs to the user, but does provide EncodedPointer, to emit // properly-encoded addresses for a given pointer encoding. // EncodedPointer uses an instance of this structure to find the base // addresses it should use; you can establish a default for all encoded // pointers appended to this section with SetEncodedPointerBases. struct EncodedPointerBases { EncodedPointerBases() : cfi(), text(), data() { } // The starting address of this CFI section in memory, for // DW_EH_PE_pcrel. DW_EH_PE_pcrel pointers may only be used in data // that has is loaded into the program's address space. uint64_t cfi; // The starting address of this file's .text section, for DW_EH_PE_textrel. uint64_t text; // The starting address of this file's .got or .eh_frame_hdr section, // for DW_EH_PE_datarel. uint64_t data; }; // Create a CFISection whose endianness is ENDIANNESS, and where // machine addresses are ADDRESS_SIZE bytes long. If EH_FRAME is // true, use the .eh_frame format, as described by the Linux // Standards Base Core Specification, instead of the DWARF CFI // format. CFISection(Endianness endianness, size_t address_size, bool eh_frame = false) : Section(endianness), address_size_(address_size), eh_frame_(eh_frame), pointer_encoding_(dwarf2reader::DW_EH_PE_absptr), encoded_pointer_bases_(), entry_length_(NULL), in_fde_(false) { // The 'start', 'Here', and 'Mark' members of a CFISection all refer // to section offsets. start() = 0; } // Return this CFISection's address size. size_t AddressSize() const { return address_size_; } // Return true if this CFISection uses the .eh_frame format, or // false if it contains ordinary DWARF CFI data. bool ContainsEHFrame() const { return eh_frame_; } // Use ENCODING for pointers in calls to FDEHeader and EncodedPointer. void SetPointerEncoding(DwarfPointerEncoding encoding) { pointer_encoding_ = encoding; } // Use the addresses in BASES as the base addresses for encoded // pointers in subsequent calls to FDEHeader or EncodedPointer. // This function makes a copy of BASES. void SetEncodedPointerBases(const EncodedPointerBases& bases) { encoded_pointer_bases_ = bases; } // Append a Common Information Entry header to this section with the // given values. If dwarf64 is true, use the 64-bit DWARF initial // length format for the CIE's initial length. Return a reference to // this section. You should call FinishEntry after writing the last // instruction for the CIE. // // Before calling this function, you will typically want to use Mark // or Here to make a label to pass to FDEHeader that refers to this // CIE's position in the section. CFISection& CIEHeader(uint64_t code_alignment_factor, int data_alignment_factor, unsigned return_address_register, uint8_t version = 3, const string& augmentation = "", bool dwarf64 = false, uint8_t address_size = 8, uint8_t segment_size = 0); // Append a Frame Description Entry header to this section with the // given values. If dwarf64 is true, use the 64-bit DWARF initial // length format for the CIE's initial length. Return a reference to // this section. You should call FinishEntry after writing the last // instruction for the CIE. // // This function doesn't support entries that are longer than // 0xffffff00 bytes. (The "initial length" is always a 32-bit // value.) Nor does it support .debug_frame sections longer than // 0xffffff00 bytes. CFISection& FDEHeader(Label cie_pointer, uint64_t initial_location, uint64_t address_range, bool dwarf64 = false); // Note the current position as the end of the last CIE or FDE we // started, after padding with DW_CFA_nops for alignment. This // defines the label representing the entry's length, cited in the // entry's header. Return a reference to this section. CFISection& FinishEntry(); // Append the contents of BLOCK as a DW_FORM_block value: an // unsigned LEB128 length, followed by that many bytes of data. CFISection& Block(const string& block) { ULEB128(block.size()); Append(block); return *this; } // Append ADDRESS to this section, in the appropriate size and // endianness. Return a reference to this section. CFISection& Address(uint64_t address) { Section::Append(endianness(), address_size_, address); return *this; } CFISection& Address(Label address) { Section::Append(endianness(), address_size_, address); return *this; } // Append ADDRESS to this section, using ENCODING and BASES. ENCODING // defaults to this section's default encoding, established by // SetPointerEncoding. BASES defaults to this section's bases, set by // SetEncodedPointerBases. If the DW_EH_PE_indirect bit is set in the // encoding, assume that ADDRESS is where the true address is stored. // Return a reference to this section. // // (C++ doesn't let me use default arguments here, because I want to // refer to members of *this in the default argument expression.) CFISection& EncodedPointer(uint64_t address) { return EncodedPointer(address, pointer_encoding_, encoded_pointer_bases_); } CFISection& EncodedPointer(uint64_t address, DwarfPointerEncoding encoding) { return EncodedPointer(address, encoding, encoded_pointer_bases_); } CFISection& EncodedPointer(uint64_t address, DwarfPointerEncoding encoding, const EncodedPointerBases& bases); // Restate some member functions, to keep chaining working nicely. CFISection& Mark(Label* label) { Section::Mark(label); return *this; } CFISection& D8(uint8_t v) { Section::D8(v); return *this; } CFISection& D16(uint16_t v) { Section::D16(v); return *this; } CFISection& D16(Label v) { Section::D16(v); return *this; } CFISection& D32(uint32_t v) { Section::D32(v); return *this; } CFISection& D32(const Label& v) { Section::D32(v); return *this; } CFISection& D64(uint64_t v) { Section::D64(v); return *this; } CFISection& D64(const Label& v) { Section::D64(v); return *this; } CFISection& LEB128(long long v) { Section::LEB128(v); return *this; } CFISection& ULEB128(uint64_t v) { Section::ULEB128(v); return *this; } private: // A length value that we've appended to the section, but is not yet // known. LENGTH is the appended value; START is a label referring // to the start of the data whose length was cited. struct PendingLength { Label length; Label start; }; // Constants used in CFI/.eh_frame data: // If the first four bytes of an "initial length" are this constant, then // the data uses the 64-bit DWARF format, and the length itself is the // subsequent eight bytes. static const uint32_t kDwarf64InitialLengthMarker = 0xffffffffU; // The CIE identifier for 32- and 64-bit DWARF CFI and .eh_frame data. static const uint32_t kDwarf32CIEIdentifier = ~(uint32_t)0; static const uint64_t kDwarf64CIEIdentifier = ~(uint64_t)0; static const uint32_t kEHFrame32CIEIdentifier = 0; static const uint64_t kEHFrame64CIEIdentifier = 0; // The size of a machine address for the data in this section. size_t address_size_; // If true, we are generating a Linux .eh_frame section, instead of // a standard DWARF .debug_frame section. bool eh_frame_; // The encoding to use for FDE pointers. DwarfPointerEncoding pointer_encoding_; // The base addresses to use when emitting encoded pointers. EncodedPointerBases encoded_pointer_bases_; // The length value for the current entry. // // Oddly, this must be dynamically allocated. Labels never get new // values; they only acquire constraints on the value they already // have, or assert if you assign them something incompatible. So // each header needs truly fresh Label objects to cite in their // headers and track their positions. The alternative is explicit // destructor invocation and a placement new. Ick. PendingLength *entry_length_; // True if we are currently emitting an FDE --- that is, we have // called FDEHeader but have not yet called FinishEntry. bool in_fde_; // If in_fde_ is true, this is its starting address. We use this for // emitting DW_EH_PE_funcrel pointers. uint64_t fde_start_address_; }; } // namespace google_breakpad #endif // PROCESSOR_CFI_ASSEMBLER_H_