michael@0: // -*- mode: C++ -*- michael@0: michael@0: // Copyright (c) 2010, Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: // Original author: Jim Blandy michael@0: michael@0: // cfi_assembler.h: Define CFISection, a class for creating properly michael@0: // (and improperly) formatted DWARF CFI data for unit tests. michael@0: michael@0: #ifndef PROCESSOR_CFI_ASSEMBLER_H_ michael@0: #define PROCESSOR_CFI_ASSEMBLER_H_ michael@0: michael@0: #include michael@0: michael@0: #include "common/dwarf/dwarf2enums.h" michael@0: #include "common/test_assembler.h" michael@0: #include "common/using_std_string.h" michael@0: #include "google_breakpad/common/breakpad_types.h" michael@0: michael@0: namespace google_breakpad { michael@0: michael@0: using dwarf2reader::DwarfPointerEncoding; michael@0: using google_breakpad::test_assembler::Endianness; michael@0: using google_breakpad::test_assembler::Label; michael@0: using google_breakpad::test_assembler::Section; michael@0: michael@0: class CFISection: public Section { michael@0: public: michael@0: michael@0: // CFI augmentation strings beginning with 'z', defined by the michael@0: // Linux/IA-64 C++ ABI, can specify interesting encodings for michael@0: // addresses appearing in FDE headers and call frame instructions (and michael@0: // for additional fields whose presence the augmentation string michael@0: // specifies). In particular, pointers can be specified to be relative michael@0: // to various base address: the start of the .text section, the michael@0: // location holding the address itself, and so on. These allow the michael@0: // frame data to be position-independent even when they live in michael@0: // write-protected pages. These variants are specified at the michael@0: // following two URLs: michael@0: // michael@0: // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html michael@0: // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html michael@0: // michael@0: // CFISection leaves the production of well-formed 'z'-augmented CIEs and michael@0: // FDEs to the user, but does provide EncodedPointer, to emit michael@0: // properly-encoded addresses for a given pointer encoding. michael@0: // EncodedPointer uses an instance of this structure to find the base michael@0: // addresses it should use; you can establish a default for all encoded michael@0: // pointers appended to this section with SetEncodedPointerBases. michael@0: struct EncodedPointerBases { michael@0: EncodedPointerBases() : cfi(), text(), data() { } michael@0: michael@0: // The starting address of this CFI section in memory, for michael@0: // DW_EH_PE_pcrel. DW_EH_PE_pcrel pointers may only be used in data michael@0: // that has is loaded into the program's address space. michael@0: uint64_t cfi; michael@0: michael@0: // The starting address of this file's .text section, for DW_EH_PE_textrel. michael@0: uint64_t text; michael@0: michael@0: // The starting address of this file's .got or .eh_frame_hdr section, michael@0: // for DW_EH_PE_datarel. michael@0: uint64_t data; michael@0: }; michael@0: michael@0: // Create a CFISection whose endianness is ENDIANNESS, and where michael@0: // machine addresses are ADDRESS_SIZE bytes long. If EH_FRAME is michael@0: // true, use the .eh_frame format, as described by the Linux michael@0: // Standards Base Core Specification, instead of the DWARF CFI michael@0: // format. michael@0: CFISection(Endianness endianness, size_t address_size, michael@0: bool eh_frame = false) michael@0: : Section(endianness), address_size_(address_size), eh_frame_(eh_frame), michael@0: pointer_encoding_(dwarf2reader::DW_EH_PE_absptr), michael@0: encoded_pointer_bases_(), entry_length_(NULL), in_fde_(false) { michael@0: // The 'start', 'Here', and 'Mark' members of a CFISection all refer michael@0: // to section offsets. michael@0: start() = 0; michael@0: } michael@0: michael@0: // Return this CFISection's address size. michael@0: size_t AddressSize() const { return address_size_; } michael@0: michael@0: // Return true if this CFISection uses the .eh_frame format, or michael@0: // false if it contains ordinary DWARF CFI data. michael@0: bool ContainsEHFrame() const { return eh_frame_; } michael@0: michael@0: // Use ENCODING for pointers in calls to FDEHeader and EncodedPointer. michael@0: void SetPointerEncoding(DwarfPointerEncoding encoding) { michael@0: pointer_encoding_ = encoding; michael@0: } michael@0: michael@0: // Use the addresses in BASES as the base addresses for encoded michael@0: // pointers in subsequent calls to FDEHeader or EncodedPointer. michael@0: // This function makes a copy of BASES. michael@0: void SetEncodedPointerBases(const EncodedPointerBases &bases) { michael@0: encoded_pointer_bases_ = bases; michael@0: } michael@0: michael@0: // Append a Common Information Entry header to this section with the michael@0: // given values. If dwarf64 is true, use the 64-bit DWARF initial michael@0: // length format for the CIE's initial length. Return a reference to michael@0: // this section. You should call FinishEntry after writing the last michael@0: // instruction for the CIE. michael@0: // michael@0: // Before calling this function, you will typically want to use Mark michael@0: // or Here to make a label to pass to FDEHeader that refers to this michael@0: // CIE's position in the section. michael@0: CFISection &CIEHeader(uint64_t code_alignment_factor, michael@0: int data_alignment_factor, michael@0: unsigned return_address_register, michael@0: uint8_t version = 3, michael@0: const string &augmentation = "", michael@0: bool dwarf64 = false); michael@0: michael@0: // Append a Frame Description Entry header to this section with the michael@0: // given values. If dwarf64 is true, use the 64-bit DWARF initial michael@0: // length format for the CIE's initial length. Return a reference to michael@0: // this section. You should call FinishEntry after writing the last michael@0: // instruction for the CIE. michael@0: // michael@0: // This function doesn't support entries that are longer than michael@0: // 0xffffff00 bytes. (The "initial length" is always a 32-bit michael@0: // value.) Nor does it support .debug_frame sections longer than michael@0: // 0xffffff00 bytes. michael@0: CFISection &FDEHeader(Label cie_pointer, michael@0: uint64_t initial_location, michael@0: uint64_t address_range, michael@0: bool dwarf64 = false); michael@0: michael@0: // Note the current position as the end of the last CIE or FDE we michael@0: // started, after padding with DW_CFA_nops for alignment. This michael@0: // defines the label representing the entry's length, cited in the michael@0: // entry's header. Return a reference to this section. michael@0: CFISection &FinishEntry(); michael@0: michael@0: // Append the contents of BLOCK as a DW_FORM_block value: an michael@0: // unsigned LEB128 length, followed by that many bytes of data. michael@0: CFISection &Block(const string &block) { michael@0: ULEB128(block.size()); michael@0: Append(block); michael@0: return *this; michael@0: } michael@0: michael@0: // Append ADDRESS to this section, in the appropriate size and michael@0: // endianness. Return a reference to this section. michael@0: CFISection &Address(uint64_t address) { michael@0: Section::Append(endianness(), address_size_, address); michael@0: return *this; michael@0: } michael@0: CFISection &Address(Label address) { michael@0: Section::Append(endianness(), address_size_, address); michael@0: return *this; michael@0: } michael@0: michael@0: // Append ADDRESS to this section, using ENCODING and BASES. ENCODING michael@0: // defaults to this section's default encoding, established by michael@0: // SetPointerEncoding. BASES defaults to this section's bases, set by michael@0: // SetEncodedPointerBases. If the DW_EH_PE_indirect bit is set in the michael@0: // encoding, assume that ADDRESS is where the true address is stored. michael@0: // Return a reference to this section. michael@0: // michael@0: // (C++ doesn't let me use default arguments here, because I want to michael@0: // refer to members of *this in the default argument expression.) michael@0: CFISection &EncodedPointer(uint64_t address) { michael@0: return EncodedPointer(address, pointer_encoding_, encoded_pointer_bases_); michael@0: } michael@0: CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding) { michael@0: return EncodedPointer(address, encoding, encoded_pointer_bases_); michael@0: } michael@0: CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding, michael@0: const EncodedPointerBases &bases); michael@0: michael@0: // Restate some member functions, to keep chaining working nicely. michael@0: CFISection &Mark(Label *label) { Section::Mark(label); return *this; } michael@0: CFISection &D8(uint8_t v) { Section::D8(v); return *this; } michael@0: CFISection &D16(uint16_t v) { Section::D16(v); return *this; } michael@0: CFISection &D16(Label v) { Section::D16(v); return *this; } michael@0: CFISection &D32(uint32_t v) { Section::D32(v); return *this; } michael@0: CFISection &D32(const Label &v) { Section::D32(v); return *this; } michael@0: CFISection &D64(uint64_t v) { Section::D64(v); return *this; } michael@0: CFISection &D64(const Label &v) { Section::D64(v); return *this; } michael@0: CFISection &LEB128(long long v) { Section::LEB128(v); return *this; } michael@0: CFISection &ULEB128(uint64_t v) { Section::ULEB128(v); return *this; } michael@0: michael@0: private: michael@0: // A length value that we've appended to the section, but is not yet michael@0: // known. LENGTH is the appended value; START is a label referring michael@0: // to the start of the data whose length was cited. michael@0: struct PendingLength { michael@0: Label length; michael@0: Label start; michael@0: }; michael@0: michael@0: // Constants used in CFI/.eh_frame data: michael@0: michael@0: // If the first four bytes of an "initial length" are this constant, then michael@0: // the data uses the 64-bit DWARF format, and the length itself is the michael@0: // subsequent eight bytes. michael@0: static const uint32_t kDwarf64InitialLengthMarker = 0xffffffffU; michael@0: michael@0: // The CIE identifier for 32- and 64-bit DWARF CFI and .eh_frame data. michael@0: static const uint32_t kDwarf32CIEIdentifier = ~(uint32_t)0; michael@0: static const uint64_t kDwarf64CIEIdentifier = ~(uint64_t)0; michael@0: static const uint32_t kEHFrame32CIEIdentifier = 0; michael@0: static const uint64_t kEHFrame64CIEIdentifier = 0; michael@0: michael@0: // The size of a machine address for the data in this section. michael@0: size_t address_size_; michael@0: michael@0: // If true, we are generating a Linux .eh_frame section, instead of michael@0: // a standard DWARF .debug_frame section. michael@0: bool eh_frame_; michael@0: michael@0: // The encoding to use for FDE pointers. michael@0: DwarfPointerEncoding pointer_encoding_; michael@0: michael@0: // The base addresses to use when emitting encoded pointers. michael@0: EncodedPointerBases encoded_pointer_bases_; michael@0: michael@0: // The length value for the current entry. michael@0: // michael@0: // Oddly, this must be dynamically allocated. Labels never get new michael@0: // values; they only acquire constraints on the value they already michael@0: // have, or assert if you assign them something incompatible. So michael@0: // each header needs truly fresh Label objects to cite in their michael@0: // headers and track their positions. The alternative is explicit michael@0: // destructor invocation and a placement new. Ick. michael@0: PendingLength *entry_length_; michael@0: michael@0: // True if we are currently emitting an FDE --- that is, we have michael@0: // called FDEHeader but have not yet called FinishEntry. michael@0: bool in_fde_; michael@0: michael@0: // If in_fde_ is true, this is its starting address. We use this for michael@0: // emitting DW_EH_PE_funcrel pointers. michael@0: uint64_t fde_start_address_; michael@0: }; michael@0: michael@0: } // namespace google_breakpad michael@0: michael@0: #endif // PROCESSOR_CFI_ASSEMBLER_H_