/* dw2gencfi.c - Support for generating Dwarf2 CFI information.
- Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+ Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
Free Software Foundation, Inc.
Contributed by Michal Ludvig <mludvig@suse.cz>
#endif
#ifndef CFI_DIFF_LSDA_OK
-# define CFI_DIFF_LSDA_OK CFI_DIFF_EXPR_OK
+#define CFI_DIFF_LSDA_OK CFI_DIFF_EXPR_OK
#endif
#if CFI_DIFF_EXPR_OK == 1 && CFI_DIFF_LSDA_OK == 0
/* We re-use DWARF2_LINE_MIN_INSN_LENGTH for the code alignment field
of the CIE. Default to 1 if not otherwise specified. */
-#ifndef DWARF2_LINE_MIN_INSN_LENGTH
-# define DWARF2_LINE_MIN_INSN_LENGTH 1
+#ifndef DWARF2_LINE_MIN_INSN_LENGTH
+#define DWARF2_LINE_MIN_INSN_LENGTH 1
#endif
/* By default, use 32-bit relocations from .eh_frame into .text. */
#ifndef DWARF2_FDE_RELOC_SIZE
-# define DWARF2_FDE_RELOC_SIZE 4
+#define DWARF2_FDE_RELOC_SIZE 4
#endif
/* By default, use a read-only .eh_frame section. */
#ifndef DWARF2_EH_FRAME_READ_ONLY
-# define DWARF2_EH_FRAME_READ_ONLY SEC_READONLY
+#define DWARF2_EH_FRAME_READ_ONLY SEC_READONLY
#endif
#ifndef EH_FRAME_ALIGNMENT
-# define EH_FRAME_ALIGNMENT (bfd_get_arch_size (stdoutput) == 64 ? 3 : 2)
+#define EH_FRAME_ALIGNMENT (bfd_get_arch_size (stdoutput) == 64 ? 3 : 2)
#endif
#ifndef tc_cfi_frame_initial_instructions
-# define tc_cfi_frame_initial_instructions() ((void)0)
+#define tc_cfi_frame_initial_instructions() ((void)0)
#endif
#ifndef DWARF2_FORMAT
-# define DWARF2_FORMAT(SEC) dwarf2_format_32bit
+#define DWARF2_FORMAT(SEC) dwarf2_format_32bit
#endif
#ifndef DWARF2_ADDR_SIZE
-# define DWARF2_ADDR_SIZE(bfd) (bfd_arch_bits_per_address (bfd) / 8)
+#define DWARF2_ADDR_SIZE(bfd) (bfd_arch_bits_per_address (bfd) / 8)
#endif
+/* Private segment collection list. */
+struct dwcfi_seg_list
+{
+ segT seg;
+ int subseg;
+ char * seg_name;
+};
+
+#define FRAME_NAME ".eh_frame"
+
+static struct hash_control *dwcfi_hash;
+
+/* Build based on segment the derived .debug_...
+ segment name containing origin segment's postfix name part. */
+
+static char *
+get_debugseg_name (segT seg, const char *base_name)
+{
+ const char *name;
+
+ if (!seg)
+ name = "";
+ else
+ {
+ const char * dollar;
+ const char * dot;
+
+ name = bfd_get_section_name (stdoutput, seg);
+
+ dollar = strchr (name, '$');
+ dot = strchr (name + 1, '.');
+
+ if (!dollar && !dot)
+ name = "";
+ else if (!dollar)
+ name = dot;
+ else if (!dot)
+ name = dollar;
+ else if (dot < dollar)
+ name = dot;
+ else
+ name = dollar;
+ }
+
+ return concat (base_name, name, NULL);
+}
+
+/* Allocate a dwcfi_seg_list structure. */
+
+static struct dwcfi_seg_list *
+alloc_debugseg_item (segT seg, int subseg, char *name)
+{
+ struct dwcfi_seg_list *r;
+
+ r = (struct dwcfi_seg_list *)
+ xmalloc (sizeof (struct dwcfi_seg_list) + strlen (name));
+ r->seg = seg;
+ r->subseg = subseg;
+ r->seg_name = name;
+ return r;
+}
+
+static segT
+is_now_linkonce_segment (void)
+{
+#if defined (TE_PE) || defined (TE_PEP)
+ if ((bfd_get_section_flags (stdoutput, now_seg)
+ & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD
+ | SEC_LINK_DUPLICATES_ONE_ONLY | SEC_LINK_DUPLICATES_SAME_SIZE
+ | SEC_LINK_DUPLICATES_SAME_CONTENTS)) != 0)
+ return now_seg;
+#endif
+ return NULL;
+}
+
+/* Generate debug... segment with same linkonce properties
+ of based segment. */
+
+static segT
+make_debug_seg (segT cseg, char *name, int sflags)
+{
+ segT save_seg = now_seg;
+ int save_subseg = now_subseg;
+ segT r;
+ flagword flags;
+
+ r = subseg_new (name, 0);
+
+ /* Check if code segment is marked as linked once. */
+ if (!cseg)
+ flags = 0;
+ else
+ flags = bfd_get_section_flags (stdoutput, cseg)
+ & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD
+ | SEC_LINK_DUPLICATES_ONE_ONLY | SEC_LINK_DUPLICATES_SAME_SIZE
+ | SEC_LINK_DUPLICATES_SAME_CONTENTS);
+
+ /* Add standard section flags. */
+ flags |= sflags;
+
+ /* Apply possibly linked once flags to new generated segment, too. */
+ if (!bfd_set_section_flags (stdoutput, r, flags))
+ as_bad (_("bfd_set_section_flags: %s"),
+ bfd_errmsg (bfd_get_error ()));
+
+ /* Restore to previous segment. */
+ if (save_seg != NULL)
+ subseg_set (save_seg, save_subseg);
+ return r;
+}
+
+static void
+dwcfi_hash_insert (const char *name, struct dwcfi_seg_list *item)
+{
+ const char *error_string;
+
+ if ((error_string = hash_jam (dwcfi_hash, name, (char *) item)))
+ as_fatal (_("Inserting \"%s\" into structure table failed: %s"),
+ name, error_string);
+}
+
+static struct dwcfi_seg_list *
+dwcfi_hash_find (char *name)
+{
+ return (struct dwcfi_seg_list *) hash_find (dwcfi_hash, name);
+}
+
+static struct dwcfi_seg_list *
+dwcfi_hash_find_or_make (segT cseg, const char *base_name, int flags)
+{
+ struct dwcfi_seg_list *item;
+ char *name;
+
+ /* Initialize dwcfi_hash once. */
+ if (!dwcfi_hash)
+ dwcfi_hash = hash_new ();
+
+ name = get_debugseg_name (cseg, base_name);
+
+ item = dwcfi_hash_find (name);
+ if (!item)
+ {
+ item = alloc_debugseg_item (make_debug_seg (cseg, name, flags), 0, name);
+
+ dwcfi_hash_insert (item->seg_name, item);
+ }
+ else
+ free (name);
+
+ return item;
+}
+
/* ??? Share this with dwarf2cfg.c. */
#ifndef TC_DWARF2_EMIT_OFFSET
#define TC_DWARF2_EMIT_OFFSET generic_dwarf2_emit_offset
}
#endif
-struct cfi_escape_data {
+struct cfi_escape_data
+{
struct cfi_escape_data *next;
expressionS exp;
};
struct cfi_insn_data
{
struct cfi_insn_data *next;
+ segT cur_seg;
int insn;
- union {
- struct {
+ union
+ {
+ struct
+ {
unsigned reg;
offsetT offset;
} ri;
- struct {
+ struct
+ {
unsigned reg1;
unsigned reg2;
} rr;
unsigned r;
offsetT i;
- struct {
+ struct
+ {
symbolS *lab1;
symbolS *lab2;
} ll;
struct cfi_escape_data *esc;
- struct {
+ struct
+ {
unsigned reg, encoding;
expressionS exp;
} ea;
struct fde_entry
{
struct fde_entry *next;
+ segT cseg;
symbolS *start_address;
symbolS *end_address;
struct cfi_insn_data *data;
expressionS lsda;
unsigned int return_column;
unsigned int signal_frame;
+ int handled;
};
struct cie_entry
{
struct cie_entry *next;
+ segT cur_seg;
symbolS *start_address;
unsigned int return_column;
unsigned int signal_frame;
struct cfi_insn_data *first, *last;
};
-
/* List of FDE entries. */
+
static struct fde_entry *all_fde_data;
static struct fde_entry **last_fde_data = &all_fde_data;
frchain_now->frch_cfi_data->cur_fde_data = fde;
*last_fde_data = fde;
last_fde_data = &fde->next;
-
+ fde->cseg = is_now_linkonce_segment ();
+ fde->handled = 0;
fde->last = &fde->data;
fde->return_column = DWARF2_DEFAULT_RETURN_COLUMN;
fde->per_encoding = DW_EH_PE_omit;
*cur_fde_data->last = insn;
cur_fde_data->last = &insn->next;
+ insn->cur_seg = is_now_linkonce_segment ();
return insn;
}
/* Construct a new FDE structure that begins at LABEL. */
-void
+void
cfi_new_fde (symbolS *label)
{
struct fde_entry *fde = alloc_fde_entry ();
/* End the currently open FDE. */
-void
+void
cfi_end_fde (symbolS *label)
{
frchain_now->frch_cfi_data->cur_fde_data->end_address = label;
/* Universal functions to store new instructions. */
static void
-cfi_add_CFA_insn(int insn)
+cfi_add_CFA_insn (int insn)
{
struct cfi_insn_data *insn_ptr = alloc_cfi_insn_data ();
#ifndef tc_parse_to_dw2regnum
static void
-tc_parse_to_dw2regnum(expressionS *exp)
+tc_parse_to_dw2regnum (expressionS *exp)
{
# ifdef tc_regname_to_dw2regnum
SKIP_WHITESPACE ();
insn_ptr = alloc_cfi_insn_data ();
insn_ptr->insn = CFI_val_encoded_addr;
-
+
insn_ptr->u.ea.reg = cfi_parse_reg ();
cfi_parse_separator ();
name = input_line_pointer;
c = get_symbol_end ();
- if (strcmp (name, ".eh_frame") == 0)
+ if (strncmp (name, ".eh_frame", sizeof ".eh_frame") == 0
+ && name[9] != '_')
sections |= CFI_EMIT_eh_frame;
- else if (strcmp (name, ".debug_frame") == 0)
+ else if (strncmp (name, ".debug_frame", sizeof ".debug_frame") == 0)
sections |= CFI_EMIT_debug_frame;
else
{
emit_expr (&insn->u.ea.exp, encoding_size);
}
break;
-
+
default:
abort ();
}
out_one (enc);
if (cie->first)
- for (i = cie->first; i != cie->last; i = i->next)
- output_cfi_insn (i);
+ {
+ for (i = cie->first; i != cie->last; i = i->next)
+ {
+ if (i->cur_seg != cie->cur_seg)
+ continue;
+ output_cfi_insn (i);
+ }
+ }
frag_align (align, DW_CFA_nop, 0);
symbol_set_value_now (end_address);
}
for (; first; first = first->next)
- output_cfi_insn (first);
+ {
+ if (first->cur_seg == fde->cseg)
+ output_cfi_insn (first);
+ }
frag_align (align, DW_CFA_nop, 0);
symbol_set_value_now (end_address);
for (cie = cie_root; cie; cie = cie->next)
{
- if (cie->return_column != fde->return_column
+ if (cie->cur_seg != fde->cseg
+ || cie->return_column != fde->return_column
|| cie->signal_frame != fde->signal_frame
|| cie->per_encoding != fde->per_encoding
|| cie->lsda_encoding != fde->lsda_encoding)
cie = (struct cie_entry *) xmalloc (sizeof (struct cie_entry));
cie->next = cie_root;
cie_root = cie;
+ cie->cur_seg = fde->cseg;
cie->return_column = fde->return_column;
cie->signal_frame = fde->signal_frame;
cie->per_encoding = fde->per_encoding;
#ifdef md_reg_eh_frame_to_debug_frame
static void
-cfi_change_reg_numbers (struct cfi_insn_data *insn)
+cfi_change_reg_numbers (struct cfi_insn_data *insn, segT ccseg)
{
for (; insn; insn = insn->next)
- switch (insn->insn)
- {
- case DW_CFA_advance_loc:
- case DW_CFA_def_cfa_offset:
- case DW_CFA_remember_state:
- case DW_CFA_restore_state:
- case DW_CFA_GNU_window_save:
- case CFI_escape:
- break;
+ {
+ if (insn->cur_seg != ccseg)
+ continue;
+ switch (insn->insn)
+ {
+ case DW_CFA_advance_loc:
+ case DW_CFA_def_cfa_offset:
+ case DW_CFA_remember_state:
+ case DW_CFA_restore_state:
+ case DW_CFA_GNU_window_save:
+ case CFI_escape:
+ break;
- case DW_CFA_def_cfa:
- case DW_CFA_offset:
- insn->u.ri.reg = md_reg_eh_frame_to_debug_frame (insn->u.ri.reg);
- break;
+ case DW_CFA_def_cfa:
+ case DW_CFA_offset:
+ insn->u.ri.reg = md_reg_eh_frame_to_debug_frame (insn->u.ri.reg);
+ break;
- case DW_CFA_def_cfa_register:
- case DW_CFA_undefined:
- case DW_CFA_same_value:
- case DW_CFA_restore:
- insn->u.r = md_reg_eh_frame_to_debug_frame (insn->u.r);
- break;
+ case DW_CFA_def_cfa_register:
+ case DW_CFA_undefined:
+ case DW_CFA_same_value:
+ case DW_CFA_restore:
+ insn->u.r = md_reg_eh_frame_to_debug_frame (insn->u.r);
+ break;
- case DW_CFA_register:
- insn->u.rr.reg1 = md_reg_eh_frame_to_debug_frame (insn->u.rr.reg1);
- insn->u.rr.reg2 = md_reg_eh_frame_to_debug_frame (insn->u.rr.reg2);
- break;
+ case DW_CFA_register:
+ insn->u.rr.reg1 = md_reg_eh_frame_to_debug_frame (insn->u.rr.reg1);
+ insn->u.rr.reg2 = md_reg_eh_frame_to_debug_frame (insn->u.rr.reg2);
+ break;
- case CFI_val_encoded_addr:
- insn->u.ea.reg = md_reg_eh_frame_to_debug_frame (insn->u.ea.reg);
- break;
+ case CFI_val_encoded_addr:
+ insn->u.ea.reg = md_reg_eh_frame_to_debug_frame (insn->u.ea.reg);
+ break;
- default:
- abort ();
- }
+ default:
+ abort ();
+ }
+ }
}
#else
-#define cfi_change_reg_numbers(insn) do { } while (0)
+#define cfi_change_reg_numbers(insn, cseg) do { } while (0)
#endif
+static segT
+get_cfi_seg (segT cseg, const char *base, flagword flags, int align)
+{
+ struct dwcfi_seg_list *l;
+
+ l = dwcfi_hash_find_or_make (cseg, base, flags);
+
+ subseg_set (l->seg, l->subseg);
+ record_alignment (l->seg, align);
+ return l->seg;
+}
+
void
cfi_finish (void)
{
- segT cfi_seg;
+ struct cie_entry *cie, *cie_next;
+ segT cfi_seg, ccseg;
struct fde_entry *fde;
- int save_flag_traditional_format;
+ struct cfi_insn_data *first;
+ int save_flag_traditional_format, seek_next_seg;
if (all_fde_data == 0)
return;
if ((cfi_sections & CFI_EMIT_eh_frame) != 0)
{
- /* Open .eh_frame section. */
- cfi_seg = subseg_new (".eh_frame", 0);
- bfd_set_section_flags (stdoutput, cfi_seg,
- SEC_ALLOC | SEC_LOAD | SEC_DATA
- | DWARF2_EH_FRAME_READ_ONLY);
- subseg_set (cfi_seg, 0);
- record_alignment (cfi_seg, EH_FRAME_ALIGNMENT);
-
-#ifdef md_fix_up_eh_frame
- md_fix_up_eh_frame (cfi_seg);
-#endif
-
/* Make sure check_eh_frame doesn't do anything with our output. */
save_flag_traditional_format = flag_traditional_format;
flag_traditional_format = 1;
- for (fde = all_fde_data; fde ; fde = fde->next)
- {
- struct cfi_insn_data *first;
- struct cie_entry *cie;
-
- if (fde->end_address == NULL)
+ do
+ {
+ ccseg = NULL;
+ cfi_seg = NULL;
+ seek_next_seg = 0;
+ for (cie = cie_root; cie; cie = cie_next)
{
- as_bad (_("open CFI at the end of file; missing .cfi_endproc directive"));
- fde->end_address = fde->start_address;
+ cie_next = cie->next;
+ free ((void *) cie);
}
+ cie_root = NULL;
- cie = select_cie_for_fde (fde, TRUE, &first, 2);
- output_fde (fde, cie, TRUE, first,
- fde->next == NULL ? EH_FRAME_ALIGNMENT : 2);
+ for (fde = all_fde_data; fde ; fde = fde->next)
+ {
+ if (fde->handled != 0)
+ continue;
+ if (seek_next_seg && fde->cseg != ccseg)
+ {
+ seek_next_seg = 2;
+ continue;
+ }
+ if (!seek_next_seg)
+ {
+ ccseg = fde->cseg;
+ /* Open .eh_frame section. */
+ cfi_seg = get_cfi_seg (ccseg, ".eh_frame",
+ SEC_ALLOC | SEC_LOAD | SEC_DATA
+ | DWARF2_EH_FRAME_READ_ONLY,
+ EH_FRAME_ALIGNMENT);
+#ifdef md_fix_up_eh_frame
+ md_fix_up_eh_frame (cfi_seg);
+#endif
+ seek_next_seg = 1;
+ }
+ fde->handled = 1;
+
+ if (fde->end_address == NULL)
+ {
+ as_bad (_("open CFI at the end of file; missing .cfi_endproc directive"));
+ fde->end_address = fde->start_address;
+ }
+
+ cie = select_cie_for_fde (fde, TRUE, &first, 2);
+ output_fde (fde, cie, TRUE, first,
+ fde->next == NULL ? EH_FRAME_ALIGNMENT : 2);
+ }
}
+ while (seek_next_seg == 2);
+
+ for (fde = all_fde_data; fde ; fde = fde->next)
+ fde->handled = 0;
flag_traditional_format = save_flag_traditional_format;
}
if ((cfi_sections & CFI_EMIT_debug_frame) != 0)
{
- struct cie_entry *cie, *cie_next;
int alignment = ffs (DWARF2_ADDR_SIZE (stdoutput)) - 1;
- for (cie = cie_root; cie; cie = cie_next)
- {
- cie_next = cie->next;
- free ((void *) cie);
- }
- cie_root = NULL;
-
- /* Open .debug_frame section. */
- cfi_seg = subseg_new (".debug_frame", 0);
- bfd_set_section_flags (stdoutput, cfi_seg,
- SEC_READONLY | SEC_DEBUGGING);
- subseg_set (cfi_seg, 0);
- record_alignment (cfi_seg, alignment);
-
- for (fde = all_fde_data; fde ; fde = fde->next)
- {
- struct cfi_insn_data *first;
-
- if (fde->end_address == NULL)
+ do
+ {
+ ccseg = NULL;
+ cfi_seg = NULL;
+ seek_next_seg = 0;
+ for (cie = cie_root; cie; cie = cie_next)
{
- as_bad (_("open CFI at the end of file; missing .cfi_endproc directive"));
- fde->end_address = fde->start_address;
+ cie_next = cie->next;
+ free ((void *) cie);
}
+ cie_root = NULL;
- fde->per_encoding = DW_EH_PE_omit;
- fde->lsda_encoding = DW_EH_PE_omit;
- cfi_change_reg_numbers (fde->data);
- cie = select_cie_for_fde (fde, FALSE, &first, alignment);
- output_fde (fde, cie, FALSE, first, alignment);
+ for (fde = all_fde_data; fde ; fde = fde->next)
+ {
+ if (fde->handled != 0)
+ continue;
+ if (seek_next_seg && fde->cseg != ccseg)
+ {
+ seek_next_seg = 2;
+ continue;
+ }
+ if (!seek_next_seg)
+ {
+ ccseg = fde->cseg;
+ /* Open .debug_frame section. */
+ cfi_seg = get_cfi_seg (ccseg, ".debug_frame",
+ SEC_READONLY | SEC_DEBUGGING,
+ alignment);
+ seek_next_seg = 1;
+ }
+ fde->handled = 1;
+ if (fde->end_address == NULL)
+ {
+ as_bad (_("open CFI at the end of file; missing .cfi_endproc directive"));
+ fde->end_address = fde->start_address;
+ }
+
+ fde->per_encoding = DW_EH_PE_omit;
+ fde->lsda_encoding = DW_EH_PE_omit;
+ cfi_change_reg_numbers (fde->data, ccseg);
+ cie = select_cie_for_fde (fde, FALSE, &first, alignment);
+ output_fde (fde, cie, FALSE, first, alignment);
+ }
}
+ while (seek_next_seg == 2);
+
+ for (fde = all_fde_data; fde ; fde = fde->next)
+ fde->handled = 0;
}
}