189857Sobrien/* .eh_frame section optimization.
2218822Sdim   Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007
3218822Sdim   Free Software Foundation, Inc.
489857Sobrien   Written by Jakub Jelinek <jakub@redhat.com>.
589857Sobrien
6130561Sobrien   This file is part of BFD, the Binary File Descriptor library.
789857Sobrien
8130561Sobrien   This program is free software; you can redistribute it and/or modify
9130561Sobrien   it under the terms of the GNU General Public License as published by
10130561Sobrien   the Free Software Foundation; either version 2 of the License, or
11130561Sobrien   (at your option) any later version.
1289857Sobrien
13130561Sobrien   This program is distributed in the hope that it will be useful,
14130561Sobrien   but WITHOUT ANY WARRANTY; without even the implied warranty of
15130561Sobrien   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16130561Sobrien   GNU General Public License for more details.
1789857Sobrien
18130561Sobrien   You should have received a copy of the GNU General Public License
19130561Sobrien   along with this program; if not, write to the Free Software
20218822Sdim   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
2189857Sobrien
22218822Sdim#include "sysdep.h"
2389857Sobrien#include "bfd.h"
2489857Sobrien#include "libbfd.h"
2589857Sobrien#include "elf-bfd.h"
2689857Sobrien#include "elf/dwarf2.h"
2789857Sobrien
2889857Sobrien#define EH_FRAME_HDR_SIZE 8
2989857Sobrien
30218822Sdimstruct cie
31218822Sdim{
32218822Sdim  unsigned int length;
33218822Sdim  unsigned int hash;
34218822Sdim  unsigned char version;
35218822Sdim  unsigned char local_personality;
36218822Sdim  char augmentation[20];
37218822Sdim  bfd_vma code_align;
38218822Sdim  bfd_signed_vma data_align;
39218822Sdim  bfd_vma ra_column;
40218822Sdim  bfd_vma augmentation_size;
41218822Sdim  union {
42218822Sdim    struct elf_link_hash_entry *h;
43218822Sdim    bfd_vma val;
44218822Sdim  } personality;
45218822Sdim  asection *output_sec;
46218822Sdim  struct eh_cie_fde *cie_inf;
47218822Sdim  unsigned char per_encoding;
48218822Sdim  unsigned char lsda_encoding;
49218822Sdim  unsigned char fde_encoding;
50218822Sdim  unsigned char initial_insn_length;
51218822Sdim  unsigned char make_relative;
52218822Sdim  unsigned char make_lsda_relative;
53218822Sdim  unsigned char initial_instructions[50];
54218822Sdim};
5589857Sobrien
56218822Sdim
57218822Sdim
58218822Sdim/* If *ITER hasn't reached END yet, read the next byte into *RESULT and
59218822Sdim   move onto the next byte.  Return true on success.  */
60218822Sdim
61218822Sdimstatic inline bfd_boolean
62218822Sdimread_byte (bfd_byte **iter, bfd_byte *end, unsigned char *result)
6389857Sobrien{
64218822Sdim  if (*iter >= end)
65218822Sdim    return FALSE;
66218822Sdim  *result = *((*iter)++);
67218822Sdim  return TRUE;
68218822Sdim}
6989857Sobrien
70218822Sdim/* Move *ITER over LENGTH bytes, or up to END, whichever is closer.
71218822Sdim   Return true it was possible to move LENGTH bytes.  */
72218822Sdim
73218822Sdimstatic inline bfd_boolean
74218822Sdimskip_bytes (bfd_byte **iter, bfd_byte *end, bfd_size_type length)
75218822Sdim{
76218822Sdim  if ((bfd_size_type) (end - *iter) < length)
7789857Sobrien    {
78218822Sdim      *iter = end;
79218822Sdim      return FALSE;
8089857Sobrien    }
81218822Sdim  *iter += length;
82218822Sdim  return TRUE;
8389857Sobrien}
8489857Sobrien
85218822Sdim/* Move *ITER over an leb128, stopping at END.  Return true if the end
86218822Sdim   of the leb128 was found.  */
8789857Sobrien
88218822Sdimstatic bfd_boolean
89218822Sdimskip_leb128 (bfd_byte **iter, bfd_byte *end)
9089857Sobrien{
9189857Sobrien  unsigned char byte;
9289857Sobrien  do
93218822Sdim    if (!read_byte (iter, end, &byte))
94218822Sdim      return FALSE;
9589857Sobrien  while (byte & 0x80);
96218822Sdim  return TRUE;
9789857Sobrien}
9889857Sobrien
99218822Sdim/* Like skip_leb128, but treat the leb128 as an unsigned value and
100218822Sdim   store it in *VALUE.  */
10189857Sobrien
102218822Sdimstatic bfd_boolean
103218822Sdimread_uleb128 (bfd_byte **iter, bfd_byte *end, bfd_vma *value)
104218822Sdim{
105218822Sdim  bfd_byte *start, *p;
10689857Sobrien
107218822Sdim  start = *iter;
108218822Sdim  if (!skip_leb128 (iter, end))
109218822Sdim    return FALSE;
110218822Sdim
111218822Sdim  p = *iter;
112218822Sdim  *value = *--p;
113218822Sdim  while (p > start)
114218822Sdim    *value = (*value << 7) | (*--p & 0x7f);
115218822Sdim
116218822Sdim  return TRUE;
117218822Sdim}
118218822Sdim
119218822Sdim/* Like read_uleb128, but for signed values.  */
120218822Sdim
121218822Sdimstatic bfd_boolean
122218822Sdimread_sleb128 (bfd_byte **iter, bfd_byte *end, bfd_signed_vma *value)
123218822Sdim{
124218822Sdim  bfd_byte *start, *p;
125218822Sdim
126218822Sdim  start = *iter;
127218822Sdim  if (!skip_leb128 (iter, end))
128218822Sdim    return FALSE;
129218822Sdim
130218822Sdim  p = *iter;
131218822Sdim  *value = ((*--p & 0x7f) ^ 0x40) - 0x40;
132218822Sdim  while (p > start)
133218822Sdim    *value = (*value << 7) | (*--p & 0x7f);
134218822Sdim
135218822Sdim  return TRUE;
136218822Sdim}
137218822Sdim
13889857Sobrien/* Return 0 if either encoding is variable width, or not yet known to bfd.  */
13989857Sobrien
14089857Sobrienstatic
141130561Sobrienint get_DW_EH_PE_width (int encoding, int ptr_size)
14289857Sobrien{
14389857Sobrien  /* DW_EH_PE_ values of 0x60 and 0x70 weren't defined at the time .eh_frame
14489857Sobrien     was added to bfd.  */
14589857Sobrien  if ((encoding & 0x60) == 0x60)
14689857Sobrien    return 0;
14789857Sobrien
14889857Sobrien  switch (encoding & 7)
14989857Sobrien    {
15089857Sobrien    case DW_EH_PE_udata2: return 2;
15189857Sobrien    case DW_EH_PE_udata4: return 4;
15289857Sobrien    case DW_EH_PE_udata8: return 8;
15389857Sobrien    case DW_EH_PE_absptr: return ptr_size;
15489857Sobrien    default:
15589857Sobrien      break;
15689857Sobrien    }
15789857Sobrien
15889857Sobrien  return 0;
15989857Sobrien}
16089857Sobrien
161130561Sobrien#define get_DW_EH_PE_signed(encoding) (((encoding) & DW_EH_PE_signed) != 0)
162130561Sobrien
16389857Sobrien/* Read a width sized value from memory.  */
16489857Sobrien
16589857Sobrienstatic bfd_vma
166130561Sobrienread_value (bfd *abfd, bfd_byte *buf, int width, int is_signed)
16789857Sobrien{
16889857Sobrien  bfd_vma value;
16989857Sobrien
17089857Sobrien  switch (width)
17189857Sobrien    {
172130561Sobrien    case 2:
173130561Sobrien      if (is_signed)
174130561Sobrien	value = bfd_get_signed_16 (abfd, buf);
175130561Sobrien      else
176130561Sobrien	value = bfd_get_16 (abfd, buf);
177130561Sobrien      break;
178130561Sobrien    case 4:
179130561Sobrien      if (is_signed)
180130561Sobrien	value = bfd_get_signed_32 (abfd, buf);
181130561Sobrien      else
182130561Sobrien	value = bfd_get_32 (abfd, buf);
183130561Sobrien      break;
184130561Sobrien    case 8:
185130561Sobrien      if (is_signed)
186130561Sobrien	value = bfd_get_signed_64 (abfd, buf);
187130561Sobrien      else
188130561Sobrien	value = bfd_get_64 (abfd, buf);
189130561Sobrien      break;
190130561Sobrien    default:
191130561Sobrien      BFD_FAIL ();
192130561Sobrien      return 0;
19389857Sobrien    }
19489857Sobrien
19589857Sobrien  return value;
19689857Sobrien}
197130561Sobrien
19889857Sobrien/* Store a width sized value to memory.  */
19989857Sobrien
20089857Sobrienstatic void
201130561Sobrienwrite_value (bfd *abfd, bfd_byte *buf, bfd_vma value, int width)
20289857Sobrien{
20389857Sobrien  switch (width)
20489857Sobrien    {
20589857Sobrien    case 2: bfd_put_16 (abfd, value, buf); break;
20689857Sobrien    case 4: bfd_put_32 (abfd, value, buf); break;
20789857Sobrien    case 8: bfd_put_64 (abfd, value, buf); break;
20889857Sobrien    default: BFD_FAIL ();
20989857Sobrien    }
21089857Sobrien}
21189857Sobrien
212218822Sdim/* Return one if C1 and C2 CIEs can be merged.  */
21389857Sobrien
214218822Sdimstatic int
215218822Sdimcie_eq (const void *e1, const void *e2)
21689857Sobrien{
217218822Sdim  const struct cie *c1 = e1;
218218822Sdim  const struct cie *c2 = e2;
219218822Sdim
220218822Sdim  if (c1->hash == c2->hash
221218822Sdim      && c1->length == c2->length
22289857Sobrien      && c1->version == c2->version
223218822Sdim      && c1->local_personality == c2->local_personality
22489857Sobrien      && strcmp (c1->augmentation, c2->augmentation) == 0
22589857Sobrien      && strcmp (c1->augmentation, "eh") != 0
22689857Sobrien      && c1->code_align == c2->code_align
22789857Sobrien      && c1->data_align == c2->data_align
22889857Sobrien      && c1->ra_column == c2->ra_column
22989857Sobrien      && c1->augmentation_size == c2->augmentation_size
230218822Sdim      && memcmp (&c1->personality, &c2->personality,
231218822Sdim		 sizeof (c1->personality)) == 0
232218822Sdim      && c1->output_sec == c2->output_sec
23389857Sobrien      && c1->per_encoding == c2->per_encoding
23489857Sobrien      && c1->lsda_encoding == c2->lsda_encoding
23589857Sobrien      && c1->fde_encoding == c2->fde_encoding
236130561Sobrien      && c1->initial_insn_length == c2->initial_insn_length
23789857Sobrien      && memcmp (c1->initial_instructions,
23889857Sobrien		 c2->initial_instructions,
23989857Sobrien		 c1->initial_insn_length) == 0)
240218822Sdim    return 1;
241218822Sdim
242218822Sdim  return 0;
243218822Sdim}
244218822Sdim
245218822Sdimstatic hashval_t
246218822Sdimcie_hash (const void *e)
247218822Sdim{
248218822Sdim  const struct cie *c = e;
249218822Sdim  return c->hash;
250218822Sdim}
251218822Sdim
252218822Sdimstatic hashval_t
253218822Sdimcie_compute_hash (struct cie *c)
254218822Sdim{
255218822Sdim  hashval_t h = 0;
256218822Sdim  h = iterative_hash_object (c->length, h);
257218822Sdim  h = iterative_hash_object (c->version, h);
258218822Sdim  h = iterative_hash (c->augmentation, strlen (c->augmentation) + 1, h);
259218822Sdim  h = iterative_hash_object (c->code_align, h);
260218822Sdim  h = iterative_hash_object (c->data_align, h);
261218822Sdim  h = iterative_hash_object (c->ra_column, h);
262218822Sdim  h = iterative_hash_object (c->augmentation_size, h);
263218822Sdim  h = iterative_hash_object (c->personality, h);
264218822Sdim  h = iterative_hash_object (c->output_sec, h);
265218822Sdim  h = iterative_hash_object (c->per_encoding, h);
266218822Sdim  h = iterative_hash_object (c->lsda_encoding, h);
267218822Sdim  h = iterative_hash_object (c->fde_encoding, h);
268218822Sdim  h = iterative_hash_object (c->initial_insn_length, h);
269218822Sdim  h = iterative_hash (c->initial_instructions, c->initial_insn_length, h);
270218822Sdim  c->hash = h;
271218822Sdim  return h;
272218822Sdim}
273218822Sdim
274218822Sdim/* Return the number of extra bytes that we'll be inserting into
275218822Sdim   ENTRY's augmentation string.  */
276218822Sdim
277218822Sdimstatic INLINE unsigned int
278218822Sdimextra_augmentation_string_bytes (struct eh_cie_fde *entry)
279218822Sdim{
280218822Sdim  unsigned int size = 0;
281218822Sdim  if (entry->cie)
282218822Sdim    {
283218822Sdim      if (entry->add_augmentation_size)
284218822Sdim	size++;
285218822Sdim      if (entry->add_fde_encoding)
286218822Sdim	size++;
287218822Sdim    }
288218822Sdim  return size;
289218822Sdim}
290218822Sdim
291218822Sdim/* Likewise ENTRY's augmentation data.  */
292218822Sdim
293218822Sdimstatic INLINE unsigned int
294218822Sdimextra_augmentation_data_bytes (struct eh_cie_fde *entry)
295218822Sdim{
296218822Sdim  unsigned int size = 0;
297218822Sdim  if (entry->cie)
298218822Sdim    {
299218822Sdim      if (entry->add_augmentation_size)
300218822Sdim	size++;
301218822Sdim      if (entry->add_fde_encoding)
302218822Sdim	size++;
303218822Sdim    }
304218822Sdim  else
305218822Sdim    {
306218822Sdim      if (entry->cie_inf->add_augmentation_size)
307218822Sdim	size++;
308218822Sdim    }
309218822Sdim  return size;
310218822Sdim}
311218822Sdim
312218822Sdim/* Return the size that ENTRY will have in the output.  ALIGNMENT is the
313218822Sdim   required alignment of ENTRY in bytes.  */
314218822Sdim
315218822Sdimstatic unsigned int
316218822Sdimsize_of_output_cie_fde (struct eh_cie_fde *entry, unsigned int alignment)
317218822Sdim{
318218822Sdim  if (entry->removed)
31989857Sobrien    return 0;
320218822Sdim  if (entry->size == 4)
321218822Sdim    return 4;
322218822Sdim  return (entry->size
323218822Sdim	  + extra_augmentation_string_bytes (entry)
324218822Sdim	  + extra_augmentation_data_bytes (entry)
325218822Sdim	  + alignment - 1) & -alignment;
326218822Sdim}
32789857Sobrien
328218822Sdim/* Assume that the bytes between *ITER and END are CFA instructions.
329218822Sdim   Try to move *ITER past the first instruction and return true on
330218822Sdim   success.  ENCODED_PTR_WIDTH gives the width of pointer entries.  */
331218822Sdim
332218822Sdimstatic bfd_boolean
333218822Sdimskip_cfa_op (bfd_byte **iter, bfd_byte *end, unsigned int encoded_ptr_width)
334218822Sdim{
335218822Sdim  bfd_byte op;
336218822Sdim  bfd_vma length;
337218822Sdim
338218822Sdim  if (!read_byte (iter, end, &op))
339218822Sdim    return FALSE;
340218822Sdim
341218822Sdim  switch (op & 0xc0 ? op & 0xc0 : op)
342218822Sdim    {
343218822Sdim    case DW_CFA_nop:
344218822Sdim    case DW_CFA_advance_loc:
345218822Sdim    case DW_CFA_restore:
346218822Sdim    case DW_CFA_remember_state:
347218822Sdim    case DW_CFA_restore_state:
348218822Sdim    case DW_CFA_GNU_window_save:
349218822Sdim      /* No arguments.  */
350218822Sdim      return TRUE;
351218822Sdim
352218822Sdim    case DW_CFA_offset:
353218822Sdim    case DW_CFA_restore_extended:
354218822Sdim    case DW_CFA_undefined:
355218822Sdim    case DW_CFA_same_value:
356218822Sdim    case DW_CFA_def_cfa_register:
357218822Sdim    case DW_CFA_def_cfa_offset:
358218822Sdim    case DW_CFA_def_cfa_offset_sf:
359218822Sdim    case DW_CFA_GNU_args_size:
360218822Sdim      /* One leb128 argument.  */
361218822Sdim      return skip_leb128 (iter, end);
362218822Sdim
363218822Sdim    case DW_CFA_val_offset:
364218822Sdim    case DW_CFA_val_offset_sf:
365218822Sdim    case DW_CFA_offset_extended:
366218822Sdim    case DW_CFA_register:
367218822Sdim    case DW_CFA_def_cfa:
368218822Sdim    case DW_CFA_offset_extended_sf:
369218822Sdim    case DW_CFA_GNU_negative_offset_extended:
370218822Sdim    case DW_CFA_def_cfa_sf:
371218822Sdim      /* Two leb128 arguments.  */
372218822Sdim      return (skip_leb128 (iter, end)
373218822Sdim	      && skip_leb128 (iter, end));
374218822Sdim
375218822Sdim    case DW_CFA_def_cfa_expression:
376218822Sdim      /* A variable-length argument.  */
377218822Sdim      return (read_uleb128 (iter, end, &length)
378218822Sdim	      && skip_bytes (iter, end, length));
379218822Sdim
380218822Sdim    case DW_CFA_expression:
381218822Sdim    case DW_CFA_val_expression:
382218822Sdim      /* A leb128 followed by a variable-length argument.  */
383218822Sdim      return (skip_leb128 (iter, end)
384218822Sdim	      && read_uleb128 (iter, end, &length)
385218822Sdim	      && skip_bytes (iter, end, length));
386218822Sdim
387218822Sdim    case DW_CFA_set_loc:
388218822Sdim      return skip_bytes (iter, end, encoded_ptr_width);
389218822Sdim
390218822Sdim    case DW_CFA_advance_loc1:
391218822Sdim      return skip_bytes (iter, end, 1);
392218822Sdim
393218822Sdim    case DW_CFA_advance_loc2:
394218822Sdim      return skip_bytes (iter, end, 2);
395218822Sdim
396218822Sdim    case DW_CFA_advance_loc4:
397218822Sdim      return skip_bytes (iter, end, 4);
398218822Sdim
399218822Sdim    case DW_CFA_MIPS_advance_loc8:
400218822Sdim      return skip_bytes (iter, end, 8);
401218822Sdim
402218822Sdim    default:
403218822Sdim      return FALSE;
404218822Sdim    }
40589857Sobrien}
40689857Sobrien
407218822Sdim/* Try to interpret the bytes between BUF and END as CFA instructions.
408218822Sdim   If every byte makes sense, return a pointer to the first DW_CFA_nop
409218822Sdim   padding byte, or END if there is no padding.  Return null otherwise.
410218822Sdim   ENCODED_PTR_WIDTH is as for skip_cfa_op.  */
411218822Sdim
412218822Sdimstatic bfd_byte *
413218822Sdimskip_non_nops (bfd_byte *buf, bfd_byte *end, unsigned int encoded_ptr_width,
414218822Sdim	       unsigned int *set_loc_count)
415218822Sdim{
416218822Sdim  bfd_byte *last;
417218822Sdim
418218822Sdim  last = buf;
419218822Sdim  while (buf < end)
420218822Sdim    if (*buf == DW_CFA_nop)
421218822Sdim      buf++;
422218822Sdim    else
423218822Sdim      {
424218822Sdim	if (*buf == DW_CFA_set_loc)
425218822Sdim	  ++*set_loc_count;
426218822Sdim	if (!skip_cfa_op (&buf, end, encoded_ptr_width))
427218822Sdim	  return 0;
428218822Sdim	last = buf;
429218822Sdim      }
430218822Sdim  return last;
431218822Sdim}
432218822Sdim
43389857Sobrien/* This function is called for each input file before the .eh_frame
43489857Sobrien   section is relocated.  It discards duplicate CIEs and FDEs for discarded
435130561Sobrien   functions.  The function returns TRUE iff any entries have been
43689857Sobrien   deleted.  */
43789857Sobrien
438130561Sobrienbfd_boolean
439130561Sobrien_bfd_elf_discard_section_eh_frame
440130561Sobrien   (bfd *abfd, struct bfd_link_info *info, asection *sec,
441130561Sobrien    bfd_boolean (*reloc_symbol_deleted_p) (bfd_vma, void *),
442130561Sobrien    struct elf_reloc_cookie *cookie)
44389857Sobrien{
444218822Sdim#define REQUIRE(COND)					\
445218822Sdim  do							\
446218822Sdim    if (!(COND))					\
447218822Sdim      goto free_no_table;				\
448218822Sdim  while (0)
449218822Sdim
45089857Sobrien  bfd_byte *ehbuf = NULL, *buf;
451218822Sdim  bfd_byte *last_fde;
452218822Sdim  struct eh_cie_fde *ent, *this_inf;
453218822Sdim  unsigned int hdr_length, hdr_id;
454218822Sdim  struct extended_cie
455218822Sdim    {
456218822Sdim      struct cie cie;
457218822Sdim      unsigned int offset;
458218822Sdim      unsigned int usage_count;
459218822Sdim      unsigned int entry;
460218822Sdim    } *ecies = NULL, *ecie;
461218822Sdim  unsigned int ecie_count = 0, ecie_alloced = 0;
462218822Sdim  struct cie *cie;
463130561Sobrien  struct elf_link_hash_table *htab;
46489857Sobrien  struct eh_frame_hdr_info *hdr_info;
46589857Sobrien  struct eh_frame_sec_info *sec_info = NULL;
466218822Sdim  unsigned int offset;
46789857Sobrien  unsigned int ptr_size;
468218822Sdim  unsigned int entry_alloced;
46989857Sobrien
470218822Sdim  if (sec->size == 0)
47189857Sobrien    {
47289857Sobrien      /* This file does not contain .eh_frame information.  */
473130561Sobrien      return FALSE;
47489857Sobrien    }
47589857Sobrien
476218822Sdim  if (bfd_is_abs_section (sec->output_section))
47789857Sobrien    {
47889857Sobrien      /* At least one of the sections is being discarded from the
479218822Sdim	 link, so we should just ignore them.  */
480130561Sobrien      return FALSE;
48189857Sobrien    }
48289857Sobrien
483130561Sobrien  htab = elf_hash_table (info);
484130561Sobrien  hdr_info = &htab->eh_info;
48589857Sobrien
486218822Sdim  if (hdr_info->cies == NULL && !info->relocatable)
487218822Sdim    hdr_info->cies = htab_try_create (1, cie_hash, cie_eq, free);
488218822Sdim
48989857Sobrien  /* Read the frame unwind information from abfd.  */
49089857Sobrien
491218822Sdim  REQUIRE (bfd_malloc_and_get_section (abfd, sec, &ehbuf));
49289857Sobrien
493218822Sdim  if (sec->size >= 4
49489857Sobrien      && bfd_get_32 (abfd, ehbuf) == 0
49589857Sobrien      && cookie->rel == cookie->relend)
49689857Sobrien    {
49789857Sobrien      /* Empty .eh_frame section.  */
49889857Sobrien      free (ehbuf);
499130561Sobrien      return FALSE;
50089857Sobrien    }
50189857Sobrien
50289857Sobrien  /* If .eh_frame section size doesn't fit into int, we cannot handle
50389857Sobrien     it (it would need to use 64-bit .eh_frame format anyway).  */
504218822Sdim  REQUIRE (sec->size == (unsigned int) sec->size);
50589857Sobrien
506218822Sdim  ptr_size = (get_elf_backend_data (abfd)
507218822Sdim	      ->elf_backend_eh_frame_address_size (abfd, sec));
508218822Sdim  REQUIRE (ptr_size != 0);
509218822Sdim
51089857Sobrien  buf = ehbuf;
51189857Sobrien  sec_info = bfd_zmalloc (sizeof (struct eh_frame_sec_info)
51289857Sobrien			  + 99 * sizeof (struct eh_cie_fde));
513218822Sdim  REQUIRE (sec_info);
51489857Sobrien
515218822Sdim  entry_alloced = 100;
516218822Sdim
51789857Sobrien#define ENSURE_NO_RELOCS(buf)				\
518218822Sdim  REQUIRE (!(cookie->rel < cookie->relend		\
519218822Sdim	     && (cookie->rel->r_offset			\
520218822Sdim		 < (bfd_size_type) ((buf) - ehbuf))	\
521218822Sdim	     && cookie->rel->r_info != 0))
52289857Sobrien
52389857Sobrien#define SKIP_RELOCS(buf)				\
52489857Sobrien  while (cookie->rel < cookie->relend			\
525218822Sdim	 && (cookie->rel->r_offset			\
52689857Sobrien	     < (bfd_size_type) ((buf) - ehbuf)))	\
52789857Sobrien    cookie->rel++
52889857Sobrien
52989857Sobrien#define GET_RELOC(buf)					\
53089857Sobrien  ((cookie->rel < cookie->relend			\
53189857Sobrien    && (cookie->rel->r_offset				\
532218822Sdim	== (bfd_size_type) ((buf) - ehbuf)))		\
53389857Sobrien   ? cookie->rel : NULL)
53489857Sobrien
53589857Sobrien  for (;;)
53689857Sobrien    {
537218822Sdim      char *aug;
538218822Sdim      bfd_byte *start, *end, *insns, *insns_end;
539218822Sdim      bfd_size_type length;
540218822Sdim      unsigned int set_loc_count;
54189857Sobrien
542218822Sdim      if (sec_info->count == entry_alloced)
54389857Sobrien	{
54489857Sobrien	  sec_info = bfd_realloc (sec_info,
54589857Sobrien				  sizeof (struct eh_frame_sec_info)
546218822Sdim				  + ((entry_alloced + 99)
547218822Sdim				     * sizeof (struct eh_cie_fde)));
548218822Sdim	  REQUIRE (sec_info);
54989857Sobrien
550218822Sdim	  memset (&sec_info->entry[entry_alloced], 0,
55189857Sobrien		  100 * sizeof (struct eh_cie_fde));
552218822Sdim	  entry_alloced += 100;
55389857Sobrien	}
55489857Sobrien
555218822Sdim      this_inf = sec_info->entry + sec_info->count;
55689857Sobrien      last_fde = buf;
55789857Sobrien
558218822Sdim      if ((bfd_size_type) (buf - ehbuf) == sec->size)
559218822Sdim	break;
56089857Sobrien
561218822Sdim      /* Read the length of the entry.  */
562218822Sdim      REQUIRE (skip_bytes (&buf, ehbuf + sec->size, 4));
563218822Sdim      hdr_length = bfd_get_32 (abfd, buf - 4);
56489857Sobrien
565218822Sdim      /* 64-bit .eh_frame is not supported.  */
566218822Sdim      REQUIRE (hdr_length != 0xffffffff);
567218822Sdim
568218822Sdim      /* The CIE/FDE must be fully contained in this input section.  */
569218822Sdim      REQUIRE ((bfd_size_type) (buf - ehbuf) + hdr_length <= sec->size);
570218822Sdim      end = buf + hdr_length;
571218822Sdim
572218822Sdim      this_inf->offset = last_fde - ehbuf;
573218822Sdim      this_inf->size = 4 + hdr_length;
574218822Sdim
575218822Sdim      if (hdr_length == 0)
576218822Sdim	{
577218822Sdim	  /* A zero-length CIE should only be found at the end of
578218822Sdim	     the section.  */
579218822Sdim	  REQUIRE ((bfd_size_type) (buf - ehbuf) == sec->size);
580218822Sdim	  ENSURE_NO_RELOCS (buf);
581218822Sdim	  sec_info->count++;
582218822Sdim	  break;
58389857Sobrien	}
58489857Sobrien
585218822Sdim      REQUIRE (skip_bytes (&buf, end, 4));
586218822Sdim      hdr_id = bfd_get_32 (abfd, buf - 4);
587218822Sdim
588218822Sdim      if (hdr_id == 0)
58989857Sobrien	{
59089857Sobrien	  unsigned int initial_insn_length;
59189857Sobrien
59289857Sobrien	  /* CIE  */
593218822Sdim	  this_inf->cie = 1;
594218822Sdim
595218822Sdim	  if (ecie_count == ecie_alloced)
59689857Sobrien	    {
597218822Sdim	      ecies = bfd_realloc (ecies,
598218822Sdim				   (ecie_alloced + 20) * sizeof (*ecies));
599218822Sdim	      REQUIRE (ecies);
600218822Sdim	      memset (&ecies[ecie_alloced], 0, 20 * sizeof (*ecies));
601218822Sdim	      ecie_alloced += 20;
60289857Sobrien	    }
60389857Sobrien
604218822Sdim	  cie = &ecies[ecie_count].cie;
605218822Sdim	  ecies[ecie_count].offset = this_inf->offset;
606218822Sdim	  ecies[ecie_count++].entry = sec_info->count;
607218822Sdim	  cie->length = hdr_length;
608218822Sdim	  start = buf;
609218822Sdim	  REQUIRE (read_byte (&buf, end, &cie->version));
61089857Sobrien
61189857Sobrien	  /* Cannot handle unknown versions.  */
612218822Sdim	  REQUIRE (cie->version == 1 || cie->version == 3);
613218822Sdim	  REQUIRE (strlen ((char *) buf) < sizeof (cie->augmentation));
61489857Sobrien
615218822Sdim	  strcpy (cie->augmentation, (char *) buf);
616218822Sdim	  buf = (bfd_byte *) strchr ((char *) buf, '\0') + 1;
61789857Sobrien	  ENSURE_NO_RELOCS (buf);
61889857Sobrien	  if (buf[0] == 'e' && buf[1] == 'h')
61989857Sobrien	    {
62089857Sobrien	      /* GCC < 3.0 .eh_frame CIE */
62189857Sobrien	      /* We cannot merge "eh" CIEs because __EXCEPTION_TABLE__
62289857Sobrien		 is private to each CIE, so we don't need it for anything.
62389857Sobrien		 Just skip it.  */
624218822Sdim	      REQUIRE (skip_bytes (&buf, end, ptr_size));
62589857Sobrien	      SKIP_RELOCS (buf);
62689857Sobrien	    }
627218822Sdim	  REQUIRE (read_uleb128 (&buf, end, &cie->code_align));
628218822Sdim	  REQUIRE (read_sleb128 (&buf, end, &cie->data_align));
629218822Sdim	  if (cie->version == 1)
630218822Sdim	    {
631218822Sdim	      REQUIRE (buf < end);
632218822Sdim	      cie->ra_column = *buf++;
633218822Sdim	    }
634218822Sdim	  else
635218822Sdim	    REQUIRE (read_uleb128 (&buf, end, &cie->ra_column));
63689857Sobrien	  ENSURE_NO_RELOCS (buf);
637218822Sdim	  cie->lsda_encoding = DW_EH_PE_omit;
638218822Sdim	  cie->fde_encoding = DW_EH_PE_omit;
639218822Sdim	  cie->per_encoding = DW_EH_PE_omit;
640218822Sdim	  aug = cie->augmentation;
64189857Sobrien	  if (aug[0] != 'e' || aug[1] != 'h')
64289857Sobrien	    {
64389857Sobrien	      if (*aug == 'z')
64489857Sobrien		{
64589857Sobrien		  aug++;
646218822Sdim		  REQUIRE (read_uleb128 (&buf, end, &cie->augmentation_size));
64789857Sobrien	  	  ENSURE_NO_RELOCS (buf);
64889857Sobrien		}
64989857Sobrien
65089857Sobrien	      while (*aug != '\0')
65189857Sobrien		switch (*aug++)
65289857Sobrien		  {
65389857Sobrien		  case 'L':
654218822Sdim		    REQUIRE (read_byte (&buf, end, &cie->lsda_encoding));
65589857Sobrien		    ENSURE_NO_RELOCS (buf);
656218822Sdim		    REQUIRE (get_DW_EH_PE_width (cie->lsda_encoding, ptr_size));
65789857Sobrien		    break;
65889857Sobrien		  case 'R':
659218822Sdim		    REQUIRE (read_byte (&buf, end, &cie->fde_encoding));
66089857Sobrien		    ENSURE_NO_RELOCS (buf);
661218822Sdim		    REQUIRE (get_DW_EH_PE_width (cie->fde_encoding, ptr_size));
66289857Sobrien		    break;
663218822Sdim		  case 'S':
664218822Sdim		    break;
66589857Sobrien		  case 'P':
66689857Sobrien		    {
66789857Sobrien		      int per_width;
66889857Sobrien
669218822Sdim		      REQUIRE (read_byte (&buf, end, &cie->per_encoding));
670218822Sdim		      per_width = get_DW_EH_PE_width (cie->per_encoding,
67189857Sobrien						      ptr_size);
672218822Sdim		      REQUIRE (per_width);
673218822Sdim		      if ((cie->per_encoding & 0xf0) == DW_EH_PE_aligned)
674218822Sdim			{
675218822Sdim			  length = -(buf - ehbuf) & (per_width - 1);
676218822Sdim			  REQUIRE (skip_bytes (&buf, end, length));
677218822Sdim			}
67889857Sobrien		      ENSURE_NO_RELOCS (buf);
679218822Sdim		      /* Ensure we have a reloc here.  */
680130561Sobrien		      if (GET_RELOC (buf) != NULL)
68189857Sobrien			{
68289857Sobrien			  unsigned long r_symndx;
68389857Sobrien
68489857Sobrien#ifdef BFD64
68589857Sobrien			  if (ptr_size == 8)
68689857Sobrien			    r_symndx = ELF64_R_SYM (cookie->rel->r_info);
68789857Sobrien			  else
68889857Sobrien#endif
68989857Sobrien			    r_symndx = ELF32_R_SYM (cookie->rel->r_info);
690218822Sdim			  if (r_symndx >= cookie->locsymcount
691218822Sdim			      || ELF_ST_BIND (cookie->locsyms[r_symndx]
692218822Sdim					      .st_info) != STB_LOCAL)
69389857Sobrien			    {
69489857Sobrien			      struct elf_link_hash_entry *h;
69589857Sobrien
69689857Sobrien			      r_symndx -= cookie->extsymoff;
69789857Sobrien			      h = cookie->sym_hashes[r_symndx];
69889857Sobrien
69989857Sobrien			      while (h->root.type == bfd_link_hash_indirect
70089857Sobrien				     || h->root.type == bfd_link_hash_warning)
70189857Sobrien				h = (struct elf_link_hash_entry *)
70289857Sobrien				    h->root.u.i.link;
70389857Sobrien
704218822Sdim			      cie->personality.h = h;
70589857Sobrien			    }
706218822Sdim			  else
707218822Sdim			    {
708218822Sdim			      Elf_Internal_Sym *sym;
709218822Sdim			      asection *sym_sec;
710218822Sdim			      bfd_vma val;
711218822Sdim
712218822Sdim			      sym = &cookie->locsyms[r_symndx];
713218822Sdim			      sym_sec = (bfd_section_from_elf_index
714218822Sdim					 (abfd, sym->st_shndx));
715218822Sdim			      if (sym_sec != NULL)
716218822Sdim				{
717218822Sdim				  if (sym_sec->kept_section != NULL)
718218822Sdim				    sym_sec = sym_sec->kept_section;
719218822Sdim				  if (sym_sec->output_section != NULL)
720218822Sdim				    {
721218822Sdim				      val = (sym->st_value
722218822Sdim					     + sym_sec->output_offset
723218822Sdim					     + sym_sec->output_section->vma);
724218822Sdim				      cie->personality.val = val;
725218822Sdim				      cie->local_personality = 1;
726218822Sdim				    }
727218822Sdim				}
728218822Sdim			    }
729218822Sdim
730218822Sdim			  /* Cope with MIPS-style composite relocations.  */
731218822Sdim			  do
732218822Sdim			    cookie->rel++;
733218822Sdim			  while (GET_RELOC (buf) != NULL);
73489857Sobrien			}
735218822Sdim		      REQUIRE (skip_bytes (&buf, end, per_width));
736218822Sdim		      REQUIRE (cie->local_personality || cie->personality.h);
73789857Sobrien		    }
73889857Sobrien		    break;
73989857Sobrien		  default:
74089857Sobrien		    /* Unrecognized augmentation. Better bail out.  */
74189857Sobrien		    goto free_no_table;
74289857Sobrien		  }
74389857Sobrien	    }
74489857Sobrien
74589857Sobrien	  /* For shared libraries, try to get rid of as many RELATIVE relocs
74691041Sobrien	     as possible.  */
747218822Sdim	  if (info->shared
748130561Sobrien	      && (get_elf_backend_data (abfd)
749130561Sobrien		  ->elf_backend_can_make_relative_eh_frame
750218822Sdim		  (abfd, info, sec)))
751218822Sdim	    {
752218822Sdim	      if ((cie->fde_encoding & 0xf0) == DW_EH_PE_absptr)
753218822Sdim		cie->make_relative = 1;
754218822Sdim	      /* If the CIE doesn't already have an 'R' entry, it's fairly
755218822Sdim		 easy to add one, provided that there's no aligned data
756218822Sdim		 after the augmentation string.  */
757218822Sdim	      else if (cie->fde_encoding == DW_EH_PE_omit
758218822Sdim		       && (cie->per_encoding & 0xf0) != DW_EH_PE_aligned)
759218822Sdim		{
760218822Sdim		  if (*cie->augmentation == 0)
761218822Sdim		    this_inf->add_augmentation_size = 1;
762218822Sdim		  this_inf->add_fde_encoding = 1;
763218822Sdim		  cie->make_relative = 1;
764218822Sdim		}
765218822Sdim	    }
76689857Sobrien
76791041Sobrien	  if (info->shared
768130561Sobrien	      && (get_elf_backend_data (abfd)
769130561Sobrien		  ->elf_backend_can_make_lsda_relative_eh_frame
770130561Sobrien		  (abfd, info, sec))
771218822Sdim	      && (cie->lsda_encoding & 0xf0) == DW_EH_PE_absptr)
772218822Sdim	    cie->make_lsda_relative = 1;
77389857Sobrien
77489857Sobrien	  /* If FDE encoding was not specified, it defaults to
77589857Sobrien	     DW_EH_absptr.  */
776218822Sdim	  if (cie->fde_encoding == DW_EH_PE_omit)
777218822Sdim	    cie->fde_encoding = DW_EH_PE_absptr;
77889857Sobrien
779218822Sdim	  initial_insn_length = end - buf;
780218822Sdim	  if (initial_insn_length <= sizeof (cie->initial_instructions))
78189857Sobrien	    {
782218822Sdim	      cie->initial_insn_length = initial_insn_length;
783218822Sdim	      memcpy (cie->initial_instructions, buf, initial_insn_length);
78489857Sobrien	    }
785218822Sdim	  insns = buf;
78689857Sobrien	  buf += initial_insn_length;
78789857Sobrien	  ENSURE_NO_RELOCS (buf);
78889857Sobrien	}
78989857Sobrien      else
79089857Sobrien	{
791218822Sdim	  /* Find the corresponding CIE.  */
792218822Sdim	  unsigned int cie_offset = this_inf->offset + 4 - hdr_id;
793218822Sdim	  for (ecie = ecies; ecie < ecies + ecie_count; ++ecie)
794218822Sdim	    if (cie_offset == ecie->offset)
795218822Sdim	      break;
79689857Sobrien
797218822Sdim	  /* Ensure this FDE references one of the CIEs in this input
798218822Sdim	     section.  */
799218822Sdim	  REQUIRE (ecie != ecies + ecie_count);
800218822Sdim	  cie = &ecie->cie;
801218822Sdim
80289857Sobrien	  ENSURE_NO_RELOCS (buf);
803218822Sdim	  REQUIRE (GET_RELOC (buf));
804218822Sdim
80589857Sobrien	  if ((*reloc_symbol_deleted_p) (buf - ehbuf, cookie))
806218822Sdim	    /* This is a FDE against a discarded section.  It should
807218822Sdim	       be deleted.  */
808218822Sdim	    this_inf->removed = 1;
80989857Sobrien	  else
81089857Sobrien	    {
81191041Sobrien	      if (info->shared
812218822Sdim		  && (((cie->fde_encoding & 0xf0) == DW_EH_PE_absptr
813218822Sdim		       && cie->make_relative == 0)
814218822Sdim		      || (cie->fde_encoding & 0xf0) == DW_EH_PE_aligned))
81591041Sobrien		{
816130561Sobrien		  /* If a shared library uses absolute pointers
81791041Sobrien		     which we cannot turn into PC relative,
81891041Sobrien		     don't create the binary search table,
81991041Sobrien		     since it is affected by runtime relocations.  */
820130561Sobrien		  hdr_info->table = FALSE;
821218822Sdim		  (*info->callbacks->einfo)
822218822Sdim		    (_("%P: fde encoding in %B(%A) prevents .eh_frame_hdr"
823218822Sdim		       " table being created.\n"), abfd, sec);
82491041Sobrien		}
825218822Sdim	      ecie->usage_count++;
82689857Sobrien	      hdr_info->fde_count++;
827218822Sdim	      this_inf->cie_inf = (void *) (ecie - ecies);
82889857Sobrien	    }
829218822Sdim
830218822Sdim	  /* Skip the initial location and address range.  */
831218822Sdim	  start = buf;
832218822Sdim	  length = get_DW_EH_PE_width (cie->fde_encoding, ptr_size);
833218822Sdim	  REQUIRE (skip_bytes (&buf, end, 2 * length));
834218822Sdim
835218822Sdim	  /* Skip the augmentation size, if present.  */
836218822Sdim	  if (cie->augmentation[0] == 'z')
837218822Sdim	    REQUIRE (read_uleb128 (&buf, end, &length));
838218822Sdim	  else
839218822Sdim	    length = 0;
840218822Sdim
841218822Sdim	  /* Of the supported augmentation characters above, only 'L'
842218822Sdim	     adds augmentation data to the FDE.  This code would need to
843218822Sdim	     be adjusted if any future augmentations do the same thing.  */
844218822Sdim	  if (cie->lsda_encoding != DW_EH_PE_omit)
84589857Sobrien	    {
846218822Sdim	      this_inf->lsda_offset = buf - start;
847218822Sdim	      /* If there's no 'z' augmentation, we don't know where the
848218822Sdim		 CFA insns begin.  Assume no padding.  */
849218822Sdim	      if (cie->augmentation[0] != 'z')
850218822Sdim		length = end - buf;
851218822Sdim	    }
85289857Sobrien
853218822Sdim	  /* Skip over the augmentation data.  */
854218822Sdim	  REQUIRE (skip_bytes (&buf, end, length));
855218822Sdim	  insns = buf;
856218822Sdim
857218822Sdim	  buf = last_fde + 4 + hdr_length;
85889857Sobrien	  SKIP_RELOCS (buf);
85989857Sobrien	}
86089857Sobrien
861218822Sdim      /* Try to interpret the CFA instructions and find the first
862218822Sdim	 padding nop.  Shrink this_inf's size so that it doesn't
863218822Sdim	 include the padding.  */
864218822Sdim      length = get_DW_EH_PE_width (cie->fde_encoding, ptr_size);
865218822Sdim      set_loc_count = 0;
866218822Sdim      insns_end = skip_non_nops (insns, end, length, &set_loc_count);
867218822Sdim      /* If we don't understand the CFA instructions, we can't know
868218822Sdim	 what needs to be adjusted there.  */
869218822Sdim      if (insns_end == NULL
870218822Sdim	  /* For the time being we don't support DW_CFA_set_loc in
871218822Sdim	     CIE instructions.  */
872218822Sdim	  || (set_loc_count && this_inf->cie))
873218822Sdim	goto free_no_table;
874218822Sdim      this_inf->size -= end - insns_end;
875218822Sdim      if (insns_end != end && this_inf->cie)
876218822Sdim	{
877218822Sdim	  cie->initial_insn_length -= end - insns_end;
878218822Sdim	  cie->length -= end - insns_end;
879218822Sdim	}
880218822Sdim      if (set_loc_count
881218822Sdim	  && ((cie->fde_encoding & 0xf0) == DW_EH_PE_pcrel
882218822Sdim	      || cie->make_relative))
883218822Sdim	{
884218822Sdim	  unsigned int cnt;
885218822Sdim	  bfd_byte *p;
886218822Sdim
887218822Sdim	  this_inf->set_loc = bfd_malloc ((set_loc_count + 1)
888218822Sdim					  * sizeof (unsigned int));
889218822Sdim	  REQUIRE (this_inf->set_loc);
890218822Sdim	  this_inf->set_loc[0] = set_loc_count;
891218822Sdim	  p = insns;
892218822Sdim	  cnt = 0;
893218822Sdim	  while (p < end)
894218822Sdim	    {
895218822Sdim	      if (*p == DW_CFA_set_loc)
896218822Sdim		this_inf->set_loc[++cnt] = p + 1 - start;
897218822Sdim	      REQUIRE (skip_cfa_op (&p, end, length));
898218822Sdim	    }
899218822Sdim	}
900218822Sdim
901218822Sdim      this_inf->fde_encoding = cie->fde_encoding;
902218822Sdim      this_inf->lsda_encoding = cie->lsda_encoding;
90389857Sobrien      sec_info->count++;
90489857Sobrien    }
90589857Sobrien
90689857Sobrien  elf_section_data (sec)->sec_info = sec_info;
907130561Sobrien  sec->sec_info_type = ELF_INFO_TYPE_EH_FRAME;
90889857Sobrien
909218822Sdim  /* Look at all CIEs in this section and determine which can be
910218822Sdim     removed as unused, which can be merged with previous duplicate
911218822Sdim     CIEs and which need to be kept.  */
912218822Sdim  for (ecie = ecies; ecie < ecies + ecie_count; ++ecie)
91389857Sobrien    {
914218822Sdim      if (ecie->usage_count == 0)
91589857Sobrien	{
916218822Sdim	  sec_info->entry[ecie->entry].removed = 1;
917218822Sdim	  continue;
918218822Sdim	}
919218822Sdim      ecie->cie.output_sec = sec->output_section;
920218822Sdim      ecie->cie.cie_inf = sec_info->entry + ecie->entry;
921218822Sdim      cie_compute_hash (&ecie->cie);
922218822Sdim      if (hdr_info->cies != NULL)
923218822Sdim	{
924218822Sdim	  void **loc = htab_find_slot_with_hash (hdr_info->cies, &ecie->cie,
925218822Sdim						 ecie->cie.hash, INSERT);
926218822Sdim	  if (loc != NULL)
92789857Sobrien	    {
928218822Sdim	      if (*loc != HTAB_EMPTY_ENTRY)
929218822Sdim		{
930218822Sdim		  sec_info->entry[ecie->entry].removed = 1;
931218822Sdim		  ecie->cie.cie_inf = ((struct cie *) *loc)->cie_inf;
932218822Sdim		  continue;
933218822Sdim		}
934218822Sdim
935218822Sdim	      *loc = malloc (sizeof (struct cie));
936218822Sdim	      if (*loc == NULL)
937218822Sdim		*loc = HTAB_DELETED_ENTRY;
938218822Sdim	      else
939218822Sdim		memcpy (*loc, &ecie->cie, sizeof (struct cie));
94089857Sobrien	    }
94189857Sobrien	}
942218822Sdim      ecie->cie.cie_inf->make_relative = ecie->cie.make_relative;
943218822Sdim      ecie->cie.cie_inf->make_lsda_relative = ecie->cie.make_lsda_relative;
944218822Sdim      ecie->cie.cie_inf->per_encoding_relative
945218822Sdim	= (ecie->cie.per_encoding & 0x70) == DW_EH_PE_pcrel;
94689857Sobrien    }
94789857Sobrien
948218822Sdim  /* Ok, now we can assign new offsets.  */
949218822Sdim  offset = 0;
950218822Sdim  for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
951218822Sdim    if (!ent->removed)
952218822Sdim      {
953218822Sdim	if (!ent->cie)
954218822Sdim	  {
955218822Sdim	    ecie = ecies + (unsigned long) ent->cie_inf;
956218822Sdim	    ent->cie_inf = ecie->cie.cie_inf;
957218822Sdim	  }
958218822Sdim	ent->new_offset = offset;
959218822Sdim	offset += size_of_output_cie_fde (ent, ptr_size);
960218822Sdim      }
96189857Sobrien
962218822Sdim  /* Resize the sec as needed.  */
963218822Sdim  sec->rawsize = sec->size;
964218822Sdim  sec->size = offset;
96589857Sobrien
96689857Sobrien  free (ehbuf);
967218822Sdim  if (ecies)
968218822Sdim    free (ecies);
969218822Sdim  return offset != sec->rawsize;
97089857Sobrien
97189857Sobrienfree_no_table:
972218822Sdim  (*info->callbacks->einfo)
973218822Sdim    (_("%P: error in %B(%A); no .eh_frame_hdr table will be created.\n"),
974218822Sdim     abfd, sec);
97589857Sobrien  if (ehbuf)
97689857Sobrien    free (ehbuf);
97789857Sobrien  if (sec_info)
97889857Sobrien    free (sec_info);
979218822Sdim  if (ecies)
980218822Sdim    free (ecies);
981130561Sobrien  hdr_info->table = FALSE;
982130561Sobrien  return FALSE;
983218822Sdim
984218822Sdim#undef REQUIRE
98589857Sobrien}
98689857Sobrien
98789857Sobrien/* This function is called for .eh_frame_hdr section after
98889857Sobrien   _bfd_elf_discard_section_eh_frame has been called on all .eh_frame
98989857Sobrien   input sections.  It finalizes the size of .eh_frame_hdr section.  */
99089857Sobrien
991130561Sobrienbfd_boolean
992130561Sobrien_bfd_elf_discard_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
99389857Sobrien{
994130561Sobrien  struct elf_link_hash_table *htab;
99589857Sobrien  struct eh_frame_hdr_info *hdr_info;
996130561Sobrien  asection *sec;
99789857Sobrien
998130561Sobrien  htab = elf_hash_table (info);
999130561Sobrien  hdr_info = &htab->eh_info;
1000218822Sdim
1001218822Sdim  if (hdr_info->cies != NULL)
1002218822Sdim    {
1003218822Sdim      htab_delete (hdr_info->cies);
1004218822Sdim      hdr_info->cies = NULL;
1005218822Sdim    }
1006218822Sdim
1007130561Sobrien  sec = hdr_info->hdr_sec;
1008130561Sobrien  if (sec == NULL)
1009130561Sobrien    return FALSE;
101089857Sobrien
1011218822Sdim  sec->size = EH_FRAME_HDR_SIZE;
101289857Sobrien  if (hdr_info->table)
1013218822Sdim    sec->size += 4 + hdr_info->fde_count * 8;
101489857Sobrien
1015130561Sobrien  elf_tdata (abfd)->eh_frame_hdr = sec;
1016130561Sobrien  return TRUE;
101789857Sobrien}
101889857Sobrien
101989857Sobrien/* This function is called from size_dynamic_sections.
102089857Sobrien   It needs to decide whether .eh_frame_hdr should be output or not,
1021218822Sdim   because when the dynamic symbol table has been sized it is too late
1022218822Sdim   to strip sections.  */
102389857Sobrien
1024130561Sobrienbfd_boolean
1025130561Sobrien_bfd_elf_maybe_strip_eh_frame_hdr (struct bfd_link_info *info)
102689857Sobrien{
1027130561Sobrien  asection *o;
102889857Sobrien  bfd *abfd;
1029130561Sobrien  struct elf_link_hash_table *htab;
103089857Sobrien  struct eh_frame_hdr_info *hdr_info;
103189857Sobrien
1032130561Sobrien  htab = elf_hash_table (info);
1033130561Sobrien  hdr_info = &htab->eh_info;
1034130561Sobrien  if (hdr_info->hdr_sec == NULL)
1035130561Sobrien    return TRUE;
103689857Sobrien
1037130561Sobrien  if (bfd_is_abs_section (hdr_info->hdr_sec->output_section))
1038130561Sobrien    {
1039130561Sobrien      hdr_info->hdr_sec = NULL;
1040130561Sobrien      return TRUE;
1041130561Sobrien    }
104289857Sobrien
104389857Sobrien  abfd = NULL;
104489857Sobrien  if (info->eh_frame_hdr)
104589857Sobrien    for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
104689857Sobrien      {
104789857Sobrien	/* Count only sections which have at least a single CIE or FDE.
104889857Sobrien	   There cannot be any CIE or FDE <= 8 bytes.  */
104989857Sobrien	o = bfd_get_section_by_name (abfd, ".eh_frame");
1050218822Sdim	if (o && o->size > 8 && !bfd_is_abs_section (o->output_section))
105189857Sobrien	  break;
105289857Sobrien      }
105389857Sobrien
105489857Sobrien  if (abfd == NULL)
105589857Sobrien    {
1056218822Sdim      hdr_info->hdr_sec->flags |= SEC_EXCLUDE;
1057130561Sobrien      hdr_info->hdr_sec = NULL;
1058130561Sobrien      return TRUE;
105989857Sobrien    }
1060130561Sobrien
1061130561Sobrien  hdr_info->table = TRUE;
1062130561Sobrien  return TRUE;
106389857Sobrien}
106489857Sobrien
106589857Sobrien/* Adjust an address in the .eh_frame section.  Given OFFSET within
106689857Sobrien   SEC, this returns the new offset in the adjusted .eh_frame section,
106789857Sobrien   or -1 if the address refers to a CIE/FDE which has been removed
106889857Sobrien   or to offset with dynamic relocation which is no longer needed.  */
106989857Sobrien
107089857Sobrienbfd_vma
1071130561Sobrien_bfd_elf_eh_frame_section_offset (bfd *output_bfd ATTRIBUTE_UNUSED,
1072218822Sdim				  struct bfd_link_info *info,
1073130561Sobrien				  asection *sec,
1074130561Sobrien				  bfd_vma offset)
107589857Sobrien{
107689857Sobrien  struct eh_frame_sec_info *sec_info;
1077218822Sdim  struct elf_link_hash_table *htab;
1078218822Sdim  struct eh_frame_hdr_info *hdr_info;
107989857Sobrien  unsigned int lo, hi, mid;
108089857Sobrien
1081130561Sobrien  if (sec->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
108289857Sobrien    return offset;
1083130561Sobrien  sec_info = elf_section_data (sec)->sec_info;
108489857Sobrien
1085218822Sdim  if (offset >= sec->rawsize)
1086218822Sdim    return offset - sec->rawsize + sec->size;
108789857Sobrien
1088218822Sdim  htab = elf_hash_table (info);
1089218822Sdim  hdr_info = &htab->eh_info;
1090218822Sdim  if (hdr_info->offsets_adjusted)
1091218822Sdim    offset += sec->output_offset;
1092218822Sdim
109389857Sobrien  lo = 0;
109489857Sobrien  hi = sec_info->count;
109589857Sobrien  mid = 0;
109689857Sobrien  while (lo < hi)
109789857Sobrien    {
109889857Sobrien      mid = (lo + hi) / 2;
109989857Sobrien      if (offset < sec_info->entry[mid].offset)
110089857Sobrien	hi = mid;
110189857Sobrien      else if (offset
110289857Sobrien	       >= sec_info->entry[mid].offset + sec_info->entry[mid].size)
110389857Sobrien	lo = mid + 1;
110489857Sobrien      else
110589857Sobrien	break;
110689857Sobrien    }
110789857Sobrien
110889857Sobrien  BFD_ASSERT (lo < hi);
110989857Sobrien
111089857Sobrien  /* FDE or CIE was removed.  */
111189857Sobrien  if (sec_info->entry[mid].removed)
111289857Sobrien    return (bfd_vma) -1;
111389857Sobrien
111489857Sobrien  /* If converting to DW_EH_PE_pcrel, there will be no need for run-time
111589857Sobrien     relocation against FDE's initial_location field.  */
1116218822Sdim  if (!sec_info->entry[mid].cie
1117218822Sdim      && sec_info->entry[mid].cie_inf->make_relative
111889857Sobrien      && offset == sec_info->entry[mid].offset + 8)
111991041Sobrien    return (bfd_vma) -2;
112089857Sobrien
112189857Sobrien  /* If converting LSDA pointers to DW_EH_PE_pcrel, there will be no need
112289857Sobrien     for run-time relocation against LSDA field.  */
1123218822Sdim  if (!sec_info->entry[mid].cie
1124218822Sdim      && sec_info->entry[mid].cie_inf->make_lsda_relative
1125130561Sobrien      && (offset == (sec_info->entry[mid].offset + 8
1126218822Sdim		     + sec_info->entry[mid].lsda_offset))
1127218822Sdim      && (sec_info->entry[mid].cie_inf->need_lsda_relative
1128218822Sdim	  || !hdr_info->offsets_adjusted))
1129218822Sdim    {
1130218822Sdim      sec_info->entry[mid].cie_inf->need_lsda_relative = 1;
1131218822Sdim      return (bfd_vma) -2;
1132218822Sdim    }
113389857Sobrien
1134218822Sdim  /* If converting to DW_EH_PE_pcrel, there will be no need for run-time
1135218822Sdim     relocation against DW_CFA_set_loc's arguments.  */
1136218822Sdim  if (sec_info->entry[mid].set_loc
1137218822Sdim      && (sec_info->entry[mid].cie
1138218822Sdim	  ? sec_info->entry[mid].make_relative
1139218822Sdim	  : sec_info->entry[mid].cie_inf->make_relative)
1140218822Sdim      && (offset >= sec_info->entry[mid].offset + 8
1141218822Sdim		    + sec_info->entry[mid].set_loc[1]))
1142218822Sdim    {
1143218822Sdim      unsigned int cnt;
1144218822Sdim
1145218822Sdim      for (cnt = 1; cnt <= sec_info->entry[mid].set_loc[0]; cnt++)
1146218822Sdim	if (offset == sec_info->entry[mid].offset + 8
1147218822Sdim		      + sec_info->entry[mid].set_loc[cnt])
1148218822Sdim	  return (bfd_vma) -2;
1149218822Sdim    }
1150218822Sdim
1151218822Sdim  if (hdr_info->offsets_adjusted)
1152218822Sdim    offset -= sec->output_offset;
1153218822Sdim  /* Any new augmentation bytes go before the first relocation.  */
115489857Sobrien  return (offset + sec_info->entry[mid].new_offset
1155218822Sdim	  - sec_info->entry[mid].offset
1156218822Sdim	  + extra_augmentation_string_bytes (sec_info->entry + mid)
1157218822Sdim	  + extra_augmentation_data_bytes (sec_info->entry + mid));
115889857Sobrien}
115989857Sobrien
116089857Sobrien/* Write out .eh_frame section.  This is called with the relocated
116189857Sobrien   contents.  */
116289857Sobrien
1163130561Sobrienbfd_boolean
1164130561Sobrien_bfd_elf_write_section_eh_frame (bfd *abfd,
1165130561Sobrien				 struct bfd_link_info *info,
1166130561Sobrien				 asection *sec,
1167130561Sobrien				 bfd_byte *contents)
116889857Sobrien{
116989857Sobrien  struct eh_frame_sec_info *sec_info;
1170130561Sobrien  struct elf_link_hash_table *htab;
117189857Sobrien  struct eh_frame_hdr_info *hdr_info;
117289857Sobrien  unsigned int ptr_size;
1173218822Sdim  struct eh_cie_fde *ent;
117489857Sobrien
1175130561Sobrien  if (sec->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
1176130561Sobrien    return bfd_set_section_contents (abfd, sec->output_section, contents,
1177218822Sdim				     sec->output_offset, sec->size);
1178218822Sdim
1179218822Sdim  ptr_size = (get_elf_backend_data (abfd)
1180218822Sdim	      ->elf_backend_eh_frame_address_size (abfd, sec));
1181218822Sdim  BFD_ASSERT (ptr_size != 0);
1182218822Sdim
1183130561Sobrien  sec_info = elf_section_data (sec)->sec_info;
1184130561Sobrien  htab = elf_hash_table (info);
1185130561Sobrien  hdr_info = &htab->eh_info;
1186218822Sdim
1187218822Sdim  /* First convert all offsets to output section offsets, so that a
1188218822Sdim     CIE offset is valid if the CIE is used by a FDE from some other
1189218822Sdim     section.  This can happen when duplicate CIEs are deleted in
1190218822Sdim     _bfd_elf_discard_section_eh_frame.  We do all sections here because
1191218822Sdim     this function might not be called on sections in the same order as
1192218822Sdim     _bfd_elf_discard_section_eh_frame.  */
1193218822Sdim  if (!hdr_info->offsets_adjusted)
1194218822Sdim    {
1195218822Sdim      bfd *ibfd;
1196218822Sdim      asection *eh;
1197218822Sdim      struct eh_frame_sec_info *eh_inf;
1198218822Sdim
1199218822Sdim      for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
1200218822Sdim	{
1201218822Sdim	  if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
1202218822Sdim	      || (ibfd->flags & DYNAMIC) != 0)
1203218822Sdim	    continue;
1204218822Sdim
1205218822Sdim	  eh = bfd_get_section_by_name (ibfd, ".eh_frame");
1206218822Sdim	  if (eh == NULL || eh->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
1207218822Sdim	    continue;
1208218822Sdim
1209218822Sdim	  eh_inf = elf_section_data (eh)->sec_info;
1210218822Sdim	  for (ent = eh_inf->entry; ent < eh_inf->entry + eh_inf->count; ++ent)
1211218822Sdim	    {
1212218822Sdim	      ent->offset += eh->output_offset;
1213218822Sdim	      ent->new_offset += eh->output_offset;
1214218822Sdim	    }
1215218822Sdim	}
1216218822Sdim      hdr_info->offsets_adjusted = TRUE;
1217218822Sdim    }
1218218822Sdim
1219130561Sobrien  if (hdr_info->table && hdr_info->array == NULL)
1220130561Sobrien    hdr_info->array
1221130561Sobrien      = bfd_malloc (hdr_info->fde_count * sizeof(*hdr_info->array));
1222130561Sobrien  if (hdr_info->array == NULL)
1223130561Sobrien    hdr_info = NULL;
122489857Sobrien
1225218822Sdim  /* The new offsets can be bigger or smaller than the original offsets.
1226218822Sdim     We therefore need to make two passes over the section: one backward
1227218822Sdim     pass to move entries up and one forward pass to move entries down.
1228218822Sdim     The two passes won't interfere with each other because entries are
1229218822Sdim     not reordered  */
1230218822Sdim  for (ent = sec_info->entry + sec_info->count; ent-- != sec_info->entry;)
1231218822Sdim    if (!ent->removed && ent->new_offset > ent->offset)
1232218822Sdim      memmove (contents + ent->new_offset - sec->output_offset,
1233218822Sdim	       contents + ent->offset - sec->output_offset, ent->size);
1234218822Sdim
1235218822Sdim  for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
1236218822Sdim    if (!ent->removed && ent->new_offset < ent->offset)
1237218822Sdim      memmove (contents + ent->new_offset - sec->output_offset,
1238218822Sdim	       contents + ent->offset - sec->output_offset, ent->size);
1239218822Sdim
1240218822Sdim  for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
124189857Sobrien    {
1242218822Sdim      unsigned char *buf, *end;
1243218822Sdim      unsigned int new_size;
1244218822Sdim
1245218822Sdim      if (ent->removed)
1246218822Sdim	continue;
1247218822Sdim
1248218822Sdim      if (ent->size == 4)
124989857Sobrien	{
1250218822Sdim	  /* Any terminating FDE must be at the end of the section.  */
1251218822Sdim	  BFD_ASSERT (ent == sec_info->entry + sec_info->count - 1);
125289857Sobrien	  continue;
125389857Sobrien	}
125489857Sobrien
1255218822Sdim      buf = contents + ent->new_offset - sec->output_offset;
1256218822Sdim      end = buf + ent->size;
1257218822Sdim      new_size = size_of_output_cie_fde (ent, ptr_size);
1258218822Sdim
1259218822Sdim      /* Update the size.  It may be shrinked.  */
1260218822Sdim      bfd_put_32 (abfd, new_size - 4, buf);
1261218822Sdim
1262218822Sdim      /* Filling the extra bytes with DW_CFA_nops.  */
1263218822Sdim      if (new_size != ent->size)
1264218822Sdim	memset (end, 0, new_size - ent->size);
1265218822Sdim
1266218822Sdim      if (ent->cie)
126789857Sobrien	{
126889857Sobrien	  /* CIE */
1269218822Sdim	  if (ent->make_relative
1270218822Sdim	      || ent->need_lsda_relative
1271218822Sdim	      || ent->per_encoding_relative)
127289857Sobrien	    {
1273218822Sdim	      char *aug;
1274218822Sdim	      unsigned int action, extra_string, extra_data;
1275218822Sdim	      unsigned int per_width, per_encoding;
127689857Sobrien
127789857Sobrien	      /* Need to find 'R' or 'L' augmentation's argument and modify
127889857Sobrien		 DW_EH_PE_* value.  */
1279218822Sdim	      action = ((ent->make_relative ? 1 : 0)
1280218822Sdim			| (ent->need_lsda_relative ? 2 : 0)
1281218822Sdim			| (ent->per_encoding_relative ? 4 : 0));
1282218822Sdim	      extra_string = extra_augmentation_string_bytes (ent);
1283218822Sdim	      extra_data = extra_augmentation_data_bytes (ent);
1284218822Sdim
128589857Sobrien	      /* Skip length, id and version.  */
128689857Sobrien	      buf += 9;
1287218822Sdim	      aug = (char *) buf;
1288218822Sdim	      buf += strlen (aug) + 1;
1289218822Sdim	      skip_leb128 (&buf, end);
1290218822Sdim	      skip_leb128 (&buf, end);
1291218822Sdim	      skip_leb128 (&buf, end);
129289857Sobrien	      if (*aug == 'z')
129389857Sobrien		{
1294218822Sdim		  /* The uleb128 will always be a single byte for the kind
1295218822Sdim		     of augmentation strings that we're prepared to handle.  */
1296218822Sdim		  *buf++ += extra_data;
129789857Sobrien		  aug++;
129889857Sobrien		}
129989857Sobrien
1300218822Sdim	      /* Make room for the new augmentation string and data bytes.  */
1301218822Sdim	      memmove (buf + extra_string + extra_data, buf, end - buf);
1302218822Sdim	      memmove (aug + extra_string, aug, buf - (bfd_byte *) aug);
1303218822Sdim	      buf += extra_string;
1304218822Sdim	      end += extra_string + extra_data;
1305218822Sdim
1306218822Sdim	      if (ent->add_augmentation_size)
1307218822Sdim		{
1308218822Sdim		  *aug++ = 'z';
1309218822Sdim		  *buf++ = extra_data - 1;
1310218822Sdim		}
1311218822Sdim	      if (ent->add_fde_encoding)
1312218822Sdim		{
1313218822Sdim		  BFD_ASSERT (action & 1);
1314218822Sdim		  *aug++ = 'R';
1315218822Sdim		  *buf++ = DW_EH_PE_pcrel;
1316218822Sdim		  action &= ~1;
1317218822Sdim		}
1318218822Sdim
131989857Sobrien	      while (action)
132089857Sobrien		switch (*aug++)
132189857Sobrien		  {
132289857Sobrien		  case 'L':
132389857Sobrien		    if (action & 2)
132489857Sobrien		      {
1325218822Sdim			BFD_ASSERT (*buf == ent->lsda_encoding);
132689857Sobrien			*buf |= DW_EH_PE_pcrel;
132789857Sobrien			action &= ~2;
132889857Sobrien		      }
132989857Sobrien		    buf++;
133089857Sobrien		    break;
133189857Sobrien		  case 'P':
133289857Sobrien		    per_encoding = *buf++;
1333218822Sdim		    per_width = get_DW_EH_PE_width (per_encoding, ptr_size);
133489857Sobrien		    BFD_ASSERT (per_width != 0);
133599461Sobrien		    BFD_ASSERT (((per_encoding & 0x70) == DW_EH_PE_pcrel)
1336218822Sdim				== ent->per_encoding_relative);
133789857Sobrien		    if ((per_encoding & 0xf0) == DW_EH_PE_aligned)
133889857Sobrien		      buf = (contents
133989857Sobrien			     + ((buf - contents + per_width - 1)
134089857Sobrien				& ~((bfd_size_type) per_width - 1)));
134199461Sobrien		    if (action & 4)
134299461Sobrien		      {
1343218822Sdim			bfd_vma val;
134499461Sobrien
1345218822Sdim			val = read_value (abfd, buf, per_width,
1346218822Sdim					  get_DW_EH_PE_signed (per_encoding));
1347218822Sdim			val += ent->offset - ent->new_offset;
1348218822Sdim			val -= extra_string + extra_data;
1349218822Sdim			write_value (abfd, buf, val, per_width);
135099461Sobrien			action &= ~4;
135199461Sobrien		      }
135289857Sobrien		    buf += per_width;
135389857Sobrien		    break;
135489857Sobrien		  case 'R':
135589857Sobrien		    if (action & 1)
135689857Sobrien		      {
1357218822Sdim			BFD_ASSERT (*buf == ent->fde_encoding);
135889857Sobrien			*buf |= DW_EH_PE_pcrel;
135989857Sobrien			action &= ~1;
136089857Sobrien		      }
136189857Sobrien		    buf++;
136289857Sobrien		    break;
1363218822Sdim		  case 'S':
1364218822Sdim		    break;
136589857Sobrien		  default:
136689857Sobrien		    BFD_FAIL ();
136789857Sobrien		  }
136889857Sobrien	    }
136989857Sobrien	}
1370218822Sdim      else
137189857Sobrien	{
137289857Sobrien	  /* FDE */
1373218822Sdim	  bfd_vma value, address;
137489857Sobrien	  unsigned int width;
1375218822Sdim	  bfd_byte *start;
137689857Sobrien
1377130561Sobrien	  /* Skip length.  */
137889857Sobrien	  buf += 4;
1379218822Sdim	  value = ent->new_offset + 4 - ent->cie_inf->new_offset;
1380218822Sdim	  bfd_put_32 (abfd, value, buf);
138189857Sobrien	  buf += 4;
1382218822Sdim	  width = get_DW_EH_PE_width (ent->fde_encoding, ptr_size);
1383218822Sdim	  value = read_value (abfd, buf, width,
1384218822Sdim			      get_DW_EH_PE_signed (ent->fde_encoding));
1385218822Sdim	  address = value;
138689857Sobrien	  if (value)
138789857Sobrien	    {
1388218822Sdim	      switch (ent->fde_encoding & 0xf0)
138989857Sobrien		{
139089857Sobrien		case DW_EH_PE_indirect:
139189857Sobrien		case DW_EH_PE_textrel:
139289857Sobrien		  BFD_ASSERT (hdr_info == NULL);
139389857Sobrien		  break;
139489857Sobrien		case DW_EH_PE_datarel:
139589857Sobrien		  {
139689857Sobrien		    asection *got = bfd_get_section_by_name (abfd, ".got");
139789857Sobrien
139889857Sobrien		    BFD_ASSERT (got != NULL);
139989857Sobrien		    address += got->vma;
140089857Sobrien		  }
140189857Sobrien		  break;
140289857Sobrien		case DW_EH_PE_pcrel:
1403218822Sdim		  value += ent->offset - ent->new_offset;
1404218822Sdim		  address += sec->output_section->vma + ent->offset + 8;
140589857Sobrien		  break;
140689857Sobrien		}
1407218822Sdim	      if (ent->cie_inf->make_relative)
1408218822Sdim		value -= sec->output_section->vma + ent->new_offset + 8;
140989857Sobrien	      write_value (abfd, buf, value, width);
141089857Sobrien	    }
141189857Sobrien
1412218822Sdim	  start = buf;
1413218822Sdim
141489857Sobrien	  if (hdr_info)
141589857Sobrien	    {
141689857Sobrien	      hdr_info->array[hdr_info->array_count].initial_loc = address;
141789857Sobrien	      hdr_info->array[hdr_info->array_count++].fde
1418218822Sdim		= sec->output_section->vma + ent->new_offset;
141989857Sobrien	    }
142089857Sobrien
1421218822Sdim	  if ((ent->lsda_encoding & 0xf0) == DW_EH_PE_pcrel
1422218822Sdim	      || ent->cie_inf->need_lsda_relative)
142389857Sobrien	    {
1424218822Sdim	      buf += ent->lsda_offset;
1425218822Sdim	      width = get_DW_EH_PE_width (ent->lsda_encoding, ptr_size);
1426130561Sobrien	      value = read_value (abfd, buf, width,
1427218822Sdim				  get_DW_EH_PE_signed (ent->lsda_encoding));
142889857Sobrien	      if (value)
142989857Sobrien		{
1430218822Sdim		  if ((ent->lsda_encoding & 0xf0) == DW_EH_PE_pcrel)
1431218822Sdim		    value += ent->offset - ent->new_offset;
1432218822Sdim		  else if (ent->cie_inf->need_lsda_relative)
1433218822Sdim		    value -= (sec->output_section->vma + ent->new_offset + 8
1434218822Sdim			      + ent->lsda_offset);
143589857Sobrien		  write_value (abfd, buf, value, width);
143689857Sobrien		}
143789857Sobrien	    }
1438218822Sdim	  else if (ent->cie_inf->add_augmentation_size)
1439218822Sdim	    {
1440218822Sdim	      /* Skip the PC and length and insert a zero byte for the
1441218822Sdim		 augmentation size.  */
1442218822Sdim	      buf += width * 2;
1443218822Sdim	      memmove (buf + 1, buf, end - buf);
1444218822Sdim	      *buf = 0;
1445218822Sdim	    }
144689857Sobrien
1447218822Sdim	  if (ent->set_loc)
1448218822Sdim	    {
1449218822Sdim	      /* Adjust DW_CFA_set_loc.  */
1450218822Sdim	      unsigned int cnt, width;
1451218822Sdim	      bfd_vma new_offset;
145289857Sobrien
1453218822Sdim	      width = get_DW_EH_PE_width (ent->fde_encoding, ptr_size);
1454218822Sdim	      new_offset = ent->new_offset + 8
1455218822Sdim			   + extra_augmentation_string_bytes (ent)
1456218822Sdim			   + extra_augmentation_data_bytes (ent);
145789857Sobrien
1458218822Sdim	      for (cnt = 1; cnt <= ent->set_loc[0]; cnt++)
1459218822Sdim		{
1460218822Sdim		  bfd_vma value;
1461218822Sdim		  buf = start + ent->set_loc[cnt];
1462130561Sobrien
1463218822Sdim		  value = read_value (abfd, buf, width,
1464218822Sdim				      get_DW_EH_PE_signed (ent->fde_encoding));
1465218822Sdim		  if (!value)
1466218822Sdim		    continue;
1467130561Sobrien
1468218822Sdim		  if ((ent->fde_encoding & 0xf0) == DW_EH_PE_pcrel)
1469218822Sdim		    value += ent->offset + 8 - new_offset;
1470218822Sdim		  if (ent->cie_inf->make_relative)
1471218822Sdim		    value -= sec->output_section->vma + new_offset
1472218822Sdim			     + ent->set_loc[cnt];
1473218822Sdim		  write_value (abfd, buf, value, width);
1474218822Sdim		}
1475218822Sdim	    }
1476130561Sobrien	}
1477130561Sobrien    }
1478130561Sobrien
1479218822Sdim  /* We don't align the section to its section alignment since the
1480218822Sdim     runtime library only expects all CIE/FDE records aligned at
1481218822Sdim     the pointer size. _bfd_elf_discard_section_eh_frame should
1482218822Sdim     have padded CIE/FDE records to multiple of pointer size with
1483218822Sdim     size_of_output_cie_fde.  */
1484218822Sdim  if ((sec->size % ptr_size) != 0)
1485218822Sdim    abort ();
148689857Sobrien
148789857Sobrien  return bfd_set_section_contents (abfd, sec->output_section,
1488218822Sdim				   contents, (file_ptr) sec->output_offset,
1489218822Sdim				   sec->size);
149089857Sobrien}
149189857Sobrien
149289857Sobrien/* Helper function used to sort .eh_frame_hdr search table by increasing
149389857Sobrien   VMA of FDE initial location.  */
149489857Sobrien
149589857Sobrienstatic int
1496130561Sobrienvma_compare (const void *a, const void *b)
149789857Sobrien{
1498130561Sobrien  const struct eh_frame_array_ent *p = a;
1499130561Sobrien  const struct eh_frame_array_ent *q = b;
150089857Sobrien  if (p->initial_loc > q->initial_loc)
150189857Sobrien    return 1;
150289857Sobrien  if (p->initial_loc < q->initial_loc)
150389857Sobrien    return -1;
150489857Sobrien  return 0;
150589857Sobrien}
150689857Sobrien
150789857Sobrien/* Write out .eh_frame_hdr section.  This must be called after
150889857Sobrien   _bfd_elf_write_section_eh_frame has been called on all input
150989857Sobrien   .eh_frame sections.
151089857Sobrien   .eh_frame_hdr format:
151189857Sobrien   ubyte version		(currently 1)
151289857Sobrien   ubyte eh_frame_ptr_enc  	(DW_EH_PE_* encoding of pointer to start of
151389857Sobrien				 .eh_frame section)
151489857Sobrien   ubyte fde_count_enc		(DW_EH_PE_* encoding of total FDE count
151589857Sobrien				 number (or DW_EH_PE_omit if there is no
151689857Sobrien				 binary search table computed))
151789857Sobrien   ubyte table_enc		(DW_EH_PE_* encoding of binary search table,
151889857Sobrien				 or DW_EH_PE_omit if not present.
151989857Sobrien				 DW_EH_PE_datarel is using address of
152089857Sobrien				 .eh_frame_hdr section start as base)
152189857Sobrien   [encoded] eh_frame_ptr	(pointer to start of .eh_frame section)
152289857Sobrien   optionally followed by:
152389857Sobrien   [encoded] fde_count		(total number of FDEs in .eh_frame section)
152489857Sobrien   fde_count x [encoded] initial_loc, fde
152589857Sobrien				(array of encoded pairs containing
152689857Sobrien				 FDE initial_location field and FDE address,
1527130561Sobrien				 sorted by increasing initial_loc).  */
152889857Sobrien
1529130561Sobrienbfd_boolean
1530130561Sobrien_bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
153189857Sobrien{
1532130561Sobrien  struct elf_link_hash_table *htab;
153389857Sobrien  struct eh_frame_hdr_info *hdr_info;
1534130561Sobrien  asection *sec;
153589857Sobrien  bfd_byte *contents;
153689857Sobrien  asection *eh_frame_sec;
153789857Sobrien  bfd_size_type size;
1538130561Sobrien  bfd_boolean retval;
1539130561Sobrien  bfd_vma encoded_eh_frame;
154089857Sobrien
1541130561Sobrien  htab = elf_hash_table (info);
1542130561Sobrien  hdr_info = &htab->eh_info;
1543130561Sobrien  sec = hdr_info->hdr_sec;
1544130561Sobrien  if (sec == NULL)
1545130561Sobrien    return TRUE;
154689857Sobrien
154789857Sobrien  size = EH_FRAME_HDR_SIZE;
154889857Sobrien  if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
154989857Sobrien    size += 4 + hdr_info->fde_count * 8;
155089857Sobrien  contents = bfd_malloc (size);
155189857Sobrien  if (contents == NULL)
1552130561Sobrien    return FALSE;
155389857Sobrien
155489857Sobrien  eh_frame_sec = bfd_get_section_by_name (abfd, ".eh_frame");
155589857Sobrien  if (eh_frame_sec == NULL)
1556130561Sobrien    {
1557130561Sobrien      free (contents);
1558130561Sobrien      return FALSE;
1559130561Sobrien    }
156089857Sobrien
156189857Sobrien  memset (contents, 0, EH_FRAME_HDR_SIZE);
1562130561Sobrien  contents[0] = 1;				/* Version.  */
1563130561Sobrien  contents[1] = get_elf_backend_data (abfd)->elf_backend_encode_eh_address
1564130561Sobrien    (abfd, info, eh_frame_sec, 0, sec, 4,
1565130561Sobrien     &encoded_eh_frame);			/* .eh_frame offset.  */
1566130561Sobrien
156789857Sobrien  if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
156889857Sobrien    {
1569130561Sobrien      contents[2] = DW_EH_PE_udata4;		/* FDE count encoding.  */
1570130561Sobrien      contents[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4; /* Search table enc.  */
157189857Sobrien    }
157289857Sobrien  else
157389857Sobrien    {
157489857Sobrien      contents[2] = DW_EH_PE_omit;
157589857Sobrien      contents[3] = DW_EH_PE_omit;
157689857Sobrien    }
1577130561Sobrien  bfd_put_32 (abfd, encoded_eh_frame, contents + 4);
1578130561Sobrien
157989857Sobrien  if (contents[2] != DW_EH_PE_omit)
158089857Sobrien    {
158189857Sobrien      unsigned int i;
158289857Sobrien
158389857Sobrien      bfd_put_32 (abfd, hdr_info->fde_count, contents + EH_FRAME_HDR_SIZE);
158489857Sobrien      qsort (hdr_info->array, hdr_info->fde_count, sizeof (*hdr_info->array),
158589857Sobrien	     vma_compare);
158689857Sobrien      for (i = 0; i < hdr_info->fde_count; i++)
158789857Sobrien	{
158889857Sobrien	  bfd_put_32 (abfd,
158989857Sobrien		      hdr_info->array[i].initial_loc
159089857Sobrien		      - sec->output_section->vma,
159189857Sobrien		      contents + EH_FRAME_HDR_SIZE + i * 8 + 4);
159289857Sobrien	  bfd_put_32 (abfd,
159389857Sobrien		      hdr_info->array[i].fde - sec->output_section->vma,
159489857Sobrien		      contents + EH_FRAME_HDR_SIZE + i * 8 + 8);
159589857Sobrien	}
159689857Sobrien    }
159789857Sobrien
1598130561Sobrien  retval = bfd_set_section_contents (abfd, sec->output_section,
1599130561Sobrien				     contents, (file_ptr) sec->output_offset,
1600218822Sdim				     sec->size);
1601130561Sobrien  free (contents);
1602130561Sobrien  return retval;
160389857Sobrien}
1604130561Sobrien
1605218822Sdim/* Return the width of FDE addresses.  This is the default implementation.  */
1606218822Sdim
1607218822Sdimunsigned int
1608218822Sdim_bfd_elf_eh_frame_address_size (bfd *abfd, asection *sec ATTRIBUTE_UNUSED)
1609218822Sdim{
1610218822Sdim  return elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64 ? 8 : 4;
1611218822Sdim}
1612218822Sdim
1613130561Sobrien/* Decide whether we can use a PC-relative encoding within the given
1614130561Sobrien   EH frame section.  This is the default implementation.  */
1615130561Sobrien
1616130561Sobrienbfd_boolean
1617130561Sobrien_bfd_elf_can_make_relative (bfd *input_bfd ATTRIBUTE_UNUSED,
1618130561Sobrien			    struct bfd_link_info *info ATTRIBUTE_UNUSED,
1619130561Sobrien			    asection *eh_frame_section ATTRIBUTE_UNUSED)
1620130561Sobrien{
1621130561Sobrien  return TRUE;
1622130561Sobrien}
1623130561Sobrien
1624130561Sobrien/* Select an encoding for the given address.  Preference is given to
1625130561Sobrien   PC-relative addressing modes.  */
1626130561Sobrien
1627130561Sobrienbfd_byte
1628130561Sobrien_bfd_elf_encode_eh_address (bfd *abfd ATTRIBUTE_UNUSED,
1629130561Sobrien			    struct bfd_link_info *info ATTRIBUTE_UNUSED,
1630130561Sobrien			    asection *osec, bfd_vma offset,
1631130561Sobrien			    asection *loc_sec, bfd_vma loc_offset,
1632130561Sobrien			    bfd_vma *encoded)
1633130561Sobrien{
1634130561Sobrien  *encoded = osec->vma + offset -
1635130561Sobrien    (loc_sec->output_section->vma + loc_sec->output_offset + loc_offset);
1636130561Sobrien  return DW_EH_PE_pcrel | DW_EH_PE_sdata4;
1637130561Sobrien}
1638