/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2014 Nexenta Systems, Inc.
*/
#include <string.h>
#include <stdio.h>
#include <sgs.h>
#include <debug.h>
#include <_libld.h>
#include <dwarf.h>
#include <stdlib.h>
/*
* A EH_FRAME_HDR consists of the following:
*
* Encoding Field
* --------------------------------
* unsigned byte version
* unsigned byte eh_frame_ptr_enc
* unsigned byte fde_count_enc
* unsigned byte table_enc
* encoded eh_frame_ptr
* encoded fde_count
* [ binary search table ]
*
* The binary search table entries each consists of:
*
* encoded initial_func_loc
* encoded FDE_address
*
* The entries in the binary search table are sorted
* in a increasing order by the initial location.
*
*
* version
*
* Version of the .eh_frame_hdr format. This value shall be 1.
*
* eh_frame_ptr_enc
*
* The encoding format of the eh_frame_ptr field. For shared
* libraries the encoding must be
* DW_EH_PE_sdata4|DW_EH_PE_pcrel or
* DW_EH_PE_sdata4|DW_EH_PE_datarel.
*
*
* fde_count_enc
*
* The encoding format of the fde_count field. A value of
* DW_EH_PE_omit indicates the binary search table is not
* present.
*
* table_enc
*
* The encoding format of the entries in the binary search
* table. A value of DW_EH_PE_omit indicates the binary search
* table is not present. For shared libraries the encoding
* must be DW_EH_PE_sdata4|DW_EH_PE_pcrel or
* DW_EH_PE_sdata4|DW_EH_PE_datarel.
*
*
* eh_frame_ptr
*
* The encoded value of the pointer to the start of the
* .eh_frame section.
*
* fde_count
*
* The encoded value of the count of entries in the binary
* search table.
*
* binary search table
*
* A binary search table containing fde_count entries. Each
* entry of the table consist of two encoded values, the
* initial location of the function to which an FDE applies,
* and the address of the FDE. The entries are sorted in an
* increasing order by the initial location value.
*
*/
/*
* EH_FRAME sections
* =================
*
* The call frame information needed for unwinding the stack is output in
* an ELF section(s) of type SHT_AMD64_UNWIND (amd64) or SHT_PROGBITS (other).
* In the simplest case there will be one such section per object file and it
* will be named ".eh_frame". An .eh_frame section consists of one or more
* subsections. Each subsection contains a CIE (Common Information Entry)
* followed by varying number of FDEs (Frame Descriptor Entry). A FDE
* corresponds to an explicit or compiler generated function in a
* compilation unit, all FDEs can access the CIE that begins their
* subsection for data.
*
* If an object file contains C++ template instantiations, there shall be
* a separate CIE immediately preceding each FDE corresponding to an
* instantiation.
*
* Using the preferred encoding specified below, the .eh_frame section can
* be entirely resolved at link time and thus can become part of the
* text segment.
*
* .eh_frame Section Layout
* ------------------------
*
* EH_PE encoding below refers to the pointer encoding as specified in the
* enhanced LSB Chapter 7 for Eh_Frame_Hdr.
*
* Common Information Entry (CIE)
* ------------------------------
* CIE has the following format:
*
* Length
* in
* Field Byte Description
* ----- ------ -----------
* 1. Length 4 Length of CIE (not including
* this 4-byte field).
*
* 2. CIE id 4 Value Zero (0) for .eh_frame
* (used to distinguish CIEs and
* FDEs when scanning the section)
*
* 3. Version 1 Value One (1)
*
* 4. CIE Augmentation string Null-terminated string with legal
* values being "" or 'z' optionally
* followed by single occurrences of
* 'P', 'L', or 'R' in any order.
* String The presence of character(s) in the
* string dictates the content of
* field 8, the Augmentation Section.
* Each character has one or two
* associated operands in the AS.
* Operand order depends on
* position in the string ('z' must
* be first).
*
* 5. Code Align Factor uleb128 To be multiplied with the
* "Advance Location" instructions in
* the Call Frame Instructions
*
* 6. Data Align Factor sleb128 To be multiplied with all offset
* in the Call Frame Instructions
*
* 7. Ret Address Reg 1 A "virtual" register representation
* of the return address. In Dwarf V2,
* this is a byte, otherwise it is
* uleb128. It is a byte in gcc 3.3.x
*
* 8. Optional CIE varying Present if Augmentation String in
* Augmentation Section field 4 is not 0.
*
* z:
* size uleb128 Length of the remainder of the
* Augmentation Section
*
* P:
* personality_enc 1 Encoding specifier - preferred
* value is a pc-relative, signed
* 4-byte
*
*
* personality routine (encoded) Encoded pointer to personality
* routine (actually to the PLT
* entry for the personality
* routine)
* R:
* code_enc 1 Non-default encoding for the
* code-pointers (FDE members
* "initial_location" and "address_range"
* and the operand for DW_CFA_set_loc)
* - preferred value is pc-relative,
* signed 4-byte.
* L:
* lsda_enc 1 FDE augmentation bodies may contain
* LSDA pointers. If so they are
* encoded as specified here -
* preferred value is pc-relative,
* signed 4-byte possibly indirect
* thru a GOT entry.
*
*
* 9. Optional Call Frame varying
* Instructions
*
* The size of the optional call frame instruction area must be computed
* based on the overall size and the offset reached while scanning the
* preceding fields of the CIE.
*
*
* Frame Descriptor Entry (FDE)
* ----------------------------
* FDE has the following format:
*
* Length
* in
* Field Byte Description
* ----- ------ -----------
* 1. Length 4 Length of remainder of this FDE
*
* 2. CIE Pointer 4 Distance from this field to the
* nearest preceding CIE
* (uthe value is subtracted from the
* current address). This value
* can never be zero and thus can
* be used to distinguish CIE's and
* FDE's when scanning the
* .eh_frame section
*
* 3. Initial Location varying Reference to the function code
* corresponding to this FDE.
* If 'R' is missing from the CIE
* Augmentation String, the field is an
* 8-byte absolute pointer. Otherwise,
* the corresponding EH_PE encoding in the
* CIE Augmentation Section is used to
* interpret the reference.
*
* 4. Address Range varying Size of the function code corresponding
* to this FDE.
* If 'R' is missing from the CIE
* Augmentation String, the field is an
* 8-byte unsigned number. Otherwise,
* the size is determined by the
* corresponding EH_PE encoding in the
* CIE Augmentation Section (the
* value is always absolute).
*
* 5. Optional FDE varying present if CIE augmentation
* Augmentation Section string is non-empty.
*
*
* 'z':
* length uleb128 length of the remainder of the
* FDE augmentation section
*
*
* 'L' (and length > 0):
* LSDA varying LSDA pointer, encoded in the
* format specified by the
* corresponding operand in the CIE's
* augmentation body.
*
* 6. Optional Call varying
* Frame Instructions
*
* The size of the optional call frame instruction area must be computed
* based on the overall size and the offset reached while scanning the
* preceding fields of the FDE.
*
* The overall size of a .eh_frame section is given in the ELF section
* header. The only way to determine the number of entries is to scan
* the section till the end and count.
*
*/
static uint_t
{
uint_t r;
if (do_swap)
UL_ASSIGN_BSWAP_WORD(p, data);
else
UL_ASSIGN_WORD(p, data);
(*ndx) += 4;
return (r);
}
/*
* Create an unwind header (.eh_frame_hdr) output section.
* The section is created and space reserved, but the data
* is not copied into place. That is done by a later call
* to ld_unwind_populate(), after active relocations have been
* processed.
*
* When GNU linkonce processing is in effect, we can end up in a situation
* where the FDEs related to discarded sections remain in the eh_frame
* section. Ideally, we would remove these dead entries from eh_frame.
* However, that optimization has not yet been implemented. In the current
* implementation, the number of dead FDEs cannot be determined until
* active relocations are processed, and that processing follows the
* call to this function. This means that we are unable to detect dead FDEs
* here, and the section created by this routine is sized for maximum case
* where all FDEs are valid.
*/
{
/*
* we only build a unwind header if we have
* some unwind information in the file.
*/
return (1);
/*
* Allocate and initialize the Elf_Data structure.
*/
return (S_ERROR);
/*
* Allocate and initialize the Shdr structure.
*/
return (S_ERROR);
shdr->sh_entsize = 0;
/*
* Allocate and initialize the Is_desc structure.
*/
return (S_ERROR);
return (S_ERROR);
/*
* Scan through all of the input Frame information, counting each FDE
* that requires an index. Each fde_entry gets a corresponding entry
* in the binary search table.
*/
fde_cnt = 0;
int os_isdescs_idx;
/*
* Extract length in lsb format. A zero length
* indicates that this CIE is a terminator and
* that processing for unwind information is
* complete.
*/
if (length == 0)
break;
/*
* Extract CIE id in lsb format.
*/
/*
* A CIE record has a id of '0', otherwise
* this is a FDE entry and the 'id' is the
* CIE pointer.
*/
if (id == 0) {
ndx += 1;
/* BEGIN CSTYLED */
return (S_ERROR);
}
/* END CSTYLED */
} else {
fde_cnt++;
}
}
}
}
/*
* section size:
* byte version +1
* byte eh_frame_ptr_enc +1
* byte fde_count_enc +1
* byte table_enc +1
* 4 bytes eh_frame_ptr +4
* 4 bytes fde_count +4
* [4 bytes] [4bytes] * fde_count ...
*/
return (S_ERROR);
return (1);
}
/*
* the comparator function needs to calculate
* the actual 'initloc' of a bintab entry - to
* do this we initialize the following global to point
* to it.
*/
static int
{
return (1);
return (-1);
return (0);
}
{
/*
* Are we building the unwind hdr?
*/
return (1);
hdroff = 0;
/*
* version == 1
*/
/*
* The encodings are:
*
* eh_frameptr_enc sdata4 | pcrel
* fde_count_enc udata4
* table_enc sdata4 | datarel
*/
/*
* Header Offsets
* -----------------------------------
* byte version +1
* byte eh_frame_ptr_enc +1
* byte fde_count_enc +1
* byte table_enc +1
* 4 bytes eh_frame_ptr +4
* 4 bytes fde_count +4
*/
/* LINTED */
first_unwind = 0;
fde_count = 0;
/*
* remember first UNWIND section to
* point to in the frame_ptr entry.
*/
if (first_unwind == 0)
first_unwind = osp;
/*
* Extract length in lsb format. A zero length
* indicates that this CIE is a terminator and that
* processing of unwind information is complete.
*/
if (length == 0)
goto done;
/*
* Extract CIE id in lsb format.
*/
/*
* A CIE record has a id of '0'; otherwise
* this is a FDE entry and the 'id' is the
* CIE pointer.
*/
if (id == 0) {
char *cieaugstr;
ciePflag = 0;
cieRflag = 0;
/*
* We need to drill through the CIE
* to find the Rflag. It's the Rflag
* which describes how the FDE code-pointers
* are encoded.
*/
ndx += 1;
/*
* augstr
*/
/*
* calign & dalign
*/
return (S_ERROR);
}
return (S_ERROR);
}
/*
* retreg
*/
if (cieversion == 1) {
ndx++;
} else {
DW_OVERFLOW) {
return (S_ERROR);
}
}
/*
* we walk through the augmentation
* section now looking for the Rflag
*/
cieaugndx++) {
/* BEGIN CSTYLED */
case 'z':
/* size */
DW_OVERFLOW) {
return (S_ERROR);
}
break;
case 'P':
/* personality */
ndx++;
/*
* Just need to extract the
* value to move on to the next
* field.
*/
switch (dwarf_ehe_extract(
case DW_OVERFLOW:
return (S_ERROR);
case DW_BAD_ENCODING:
return (S_ERROR);
case DW_SUCCESS:
break;
}
break;
case 'R':
/* code encoding */
ndx++;
break;
case 'L':
/* lsda encoding */
ndx++;
break;
}
/* END CSTYLED */
}
} else {
gotaddr =
case DW_OVERFLOW:
return (S_ERROR);
case DW_BAD_ENCODING:
return (S_ERROR);
case DW_SUCCESS:
break;
}
/*
* Ignore FDEs with initloc set to 0.
* initloc will not be 0 unless this FDE was
* abandoned due to GNU linkonce processing.
* The 0 value occurs because we don't resolve
* sloppy relocations for unwind header target
* sections.
*/
if (initloc != 0) {
fde_count++;
/*
* FDEaddr is adjusted
* to account for the length & id which
* have already been consumed.
*/
}
}
/*
* the length does not include the length
* itself - so account for that too.
*/
}
}
done:
/*
* Do a quicksort on the binary table. If this is a cross
* link from a system with the opposite byte order, xlate
* the resulting values into LSB order.
*/
if (bswap) {
}
/*
* Fill in:
* first_frame_ptr
* fde_count
*/
hdroff = 4;
/* LINTED */
if (bswap)
hdroff += 4;
/* LINTED */
if (bswap)
/*
* If relaxed relocations are active, then there is a chance
* that we didn't use all the space reserved for this section.
* For details, see the note at head of ld_unwind_make_hdr() above.
*
* Find the PT_SUNW_UNWIND program header, and change the size values
* to the size of the subset of the section that was actually used.
*/
break;
}
}
}
return (1);
}
/*
* Append an .eh_frame section to our output list if not already present.
*
* Usually, there is a single .eh_frame output section. However, there can
* be more if there are incompatible section flags on incoming sections.
* If this does happen, the frame_ptr field of the eh_frame_hdr section
* will point at the base of the first output section, and the other
* sections will not be accessible via frame_ptr. However, the .eh_frame_hdr
* will be able to access all the data in the different .eh_frame sections,
* because the entries in sorted table are all encoded as DW_EH_PE_datarel.
*/
{
/*
* Check to see if this output section is already
* on the list.
*/
return (1);
/*
* Append output section to unwind list
*/
return (S_ERROR);
return (1);
}