diff --git a/gas/ChangeLog b/gas/ChangeLog index be46061fc4..b1ad1003e1 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,21 @@ +2009-10-02 Jakub Jelinek + + * dw2gencfi.c: Include dwarf2dbg.h. + (DWARF2_FORMAT): Define if not defined. + (dot_cfi_sections): New function. + (cfi_pseudo_table): Handle .cfi_sections. + (CFI_EMIT_eh_frame, CFI_EMIT_debug_frame): Define. + (cfi_sections): New variable. + (output_cie, output_fde, select_cie_for_fde): Add eh_frame + argument, add supporting for outputting .debug_frame + section. + (cfi_change_reg_numbers): New function or macro. + (cfi_finish): Only emit .eh_frame if + cfi_sections & CFI_EMIT_eh_frame. Emit .debug_frame if + cfi_sections & CFI_EMIT_debug_frame. + * config/tc-ppc.h (md_reg_eh_frame_to_debug_frame): Define. + * doc/as.texinfo (CFI directives): Document .cfi_sections. + 2009-10-01 Peter Bergner * config/tc-ppc.c (md_show_usage): Rename "ppca2" to "a2". diff --git a/gas/config/tc-ppc.h b/gas/config/tc-ppc.h index 37de84e0cf..be474bde59 100644 --- a/gas/config/tc-ppc.h +++ b/gas/config/tc-ppc.h @@ -249,6 +249,12 @@ extern int ppc_parse_name (const char *, struct expressionS *); #define md_cleanup() ppc_cleanup () extern void ppc_cleanup (void); +/* ppc uses different register numbers between .eh_frame and .debug_frame. + This macro translates the .eh_frame register numbers to .debug_frame + register numbers. */ +#define md_reg_eh_frame_to_debug_frame(regno) \ + ((regno) == 70 ? 64 /* cr2 */ : (regno)) + #define TARGET_USE_CFIPOP 1 #define tc_cfi_frame_initial_instructions ppc_cfi_frame_initial_instructions diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo index e676f84fb3..4bfe79394a 100644 --- a/gas/doc/as.texinfo +++ b/gas/doc/as.texinfo @@ -4197,6 +4197,15 @@ undefined. Each expression is assembled into the next byte. @node CFI directives +@section @code{.cfi_sections @var{section_list}} +@cindex @code{cfi_sections} directive +@code{.cfi_sections} may be used to specify whether CFI directives +should emit @code{.eh_frame} section and/or @code{.debug_frame} section. +If @var{section_list} is @code{.eh_frame}, @code{.eh_frame} is emitted, +if @var{section_list} is @code{.debug_frame}, @code{.debug_frame} is emitted. +To emit both use @code{.eh_frame, .debug_frame}. The default if this +directive is not used is @code{.cfi_sections .eh_frame}. + @section @code{.cfi_startproc [simple]} @cindex @code{cfi_startproc} directive @code{.cfi_startproc} is used at the beginning of each function that diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c index e8ba9163b0..5d35712bb6 100644 --- a/gas/dw2gencfi.c +++ b/gas/dw2gencfi.c @@ -23,6 +23,7 @@ #include "as.h" #include "dw2gencfi.h" #include "subsegs.h" +#include "dwarf2dbg.h" #ifdef TARGET_USE_CFIPOP @@ -59,6 +60,10 @@ # define tc_cfi_frame_initial_instructions() ((void)0) #endif +#ifndef DWARF2_FORMAT +# define DWARF2_FORMAT(SEC) dwarf2_format_32bit +#endif + #ifndef DWARF2_ADDR_SIZE # define DWARF2_ADDR_SIZE(bfd) (bfd_arch_bits_per_address (bfd) / 8) #endif @@ -386,6 +391,7 @@ cfi_add_CFA_restore_state (void) static void dot_cfi (int); static void dot_cfi_escape (int); +static void dot_cfi_sections (int); static void dot_cfi_startproc (int); static void dot_cfi_endproc (int); static void dot_cfi_personality (int); @@ -402,6 +408,7 @@ static void dot_cfi_val_encoded_addr (int); const pseudo_typeS cfi_pseudo_table[] = { + { "cfi_sections", dot_cfi_sections, 0 }, { "cfi_startproc", dot_cfi_startproc, 0 }, { "cfi_endproc", dot_cfi_endproc, 0 }, { "cfi_def_cfa", dot_cfi, DW_CFA_def_cfa }, @@ -864,6 +871,56 @@ dot_cfi_val_encoded_addr (int ignored ATTRIBUTE_UNUSED) demand_empty_rest_of_line (); } +/* By default emit .eh_frame only, not .debug_frame. */ +#define CFI_EMIT_eh_frame (1 << 0) +#define CFI_EMIT_debug_frame (1 << 1) +static int cfi_sections = CFI_EMIT_eh_frame; + +static void +dot_cfi_sections (int ignored ATTRIBUTE_UNUSED) +{ + int sections = 0; + + SKIP_WHITESPACE (); + if (is_name_beginner (*input_line_pointer)) + while (1) + { + char *name, c; + + name = input_line_pointer; + c = get_symbol_end (); + + if (strcmp (name, ".eh_frame") == 0) + sections |= CFI_EMIT_eh_frame; + else if (strcmp (name, ".debug_frame") == 0) + sections |= CFI_EMIT_debug_frame; + else + { + *input_line_pointer = c; + input_line_pointer = name; + break; + } + + *input_line_pointer = c; + SKIP_WHITESPACE (); + if (*input_line_pointer == ',') + { + name = input_line_pointer++; + SKIP_WHITESPACE (); + if (!is_name_beginner (*input_line_pointer)) + { + input_line_pointer = name; + break; + } + } + else if (is_name_beginner (*input_line_pointer)) + break; + } + + demand_empty_rest_of_line (); + cfi_sections = sections; +} + static void dot_cfi_startproc (int ignored ATTRIBUTE_UNUSED) { @@ -1194,13 +1251,14 @@ encoding_size (unsigned char encoding) } static void -output_cie (struct cie_entry *cie) +output_cie (struct cie_entry *cie, bfd_boolean eh_frame, int align) { symbolS *after_size_address, *end_address; expressionS exp; struct cfi_insn_data *i; offsetT augmentation_size; int enc; + enum dwarf2_format fmt = DWARF2_FORMAT (now_seg); cie->start_address = symbol_temp_new_now (); after_size_address = symbol_temp_make (); @@ -1211,18 +1269,35 @@ output_cie (struct cie_entry *cie) exp.X_op_symbol = after_size_address; exp.X_add_number = 0; - emit_expr (&exp, 4); /* Length. */ + if (eh_frame || fmt == dwarf2_format_32bit) + emit_expr (&exp, 4); /* Length. */ + else + { + if (fmt == dwarf2_format_64bit) + out_four (-1); + emit_expr (&exp, 8); /* Length. */ + } symbol_set_value_now (after_size_address); - out_four (0); /* CIE id. */ + if (eh_frame) + out_four (0); /* CIE id. */ + else + { + out_four (-1); /* CIE id. */ + if (fmt != dwarf2_format_32bit) + out_four (-1); + } out_one (DW_CIE_VERSION); /* Version. */ - out_one ('z'); /* Augmentation. */ - if (cie->per_encoding != DW_EH_PE_omit) - out_one ('P'); - if (cie->lsda_encoding != DW_EH_PE_omit) - out_one ('L'); - out_one ('R'); - if (cie->signal_frame) - out_one ('S'); + if (eh_frame) + { + out_one ('z'); /* Augmentation. */ + if (cie->per_encoding != DW_EH_PE_omit) + out_one ('P'); + if (cie->lsda_encoding != DW_EH_PE_omit) + out_one ('L'); + out_one ('R'); + if (cie->signal_frame) + out_one ('S'); + } out_one (0); out_uleb128 (DWARF2_LINE_MIN_INSN_LENGTH); /* Code alignment. */ out_sleb128 (DWARF2_CIE_DATA_ALIGNMENT); /* Data alignment. */ @@ -1230,10 +1305,13 @@ output_cie (struct cie_entry *cie) out_one (cie->return_column); else out_uleb128 (cie->return_column); - augmentation_size = 1 + (cie->lsda_encoding != DW_EH_PE_omit); - if (cie->per_encoding != DW_EH_PE_omit) - augmentation_size += 1 + encoding_size (cie->per_encoding); - out_uleb128 (augmentation_size); /* Augmentation size. */ + if (eh_frame) + { + augmentation_size = 1 + (cie->lsda_encoding != DW_EH_PE_omit); + if (cie->per_encoding != DW_EH_PE_omit) + augmentation_size += 1 + encoding_size (cie->per_encoding); + out_uleb128 (augmentation_size); /* Augmentation size. */ + } if (cie->per_encoding != DW_EH_PE_omit) { offsetT size = encoding_size (cie->per_encoding); @@ -1274,23 +1352,28 @@ output_cie (struct cie_entry *cie) #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr enc |= DW_EH_PE_pcrel; #endif - out_one (enc); + if (eh_frame) + out_one (enc); if (cie->first) for (i = cie->first; i != cie->last; i = i->next) output_cfi_insn (i); - frag_align (2, DW_CFA_nop, 0); + frag_align (align, DW_CFA_nop, 0); symbol_set_value_now (end_address); } static void output_fde (struct fde_entry *fde, struct cie_entry *cie, - struct cfi_insn_data *first, int align) + bfd_boolean eh_frame, struct cfi_insn_data *first, + int align) { symbolS *after_size_address, *end_address; expressionS exp; offsetT augmentation_size; + enum dwarf2_format fmt = DWARF2_FORMAT (now_seg); + int offset_size; + int addr_size; after_size_address = symbol_temp_make (); end_address = symbol_temp_make (); @@ -1299,35 +1382,63 @@ output_fde (struct fde_entry *fde, struct cie_entry *cie, exp.X_add_symbol = end_address; exp.X_op_symbol = after_size_address; exp.X_add_number = 0; - emit_expr (&exp, 4); /* Length. */ + if (eh_frame || fmt == dwarf2_format_32bit) + offset_size = 4; + else + { + if (fmt == dwarf2_format_64bit) + out_four (-1); + offset_size = 8; + } + emit_expr (&exp, offset_size); /* Length. */ symbol_set_value_now (after_size_address); - exp.X_add_symbol = after_size_address; - exp.X_op_symbol = cie->start_address; - emit_expr (&exp, 4); /* CIE offset. */ + if (eh_frame) + { + exp.X_add_symbol = after_size_address; + exp.X_op_symbol = cie->start_address; + } + else + { + exp.X_op = O_symbol; + exp.X_add_symbol = cie->start_address; + exp.X_op_symbol = NULL; + } + emit_expr (&exp, offset_size); /* CIE offset. */ + if (eh_frame) + { #if CFI_DIFF_EXPR_OK - exp.X_add_symbol = fde->start_address; - exp.X_op_symbol = symbol_temp_new_now (); - emit_expr (&exp, DWARF2_FDE_RELOC_SIZE); /* Code offset. */ + exp.X_add_symbol = fde->start_address; + exp.X_op_symbol = symbol_temp_new_now (); + emit_expr (&exp, DWARF2_FDE_RELOC_SIZE); /* Code offset. */ #else - exp.X_op = O_symbol; - exp.X_add_symbol = fde->start_address; - exp.X_op_symbol = NULL; + exp.X_op = O_symbol; + exp.X_add_symbol = fde->start_address; + exp.X_op_symbol = NULL; #ifdef tc_cfi_emit_pcrel_expr - tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE); /* Code offset. */ + tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE); /* Code offset. */ #else - emit_expr (&exp, DWARF2_FDE_RELOC_SIZE); /* Code offset. */ + emit_expr (&exp, DWARF2_FDE_RELOC_SIZE); /* Code offset. */ #endif - exp.X_op = O_subtract; #endif + addr_size = DWARF2_FDE_RELOC_SIZE; + } + else + { + exp.X_add_symbol = fde->start_address; + addr_size = DWARF2_ADDR_SIZE (stdoutput); + emit_expr (&exp, addr_size); + } + exp.X_op = O_subtract; exp.X_add_symbol = fde->end_address; exp.X_op_symbol = fde->start_address; /* Code length. */ - emit_expr (&exp, DWARF2_FDE_RELOC_SIZE); + emit_expr (&exp, addr_size); augmentation_size = encoding_size (fde->lsda_encoding); - out_uleb128 (augmentation_size); /* Augmentation size. */ + if (eh_frame) + out_uleb128 (augmentation_size); /* Augmentation size. */ if (fde->lsda_encoding != DW_EH_PE_omit) { @@ -1356,7 +1467,8 @@ output_fde (struct fde_entry *fde, struct cie_entry *cie, } static struct cie_entry * -select_cie_for_fde (struct fde_entry *fde, struct cfi_insn_data **pfirst) +select_cie_for_fde (struct fde_entry *fde, bfd_boolean eh_frame, + struct cfi_insn_data **pfirst, int align) { struct cfi_insn_data *i, *j; struct cie_entry *cie; @@ -1477,12 +1589,56 @@ select_cie_for_fde (struct fde_entry *fde, struct cfi_insn_data **pfirst) cie->last = i; *pfirst = i; - - output_cie (cie); + + output_cie (cie, eh_frame, align); return cie; } +#ifdef md_reg_eh_frame_to_debug_frame +static void +cfi_change_reg_numbers (struct cfi_insn_data *insn) +{ + 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; + + 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_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; + + default: + abort (); + } +} +#else +#define cfi_change_reg_numbers(insn) do { } while (0) +#endif + void cfi_finish (void) { @@ -1493,38 +1649,80 @@ cfi_finish (void) if (all_fde_data == 0) return; - /* 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); + 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); + 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; + /* 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) + for (fde = all_fde_data; fde ; fde = fde->next) { - as_bad (_("open CFI at the end of file; missing .cfi_endproc directive")); - fde->end_address = fde->start_address; + struct cfi_insn_data *first; + struct cie_entry *cie; + + 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); } - cie = select_cie_for_fde (fde, &first); - output_fde (fde, cie, first, fde->next == NULL ? EH_FRAME_ALIGNMENT : 2); + flag_traditional_format = save_flag_traditional_format; } - 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; + struct cie_entry *cie; + + 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); + cie = select_cie_for_fde (fde, FALSE, &first, alignment); + output_fde (fde, cie, FALSE, first, alignment); + } + } } #else /* TARGET_USE_CFIPOP */