diff options
-rw-r--r-- | gas/ChangeLog | 13 | ||||
-rw-r--r-- | gas/doc/as.texinfo | 19 | ||||
-rw-r--r-- | gas/dw2gencfi.c | 272 | ||||
-rw-r--r-- | gas/testsuite/ChangeLog | 4 | ||||
-rw-r--r-- | gas/testsuite/gas/cfi/cfi-common-6.d | 73 | ||||
-rw-r--r-- | gas/testsuite/gas/cfi/cfi-common-6.s | 40 | ||||
-rw-r--r-- | gas/testsuite/gas/cfi/cfi.exp | 1 |
7 files changed, 412 insertions, 10 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog index 0a62c21..e1ba472 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,5 +1,18 @@ 2006-11-03 Jakub Jelinek <jakub@redhat.com> + * dw2gencfi.c (struct fde_entry): Add per_encoding, lsda_encoding, + personality and lsda. + (struct cie_entry): Add per_encoding, lsda_encoding and personality. + (alloc_fde_entry): Initialize per_encoding and lsda_encoding. + (cfi_pseudo_table): Handle .cfi_personality and .cfi_lsda. + (dot_cfi_personality, dot_cfi_lsda, encoding_size): New functions. + (output_cie): Output personality including its encoding and LSDA encoding. + (output_fde): Output LSDA. + (select_cie_for_fde): Don't share CIE if personality, its encoding or + LSDA encoding are different. Copy the 3 fields from fde_entry to + cie_entry. + * doc/as.texinfo (.cfi_personality, .cfi_lsda): Document. + * subsegs.h (struct frchain): Add frch_cfi_data field. * dw2gencfi.c: Include subsegs.h. (cur_fde_data, last_address, cur_cfa_offset, cfa_save_stack): Removed. diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo index a43b77e..be21112 100644 --- a/gas/doc/as.texinfo +++ b/gas/doc/as.texinfo @@ -4102,6 +4102,25 @@ Don't forget to close the function by unwind entry previously opened by @code{.cfi_startproc}, and emits it to @code{.eh_frame}. +@section @code{.cfi_personality @var{encoding} [, @var{exp}]} +@code{.cfi_personality} defines personality routine and its encoding. +@var{encoding} must be a constant determining how the personality +should be encoded. If it is 255 (@code{DW_EH_PE_omit}), second +argument is not present, otherwise second argument should be +a constant or a symbol name. When using indirect encodings, +the symbol provided should be the location where personality +can be loaded from, not the personality routine itself. +The default after @code{.cfi_startproc} is @code{.cfi_personality 0xff}, +no personality routine. + +@section @code{.cfi_lsda @var{encoding} [, @var{exp}]} +@code{.cfi_lsda} defines LSDA and its encoding. +@var{encoding} must be a constant determining how the LSDA +should be encoded. If it is 255 (@code{DW_EH_PE_omit}), second +argument is not present, otherwise second argument should be a constant +or a symbol name. The default after @code{.cfi_startproc} is @code{.cfi_lsda 0xff}, +no LSDA. + @section @code{.cfi_def_cfa @var{register}, @var{offset}} @code{.cfi_def_cfa} defines a rule for computing CFA as: @i{take address from @var{register} and add @var{offset} to it}. diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c index 4e39ac5..97e1237 100644 --- a/gas/dw2gencfi.c +++ b/gas/dw2gencfi.c @@ -88,6 +88,10 @@ struct fde_entry symbolS *end_address; struct cfi_insn_data *data; struct cfi_insn_data **last; + unsigned char per_encoding; + unsigned char lsda_encoding; + expressionS personality; + expressionS lsda; unsigned int return_column; unsigned int signal_frame; }; @@ -98,6 +102,9 @@ struct cie_entry symbolS *start_address; unsigned int return_column; unsigned int signal_frame; + unsigned char per_encoding; + unsigned char lsda_encoding; + expressionS personality; struct cfi_insn_data *first, *last; }; @@ -139,6 +146,8 @@ alloc_fde_entry (void) fde->last = &fde->data; fde->return_column = DWARF2_DEFAULT_RETURN_COLUMN; + fde->per_encoding = DW_EH_PE_omit; + fde->lsda_encoding = DW_EH_PE_omit; return fde; } @@ -357,6 +366,8 @@ static void dot_cfi (int); static void dot_cfi_escape (int); static void dot_cfi_startproc (int); static void dot_cfi_endproc (int); +static void dot_cfi_personality (int); +static void dot_cfi_lsda (int); /* Fake CFI type; outside the byte range of any real CFI insn. */ #define CFI_adjust_cfa_offset 0x100 @@ -385,6 +396,8 @@ const pseudo_typeS cfi_pseudo_table[] = { "cfi_window_save", dot_cfi, DW_CFA_GNU_window_save }, { "cfi_escape", dot_cfi_escape, 0 }, { "cfi_signal_frame", dot_cfi, CFI_signal_frame }, + { "cfi_personality", dot_cfi_personality, 0 }, + { "cfi_lsda", dot_cfi_lsda, 0 }, { NULL, NULL, 0 } }; @@ -611,6 +624,148 @@ dot_cfi_escape (int ignored ATTRIBUTE_UNUSED) } static void +dot_cfi_personality (int ignored ATTRIBUTE_UNUSED) +{ + struct fde_entry *fde; + offsetT encoding; + + if (frchain_now->frch_cfi_data == NULL) + { + as_bad (_("CFI instruction used without previous .cfi_startproc")); + ignore_rest_of_line (); + return; + } + + fde = frchain_now->frch_cfi_data->cur_fde_data; + encoding = get_absolute_expression (); + if (encoding == DW_EH_PE_omit) + { + demand_empty_rest_of_line (); + fde->per_encoding = encoding; + return; + } + + if ((encoding & 0xff) != encoding + || ((encoding & 0x70) != 0 +#if defined DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr + && (encoding & 0x70) != DW_EH_PE_pcrel +#endif + ) + /* leb128 can be handled, but does something actually need it? */ + || (encoding & 7) == DW_EH_PE_uleb128 + || (encoding & 7) > DW_EH_PE_udata8) + { + as_bad (_("invalid or unsupported encoding in .cfi_personality")); + ignore_rest_of_line (); + return; + } + + if (*input_line_pointer++ != ',') + { + as_bad (_(".cfi_personality requires encoding and symbol arguments")); + ignore_rest_of_line (); + return; + } + + expression_and_evaluate (&fde->personality); + switch (fde->personality.X_op) + { + case O_symbol: + break; + case O_constant: + if ((encoding & 0x70) == DW_EH_PE_pcrel) + encoding = DW_EH_PE_omit; + break; + default: + encoding = DW_EH_PE_omit; + break; + } + + fde->per_encoding = encoding; + + if (encoding == DW_EH_PE_omit) + { + as_bad (_("wrong second argument to .cfi_personality")); + ignore_rest_of_line (); + return; + } + + demand_empty_rest_of_line (); +} + +static void +dot_cfi_lsda (int ignored ATTRIBUTE_UNUSED) +{ + struct fde_entry *fde; + offsetT encoding; + + if (frchain_now->frch_cfi_data == NULL) + { + as_bad (_("CFI instruction used without previous .cfi_startproc")); + ignore_rest_of_line (); + return; + } + + fde = frchain_now->frch_cfi_data->cur_fde_data; + encoding = get_absolute_expression (); + if (encoding == DW_EH_PE_omit) + { + demand_empty_rest_of_line (); + fde->lsda_encoding = encoding; + return; + } + + if ((encoding & 0xff) != encoding + || ((encoding & 0x70) != 0 +#if defined DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr + && (encoding & 0x70) != DW_EH_PE_pcrel +#endif + ) + /* leb128 can be handled, but does something actually need it? */ + || (encoding & 7) == DW_EH_PE_uleb128 + || (encoding & 7) > DW_EH_PE_udata8) + { + as_bad (_("invalid or unsupported encoding in .cfi_lsda")); + ignore_rest_of_line (); + return; + } + + if (*input_line_pointer++ != ',') + { + as_bad (_(".cfi_lsda requires encoding and symbol arguments")); + ignore_rest_of_line (); + return; + } + + fde->lsda_encoding = encoding; + + expression_and_evaluate (&fde->lsda); + switch (fde->lsda.X_op) + { + case O_symbol: + break; + case O_constant: + if ((encoding & 0x70) == DW_EH_PE_pcrel) + encoding = DW_EH_PE_omit; + break; + default: + encoding = DW_EH_PE_omit; + break; + } + + fde->lsda_encoding = encoding; + + if (encoding == DW_EH_PE_omit) + { + as_bad (_("wrong second argument to .cfi_lsda")); + ignore_rest_of_line (); + return; + } + + demand_empty_rest_of_line (); +} + +static void dot_cfi_startproc (int ignored ATTRIBUTE_UNUSED) { int simple = 0; @@ -725,18 +880,18 @@ output_cfi_insn (struct cfi_insn_data *insn) out_one (DW_CFA_advance_loc + scaled); else if (delta <= 0xFF) { - out_one (DW_CFA_advance_loc1); - out_one (delta); + out_one (DW_CFA_advance_loc1); + out_one (delta); } else if (delta <= 0xFFFF) { - out_one (DW_CFA_advance_loc2); - out_two (delta); + out_one (DW_CFA_advance_loc2); + out_two (delta); } else { - out_one (DW_CFA_advance_loc4); - out_four (delta); + out_one (DW_CFA_advance_loc4); + out_four (delta); } } else @@ -861,12 +1016,33 @@ output_cfi_insn (struct cfi_insn_data *insn) } } +static offsetT +encoding_size (unsigned char encoding) +{ + if (encoding == DW_EH_PE_omit) + return 0; + switch (encoding & 0x7) + { + case 0: + return bfd_get_arch_size (stdoutput) == 64 ? 8 : 4; + case DW_EH_PE_udata2: + return 2; + case DW_EH_PE_udata4: + return 4; + case DW_EH_PE_udata8: + return 8; + default: + abort (); + } +} + static void output_cie (struct cie_entry *cie) { symbolS *after_size_address, *end_address; expressionS exp; struct cfi_insn_data *i; + offsetT augmentation_size; cie->start_address = symbol_temp_new_now (); after_size_address = symbol_temp_make (); @@ -882,6 +1058,10 @@ output_cie (struct cie_entry *cie) out_four (0); /* CIE id. */ 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'); @@ -892,7 +1072,32 @@ output_cie (struct cie_entry *cie) out_one (cie->return_column); else out_uleb128 (cie->return_column); - out_uleb128 (1); /* Augmentation size. */ + 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); + out_one (cie->per_encoding); + exp = cie->personality; + if ((cie->per_encoding & 0x70) == DW_EH_PE_pcrel) + { +#ifdef DIFF_EXPR_OK + exp.X_op = O_subtract; + exp.X_op_symbol = symbol_temp_new_now (); + emit_expr (&exp, size); +#elif defined (tc_cfi_emit_pcrel_expr) + tc_cfi_emit_pcrel_expr (&exp, size); +#else + abort (); +#endif + } + else + emit_expr (&exp, size); + } + if (cie->lsda_encoding != DW_EH_PE_omit) + out_one (cie->lsda_encoding); #if defined DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr out_one (DW_EH_PE_pcrel | DW_EH_PE_sdata4); #else @@ -913,6 +1118,7 @@ output_fde (struct fde_entry *fde, struct cie_entry *cie, { symbolS *after_size_address, *end_address; expressionS exp; + offsetT augmentation_size; after_size_address = symbol_temp_make (); end_address = symbol_temp_make (); @@ -928,7 +1134,7 @@ output_fde (struct fde_entry *fde, struct cie_entry *cie, exp.X_op_symbol = cie->start_address; emit_expr (&exp, 4); /* CIE offset. */ -#ifdef DIFF_EXPR_OK +#ifdef DIFF_EXPR_OK exp.X_add_symbol = fde->start_address; exp.X_op_symbol = symbol_temp_new_now (); emit_expr (&exp, 4); /* Code offset. */ @@ -948,7 +1154,27 @@ output_fde (struct fde_entry *fde, struct cie_entry *cie, exp.X_op_symbol = fde->start_address; /* Code length. */ emit_expr (&exp, 4); - out_uleb128 (0); /* Augmentation size. */ + augmentation_size = encoding_size (fde->lsda_encoding); + out_uleb128 (augmentation_size); /* Augmentation size. */ + + if (fde->lsda_encoding != DW_EH_PE_omit) + { + exp = fde->lsda; + if ((fde->lsda_encoding & 0x70) == DW_EH_PE_pcrel) + { +#ifdef DIFF_EXPR_OK + exp.X_op = O_subtract; + exp.X_op_symbol = symbol_temp_new_now (); + emit_expr (&exp, augmentation_size); +#elif defined (tc_cfi_emit_pcrel_expr) + tc_cfi_emit_pcrel_expr (&exp, augmentation_size); +#else + abort (); +#endif + } + else + emit_expr (&exp, augmentation_size); + } for (; first; first = first->next) output_cfi_insn (first); @@ -966,8 +1192,31 @@ select_cie_for_fde (struct fde_entry *fde, struct cfi_insn_data **pfirst) for (cie = cie_root; cie; cie = cie->next) { if (cie->return_column != fde->return_column - || cie->signal_frame != fde->signal_frame) + || cie->signal_frame != fde->signal_frame + || cie->per_encoding != fde->per_encoding + || cie->lsda_encoding != fde->lsda_encoding) continue; + if (cie->per_encoding != DW_EH_PE_omit) + { + if (cie->personality.X_op != fde->personality.X_op + || cie->personality.X_add_number + != fde->personality.X_add_number) + continue; + switch (cie->personality.X_op) + { + case O_constant: + if (cie->personality.X_unsigned != fde->personality.X_unsigned) + continue; + break; + case O_symbol: + if (cie->personality.X_add_symbol + != fde->personality.X_add_symbol) + continue; + break; + default: + abort (); + } + } for (i = cie->first, j = fde->data; i != cie->last && j != NULL; i = i->next, j = j->next) @@ -1040,6 +1289,9 @@ select_cie_for_fde (struct fde_entry *fde, struct cfi_insn_data **pfirst) cie_root = cie; 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->personality = fde->personality; cie->first = fde->data; for (i = cie->first; i ; i = i->next) diff --git a/gas/testsuite/ChangeLog b/gas/testsuite/ChangeLog index 0034982..30c77b3 100644 --- a/gas/testsuite/ChangeLog +++ b/gas/testsuite/ChangeLog @@ -1,5 +1,9 @@ 2006-11-03 Jakub Jelinek <jakub@redhat.com> + * gas/cfi/cfi-common-6.d: New test. + * gas/cfi/cfi-common-6.s: New. + * gas/cfi/cfi.exp: Add cfi-common-6 test. + * gas/cfi/cfi-common-5.d: New test. * gas/cfi/cfi-common-5.s: New. * gas/cfi/cfi.exp: Add cfi-common-5 test. diff --git a/gas/testsuite/gas/cfi/cfi-common-6.d b/gas/testsuite/gas/cfi/cfi-common-6.d new file mode 100644 index 0000000..dcc7b79 --- /dev/null +++ b/gas/testsuite/gas/cfi/cfi-common-6.d @@ -0,0 +1,73 @@ +#readelf: -wf +#name: CFI common 6 +The section .eh_frame contains: + +00000000 00000018 00000000 CIE + Version: 1 + Augmentation: "zPLR" + Code alignment factor: .* + Data alignment factor: .* + Return address column: .* + Augmentation data: 03 .. .. .. .. 0c 1b + + DW_CFA_nop + DW_CFA_nop + DW_CFA_nop + +0000001c 00000018 00000020 FDE cie=00000000 pc=00000000..00000004 + Augmentation data: (00 00 00 00 de ad be ef|ef be ad de 00 00 00 00) + + DW_CFA_nop + DW_CFA_nop + DW_CFA_nop + +00000038 00000010 00000000 CIE + Version: 1 + Augmentation: "zLR" + Code alignment factor: .* + Data alignment factor: .* + Return address column: .* + Augmentation data: 0c 1b + + DW_CFA_nop + +0000004c 00000018 00000018 FDE cie=00000038 pc=00000004..00000008 + Augmentation data: (00 00 00 00 de ad be ef|ef be ad de 00 00 00 00) + + DW_CFA_nop + DW_CFA_nop + DW_CFA_nop + +00000068 00000018 0000006c FDE cie=00000000 pc=00000008..0000000c + Augmentation data: (00 00 00 00 be ef de ad|ad de ef be 00 00 00 00) + + DW_CFA_nop + DW_CFA_nop + DW_CFA_nop + +00000084 00000018 00000000 CIE + Version: 1 + Augmentation: "zPLR" + Code alignment factor: .* + Data alignment factor: .* + Return address column: .* + Augmentation data: 1b .. .. .. .. 1b 1b + + DW_CFA_nop + DW_CFA_nop + DW_CFA_nop + +000000a0 00000014 00000020 FDE cie=00000084 pc=0000000c..00000010 + Augmentation data: .. .. .. .. + + DW_CFA_nop + DW_CFA_nop + DW_CFA_nop + +000000b8 00000014 00000038 FDE cie=00000084 pc=00000010..00000014 + Augmentation data: .. .. .. .. + + DW_CFA_nop + DW_CFA_nop + DW_CFA_nop + diff --git a/gas/testsuite/gas/cfi/cfi-common-6.s b/gas/testsuite/gas/cfi/cfi-common-6.s new file mode 100644 index 0000000..6fa52a5 --- /dev/null +++ b/gas/testsuite/gas/cfi/cfi-common-6.s @@ -0,0 +1,40 @@ + .text + .cfi_startproc simple + .cfi_personality 3, my_personality_v0 + .cfi_lsda 12, 0xdeadbeef + .long 0 + .cfi_endproc + + .cfi_startproc simple + .cfi_personality 3, my_personality_v0 + .cfi_lsda 12, 0xdeadbeef + .cfi_personality 0xff + .long 0 + .cfi_endproc + + .cfi_startproc simple + .cfi_personality 3, my_personality_v0 + .cfi_lsda 12, 0xbeefdead + .long 0 + .cfi_endproc + + .cfi_startproc simple + .cfi_personality (0x10 | 11), my_personality_v1 + .cfi_lsda 27, 1f + .long 0 + .cfi_endproc + + .cfi_startproc simple + .cfi_personality (0x10 | 11), my_personality_v1 + .cfi_lsda 27, 2f + .long 0 + .cfi_endproc + +my_personality_v0: + .long 0 +my_personality_v1: + .long 0 +1: + .long 0 +2: + .long 0 diff --git a/gas/testsuite/gas/cfi/cfi.exp b/gas/testsuite/gas/cfi/cfi.exp index 60b7952..da9ef26 100644 --- a/gas/testsuite/gas/cfi/cfi.exp +++ b/gas/testsuite/gas/cfi/cfi.exp @@ -73,3 +73,4 @@ run_dump_test "cfi-common-2" run_dump_test "cfi-common-3" run_dump_test "cfi-common-4" run_dump_test "cfi-common-5" +run_dump_test "cfi-common-6" |