| /* scfidw2gen.c - Support for emission of synthesized Dwarf2 CFI. |
| Copyright (C) 2023 Free Software Foundation, Inc. |
| |
| This file is part of GAS, the GNU Assembler. |
| |
| GAS is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GAS is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GAS; see the file COPYING. If not, write to the Free |
| Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA |
| 02110-1301, USA. */ |
| |
| #include "as.h" |
| #include "ginsn.h" |
| #include "scfi.h" |
| #include "dw2gencfi.h" |
| #include "subsegs.h" |
| #include "scfidw2gen.h" |
| |
| #if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN) |
| |
| static bool scfi_ignore_warn_once; |
| |
| static void |
| dot_scfi_ignore (int ignored ATTRIBUTE_UNUSED) |
| { |
| gas_assert (flag_synth_cfi); |
| |
| if (!scfi_ignore_warn_once) |
| { |
| as_warn (_("SCFI ignores most user-specified CFI directives")); |
| scfi_ignore_warn_once = true; |
| } |
| ignore_rest_of_line (); |
| } |
| |
| static void |
| scfi_process_cfi_label (void) |
| { |
| char *name; |
| ginsnS *ginsn; |
| |
| name = read_symbol_name (); |
| if (name == NULL) |
| return; |
| |
| /* Add a new ginsn. */ |
| ginsn = ginsn_new_phantom (symbol_temp_new_now ()); |
| frch_ginsn_data_append (ginsn); |
| |
| scfi_op_add_cfi_label (ginsn, name); |
| /* NB: Can't free NAME here since it will be used later. Free it in |
| handle_scfi_dot_cfi after it is unused. */ |
| |
| demand_empty_rest_of_line (); |
| } |
| |
| static void |
| scfi_process_cfi_signal_frame (void) |
| { |
| ginsnS *ginsn; |
| |
| ginsn = ginsn_new_phantom (symbol_temp_new_now ()); |
| frch_ginsn_data_append (ginsn); |
| |
| scfi_op_add_signal_frame (ginsn); |
| } |
| |
| static void |
| dot_scfi (int arg) |
| { |
| switch (arg) |
| { |
| case CFI_label: |
| scfi_process_cfi_label (); |
| break; |
| case CFI_signal_frame: |
| scfi_process_cfi_signal_frame (); |
| break; |
| default: |
| abort (); |
| } |
| } |
| |
| const pseudo_typeS scfi_pseudo_table[] = |
| { |
| { "cfi_sections", dot_cfi_sections, 0 }, /* No ignore. */ |
| { "cfi_signal_frame", dot_scfi, CFI_signal_frame }, /* No ignore. */ |
| { "cfi_label", dot_scfi, CFI_label }, /* No ignore. */ |
| { "cfi_startproc", dot_scfi_ignore, 0 }, |
| { "cfi_endproc", dot_scfi_ignore, 0 }, |
| { "cfi_fde_data", dot_scfi_ignore, 0 }, |
| { "cfi_def_cfa", dot_scfi_ignore, 0 }, |
| { "cfi_def_cfa_register", dot_scfi_ignore, 0 }, |
| { "cfi_def_cfa_offset", dot_scfi_ignore, 0 }, |
| { "cfi_adjust_cfa_offset", dot_scfi_ignore, 0 }, |
| { "cfi_offset", dot_scfi_ignore, 0 }, |
| { "cfi_rel_offset", dot_scfi_ignore, 0 }, |
| { "cfi_register", dot_scfi_ignore, 0 }, |
| { "cfi_return_column", dot_scfi_ignore, 0 }, |
| { "cfi_restore", dot_scfi_ignore, 0 }, |
| { "cfi_undefined", dot_scfi_ignore, 0 }, |
| { "cfi_same_value", dot_scfi_ignore, 0 }, |
| { "cfi_remember_state", dot_scfi_ignore, 0 }, |
| { "cfi_restore_state", dot_scfi_ignore, 0 }, |
| { "cfi_window_save", dot_scfi_ignore, 0 }, |
| { "cfi_negate_ra_state", dot_scfi_ignore, 0 }, |
| { "cfi_escape", dot_scfi_ignore, 0 }, |
| { "cfi_personality", dot_scfi_ignore, 0 }, |
| { "cfi_personality_id", dot_scfi_ignore, 0 }, |
| { "cfi_lsda", dot_scfi_ignore, 0 }, |
| { "cfi_val_encoded_addr", dot_scfi_ignore, 0 }, |
| { "cfi_inline_lsda", dot_scfi_ignore, 0 }, |
| { "cfi_val_offset", dot_scfi_ignore, 0 }, |
| { NULL, NULL, 0 } |
| }; |
| |
| void |
| scfi_dot_cfi_startproc (const symbolS *start_sym) |
| { |
| if (frchain_now->frch_cfi_data != NULL) |
| { |
| as_bad (_("SCFI: missing previous SCFI endproc marker")); |
| return; |
| } |
| |
| cfi_new_fde ((symbolS *)start_sym); |
| |
| cfi_set_sections (); |
| |
| frchain_now->frch_cfi_data->cur_cfa_offset = 0; |
| |
| /* By default, SCFI machinery assumes .cfi_startproc is used without |
| parameter simple. */ |
| tc_cfi_frame_initial_instructions (); |
| |
| if ((all_cfi_sections & CFI_EMIT_target) != 0) |
| tc_cfi_startproc (); |
| } |
| |
| void |
| scfi_dot_cfi_endproc (const symbolS *end_sym) |
| { |
| struct fde_entry *fde_last; |
| |
| if (frchain_now->frch_cfi_data == NULL) |
| { |
| as_bad (_(".cfi_endproc without corresponding .cfi_startproc")); |
| return; |
| } |
| |
| fde_last = frchain_now->frch_cfi_data->cur_fde_data; |
| cfi_set_last_fde (fde_last); |
| |
| cfi_end_fde ((symbolS *)end_sym); |
| |
| if ((all_cfi_sections & CFI_EMIT_target) != 0) |
| tc_cfi_endproc (fde_last); |
| } |
| |
| void |
| scfi_dot_cfi (int arg, unsigned reg1, unsigned reg2, offsetT offset, |
| const char *name, const symbolS *advloc) |
| { |
| if (frchain_now->frch_cfi_data == NULL) |
| { |
| as_bad (_("CFI instruction used without previous .cfi_startproc")); |
| return; |
| } |
| |
| /* If the last address was not at the current PC, advance to current. */ |
| if (frchain_now->frch_cfi_data->last_address != advloc) |
| cfi_add_advance_loc ((symbolS *)advloc); |
| |
| switch (arg) |
| { |
| case DW_CFA_offset: |
| cfi_add_CFA_offset (reg1, offset); |
| break; |
| |
| case DW_CFA_val_offset: |
| cfi_add_CFA_val_offset (reg1, offset); |
| break; |
| |
| case CFI_rel_offset: |
| cfi_add_CFA_offset (reg1, |
| offset - frchain_now->frch_cfi_data->cur_cfa_offset); |
| break; |
| |
| case DW_CFA_def_cfa: |
| cfi_add_CFA_def_cfa (reg1, offset); |
| break; |
| |
| case DW_CFA_register: |
| cfi_add_CFA_register (reg1, reg2); |
| break; |
| |
| case DW_CFA_def_cfa_register: |
| cfi_add_CFA_def_cfa_register (reg1); |
| break; |
| |
| case DW_CFA_def_cfa_offset: |
| cfi_add_CFA_def_cfa_offset (offset); |
| break; |
| |
| case CFI_adjust_cfa_offset: |
| cfi_add_CFA_def_cfa_offset (frchain_now->frch_cfi_data->cur_cfa_offset |
| + offset); |
| break; |
| |
| case DW_CFA_restore: |
| cfi_add_CFA_restore (reg1); |
| break; |
| |
| case DW_CFA_remember_state: |
| cfi_add_CFA_remember_state (); |
| break; |
| |
| case DW_CFA_restore_state: |
| cfi_add_CFA_restore_state (); |
| break; |
| |
| case CFI_label: |
| cfi_add_label (name); |
| break; |
| |
| case CFI_signal_frame: |
| frchain_now->frch_cfi_data->cur_fde_data->signal_frame = 1; |
| break; |
| |
| /* |
| case DW_CFA_undefined: |
| for (;;) |
| { |
| reg1 = cfi_parse_reg (); |
| cfi_add_CFA_undefined (reg1); |
| SKIP_WHITESPACE (); |
| if (*input_line_pointer != ',') |
| break; |
| ++input_line_pointer; |
| } |
| break; |
| |
| case DW_CFA_same_value: |
| reg1 = cfi_parse_reg (); |
| cfi_add_CFA_same_value (reg1); |
| break; |
| |
| case CFI_return_column: |
| reg1 = cfi_parse_reg (); |
| cfi_set_return_column (reg1); |
| break; |
| |
| case DW_CFA_GNU_window_save: |
| cfi_add_CFA_insn (DW_CFA_GNU_window_save); |
| break; |
| |
| */ |
| default: |
| abort (); |
| } |
| } |
| |
| #endif |