diff options
Diffstat (limited to 'bfd/elf-eh-frame.c')
-rw-r--r-- | bfd/elf-eh-frame.c | 125 |
1 files changed, 121 insertions, 4 deletions
diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c index 9ccf561..ab316ad 100644 --- a/bfd/elf-eh-frame.c +++ b/bfd/elf-eh-frame.c @@ -260,6 +260,102 @@ size_of_output_cie_fde (struct eh_cie_fde *entry, unsigned int alignment) + alignment - 1) & -alignment; } +/* Assume that the bytes between *ITER and END are CFA instructions. + Try to move *ITER past the first instruction and return true on + success. ENCODED_PTR_WIDTH gives the width of pointer entries. */ + +static bfd_boolean +skip_cfa_op (bfd_byte **iter, bfd_byte *end, unsigned int encoded_ptr_width) +{ + bfd_byte op; + bfd_vma length; + + if (!read_byte (iter, end, &op)) + return FALSE; + + switch (op & 0x80 ? op & 0xc0 : op) + { + case DW_CFA_nop: + case DW_CFA_advance_loc: + case DW_CFA_restore: + /* No arguments. */ + return TRUE; + + case DW_CFA_offset: + case DW_CFA_restore_extended: + case DW_CFA_undefined: + case DW_CFA_same_value: + case DW_CFA_def_cfa_register: + case DW_CFA_def_cfa_offset: + case DW_CFA_def_cfa_offset_sf: + case DW_CFA_GNU_args_size: + /* One leb128 argument. */ + return skip_leb128 (iter, end); + + case DW_CFA_offset_extended: + case DW_CFA_register: + case DW_CFA_def_cfa: + case DW_CFA_offset_extended_sf: + case DW_CFA_GNU_negative_offset_extended: + case DW_CFA_def_cfa_sf: + /* Two leb128 arguments. */ + return (skip_leb128 (iter, end) + && skip_leb128 (iter, end)); + + case DW_CFA_def_cfa_expression: + /* A variable-length argument. */ + return (read_uleb128 (iter, end, &length) + && skip_bytes (iter, end, length)); + + case DW_CFA_expression: + /* A leb128 followed by a variable-length argument. */ + return (skip_leb128 (iter, end) + && read_uleb128 (iter, end, &length) + && skip_bytes (iter, end, length)); + + case DW_CFA_set_loc: + return skip_bytes (iter, end, encoded_ptr_width); + + case DW_CFA_advance_loc1: + return skip_bytes (iter, end, 1); + + case DW_CFA_advance_loc2: + return skip_bytes (iter, end, 2); + + case DW_CFA_advance_loc4: + return skip_bytes (iter, end, 4); + + case DW_CFA_MIPS_advance_loc8: + return skip_bytes (iter, end, 8); + + default: + return FALSE; + } +} + +/* Try to interpret the bytes between BUF and END as CFA instructions. + If every byte makes sense, return a pointer to the first DW_CFA_nop + padding byte, or END if there is no padding. Return null otherwise. + ENCODED_PTR_WIDTH is as for skip_cfa_op. */ + +static bfd_byte * +skip_non_nops (bfd_byte *buf, bfd_byte *end, unsigned int encoded_ptr_width) +{ + bfd_byte *last; + + last = buf; + while (buf < end) + if (*buf == DW_CFA_nop) + buf++; + else + { + if (!skip_cfa_op (&buf, end, encoded_ptr_width)) + return 0; + last = buf; + } + return last; +} + /* This function is called for each input file before the .eh_frame section is relocated. It discards duplicate CIEs and FDEs for discarded functions. The function returns TRUE iff any entries have been @@ -356,7 +452,7 @@ _bfd_elf_discard_section_eh_frame for (;;) { unsigned char *aug; - bfd_byte *start, *end; + bfd_byte *start, *end, *insns; bfd_size_type length; if (sec_info->count == sec_info->alloced) @@ -602,12 +698,13 @@ _bfd_elf_discard_section_eh_frame if (cie.fde_encoding == DW_EH_PE_omit) cie.fde_encoding = DW_EH_PE_absptr; - initial_insn_length = cie.hdr.length - (buf - last_fde - 4); + initial_insn_length = end - buf; if (initial_insn_length <= 50) { cie.initial_insn_length = initial_insn_length; memcpy (cie.initial_instructions, buf, initial_insn_length); } + insns = buf; buf += initial_insn_length; ENSURE_NO_RELOCS (buf); last_cie = last_fde; @@ -648,18 +745,38 @@ _bfd_elf_discard_section_eh_frame /* Skip the augmentation size, if present. */ if (cie.augmentation[0] == 'z') - REQUIRE (skip_leb128 (&buf, end)); + REQUIRE (read_uleb128 (&buf, end, &length)); + else + length = 0; /* Of the supported augmentation characters above, only 'L' adds augmentation data to the FDE. This code would need to be adjusted if any future augmentations do the same thing. */ if (cie.lsda_encoding != DW_EH_PE_omit) - this_inf->lsda_offset = buf - start; + { + this_inf->lsda_offset = buf - start; + /* If there's no 'z' augmentation, we don't know where the + CFA insns begin. Assume no padding. */ + if (cie.augmentation[0] != 'z') + length = end - buf; + } + + /* Skip over the augmentation data. */ + REQUIRE (skip_bytes (&buf, end, length)); + insns = buf; buf = last_fde + 4 + hdr.length; SKIP_RELOCS (buf); } + /* Try to interpret the CFA instructions and find the first + padding nop. Shrink this_inf's size so that it doesn't + including the padding. */ + length = get_DW_EH_PE_width (cie.fde_encoding, ptr_size); + insns = skip_non_nops (insns, end, length); + if (insns != 0) + this_inf->size -= end - insns; + this_inf->fde_encoding = cie.fde_encoding; this_inf->lsda_encoding = cie.lsda_encoding; sec_info->count++; |