#define EH_FRAME_HDR_SIZE 8
-#define read_uleb128(VAR, BUF) \
-do \
- { \
- (VAR) = read_unsigned_leb128 (abfd, buf, &leb128_tmp); \
- (BUF) += leb128_tmp; \
- } \
-while (0)
-
-#define read_sleb128(VAR, BUF) \
-do \
- { \
- (VAR) = read_signed_leb128 (abfd, buf, &leb128_tmp); \
- (BUF) += leb128_tmp; \
- } \
-while (0)
+/* If *ITER hasn't reached END yet, read the next byte into *RESULT and
+ move onto the next byte. Return true on success. */
+
+static inline bfd_boolean
+read_byte (bfd_byte **iter, bfd_byte *end, unsigned char *result)
+{
+ if (*iter >= end)
+ return FALSE;
+ *result = *((*iter)++);
+ return TRUE;
+}
+
+/* Move *ITER over LENGTH bytes, or up to END, whichever is closer.
+ Return true it was possible to move LENGTH bytes. */
+
+static inline bfd_boolean
+skip_bytes (bfd_byte **iter, bfd_byte *end, bfd_size_type length)
+{
+ if ((bfd_size_type) (end - *iter) < length)
+ {
+ *iter = end;
+ return FALSE;
+ }
+ *iter += length;
+ return TRUE;
+}
+
+/* Move *ITER over an leb128, stopping at END. Return true if the end
+ of the leb128 was found. */
+
+static bfd_boolean
+skip_leb128 (bfd_byte **iter, bfd_byte *end)
+{
+ unsigned char byte;
+ do
+ if (!read_byte (iter, end, &byte))
+ return FALSE;
+ while (byte & 0x80);
+ return TRUE;
+}
+
+/* Like skip_leb128, but treat the leb128 as an unsigned value and
+ store it in *VALUE. */
+
+static bfd_boolean
+read_uleb128 (bfd_byte **iter, bfd_byte *end, bfd_vma *value)
+{
+ bfd_byte *start, *p;
+
+ start = *iter;
+ if (!skip_leb128 (iter, end))
+ return FALSE;
+
+ p = *iter;
+ *value = *--p;
+ while (p > start)
+ *value = (*value << 7) | (*--p & 0x7f);
+
+ return TRUE;
+}
+
+/* Like read_uleb128, but for signed values. */
+
+static bfd_boolean
+read_sleb128 (bfd_byte **iter, bfd_byte *end, bfd_signed_vma *value)
+{
+ bfd_byte *start, *p;
+
+ start = *iter;
+ if (!skip_leb128 (iter, end))
+ return FALSE;
+
+ p = *iter;
+ *value = ((*--p & 0x7f) ^ 0x40) - 0x40;
+ while (p > start)
+ *value = (*value << 7) | (*--p & 0x7f);
+
+ return TRUE;
+}
/* Return 0 if either encoding is variable width, or not yet known to bfd. */
+ 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
struct elf_link_hash_table *htab;
struct eh_frame_hdr_info *hdr_info;
struct eh_frame_sec_info *sec_info = NULL;
- unsigned int leb128_tmp;
unsigned int cie_usage_count, offset;
unsigned int ptr_size;
it (it would need to use 64-bit .eh_frame format anyway). */
REQUIRE (sec->size == (unsigned int) sec->size);
- ptr_size = (elf_elfheader (abfd)->e_ident[EI_CLASS]
- == ELFCLASS64) ? 8 : 4;
+ ptr_size = (get_elf_backend_data (abfd)
+ ->elf_backend_eh_frame_address_size (abfd, sec));
+ REQUIRE (ptr_size != 0);
+
buf = ehbuf;
last_cie = NULL;
last_cie_inf = NULL;
for (;;)
{
- unsigned char *aug;
+ char *aug;
+ bfd_byte *start, *end, *insns;
+ bfd_size_type length;
if (sec_info->count == sec_info->alloced)
{
/* If we are at the end of the section, we still need to decide
on whether to output or discard last encountered CIE (if any). */
if ((bfd_size_type) (buf - ehbuf) == sec->size)
- hdr.id = (unsigned int) -1;
+ {
+ hdr.length = 0;
+ hdr.id = (unsigned int) -1;
+ end = buf;
+ }
else
{
/* Read the length of the entry. */
- REQUIRE ((bfd_size_type) (buf - ehbuf) + 4 <= sec->size);
- hdr.length = bfd_get_32 (abfd, buf);
- buf += 4;
+ REQUIRE (skip_bytes (&buf, ehbuf + sec->size, 4));
+ hdr.length = bfd_get_32 (abfd, buf - 4);
/* 64-bit .eh_frame is not supported. */
REQUIRE (hdr.length != 0xffffffff);
/* The CIE/FDE must be fully contained in this input section. */
REQUIRE ((bfd_size_type) (buf - ehbuf) + hdr.length <= sec->size);
+ end = buf + hdr.length;
this_inf->offset = last_fde - ehbuf;
this_inf->size = 4 + hdr.length;
}
else
{
- hdr.id = bfd_get_32 (abfd, buf);
- buf += 4;
+ REQUIRE (skip_bytes (&buf, end, 4));
+ hdr.id = bfd_get_32 (abfd, buf - 4);
REQUIRE (hdr.id != (unsigned int) -1);
}
}
cie_usage_count = 0;
memset (&cie, 0, sizeof (cie));
cie.hdr = hdr;
- cie.version = *buf++;
+ REQUIRE (read_byte (&buf, end, &cie.version));
/* Cannot handle unknown versions. */
REQUIRE (cie.version == 1 || cie.version == 3);
- REQUIRE (strlen (buf) < sizeof (cie.augmentation));
+ REQUIRE (strlen ((char *) buf) < sizeof (cie.augmentation));
- strcpy (cie.augmentation, buf);
- buf = strchr (buf, '\0') + 1;
+ strcpy (cie.augmentation, (char *) buf);
+ buf = (bfd_byte *) strchr ((char *) buf, '\0') + 1;
ENSURE_NO_RELOCS (buf);
if (buf[0] == 'e' && buf[1] == 'h')
{
/* We cannot merge "eh" CIEs because __EXCEPTION_TABLE__
is private to each CIE, so we don't need it for anything.
Just skip it. */
- buf += ptr_size;
+ REQUIRE (skip_bytes (&buf, end, ptr_size));
SKIP_RELOCS (buf);
}
- read_uleb128 (cie.code_align, buf);
- read_sleb128 (cie.data_align, buf);
+ REQUIRE (read_uleb128 (&buf, end, &cie.code_align));
+ REQUIRE (read_sleb128 (&buf, end, &cie.data_align));
if (cie.version == 1)
- cie.ra_column = *buf++;
+ {
+ REQUIRE (buf < end);
+ cie.ra_column = *buf++;
+ }
else
- read_uleb128 (cie.ra_column, buf);
+ REQUIRE (read_uleb128 (&buf, end, &cie.ra_column));
ENSURE_NO_RELOCS (buf);
cie.lsda_encoding = DW_EH_PE_omit;
cie.fde_encoding = DW_EH_PE_omit;
if (*aug == 'z')
{
aug++;
- read_uleb128 (cie.augmentation_size, buf);
+ REQUIRE (read_uleb128 (&buf, end, &cie.augmentation_size));
ENSURE_NO_RELOCS (buf);
}
switch (*aug++)
{
case 'L':
- cie.lsda_encoding = *buf++;
+ REQUIRE (read_byte (&buf, end, &cie.lsda_encoding));
ENSURE_NO_RELOCS (buf);
REQUIRE (get_DW_EH_PE_width (cie.lsda_encoding, ptr_size));
break;
case 'R':
- cie.fde_encoding = *buf++;
+ REQUIRE (read_byte (&buf, end, &cie.fde_encoding));
ENSURE_NO_RELOCS (buf);
REQUIRE (get_DW_EH_PE_width (cie.fde_encoding, ptr_size));
break;
{
int per_width;
- cie.per_encoding = *buf++;
+ REQUIRE (read_byte (&buf, end, &cie.per_encoding));
per_width = get_DW_EH_PE_width (cie.per_encoding,
ptr_size);
REQUIRE (per_width);
if ((cie.per_encoding & 0xf0) == DW_EH_PE_aligned)
- buf = (ehbuf
- + ((buf - ehbuf + per_width - 1)
- & ~((bfd_size_type) per_width - 1)));
+ {
+ length = -(buf - ehbuf) & (per_width - 1);
+ REQUIRE (skip_bytes (&buf, end, length));
+ }
ENSURE_NO_RELOCS (buf);
/* Ensure we have a reloc here, against
a global symbol. */
cookie->rel++;
while (GET_RELOC (buf) != NULL);
}
- buf += per_width;
+ REQUIRE (skip_bytes (&buf, end, per_width));
}
break;
default:
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;
cie_usage_count++;
hdr_info->fde_count++;
}
+ /* Skip the initial location and address range. */
+ start = buf;
+ length = get_DW_EH_PE_width (cie.fde_encoding, ptr_size);
+ REQUIRE (skip_bytes (&buf, end, 2 * length));
+
+ /* Skip the augmentation size, if present. */
+ if (cie.augmentation[0] == 'z')
+ 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)
{
- unsigned int dummy;
-
- aug = buf;
- buf += 2 * get_DW_EH_PE_width (cie.fde_encoding, ptr_size);
- if (cie.augmentation[0] == 'z')
- read_uleb128 (dummy, buf);
- /* If some new augmentation data is added before LSDA
- in FDE augmentation area, this need to be adjusted. */
- this_inf->lsda_offset = (buf - aug);
+ 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++;
struct eh_frame_sec_info *sec_info;
struct elf_link_hash_table *htab;
struct eh_frame_hdr_info *hdr_info;
- unsigned int leb128_tmp;
unsigned int ptr_size;
struct eh_cie_fde *ent;
- ptr_size = (elf_elfheader (sec->owner)->e_ident[EI_CLASS]
- == ELFCLASS64) ? 8 : 4;
-
if (sec->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
return bfd_set_section_contents (abfd, sec->output_section, contents,
sec->output_offset, sec->size);
+
+ ptr_size = (get_elf_backend_data (abfd)
+ ->elf_backend_eh_frame_address_size (abfd, sec));
+ BFD_ASSERT (ptr_size != 0);
+
sec_info = elf_section_data (sec)->sec_info;
htab = elf_hash_table (info);
hdr_info = &htab->eh_info;
|| ent->need_lsda_relative
|| ent->per_encoding_relative)
{
- unsigned char *aug;
+ char *aug;
unsigned int action, extra_string, extra_data;
- unsigned int dummy, per_width, per_encoding;
+ unsigned int per_width, per_encoding;
/* Need to find 'R' or 'L' augmentation's argument and modify
DW_EH_PE_* value. */
/* Skip length, id and version. */
buf += 9;
- aug = buf;
- buf = strchr (buf, '\0') + 1;
- read_uleb128 (dummy, buf);
- read_sleb128 (dummy, buf);
- read_uleb128 (dummy, buf);
+ aug = (char *) buf;
+ buf += strlen (aug) + 1;
+ skip_leb128 (&buf, end);
+ skip_leb128 (&buf, end);
+ skip_leb128 (&buf, end);
if (*aug == 'z')
{
/* The uleb128 will always be a single byte for the kind
/* Make room for the new augmentation string and data bytes. */
memmove (buf + extra_string + extra_data, buf, end - buf);
- memmove (aug + extra_string, aug, buf - aug);
+ memmove (aug + extra_string, aug, buf - (bfd_byte *) aug);
buf += extra_string;
+ end += extra_string + extra_data;
if (ent->add_augmentation_size)
{
return retval;
}
+/* Return the width of FDE addresses. This is the default implementation. */
+
+unsigned int
+_bfd_elf_eh_frame_address_size (bfd *abfd, asection *sec ATTRIBUTE_UNUSED)
+{
+ return elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64 ? 8 : 4;
+}
+
/* Decide whether we can use a PC-relative encoding within the given
EH frame section. This is the default implementation. */