* elf-bfd.h (struct cie): Use bfd_vmas for code_align, ra_column and

augmentation_size.  Use bfd_signed_vmas for data_align.
	* elf-eh-frame.c (read_uleb128, read_sleb128): Delete in favor of...
	(read_byte, skip_leb128, read_uleb128, read_sleb128): ...these new
	functions.  Don't read past the end of the enclosing CIE or FDE.
	(skip_bytes): New utility function.
	(_bfd_elf_discard_section_eh_frame): Use new functions, adding more
	sanity checking.
	(_bfd_elf_write_section_eh_frame): Use new functions.
This commit is contained in:
Richard Sandiford 2005-01-17 17:41:47 +00:00
parent acfe556796
commit 2c42be6506
3 changed files with 143 additions and 56 deletions

View file

@ -1,3 +1,15 @@
2005-01-17 Richard Sandiford <rsandifo@redhat.com>
* elf-bfd.h (struct cie): Use bfd_vmas for code_align, ra_column and
augmentation_size. Use bfd_signed_vmas for data_align.
* elf-eh-frame.c (read_uleb128, read_sleb128): Delete in favor of...
(read_byte, skip_leb128, read_uleb128, read_sleb128): ...these new
functions. Don't read past the end of the enclosing CIE or FDE.
(skip_bytes): New utility function.
(_bfd_elf_discard_section_eh_frame): Use new functions, adding more
sanity checking.
(_bfd_elf_write_section_eh_frame): Use new functions.
2005-01-17 Richard Sandiford <rsandifo@redhat.com> 2005-01-17 Richard Sandiford <rsandifo@redhat.com>
* elf-eh-frame.c (_bfd_elf_discard_section_eh_frame): Use an * elf-eh-frame.c (_bfd_elf_discard_section_eh_frame): Use an

View file

@ -1,6 +1,6 @@
/* BFD back-end data structures for ELF files. /* BFD back-end data structures for ELF files.
Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
2002, 2003, 2004 Free Software Foundation, Inc. 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
Written by Cygnus Support. Written by Cygnus Support.
This file is part of BFD, the Binary File Descriptor library. This file is part of BFD, the Binary File Descriptor library.
@ -270,10 +270,10 @@ struct cie
struct cie_header hdr; struct cie_header hdr;
unsigned char version; unsigned char version;
unsigned char augmentation[20]; unsigned char augmentation[20];
unsigned int code_align; bfd_vma code_align;
int data_align; bfd_signed_vma data_align;
unsigned int ra_column; bfd_vma ra_column;
unsigned int augmentation_size; bfd_vma augmentation_size;
struct elf_link_hash_entry *personality; struct elf_link_hash_entry *personality;
unsigned char per_encoding; unsigned char per_encoding;
unsigned char lsda_encoding; unsigned char lsda_encoding;

View file

@ -26,21 +26,85 @@
#define EH_FRAME_HDR_SIZE 8 #define EH_FRAME_HDR_SIZE 8
#define read_uleb128(VAR, BUF) \ /* If *ITER hasn't reached END yet, read the next byte into *RESULT and
do \ move onto the next byte. Return true on success. */
{ \
(VAR) = read_unsigned_leb128 (abfd, buf, &leb128_tmp); \
(BUF) += leb128_tmp; \
} \
while (0)
#define read_sleb128(VAR, BUF) \ static inline bfd_boolean
do \ read_byte (bfd_byte **iter, bfd_byte *end, unsigned char *result)
{ \ {
(VAR) = read_signed_leb128 (abfd, buf, &leb128_tmp); \ if (*iter >= end)
(BUF) += leb128_tmp; \ return FALSE;
} \ *result = *((*iter)++);
while (0) 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. */ /* Return 0 if either encoding is variable width, or not yet known to bfd. */
@ -221,7 +285,6 @@ _bfd_elf_discard_section_eh_frame
struct elf_link_hash_table *htab; struct elf_link_hash_table *htab;
struct eh_frame_hdr_info *hdr_info; struct eh_frame_hdr_info *hdr_info;
struct eh_frame_sec_info *sec_info = NULL; struct eh_frame_sec_info *sec_info = NULL;
unsigned int leb128_tmp;
unsigned int cie_usage_count, offset; unsigned int cie_usage_count, offset;
unsigned int ptr_size; unsigned int ptr_size;
@ -293,6 +356,8 @@ _bfd_elf_discard_section_eh_frame
for (;;) for (;;)
{ {
unsigned char *aug; unsigned char *aug;
bfd_byte *start, *end;
bfd_size_type length;
if (sec_info->count == sec_info->alloced) if (sec_info->count == sec_info->alloced)
{ {
@ -318,19 +383,22 @@ _bfd_elf_discard_section_eh_frame
/* If we are at the end of the section, we still need to decide /* 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). */ on whether to output or discard last encountered CIE (if any). */
if ((bfd_size_type) (buf - ehbuf) == sec->size) if ((bfd_size_type) (buf - ehbuf) == sec->size)
hdr.id = (unsigned int) -1; {
hdr.id = (unsigned int) -1;
end = buf;
}
else else
{ {
/* Read the length of the entry. */ /* Read the length of the entry. */
REQUIRE ((bfd_size_type) (buf - ehbuf) + 4 <= sec->size); REQUIRE (skip_bytes (&buf, ehbuf + sec->size, 4));
hdr.length = bfd_get_32 (abfd, buf); hdr.length = bfd_get_32 (abfd, buf - 4);
buf += 4;
/* 64-bit .eh_frame is not supported. */ /* 64-bit .eh_frame is not supported. */
REQUIRE (hdr.length != 0xffffffff); REQUIRE (hdr.length != 0xffffffff);
/* The CIE/FDE must be fully contained in this input section. */ /* The CIE/FDE must be fully contained in this input section. */
REQUIRE ((bfd_size_type) (buf - ehbuf) + hdr.length <= sec->size); REQUIRE ((bfd_size_type) (buf - ehbuf) + hdr.length <= sec->size);
end = buf + hdr.length;
this_inf->offset = last_fde - ehbuf; this_inf->offset = last_fde - ehbuf;
this_inf->size = 4 + hdr.length; this_inf->size = 4 + hdr.length;
@ -348,8 +416,8 @@ _bfd_elf_discard_section_eh_frame
} }
else else
{ {
hdr.id = bfd_get_32 (abfd, buf); REQUIRE (skip_bytes (&buf, end, 4));
buf += 4; hdr.id = bfd_get_32 (abfd, buf - 4);
REQUIRE (hdr.id != (unsigned int) -1); REQUIRE (hdr.id != (unsigned int) -1);
} }
} }
@ -392,7 +460,7 @@ _bfd_elf_discard_section_eh_frame
cie_usage_count = 0; cie_usage_count = 0;
memset (&cie, 0, sizeof (cie)); memset (&cie, 0, sizeof (cie));
cie.hdr = hdr; cie.hdr = hdr;
cie.version = *buf++; REQUIRE (read_byte (&buf, end, &cie.version));
/* Cannot handle unknown versions. */ /* Cannot handle unknown versions. */
REQUIRE (cie.version == 1 || cie.version == 3); REQUIRE (cie.version == 1 || cie.version == 3);
@ -407,15 +475,18 @@ _bfd_elf_discard_section_eh_frame
/* We cannot merge "eh" CIEs because __EXCEPTION_TABLE__ /* We cannot merge "eh" CIEs because __EXCEPTION_TABLE__
is private to each CIE, so we don't need it for anything. is private to each CIE, so we don't need it for anything.
Just skip it. */ Just skip it. */
buf += ptr_size; REQUIRE (skip_bytes (&buf, end, ptr_size));
SKIP_RELOCS (buf); SKIP_RELOCS (buf);
} }
read_uleb128 (cie.code_align, buf); REQUIRE (read_uleb128 (&buf, end, &cie.code_align));
read_sleb128 (cie.data_align, buf); REQUIRE (read_sleb128 (&buf, end, &cie.data_align));
if (cie.version == 1) if (cie.version == 1)
cie.ra_column = *buf++; {
REQUIRE (buf < end);
cie.ra_column = *buf++;
}
else else
read_uleb128 (cie.ra_column, buf); REQUIRE (read_uleb128 (&buf, end, &cie.ra_column));
ENSURE_NO_RELOCS (buf); ENSURE_NO_RELOCS (buf);
cie.lsda_encoding = DW_EH_PE_omit; cie.lsda_encoding = DW_EH_PE_omit;
cie.fde_encoding = DW_EH_PE_omit; cie.fde_encoding = DW_EH_PE_omit;
@ -426,7 +497,7 @@ _bfd_elf_discard_section_eh_frame
if (*aug == 'z') if (*aug == 'z')
{ {
aug++; aug++;
read_uleb128 (cie.augmentation_size, buf); REQUIRE (read_uleb128 (&buf, end, &cie.augmentation_size));
ENSURE_NO_RELOCS (buf); ENSURE_NO_RELOCS (buf);
} }
@ -434,12 +505,12 @@ _bfd_elf_discard_section_eh_frame
switch (*aug++) switch (*aug++)
{ {
case 'L': case 'L':
cie.lsda_encoding = *buf++; REQUIRE (read_byte (&buf, end, &cie.lsda_encoding));
ENSURE_NO_RELOCS (buf); ENSURE_NO_RELOCS (buf);
REQUIRE (get_DW_EH_PE_width (cie.lsda_encoding, ptr_size)); REQUIRE (get_DW_EH_PE_width (cie.lsda_encoding, ptr_size));
break; break;
case 'R': case 'R':
cie.fde_encoding = *buf++; REQUIRE (read_byte (&buf, end, &cie.fde_encoding));
ENSURE_NO_RELOCS (buf); ENSURE_NO_RELOCS (buf);
REQUIRE (get_DW_EH_PE_width (cie.fde_encoding, ptr_size)); REQUIRE (get_DW_EH_PE_width (cie.fde_encoding, ptr_size));
break; break;
@ -447,14 +518,15 @@ _bfd_elf_discard_section_eh_frame
{ {
int per_width; 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, per_width = get_DW_EH_PE_width (cie.per_encoding,
ptr_size); ptr_size);
REQUIRE (per_width); REQUIRE (per_width);
if ((cie.per_encoding & 0xf0) == DW_EH_PE_aligned) if ((cie.per_encoding & 0xf0) == DW_EH_PE_aligned)
buf = (ehbuf {
+ ((buf - ehbuf + per_width - 1) length = -(buf - ehbuf) & (per_width - 1);
& ~((bfd_size_type) per_width - 1))); REQUIRE (skip_bytes (&buf, end, length));
}
ENSURE_NO_RELOCS (buf); ENSURE_NO_RELOCS (buf);
/* Ensure we have a reloc here, against /* Ensure we have a reloc here, against
a global symbol. */ a global symbol. */
@ -487,7 +559,7 @@ _bfd_elf_discard_section_eh_frame
cookie->rel++; cookie->rel++;
while (GET_RELOC (buf) != NULL); while (GET_RELOC (buf) != NULL);
} }
buf += per_width; REQUIRE (skip_bytes (&buf, end, per_width));
} }
break; break;
default: default:
@ -569,18 +641,21 @@ _bfd_elf_discard_section_eh_frame
cie_usage_count++; cie_usage_count++;
hdr_info->fde_count++; hdr_info->fde_count++;
} }
if (cie.lsda_encoding != DW_EH_PE_omit) /* Skip the initial location and address range. */
{ start = buf;
unsigned int dummy; 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 (skip_leb128 (&buf, end));
/* 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;
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);
}
buf = last_fde + 4 + hdr.length; buf = last_fde + 4 + hdr.length;
SKIP_RELOCS (buf); SKIP_RELOCS (buf);
} }
@ -792,7 +867,6 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
struct eh_frame_sec_info *sec_info; struct eh_frame_sec_info *sec_info;
struct elf_link_hash_table *htab; struct elf_link_hash_table *htab;
struct eh_frame_hdr_info *hdr_info; struct eh_frame_hdr_info *hdr_info;
unsigned int leb128_tmp;
unsigned int ptr_size; unsigned int ptr_size;
struct eh_cie_fde *ent; struct eh_cie_fde *ent;
@ -894,7 +968,7 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
{ {
unsigned char *aug; unsigned char *aug;
unsigned int action, extra_string, extra_data; 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 /* Need to find 'R' or 'L' augmentation's argument and modify
DW_EH_PE_* value. */ DW_EH_PE_* value. */
@ -908,9 +982,9 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
buf += 9; buf += 9;
aug = buf; aug = buf;
buf = strchr (buf, '\0') + 1; buf = strchr (buf, '\0') + 1;
read_uleb128 (dummy, buf); skip_leb128 (&buf, end);
read_sleb128 (dummy, buf); skip_leb128 (&buf, end);
read_uleb128 (dummy, buf); skip_leb128 (&buf, end);
if (*aug == 'z') if (*aug == 'z')
{ {
/* The uleb128 will always be a single byte for the kind /* The uleb128 will always be a single byte for the kind
@ -923,6 +997,7 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
memmove (buf + extra_string + extra_data, buf, end - buf); memmove (buf + extra_string + extra_data, buf, end - buf);
memmove (aug + extra_string, aug, buf - aug); memmove (aug + extra_string, aug, buf - aug);
buf += extra_string; buf += extra_string;
end += extra_string + extra_data;
if (ent->add_augmentation_size) if (ent->add_augmentation_size)
{ {