diff options
author | Nick Clifton <nickc@redhat.com> | 2011-04-26 15:28:08 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2011-04-26 15:28:08 +0000 |
commit | 72b016b4ac0d7532a8f173fe3472e2e88ac49d45 (patch) | |
tree | e6d8dcd1c9cabcafcfe1713f55b366a2cbeefbec /gas/dw2gencfi.c | |
parent | 4964e065b452a479b9ba0d636b28455c496ba9ab (diff) | |
download | binutils-gdb-72b016b4ac0d7532a8f173fe3472e2e88ac49d45.tar.gz |
* dw2gencfi.c (dwcfi_seg_list): New struct.
(dwcfi_hash): New static hash variable.
(get_debugseg_name): New.
(alloc_debugseg_item): New.
(make_debug_seg): New.
(dwcfi_hash_insert): New.
(dwcfi_hash_find): New.
(dwcfi_hash_find_or_make): New.
(cfi_insn_data): New member cur_seg.
(cie_entry): Likewise.
(fde_entry): New cseg and handled members.
(alloc_fde_entry): Initialize cseg member.
(alloc_cfi_insn_data): Initialize cur_seg member.
(dot_cfi_sections): Compare for beginning of
section names via strncmp.
(get_cfi_seg): New.
(cfi_finish): Treat link-once sections.
(is_now_linkonce_segment): New local helper.
(output_cie): Ignore cie entries not member of current
segment.
(output_fde): Likewise.
(select_cie_for_fde): Likewise.
(cfi_change_reg_numbers): Add new argument for current segment
and ignore insn elements, if not part of current segment.
* ehopt.c (get_cie_info): Use strncmp for
section name matching.
(heck_eh_frame): Likewise.
* coffcode.h (sec_to_styp_flags): Allow linkonce for
debugging sections.
* scripttempl/pe.sc: Handle .eh_frame($|.)* sections.
* scripttempl/pep.sc: Likewise.
* ld-pe/pe.exp: Add cfi/cfi32 tests.
* ld-pe/cfi.d: New.
* ld-pe/cfi32.d: New.
* ld-pe/cfia.s: New.
* ld-pe/cfib.s: New.
Diffstat (limited to 'gas/dw2gencfi.c')
-rw-r--r-- | gas/dw2gencfi.c | 461 |
1 files changed, 348 insertions, 113 deletions
diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c index 7a051e35c3b..8f6300d2a43 100644 --- a/gas/dw2gencfi.c +++ b/gas/dw2gencfi.c @@ -1,5 +1,5 @@ /* 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> @@ -37,7 +37,7 @@ #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 @@ -46,36 +46,188 @@ /* 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 @@ -94,7 +246,8 @@ generic_dwarf2_emit_offset (symbolS *symbol, unsigned int size) } #endif -struct cfi_escape_data { +struct cfi_escape_data +{ struct cfi_escape_data *next; expressionS exp; }; @@ -102,14 +255,18 @@ struct cfi_escape_data { 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; @@ -117,14 +274,16 @@ struct cfi_insn_data unsigned r; offsetT i; - struct { + struct + { symbolS *lab1; symbolS *lab2; } ll; struct cfi_escape_data *esc; - struct { + struct + { unsigned reg, encoding; expressionS exp; } ea; @@ -134,6 +293,7 @@ struct cfi_insn_data struct fde_entry { struct fde_entry *next; + segT cseg; symbolS *start_address; symbolS *end_address; struct cfi_insn_data *data; @@ -144,11 +304,13 @@ struct fde_entry 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; @@ -158,8 +320,8 @@ struct cie_entry 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; @@ -195,7 +357,8 @@ alloc_fde_entry (void) 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; @@ -219,13 +382,14 @@ alloc_cfi_insn_data (void) *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 (); @@ -235,7 +399,7 @@ cfi_new_fde (symbolS *label) /* End the currently open FDE. */ -void +void cfi_end_fde (symbolS *label) { frchain_now->frch_cfi_data->cur_fde_data->end_address = label; @@ -254,7 +418,7 @@ cfi_set_return_column (unsigned regno) /* 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 (); @@ -471,7 +635,7 @@ cfi_parse_separator (void) #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 (); @@ -853,7 +1017,7 @@ dot_cfi_val_encoded_addr (int ignored ATTRIBUTE_UNUSED) insn_ptr = alloc_cfi_insn_data (); insn_ptr->insn = CFI_val_encoded_addr; - + insn_ptr->u.ea.reg = cfi_parse_reg (); cfi_parse_separator (); @@ -916,9 +1080,10 @@ dot_cfi_sections (int ignored ATTRIBUTE_UNUSED) 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 { @@ -1250,7 +1415,7 @@ output_cfi_insn (struct cfi_insn_data *insn) emit_expr (&insn->u.ea.exp, encoding_size); } break; - + default: abort (); } @@ -1384,8 +1549,14 @@ output_cie (struct cie_entry *cie, bfd_boolean eh_frame, int align) 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); @@ -1492,7 +1663,10 @@ output_fde (struct fde_entry *fde, struct cie_entry *cie, } 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); @@ -1507,7 +1681,8 @@ select_cie_for_fde (struct fde_entry *fde, bfd_boolean eh_frame, 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) @@ -1605,6 +1780,7 @@ select_cie_for_fde (struct fde_entry *fde, bfd_boolean eh_frame, 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; @@ -1629,130 +1805,189 @@ select_cie_for_fde (struct fde_entry *fde, bfd_boolean eh_frame, #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; } } |