diff options
Diffstat (limited to 'libdwarf/dwarf_frame2.c')
-rw-r--r-- | libdwarf/dwarf_frame2.c | 1531 |
1 files changed, 1531 insertions, 0 deletions
diff --git a/libdwarf/dwarf_frame2.c b/libdwarf/dwarf_frame2.c new file mode 100644 index 0000000..870442d --- /dev/null +++ b/libdwarf/dwarf_frame2.c @@ -0,0 +1,1531 @@ +/* + + Copyright (C) 2000-2006 Silicon Graphics, Inc. All Rights Reserved. + Portions Copyright (C) 2007-2011 David Anderson. All Rights Reserved. + Portions Copyright (C) 2010 SN Systems Ltd. All Rights Reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2.1 of the GNU Lesser General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it would be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + Further, this software is distributed without any warranty that it is + free of the rightful claim of any third person regarding infringement + or the like. Any license provided herein, whether implied or + otherwise, applies only to this software file. Patent licenses, if + any, provided herein do not apply to combinations of this program with + other software, or any other product whatsoever. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, write the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston MA 02110-1301, + USA. + + Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane, + Mountain View, CA 94043, or: + + http://www.sgi.com + + For further information regarding this notice, see: + + http://oss.sgi.com/projects/GenInfo/NoticeExplan + +*/ +/* The address of the Free Software Foundation is + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + Boston, MA 02110-1301, USA. + SGI has moved from the Crittenden Lane address. +*/ + + +/* This implements _dwarf_get_fde_list_internal() + and related helper functions for reading cie/fde data. */ + + + +#include "config.h" +#include "dwarf_incl.h" +#include <stdio.h> +#include <stdlib.h> +#include "dwarf_frame.h" +#include "dwarf_arange.h" /* using Arange as a way to build a list */ + + +static int dwarf_find_existing_cie_ptr(Dwarf_Small * cie_ptr, + Dwarf_Cie cur_cie_ptr, + Dwarf_Cie * cie_ptr_to_use_out, + Dwarf_Cie head_cie_ptr); +static void dealloc_fde_cie_list_internal(Dwarf_Fde head_fde_ptr, + Dwarf_Cie head_cie_ptr); +static int dwarf_create_cie_from_start(Dwarf_Debug dbg, + Dwarf_Small * cie_ptr_val, + Dwarf_Small * section_ptr, + Dwarf_Unsigned section_index, + Dwarf_Unsigned section_length, + Dwarf_Small * frame_ptr_end, + Dwarf_Unsigned cie_id_value, + Dwarf_Unsigned cie_count, +int use_gnu_cie_calc, +Dwarf_Cie * cie_ptr_to_use_out, +Dwarf_Error * error); + +static Dwarf_Small *get_cieptr_given_offset(Dwarf_Unsigned cie_id_value, + int use_gnu_cie_calc, + Dwarf_Small * section_ptr, + Dwarf_Small * cie_id_addr); +static int get_gcc_eh_augmentation(Dwarf_Debug dbg, + Dwarf_Small * frame_ptr, + unsigned long + *size_of_augmentation_data, + enum Dwarf_augmentation_type augtype, + Dwarf_Small * section_pointer, + Dwarf_Small * fde_eh_encoding_out, + char *augmentation); + +static int +gnu_aug_encodings(Dwarf_Debug dbg, char *augmentation, + Dwarf_Small * aug_data, Dwarf_Unsigned aug_data_len, + Dwarf_Half address_size, + unsigned char *pers_hand_enc_out, + unsigned char *lsda_enc_out, + unsigned char *fde_begin_enc_out, + Dwarf_Addr * gnu_pers_addr_out); + + +static int read_encoded_ptr(Dwarf_Debug dbg, + Dwarf_Small * section_pointer, + Dwarf_Small * input_field, + int gnu_encoding, + Dwarf_Half address_size, + Dwarf_Unsigned * addr, + Dwarf_Small ** input_field_out); + + + +static int qsort_compare(const void *elem1, const void *elem2); + + +/* Adds 'newone' to the end of the list starting at 'head' + and makes the new one 'cur'rent. */ +static void +chain_up_fde(Dwarf_Fde newone, Dwarf_Fde * head, Dwarf_Fde * cur) +{ + if (*head == NULL) + *head = newone; + else { + (*cur)->fd_next = newone; + } + *cur = newone; + +} + +/* Adds 'newone' to the end of the list starting at 'head' + and makes the new one 'cur'rent. */ +static void +chain_up_cie(Dwarf_Cie newone, Dwarf_Cie * head, Dwarf_Cie * cur) +{ + if (*head == NULL) { + *head = newone; + } else { + (*cur)->ci_next = newone; + } + *cur = newone; +} + +/* The size of the length field plus the + value of length must be an integral + multiple of the address size. Dwarf4 standard. + + A constant that gives the number of bytes of the CIE + structure, not including the length field itself + (where length mod <size of an address> == 0) + (see Section 7.2.2). Dwarf3 standard. + + A uword constant that gives the number of bytes of + the CIE structure, not including the + length field, itself (length mod <addressing unit size> == 0). + Dwarf2 standard.*/ +static void +validate_length(Dwarf_Debug dbg, + Dwarf_Cie cieptr, Dwarf_Unsigned length, + Dwarf_Unsigned length_size, + Dwarf_Unsigned extension_size, + Dwarf_Small * section_ptr, + Dwarf_Small * ciefde_start, + const char * cieorfde) +{ + Dwarf_Unsigned address_size = cieptr->ci_address_size; + Dwarf_Unsigned length_field_summed = length_size + extension_size; + Dwarf_Unsigned total_len = length + length_field_summed; + Dwarf_Unsigned mod = total_len % address_size; + + if (mod != 0) { + char msg[DW_HARMLESS_ERROR_MSG_STRING_SIZE]; + Dwarf_Unsigned sectionoffset = ciefde_start - section_ptr; + snprintf(msg,sizeof(msg), + "DW_DLE_DEBUG_FRAME_LENGTH_NOT_MULTIPLE" + " len=0x%" DW_PR_XZEROS DW_PR_DUx + ", len size=0x%" DW_PR_XZEROS DW_PR_DUx + ", extn size=0x%" DW_PR_XZEROS DW_PR_DUx + ", totl length=0x%" DW_PR_XZEROS DW_PR_DUx + ", addr size=0x%" DW_PR_XZEROS DW_PR_DUx + ", mod=0x%" DW_PR_XZEROS DW_PR_DUx " must be zero" + " in %s" + ", offset 0x%" DW_PR_XZEROS DW_PR_DUx ".", + length, + length_size, + extension_size, + total_len,address_size, mod, + cieorfde, + sectionoffset); + dwarf_insert_harmless_error(dbg,msg); + } + return; +} + + +#if 0 +/* For debugging only. */ +static void +print_prefix(struct cie_fde_prefix_s *prefix, int line) +{ + printf("prefix-print, prefix at 0x%lx, line %d\n", + (long) prefix, line); + printf(" start addr 0x%lx after prefix 0x%lx\n", + (long) prefix->cf_start_addr, + (long) prefix->cf_addr_after_prefix); + printf(" length 0x%" DW_PR_DUx ", len size %d ext size %d\n", + (Dwarf_Unsigned) prefix->cf_length, + prefix->cf_local_length_size, + prefix->cf_local_extension_size); + printf(" cie_id 0x%" DW_PR_DUx " cie_id cie_id_addr 0x%lx\n", + (Dwarf_Unsigned) prefix->cf_cie_id, + (long) prefix->cf_cie_id_addr); + printf + (" sec ptr 0x%lx sec index %" DW_PR_DSd " sec len 0x%" DW_PR_DUx " sec past end 0x%lx\n", + (long) prefix->cf_section_ptr, + (Dwarf_Signed) prefix->cf_section_index, + (Dwarf_Unsigned) prefix->cf_section_length, + (long) prefix->cf_section_ptr + prefix->cf_section_length); +} +#endif + + + +/* Internal function called from various places to create + lists of CIEs and FDEs. Not directly called + by consumer code */ +int +_dwarf_get_fde_list_internal(Dwarf_Debug dbg, Dwarf_Cie ** cie_data, + Dwarf_Signed * cie_element_count, + Dwarf_Fde ** fde_data, + Dwarf_Signed * fde_element_count, + Dwarf_Small * section_ptr, + Dwarf_Unsigned section_index, + Dwarf_Unsigned section_length, + Dwarf_Unsigned cie_id_value, + int use_gnu_cie_calc, Dwarf_Error * error) +{ + /* Scans the debug_frame section. */ + Dwarf_Small *frame_ptr = section_ptr; + Dwarf_Small *frame_ptr_end = section_ptr + section_length; + + + + /* New_cie points to the Cie being read, and head_cie_ptr and + cur_cie_ptr are used for chaining them up in sequence. + In case cie's are reused aggressively we need tail_cie_ptr + to add to the chain. If we re-use an early cie + later on, that does not mean we chain a new cie to the early one, + we always chain it to the tail. */ + Dwarf_Cie head_cie_ptr = NULL; + Dwarf_Cie cur_cie_ptr = NULL; + Dwarf_Cie tail_cie_ptr = NULL; + Dwarf_Word cie_count = 0; + + /* Points to a list of contiguous pointers to Dwarf_Cie structures. + */ + Dwarf_Cie *cie_list_ptr = 0; + + + /* New_fde points to the Fde being created, and head_fde_ptr and + cur_fde_ptr are used to chain them up. */ + Dwarf_Fde head_fde_ptr = NULL; + Dwarf_Fde cur_fde_ptr = NULL; + Dwarf_Word fde_count = 0; + + /* Points to a list of contiguous pointers to Dwarf_Fde structures. + */ + Dwarf_Fde *fde_list_ptr = NULL; + + Dwarf_Word i = 0; + int res = DW_DLV_ERROR; + + if (frame_ptr == 0) { + return DW_DLV_NO_ENTRY; + } + + /* We create the fde and cie arrays. Processing each CIE as we come + to it or as an FDE refers to it. We cannot process 'late' CIEs + late as GNU .eh_frame complexities mean we need the whole CIE + before we can process the FDE correctly. */ + while (frame_ptr < frame_ptr_end) { + + struct cie_fde_prefix_s prefix; + + /* First read in the 'common prefix' to figure out what we are + to do with this entry. */ + memset(&prefix, 0, sizeof(prefix)); + res = dwarf_read_cie_fde_prefix(dbg, + frame_ptr, section_ptr, + section_index, + section_length, &prefix, error); + if (res == DW_DLV_ERROR) { + dealloc_fde_cie_list_internal(head_fde_ptr, head_cie_ptr); + return res; + } + if (res == DW_DLV_NO_ENTRY) + break; + frame_ptr = prefix.cf_addr_after_prefix; + if (frame_ptr >= frame_ptr_end) { + dealloc_fde_cie_list_internal(head_fde_ptr, head_cie_ptr); + _dwarf_error(dbg, error, DW_DLE_DEBUG_FRAME_LENGTH_BAD); + return DW_DLV_ERROR; + + } + + if (prefix.cf_cie_id == cie_id_value) { + /* This is a CIE. */ + Dwarf_Cie cie_ptr_to_use = 0; + + int res = dwarf_find_existing_cie_ptr(prefix.cf_start_addr, + cur_cie_ptr, + &cie_ptr_to_use, + head_cie_ptr); + if (res == DW_DLV_OK) { + cur_cie_ptr = cie_ptr_to_use; + /* Ok. Seen already. */ + } else if (res == DW_DLV_NO_ENTRY) { + /* CIE before its FDE in this case. */ + res = dwarf_create_cie_from_after_start(dbg, + &prefix, + section_ptr, + frame_ptr, + cie_count, + use_gnu_cie_calc, + &cie_ptr_to_use, + error); + /* ASSERT: res==DW_DLV_NO_ENTRY impossible. */ + if (res == DW_DLV_ERROR) { + dealloc_fde_cie_list_internal(head_fde_ptr, + head_cie_ptr); + return res; + } + /* ASSERT res != DW_DLV_NO_ENTRY */ + cie_count++; + chain_up_cie(cie_ptr_to_use, &head_cie_ptr, + &tail_cie_ptr); + cur_cie_ptr = tail_cie_ptr; + } else { /* res == DW_DLV_ERROR */ + + dealloc_fde_cie_list_internal(head_fde_ptr, + head_cie_ptr); + return res; + } + frame_ptr = cie_ptr_to_use->ci_cie_start + + cie_ptr_to_use->ci_length + + cie_ptr_to_use->ci_length_size + + cie_ptr_to_use->ci_extension_size; + continue; + } else { + /* This is an FDE, Frame Description Entry, see the Dwarf + Spec, section 6.4.1 */ + int res = DW_DLV_ERROR; + Dwarf_Cie cie_ptr_to_use = 0; + Dwarf_Fde fde_ptr_to_use = 0; + + /* Do not call this twice on one prefix, as + prefix.cf_cie_id_addr is altered as a side effect. */ + Dwarf_Small *cieptr_val = + get_cieptr_given_offset(prefix.cf_cie_id, + use_gnu_cie_calc, + section_ptr, + prefix.cf_cie_id_addr); + + res = dwarf_find_existing_cie_ptr(cieptr_val, + cur_cie_ptr, + &cie_ptr_to_use, + head_cie_ptr); + if (res == DW_DLV_OK) { + cur_cie_ptr = cie_ptr_to_use; + /* Ok. Seen CIE already. */ + } else if (res == DW_DLV_NO_ENTRY) { + res = dwarf_create_cie_from_start(dbg, + cieptr_val, + section_ptr, + section_index, + section_length, + frame_ptr_end, + cie_id_value, + cie_count, + use_gnu_cie_calc, + &cie_ptr_to_use, + error); + if (res == DW_DLV_ERROR) { + dealloc_fde_cie_list_internal(head_fde_ptr, + head_cie_ptr); + return res; + } else if (res == DW_DLV_NO_ENTRY) { + return res; + } + ++cie_count; + chain_up_cie(cie_ptr_to_use, &head_cie_ptr, + &tail_cie_ptr); + cur_cie_ptr = tail_cie_ptr; + + } else { + /* DW_DLV_ERROR */ + return res; + } + + res = dwarf_create_fde_from_after_start(dbg, + &prefix, + section_ptr, + frame_ptr, + use_gnu_cie_calc, + cie_ptr_to_use, + &fde_ptr_to_use, + error); + if (res == DW_DLV_ERROR) { + return res; + } + chain_up_fde(fde_ptr_to_use, &head_fde_ptr, &cur_fde_ptr); + fde_count++; + /* ASSERT: DW_DLV_OK. */ + frame_ptr = fde_ptr_to_use->fd_fde_start + + fde_ptr_to_use->fd_length + + fde_ptr_to_use->fd_length_size + + fde_ptr_to_use->fd_extension_size; + if (frame_ptr < fde_ptr_to_use->fd_fde_instr_start) { + /* Sanity check. With a really short fde instruction + set and address_size we think is 8 + as it is ELF64 (but is + really 4, as in DWARF{2,3} where we have + no FDE address_size) we emit an error. + This error means things will not go well. */ + _dwarf_error(dbg,error, + DW_DLE_DEBUG_FRAME_POSSIBLE_ADDRESS_BOTCH); + return DW_DLV_ERROR; + } + + + continue; + } + } + /* Now build list of CIEs from the list. If there are no CIEs + there should be no FDEs. */ + if (cie_count > 0) { + cie_list_ptr = (Dwarf_Cie *) + _dwarf_get_alloc(dbg, DW_DLA_LIST, cie_count); + } else { + if(fde_count > 0) { + dealloc_fde_cie_list_internal(head_fde_ptr, head_cie_ptr); + _dwarf_error(dbg, error, DW_DLE_ORPHAN_FDE); + return DW_DLV_ERROR; + } + dealloc_fde_cie_list_internal(head_fde_ptr, head_cie_ptr); + return DW_DLV_NO_ENTRY; + } + if (cie_list_ptr == NULL) { + dealloc_fde_cie_list_internal(head_fde_ptr, head_cie_ptr); + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return DW_DLV_ERROR; + } + cur_cie_ptr = head_cie_ptr; + for (i = 0; i < cie_count; i++) { + *(cie_list_ptr + i) = cur_cie_ptr; + cur_cie_ptr = cur_cie_ptr->ci_next; + } + + /* Now build array of FDEs from the list. + With orphan CIEs (meaning no FDEs) + lets not return DW_DLV_NO_ENTRY */ + if (fde_count > 0) { + fde_list_ptr = (Dwarf_Fde *) + _dwarf_get_alloc(dbg, DW_DLA_LIST, fde_count); + } + + // It is ok if fde_list_ptr is NULL, we just have no fdes. + cur_fde_ptr = head_fde_ptr; + for (i = 0; i < fde_count; i++) { + *(fde_list_ptr + i) = cur_fde_ptr; + cur_fde_ptr = cur_fde_ptr->fd_next; + } + + + /* Return arguments. */ + *cie_data = cie_list_ptr; + *cie_element_count = cie_count; + + *fde_data = fde_list_ptr; + *fde_element_count = fde_count; + if(use_gnu_cie_calc) { + dbg->de_fde_data_eh = fde_list_ptr; + dbg->de_fde_count_eh = fde_count; + dbg->de_cie_data_eh = cie_list_ptr; + dbg->de_cie_count_eh = cie_count; + } else { + dbg->de_fde_data = fde_list_ptr; + dbg->de_fde_count = fde_count; + dbg->de_cie_data = cie_list_ptr; + dbg->de_cie_count = cie_count; + } + + /* Sort the list by the address so that dwarf_get_fde_at_pc() can + binary search this list. */ + if(fde_count > 0) { + qsort((void *) fde_list_ptr, fde_count, sizeof(Dwarf_Ptr), + qsort_compare); + } + + return (DW_DLV_OK); +} + +/* Internal function, not called by consumer code. + 'prefix' has accumulated the info up thru the cie-id + and now we consume the rest and build a Dwarf_Cie_s structure. +*/ +int +dwarf_create_cie_from_after_start(Dwarf_Debug dbg, + struct cie_fde_prefix_s *prefix, + Dwarf_Small * section_pointer, + Dwarf_Small * frame_ptr, + Dwarf_Unsigned cie_count, + int use_gnu_cie_calc, + Dwarf_Cie * cie_ptr_out, + Dwarf_Error * error) +{ + Dwarf_Cie new_cie = 0; + + /* egcs-1.1.2 .eh_frame uses 0 as the distinguishing id. sgi uses + -1 (in .debug_frame). .eh_frame not quite identical to + .debug_frame */ + /* We here default the address size as it is not present + in DWARF2 or DWARF3 cie data, below we set it right if + it is present. */ + Dwarf_Half address_size = dbg->de_pointer_size; + Dwarf_Small eh_fde_encoding = 0; + Dwarf_Small *augmentation = 0; + Dwarf_Half segment_size = 0; + Dwarf_Sword data_alignment_factor = -1; + Dwarf_Word code_alignment_factor = 4; + Dwarf_Unsigned return_address_register = 31; + int local_length_size = 0; + Dwarf_Word leb128_length = 0; + Dwarf_Unsigned cie_aug_data_len = 0; + Dwarf_Small *cie_aug_data = 0; + Dwarf_Addr gnu_personality_handler_addr = 0; + unsigned char gnu_personality_handler_encoding = 0; + unsigned char gnu_lsda_encoding = 0; + unsigned char gnu_fde_begin_encoding = 0; + + + enum Dwarf_augmentation_type augt = aug_unknown; + + + /* This is a CIE, Common Information Entry: See the dwarf spec, + section 6.4.1 */ + Dwarf_Small version = *(Dwarf_Small *) frame_ptr; + + frame_ptr++; + if (version != DW_CIE_VERSION && version != DW_CIE_VERSION3 && + version != DW_CIE_VERSION4) { + _dwarf_error(dbg, error, DW_DLE_FRAME_VERSION_BAD); + return (DW_DLV_ERROR); + } + + augmentation = frame_ptr; + frame_ptr = frame_ptr + strlen((char *) frame_ptr) + 1; + augt = _dwarf_get_augmentation_type(dbg, + augmentation, use_gnu_cie_calc); + if (augt == aug_eh) { + /* REFERENCED *//* Not used in this instance */ + Dwarf_Unsigned exception_table_addr; + + /* this is per egcs-1.1.2 as on RH 6.0 */ + READ_UNALIGNED(dbg, exception_table_addr, + Dwarf_Unsigned, frame_ptr, local_length_size); + frame_ptr += local_length_size; + } + { + Dwarf_Unsigned lreg = 0; + unsigned long size = 0; + + if( version == DW_CIE_VERSION4) { + address_size = *((unsigned char *)frame_ptr); + if(address_size > sizeof(Dwarf_Addr)) { + _dwarf_error(dbg, error, DW_DLE_ADDRESS_SIZE_ERROR); + return (DW_DLV_ERROR); + } + ++frame_ptr; + segment_size = *((unsigned char *)frame_ptr); + ++frame_ptr; + if(segment_size > sizeof(Dwarf_Addr)) { + _dwarf_error(dbg, error, DW_DLE_SEGMENT_SIZE_BAD); + return (DW_DLV_ERROR); + } + } + + DECODE_LEB128_UWORD(frame_ptr, lreg); + code_alignment_factor = (Dwarf_Word) lreg; + data_alignment_factor = + (Dwarf_Sword) _dwarf_decode_s_leb128(frame_ptr, + &leb128_length); + frame_ptr = frame_ptr + leb128_length; + return_address_register = + _dwarf_get_return_address_reg(frame_ptr, version, &size); + if (return_address_register > dbg->de_frame_reg_rules_entry_count) { + _dwarf_error(dbg, error, DW_DLE_CIE_RET_ADDR_REG_ERROR); + return (DW_DLV_ERROR); + } + frame_ptr += size; + } + switch (augt) { + case aug_empty_string: + break; + case aug_irix_mti_v1: + break; + case aug_irix_exception_table:{ + Dwarf_Unsigned lreg = 0; + Dwarf_Word length_of_augmented_fields; + + /* Decode the length of augmented fields. */ + DECODE_LEB128_UWORD(frame_ptr, lreg); + length_of_augmented_fields = (Dwarf_Word) lreg; + /* set the frame_ptr to point at the instruction start. */ + frame_ptr += length_of_augmented_fields; + } + break; + + case aug_eh:{ + int err = 0; + unsigned long increment = 0; + + if (!use_gnu_cie_calc) { + /* This should be impossible. */ + _dwarf_error(dbg, error, + DW_DLE_FRAME_AUGMENTATION_UNKNOWN); + return DW_DLV_ERROR; + } + + err = get_gcc_eh_augmentation(dbg, frame_ptr, &increment, + augt, + prefix->cf_section_ptr, + &eh_fde_encoding, + (char *) augmentation); + if (err == DW_DLV_ERROR) { + _dwarf_error(dbg, error, + DW_DLE_FRAME_AUGMENTATION_UNKNOWN); + return DW_DLV_ERROR; + } + frame_ptr += increment; + } + break; + case aug_gcc_eh_z:{ + /* Here we have Augmentation Data Length (uleb128) followed + by Augmentation Data bytes. */ + int res = DW_DLV_ERROR; + Dwarf_Unsigned adlen = 0; + + DECODE_LEB128_UWORD(frame_ptr, adlen); + cie_aug_data_len = adlen; + cie_aug_data = frame_ptr; + res = gnu_aug_encodings(dbg, + (char *) augmentation, + cie_aug_data, + cie_aug_data_len, + address_size, + &gnu_personality_handler_encoding, + &gnu_lsda_encoding, + &gnu_fde_begin_encoding, + &gnu_personality_handler_addr); + if (res != DW_DLV_OK) { + _dwarf_error(dbg, error, + DW_DLE_FRAME_AUGMENTATION_UNKNOWN); + return res; + } + frame_ptr += adlen; + } + break; + case aug_armcc: + break; + default:{ + /* We do not understand the augmentation string. No + assumption can be made about any fields other than what + we have already read. */ + frame_ptr = prefix->cf_start_addr + + prefix->cf_length + prefix->cf_local_length_size + + prefix->cf_local_extension_size; + /* FIX -- What are the values of data_alignment_factor, + code_alignement_factor, return_address_register and + instruction start? They were clearly uninitalized in the + previous version and I am leaving them the same way. */ + } + break; + } /* End switch on augmentation type. */ + + new_cie = (Dwarf_Cie) _dwarf_get_alloc(dbg, DW_DLA_CIE, 1); + if (new_cie == NULL) { + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + new_cie->ci_cie_version_number = version; + new_cie->ci_initial_table = NULL; + new_cie->ci_length = (Dwarf_Word) prefix->cf_length; + new_cie->ci_length_size = prefix->cf_local_length_size; + new_cie->ci_extension_size = prefix->cf_local_extension_size; + new_cie->ci_augmentation = (char *) augmentation; + + new_cie->ci_data_alignment_factor = + (Dwarf_Sbyte) data_alignment_factor; + new_cie->ci_code_alignment_factor = + (Dwarf_Small) code_alignment_factor; + new_cie->ci_return_address_register = return_address_register; + new_cie->ci_cie_start = prefix->cf_start_addr; + new_cie->ci_cie_instr_start = frame_ptr; + new_cie->ci_dbg = dbg; + new_cie->ci_augmentation_type = augt; + new_cie->ci_gnu_eh_augmentation_len = cie_aug_data_len; + new_cie->ci_gnu_eh_augmentation_bytes = cie_aug_data; + new_cie->ci_gnu_personality_handler_encoding = + gnu_personality_handler_encoding; + new_cie->ci_gnu_personality_handler_addr = + gnu_personality_handler_addr; + new_cie->ci_gnu_lsda_encoding = gnu_lsda_encoding; + new_cie->ci_gnu_fde_begin_encoding = gnu_fde_begin_encoding; + + new_cie->ci_index = cie_count; + new_cie->ci_section_ptr = prefix->cf_section_ptr; + /* The Following new in DWARF4 */ + new_cie->ci_address_size = address_size; + new_cie->ci_segment_size = segment_size; + validate_length(dbg,new_cie,new_cie->ci_length, + new_cie->ci_length_size, new_cie->ci_extension_size, + new_cie->ci_section_ptr, + new_cie->ci_cie_start,"cie"); + + *cie_ptr_out = new_cie; + return DW_DLV_OK; + +} + + +/* Internal function, not called by consumer code. + 'prefix' has accumulated the info up thru the cie-id + and now we consume the rest and build a Dwarf_Fde_s structure. */ + +int +dwarf_create_fde_from_after_start(Dwarf_Debug dbg, + struct cie_fde_prefix_s *prefix, + Dwarf_Small * section_pointer, + Dwarf_Small * frame_ptr, + int use_gnu_cie_calc, + Dwarf_Cie cie_ptr_in, + Dwarf_Fde * fde_ptr_out, + Dwarf_Error * error) +{ + Dwarf_Fde new_fde = 0; + Dwarf_Cie cieptr = cie_ptr_in; + Dwarf_Small *saved_frame_ptr = 0; + + Dwarf_Small *initloc = frame_ptr; + Dwarf_Signed offset_into_exception_tables + /* must be min dwarf_sfixed in size */ + = (Dwarf_Signed) DW_DLX_NO_EH_OFFSET; + Dwarf_Small *fde_aug_data = 0; + Dwarf_Unsigned fde_aug_data_len = 0; + Dwarf_Addr cie_base_offset = prefix->cf_cie_id; + Dwarf_Addr initial_location = 0; /* must be min de_pointer_size + bytes in size */ + Dwarf_Addr address_range = 0; /* must be min de_pointer_size + bytes in size */ + Dwarf_Half address_size = cie_ptr_in->ci_address_size; + + enum Dwarf_augmentation_type augt = cieptr->ci_augmentation_type; + + if (augt == aug_gcc_eh_z) { + /* If z augmentation this is eh_frame, and initial_location and + address_range in the FDE are read according to the CIE + augmentation string instructions. */ + + { + Dwarf_Small *fp_updated = 0; + int res = read_encoded_ptr(dbg, + section_pointer, + frame_ptr, + cieptr-> ci_gnu_fde_begin_encoding, + address_size, + &initial_location, + &fp_updated); + if (res != DW_DLV_OK) { + _dwarf_error(dbg, error, + DW_DLE_FRAME_AUGMENTATION_UNKNOWN); + return DW_DLV_ERROR; + } + frame_ptr = fp_updated; + /* For the address-range it makes no sense to be + pc-relative, so we turn it off with a section_pointer of + NULL. Masking off DW_EH_PE_pcrel from the + ci_gnu_fde_begin_encoding in this call would also work + to turn off DW_EH_PE_pcrel. */ + res = read_encoded_ptr(dbg, (Dwarf_Small *) NULL, + frame_ptr, + cieptr->ci_gnu_fde_begin_encoding, + address_size, + &address_range, &fp_updated); + if (res != DW_DLV_OK) { + _dwarf_error(dbg, error, + DW_DLE_FRAME_AUGMENTATION_UNKNOWN); + return DW_DLV_ERROR; + } + frame_ptr = fp_updated; + } + { + Dwarf_Unsigned adlen = 0; + + DECODE_LEB128_UWORD(frame_ptr, adlen); + fde_aug_data_len = adlen; + fde_aug_data = frame_ptr; + frame_ptr += adlen; + } + + } else { + READ_UNALIGNED(dbg, initial_location, Dwarf_Addr, + frame_ptr, address_size); + frame_ptr += address_size; + READ_UNALIGNED(dbg, address_range, Dwarf_Addr, + frame_ptr, address_size); + frame_ptr += address_size; + } + switch (augt) { + case aug_irix_mti_v1: + case aug_empty_string: + break; + case aug_irix_exception_table:{ + Dwarf_Unsigned lreg = 0; + Dwarf_Word length_of_augmented_fields = 0; + + DECODE_LEB128_UWORD(frame_ptr, lreg); + length_of_augmented_fields = (Dwarf_Word) lreg; + + saved_frame_ptr = frame_ptr; + /* The first word is an offset into exception tables. + Defined as a 32bit offset even for CC -64. */ + READ_UNALIGNED(dbg, offset_into_exception_tables, + Dwarf_Addr, frame_ptr, sizeof(Dwarf_sfixed)); + SIGN_EXTEND(offset_into_exception_tables, + sizeof(Dwarf_sfixed)); + frame_ptr = saved_frame_ptr + length_of_augmented_fields; + } + break; + case aug_eh:{ + Dwarf_Unsigned eh_table_value = 0; + + if (!use_gnu_cie_calc) { + /* This should be impossible. */ + _dwarf_error(dbg, error, + DW_DLE_FRAME_AUGMENTATION_UNKNOWN); + return DW_DLV_ERROR; + } + + /* gnu eh fde case. we do not need to do anything */ + /*REFERENCED*/ /* Not used in this instance of the macro */ + READ_UNALIGNED(dbg, eh_table_value, + Dwarf_Unsigned, frame_ptr, + address_size); + frame_ptr += address_size; + } + break; + + case aug_gcc_eh_z:{ + /* The Augmentation Data Length is here, followed by the + Augmentation Data bytes themselves. */ + } + break; + case aug_armcc: + break; + case aug_past_last: + break; + case aug_unknown: + _dwarf_error(dbg, error, DW_DLE_FRAME_AUGMENTATION_UNKNOWN); + return DW_DLV_ERROR; + } /* End switch on augmentation type */ + new_fde = (Dwarf_Fde) _dwarf_get_alloc(dbg, DW_DLA_FDE, 1); + if (new_fde == NULL) { + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + new_fde->fd_length = prefix->cf_length; + new_fde->fd_length_size = prefix->cf_local_length_size; + new_fde->fd_extension_size = prefix->cf_local_extension_size; + new_fde->fd_is_eh = use_gnu_cie_calc; + new_fde->fd_cie_offset = cie_base_offset; + new_fde->fd_cie_index = cieptr->ci_index; + new_fde->fd_cie = cieptr; + new_fde->fd_initial_location = initial_location; + new_fde->fd_initial_loc_pos = initloc; + new_fde->fd_address_range = address_range; + new_fde->fd_fde_start = prefix->cf_start_addr; + new_fde->fd_fde_instr_start = frame_ptr; + new_fde->fd_dbg = dbg; + new_fde->fd_offset_into_exception_tables = + offset_into_exception_tables; + + new_fde->fd_section_ptr = prefix->cf_section_ptr; + new_fde->fd_section_index = prefix->cf_section_index; + new_fde->fd_section_length = prefix->cf_section_length; + + new_fde->fd_gnu_eh_augmentation_bytes = fde_aug_data; + new_fde->fd_gnu_eh_augmentation_len = fde_aug_data_len; + validate_length(dbg,cieptr,new_fde->fd_length, + new_fde->fd_length_size, new_fde->fd_extension_size, + new_fde->fd_section_ptr,new_fde->fd_fde_start,"fde"); + + + *fde_ptr_out = new_fde; + return DW_DLV_OK; +} + +/* Called by qsort to compare FDE entries. + Consumer code expects the array of FDE pointers to be + in address order. +*/ +static int +qsort_compare(const void *elem1, const void *elem2) +{ + Dwarf_Fde fde1 = *(Dwarf_Fde *) elem1; + Dwarf_Fde fde2 = *(Dwarf_Fde *) elem2; + Dwarf_Addr addr1 = fde1->fd_initial_location; + Dwarf_Addr addr2 = fde2->fd_initial_location; + + if (addr1 < addr2) { + return -1; + } else if (addr1 > addr2) { + return 1; + } + return 0; +} + + +/* Read in the common cie/fde prefix, including reading + the cie-value which shows which this is: cie or fde. */ +int +dwarf_read_cie_fde_prefix(Dwarf_Debug dbg, + Dwarf_Small * frame_ptr_in, + Dwarf_Small * section_ptr_in, + Dwarf_Unsigned section_index_in, + Dwarf_Unsigned section_length_in, + struct cie_fde_prefix_s *data_out, + Dwarf_Error * error) +{ + Dwarf_Unsigned length = 0; + int local_length_size = 0; + int local_extension_size = 0; + Dwarf_Small *frame_ptr = frame_ptr_in; + Dwarf_Small *cie_ptr_addr = 0; + Dwarf_Unsigned cie_id = 0; + + /* READ_AREA_LENGTH updates frame_ptr for consumed bytes */ + READ_AREA_LENGTH(dbg, length, Dwarf_Unsigned, + frame_ptr, local_length_size, + local_extension_size); + + if (length == 0) { + /* nul bytes at end of section, seen at end of egcs eh_frame + sections (in a.out). Take this as meaning no more CIE/FDE + data. We should be very close to end of section. */ + return DW_DLV_NO_ENTRY; + } + + cie_ptr_addr = frame_ptr; + READ_UNALIGNED(dbg, cie_id, Dwarf_Unsigned, + frame_ptr, local_length_size); + SIGN_EXTEND(cie_id, local_length_size); + frame_ptr += local_length_size; + + data_out->cf_start_addr = frame_ptr_in; + data_out->cf_addr_after_prefix = frame_ptr; + + data_out->cf_length = length; + data_out->cf_local_length_size = local_length_size; + data_out->cf_local_extension_size = local_extension_size; + data_out->cf_cie_id = cie_id; + data_out->cf_cie_id_addr = cie_ptr_addr; + data_out->cf_section_ptr = section_ptr_in; + data_out->cf_section_index = section_index_in; + data_out->cf_section_length = section_length_in; + return DW_DLV_OK; +} + +/* On various errors previously-allocated CIEs and FDEs + must be cleaned up. + This helps avoid leaks in case of errors. +*/ +static void +dealloc_fde_cie_list_internal(Dwarf_Fde head_fde_ptr, + Dwarf_Cie head_cie_ptr) +{ + Dwarf_Fde curfde = 0; + Dwarf_Cie curcie = 0; + Dwarf_Fde nextfde = 0; + Dwarf_Cie nextcie = 0; + + for (curfde = head_fde_ptr; curfde; curfde = nextfde) { + nextfde = curfde->fd_next; + dwarf_dealloc(curfde->fd_dbg, curfde, DW_DLA_FDE); + } + for (curcie = head_cie_ptr; curcie; curcie = nextcie) { + Dwarf_Frame frame = curcie->ci_initial_table; + + nextcie = curcie->ci_next; + if (frame) + dwarf_dealloc(curcie->ci_dbg, frame, DW_DLA_FRAME); + dwarf_dealloc(curcie->ci_dbg, curcie, DW_DLA_CIE); + } +} + +/* Find the cie whose id value is given: the id + value is, per DWARF2/3, an offset in the section. + For .debug_frame, zero is a legal offset. For + GNU .eh_frame it is not a legal offset. + 'cie_ptr' is a pointer into our section, not an offset. */ +static int +dwarf_find_existing_cie_ptr(Dwarf_Small * cie_ptr, + Dwarf_Cie cur_cie_ptr, + Dwarf_Cie * cie_ptr_to_use_out, + Dwarf_Cie head_cie_ptr) +{ + Dwarf_Cie next = 0; + + if (cur_cie_ptr && cie_ptr == cur_cie_ptr->ci_cie_start) { + /* Usually, we use the same cie again and again. */ + *cie_ptr_to_use_out = cur_cie_ptr; + return DW_DLV_OK; + } + for (next = head_cie_ptr; next; next = next->ci_next) { + if (cie_ptr == next->ci_cie_start) { + *cie_ptr_to_use_out = next; + return DW_DLV_OK; + } + } + return DW_DLV_NO_ENTRY; +} + + +/* We have a valid cie_ptr_val that has not been + turned into an internal Cie yet. Do so now. + Returns DW_DLV_OK or DW_DLV_ERROR, never + DW_DLV_NO_ENTRY. + + 'section_ptr' - Points to first byte of section data. + 'section_length' - Length of the section, in bytes. + 'frame_ptr_end' - Points 1-past last byte of section data. */ +static int +dwarf_create_cie_from_start(Dwarf_Debug dbg, + Dwarf_Small * cie_ptr_val, + Dwarf_Small * section_ptr, + Dwarf_Unsigned section_index, + Dwarf_Unsigned section_length, + Dwarf_Small * frame_ptr_end, + Dwarf_Unsigned cie_id_value, + Dwarf_Unsigned cie_count, + int use_gnu_cie_calc, + Dwarf_Cie * cie_ptr_to_use_out, + Dwarf_Error * error) +{ + struct cie_fde_prefix_s prefix; + int res = DW_DLV_ERROR; + Dwarf_Small *frame_ptr = cie_ptr_val; + + if (frame_ptr < section_ptr || frame_ptr > frame_ptr_end) { + _dwarf_error(dbg, error, DW_DLE_DEBUG_FRAME_LENGTH_BAD); + return DW_DLV_ERROR; + } + /* First read in the 'common prefix' to figure out what * we are to + do with this entry. If it is not a cie * we are in big trouble. */ + memset(&prefix, 0, sizeof(prefix)); + res = dwarf_read_cie_fde_prefix(dbg, frame_ptr, section_ptr, + section_index, section_length, + &prefix, error); + if (res == DW_DLV_ERROR) { + return res; + } + if (res == DW_DLV_NO_ENTRY) { + /* error. */ + _dwarf_error(dbg, error, DW_DLE_FRAME_CIE_DECODE_ERROR); + return DW_DLV_ERROR; + + } + + if (prefix.cf_cie_id != cie_id_value) { + _dwarf_error(dbg, error, DW_DLE_FRAME_CIE_DECODE_ERROR); + return DW_DLV_ERROR; + } + frame_ptr = prefix.cf_addr_after_prefix; + res = dwarf_create_cie_from_after_start(dbg, + &prefix, + section_ptr, + frame_ptr, + cie_count, + use_gnu_cie_calc, + cie_ptr_to_use_out, error); + return res; + +} + + +/* This is for gnu eh frames, the 'z' case. + We find the letter involved + Return the augmentation character and, if applicable, + the personality routine address. + + personality_routine_out - + if 'P' is augchar, is personality handler addr. + Otherwise is not set. + aug_data - if 'P' points to data space of the + aug_data_len - length of areas aug_data points to. + +*/ +#if 0 +/* For debugging only. */ +void +dump_bytes(Dwarf_Small * start, long len) +{ + Dwarf_Small *end = start + len; + Dwarf_Small *cur = start; + + for (; cur < end; cur++) { + printf(" byte %d, data %02x\n", (int) (cur - start), *cur); + } + +} +#endif +static int +gnu_aug_encodings(Dwarf_Debug dbg, char *augmentation, + Dwarf_Small * aug_data, Dwarf_Unsigned aug_data_len, + Dwarf_Half address_size, + unsigned char *pers_hand_enc_out, + unsigned char *lsda_enc_out, + unsigned char *fde_begin_enc_out, + Dwarf_Addr * gnu_pers_addr_out) +{ + char *nc = 0; + Dwarf_Small *cur_aug_p = aug_data; + Dwarf_Small *end_aug_p = aug_data + aug_data_len; + + for (nc = augmentation; *nc; ++nc) { + char c = *nc; + + switch (c) { + case 'z': + /* Means that the augmentation data is present. */ + continue; + + case 'S': + /* Indicates this is a signal stack frame. + Debuggers have to do + special handling. We don't need to do more than + print this flag at the right time, though + (see dwarfdump where it prints the augmentation + string). + A signal stack frame (in some OS's) can only be + unwound (backtraced) by knowing it is a signal + stack frame (perhaps by noticing the name of the + function for the stack frame if the name can be + found somehow) and figuring + out (or knowing) how the kernel and libc + pushed a structure + onto the stack and loading registers from that structure. + Totally different from normal stack unwinding. + This flag gives an unwinder a big leg up by + decoupling the 'hint: this is a stack frame' + from knowledge like + the function name (the name might be + unavailable at unwind time). + */ + break; + + case 'L': + if (cur_aug_p > end_aug_p) { + return DW_DLV_ERROR; + } + *lsda_enc_out = *(unsigned char *) cur_aug_p; + ++cur_aug_p; + break; + case 'R': + /* Followed by a one byte argument giving the + pointer encoding for the address pointers in the fde. */ + if (cur_aug_p >= end_aug_p) { + return DW_DLV_ERROR; + } + *fde_begin_enc_out = *(unsigned char *) cur_aug_p; + ++cur_aug_p; + break; + case 'P':{ + int res = DW_DLV_ERROR; + Dwarf_Small *updated_aug_p = 0; + unsigned char encoding = 0; + + if (cur_aug_p >= end_aug_p) { + return DW_DLV_ERROR; + } + encoding = *(unsigned char *) cur_aug_p; + *pers_hand_enc_out = encoding; + ++cur_aug_p; + if (cur_aug_p > end_aug_p) { + return DW_DLV_ERROR; + } + /* DW_EH_PE_pcrel makes no sense here, so we turn it + off via a section pointer of NULL. */ + res = read_encoded_ptr(dbg, + (Dwarf_Small *) NULL, + cur_aug_p, + encoding, + address_size, + gnu_pers_addr_out, + &updated_aug_p); + if (res != DW_DLV_OK) { + return res; + } + cur_aug_p = updated_aug_p; + if (cur_aug_p > end_aug_p) { + return DW_DLV_ERROR; + } + } + break; + default: + return DW_DLV_ERROR; + + } + } + + return DW_DLV_OK; +} + +/* Given augmentation character (the encoding) giving the + address format, read the address from input_field + and return an incremented value 1 past the input bytes of the + address. + Push the address read back thru the *addr pointer. + See LSB (Linux Standard Base) exception handling documents. */ +static int +read_encoded_ptr(Dwarf_Debug dbg, + Dwarf_Small * section_pointer, + Dwarf_Small * input_field, + int gnu_encoding, + Dwarf_Half address_size, + Dwarf_Unsigned * addr, + Dwarf_Small ** input_field_updated) +{ + Dwarf_Word length = 0; + int value_type = gnu_encoding & 0xf; + Dwarf_Small *input_field_original = input_field; + + if (gnu_encoding == 0xff) { + /* There is no data here. */ + + *addr = 0; + *input_field_updated = input_field; + /* Should we return DW_DLV_NO_ENTRY? */ + return DW_DLV_OK; + } + switch (value_type) { + case DW_EH_PE_absptr:{ + /* value_type is zero. Treat as pointer size of the object. + */ + Dwarf_Unsigned ret_value = 0; + + READ_UNALIGNED(dbg, ret_value, Dwarf_Unsigned, + input_field, address_size); + *addr = ret_value; + *input_field_updated = input_field + address_size; + } + break; + case DW_EH_PE_uleb128:{ + Dwarf_Unsigned val = _dwarf_decode_u_leb128(input_field, + &length); + + *addr = val; + *input_field_updated = input_field + length; + } + break; + case DW_EH_PE_udata2:{ + Dwarf_Unsigned ret_value = 0; + + READ_UNALIGNED(dbg, ret_value, Dwarf_Unsigned, + input_field, 2); + *addr = ret_value; + *input_field_updated = input_field + 2; + } + break; + + case DW_EH_PE_udata4:{ + Dwarf_Unsigned ret_value = 0; + + /* ASSERT: sizeof(Dwarf_ufixed) == 4 */ + READ_UNALIGNED(dbg, ret_value, Dwarf_Unsigned, + input_field, sizeof(Dwarf_ufixed)); + *addr = ret_value; + *input_field_updated = input_field + sizeof(Dwarf_ufixed); + } + break; + + case DW_EH_PE_udata8:{ + Dwarf_Unsigned ret_value = 0; + + /* ASSERT: sizeof(Dwarf_Unsigned) == 8 */ + READ_UNALIGNED(dbg, ret_value, Dwarf_Unsigned, + input_field, sizeof(Dwarf_Unsigned)); + *addr = ret_value; + *input_field_updated = input_field + sizeof(Dwarf_Unsigned); + } + break; + + case DW_EH_PE_sleb128:{ + Dwarf_Signed val = _dwarf_decode_s_leb128(input_field, + &length); + + *addr = (Dwarf_Unsigned) val; + *input_field_updated = input_field + length; + } + break; + case DW_EH_PE_sdata2:{ + Dwarf_Unsigned val = 0; + + READ_UNALIGNED(dbg, val, Dwarf_Unsigned, input_field, 2); + SIGN_EXTEND(val, 2); + *addr = (Dwarf_Unsigned) val; + *input_field_updated = input_field + 2; + } + break; + + case DW_EH_PE_sdata4:{ + Dwarf_Unsigned val = 0; + + /* ASSERT: sizeof(Dwarf_ufixed) == 4 */ + READ_UNALIGNED(dbg, val, + Dwarf_Unsigned, input_field, + sizeof(Dwarf_ufixed)); + SIGN_EXTEND(val, sizeof(Dwarf_ufixed)); + *addr = (Dwarf_Unsigned) val; + *input_field_updated = input_field + sizeof(Dwarf_ufixed); + } + break; + case DW_EH_PE_sdata8:{ + Dwarf_Unsigned val = 0; + + /* ASSERT: sizeof(Dwarf_Unsigned) == 8 */ + READ_UNALIGNED(dbg, val, + Dwarf_Unsigned, input_field, + sizeof(Dwarf_Unsigned)); + *addr = (Dwarf_Unsigned) val; + *input_field_updated = input_field + sizeof(Dwarf_Unsigned); + } + break; + default: + return DW_DLV_ERROR; + + }; + /* The ELF ABI for gnu does not document the meaning of + DW_EH_PE_pcrel, which is awkward. It apparently means the value + we got above is pc-relative (meaning section-relative), so we + adjust the value. Section_pointer may be null if it is known + DW_EH_PE_pcrel cannot apply, such as for .debug_frame or for an + address-range value. */ + if (section_pointer && ((gnu_encoding & 0x70) == DW_EH_PE_pcrel)) { + /* Address (*addr) above is pc relative with respect to a + section. Add to the offset the base address (from elf) of + section and the distance of the field we are reading from + the section-beginning to get the actual address. */ + /* ASSERT: input_field_original >= section_pointer */ + Dwarf_Unsigned distance = + input_field_original - section_pointer; + *addr += dbg->de_debug_frame_eh_gnu.dss_addr + distance; + } + return DW_DLV_OK; +} + + + + +/* All augmentation string checking done here now. + + For .eh_frame, gcc from 3.3 uses the z style, earlier used + only "eh" as augmentation. We don't yet handle + decoding .eh_frame with the z style extensions like L P. + + These are nasty heuristics, but then that's life + as augmentations are implementation specific. */ +/* ARGSUSED */ +enum Dwarf_augmentation_type +_dwarf_get_augmentation_type(Dwarf_Debug dbg, + Dwarf_Small * augmentation_string, + int is_gcc_eh_frame) +{ + enum Dwarf_augmentation_type t = aug_unknown; + char *ag_string = (char *) augmentation_string; + + if (ag_string[0] == 0) { + /* Empty string. We'll just guess that we know what this means: + standard dwarf2/3 with no implementation-defined fields. */ + t = aug_empty_string; + } else if (strcmp(ag_string, DW_DEBUG_FRAME_AUGMENTER_STRING) == 0) { + /* The string is "mti v1". Used internally at SGI, probably + never shipped. Replaced by "z". Treat like 'nothing + special'. */ + t = aug_irix_mti_v1; + } else if (ag_string[0] == 'z') { + /* If it's IRIX cc, z means aug_irix_exception_table. z1 z2 + were designed as for IRIX CC, but never implemented */ + /* If it's gcc, z may be any of several things. "z" or z + followed optionally followed by one or more of L R P, each + of which means a value may be present. Should be in eh_frame + only, I think. */ + if (is_gcc_eh_frame) { + t = aug_gcc_eh_z; + } else if (ag_string[1] == 0) { + /* This is the normal IRIX C++ case, where there is an + offset into a table in each fde. The table being for + IRIX CC exception handling. */ + /* DW_CIE_AUGMENTER_STRING_V0 "z" */ + t = aug_irix_exception_table; + } /* Else unknown. */ + } else if (strncmp(ag_string, "eh", 2) == 0) { + /* gcc .eh_frame augmentation for egcs and gcc 2.x, at least + for x86. */ + t = aug_eh; + } else if (strcmp(ag_string, "armcc+") == 0) { + /* Arm uses this string to mean a bug in + in Arm compilers was fixed, changing to the standard + calculation of the CFA. See + http://sourceware.org/ml/gdb-patches/2006-12/msg00249.html + for details. */ + t = aug_armcc; + } else { + + } + return t; +} + +/* Using augmentation, and version + read in the augmentation data for GNU eh. + + Return DW_DLV_OK if we succeeded, + DW_DLV_ERR if we fail. + + On success, update 'size_of_augmentation_data' with + the length of the fields that are part of augmentation (so the + caller can increment frame_ptr appropriately). + + 'frame_ptr' points within section. + 'section_pointer' points to section base address in memory. +*/ +/* ARGSUSED */ +static int +get_gcc_eh_augmentation(Dwarf_Debug dbg, Dwarf_Small * frame_ptr, + unsigned long *size_of_augmentation_data, + enum Dwarf_augmentation_type augtype, + Dwarf_Small * section_pointer, + Dwarf_Small * fde_eh_encoding_out, + char *augmentation) +{ + char *suffix = 0; + unsigned long augdata_size = 0; + + if (augtype == aug_gcc_eh_z) { + /* Has leading 'z'. */ + Dwarf_Word leb128_length = 0; + + /* Dwarf_Unsigned eh_value = */ + _dwarf_decode_u_leb128(frame_ptr, &leb128_length); + augdata_size += leb128_length; + frame_ptr += leb128_length; + suffix = augmentation + 1; + } else { + /* Prefix is 'eh'. As in gcc 3.2. No suffix present + apparently. */ + suffix = augmentation + 2; + } + for (; *suffix; ++suffix) { + /* We have no idea what this is as yet. Some extensions beyond + dwarf exist which we do not yet handle. */ + return DW_DLV_ERROR; + + } + + *size_of_augmentation_data = augdata_size; + return DW_DLV_OK; +} + + +/* Make the 'cie_id_addr' consistent across .debug_frame and .eh_frame. + Calculate a pointer into section bytes given a cie_id, which is + trivial for .debug_frame, but a bit more work for .eh_frame. +*/ +static Dwarf_Small * +get_cieptr_given_offset(Dwarf_Unsigned cie_id_value, + int use_gnu_cie_calc, + Dwarf_Small * section_ptr, + Dwarf_Small * cie_id_addr) +{ + Dwarf_Small *cieptr = 0; + + if (use_gnu_cie_calc) { + /* cie_id value is offset, in section, of the cie_id itself, to + use vm ptr of the value, less the value, to get to the cie + itself. In addition, munge *cie_id_addr to look *as if* it + was from real dwarf. */ + cieptr = (Dwarf_Small *) ((Dwarf_Unsigned) cie_id_addr) - + ((Dwarf_Unsigned) cie_id_value); + } else { + /* Traditional dwarf section offset is in cie_id */ + cieptr = (section_ptr + cie_id_value); + } + return cieptr; +} + +/* To properly release all spaced used. + Earlier approaches (before July 15, 2005) + letting client do the dealloc directly left + some data allocated. + This is directly called by consumer code. +*/ +void +dwarf_fde_cie_list_dealloc(Dwarf_Debug dbg, + Dwarf_Cie * cie_data, + Dwarf_Signed cie_element_count, + Dwarf_Fde * fde_data, + Dwarf_Signed fde_element_count) +{ + Dwarf_Signed i = 0; + + for (i = 0; i < cie_element_count; ++i) { + Dwarf_Frame frame = cie_data[i]->ci_initial_table; + + if (frame) + dwarf_dealloc(dbg, frame, DW_DLA_FRAME); + dwarf_dealloc(dbg, cie_data[i], DW_DLA_CIE); + } + for (i = 0; i < fde_element_count; ++i) { + dwarf_dealloc(dbg, fde_data[i], DW_DLA_FDE); + } + if (cie_data) + dwarf_dealloc(dbg, cie_data, DW_DLA_LIST); + if (fde_data) + dwarf_dealloc(dbg, fde_data, DW_DLA_LIST); +} |