1130561Sobrien/* dw2gencfi.c - Support for generating Dwarf2 CFI information. 2218822Sdim Copyright 2003, 2004, 2005, 2006 Free Software Foundation, Inc. 3130561Sobrien Contributed by Michal Ludvig <mludvig@suse.cz> 4130561Sobrien 5130561Sobrien This file is part of GAS, the GNU Assembler. 6130561Sobrien 7130561Sobrien GAS is free software; you can redistribute it and/or modify 8130561Sobrien it under the terms of the GNU General Public License as published by 9130561Sobrien the Free Software Foundation; either version 2, or (at your option) 10130561Sobrien any later version. 11130561Sobrien 12130561Sobrien GAS is distributed in the hope that it will be useful, 13130561Sobrien but WITHOUT ANY WARRANTY; without even the implied warranty of 14130561Sobrien MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15130561Sobrien GNU General Public License for more details. 16130561Sobrien 17130561Sobrien You should have received a copy of the GNU General Public License 18130561Sobrien along with GAS; see the file COPYING. If not, write to the Free 19218822Sdim Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 20218822Sdim 02110-1301, USA. */ 21130561Sobrien 22130561Sobrien#include "as.h" 23130561Sobrien#include "dw2gencfi.h" 24218822Sdim#include "subsegs.h" 25130561Sobrien 26130561Sobrien 27130561Sobrien/* We re-use DWARF2_LINE_MIN_INSN_LENGTH for the code alignment field 28130561Sobrien of the CIE. Default to 1 if not otherwise specified. */ 29218822Sdim#ifndef DWARF2_LINE_MIN_INSN_LENGTH 30130561Sobrien# define DWARF2_LINE_MIN_INSN_LENGTH 1 31130561Sobrien#endif 32130561Sobrien 33130561Sobrien/* If TARGET_USE_CFIPOP is defined, it is required that the target 34130561Sobrien provide the following definitions. Otherwise provide them to 35130561Sobrien allow compilation to continue. */ 36130561Sobrien#ifndef TARGET_USE_CFIPOP 37218822Sdim# ifndef DWARF2_DEFAULT_RETURN_COLUMN 38130561Sobrien# define DWARF2_DEFAULT_RETURN_COLUMN 0 39130561Sobrien# endif 40218822Sdim# ifndef DWARF2_CIE_DATA_ALIGNMENT 41130561Sobrien# define DWARF2_CIE_DATA_ALIGNMENT 1 42130561Sobrien# endif 43130561Sobrien#endif 44130561Sobrien 45130561Sobrien#ifndef EH_FRAME_ALIGNMENT 46218822Sdim# define EH_FRAME_ALIGNMENT (bfd_get_arch_size (stdoutput) == 64 ? 3 : 2) 47130561Sobrien#endif 48130561Sobrien 49130561Sobrien#ifndef tc_cfi_frame_initial_instructions 50130561Sobrien# define tc_cfi_frame_initial_instructions() ((void)0) 51130561Sobrien#endif 52130561Sobrien 53130561Sobrien 54130561Sobrienstruct cfi_insn_data 55130561Sobrien{ 56130561Sobrien struct cfi_insn_data *next; 57130561Sobrien int insn; 58130561Sobrien union { 59130561Sobrien struct { 60130561Sobrien unsigned reg; 61130561Sobrien offsetT offset; 62130561Sobrien } ri; 63130561Sobrien 64130561Sobrien struct { 65130561Sobrien unsigned reg1; 66130561Sobrien unsigned reg2; 67130561Sobrien } rr; 68130561Sobrien 69130561Sobrien unsigned r; 70130561Sobrien offsetT i; 71130561Sobrien 72130561Sobrien struct { 73130561Sobrien symbolS *lab1; 74130561Sobrien symbolS *lab2; 75130561Sobrien } ll; 76130561Sobrien 77130561Sobrien struct cfi_escape_data { 78130561Sobrien struct cfi_escape_data *next; 79130561Sobrien expressionS exp; 80130561Sobrien } *esc; 81130561Sobrien } u; 82130561Sobrien}; 83130561Sobrien 84130561Sobrienstruct fde_entry 85130561Sobrien{ 86130561Sobrien struct fde_entry *next; 87130561Sobrien symbolS *start_address; 88130561Sobrien symbolS *end_address; 89130561Sobrien struct cfi_insn_data *data; 90130561Sobrien struct cfi_insn_data **last; 91218822Sdim unsigned char per_encoding; 92218822Sdim unsigned char lsda_encoding; 93218822Sdim expressionS personality; 94218822Sdim expressionS lsda; 95130561Sobrien unsigned int return_column; 96218822Sdim unsigned int signal_frame; 97130561Sobrien}; 98130561Sobrien 99130561Sobrienstruct cie_entry 100130561Sobrien{ 101130561Sobrien struct cie_entry *next; 102130561Sobrien symbolS *start_address; 103130561Sobrien unsigned int return_column; 104218822Sdim unsigned int signal_frame; 105218822Sdim unsigned char per_encoding; 106218822Sdim unsigned char lsda_encoding; 107218822Sdim expressionS personality; 108130561Sobrien struct cfi_insn_data *first, *last; 109130561Sobrien}; 110130561Sobrien 111130561Sobrien 112130561Sobrien/* List of FDE entries. */ 113130561Sobrienstatic struct fde_entry *all_fde_data; 114130561Sobrienstatic struct fde_entry **last_fde_data = &all_fde_data; 115130561Sobrien 116130561Sobrien/* List of CIEs so that they could be reused. */ 117130561Sobrienstatic struct cie_entry *cie_root; 118130561Sobrien 119130561Sobrien/* Stack of old CFI data, for save/restore. */ 120130561Sobrienstruct cfa_save_data 121130561Sobrien{ 122130561Sobrien struct cfa_save_data *next; 123130561Sobrien offsetT cfa_offset; 124130561Sobrien}; 125130561Sobrien 126218822Sdim/* Current open FDE entry. */ 127218822Sdimstruct frch_cfi_data 128218822Sdim{ 129218822Sdim struct fde_entry *cur_fde_data; 130218822Sdim symbolS *last_address; 131218822Sdim offsetT cur_cfa_offset; 132218822Sdim struct cfa_save_data *cfa_save_stack; 133218822Sdim}; 134130561Sobrien 135130561Sobrien/* Construct a new FDE structure and add it to the end of the fde list. */ 136130561Sobrien 137130561Sobrienstatic struct fde_entry * 138130561Sobrienalloc_fde_entry (void) 139130561Sobrien{ 140130561Sobrien struct fde_entry *fde = xcalloc (1, sizeof (struct fde_entry)); 141130561Sobrien 142218822Sdim frchain_now->frch_cfi_data = xcalloc (1, sizeof (struct frch_cfi_data)); 143218822Sdim frchain_now->frch_cfi_data->cur_fde_data = fde; 144130561Sobrien *last_fde_data = fde; 145130561Sobrien last_fde_data = &fde->next; 146130561Sobrien 147130561Sobrien fde->last = &fde->data; 148130561Sobrien fde->return_column = DWARF2_DEFAULT_RETURN_COLUMN; 149218822Sdim fde->per_encoding = DW_EH_PE_omit; 150218822Sdim fde->lsda_encoding = DW_EH_PE_omit; 151130561Sobrien 152130561Sobrien return fde; 153130561Sobrien} 154130561Sobrien 155130561Sobrien/* The following functions are available for a backend to construct its 156130561Sobrien own unwind information, usually from legacy unwind directives. */ 157130561Sobrien 158130561Sobrien/* Construct a new INSN structure and add it to the end of the insn list 159130561Sobrien for the currently active FDE. */ 160130561Sobrien 161130561Sobrienstatic struct cfi_insn_data * 162130561Sobrienalloc_cfi_insn_data (void) 163130561Sobrien{ 164130561Sobrien struct cfi_insn_data *insn = xcalloc (1, sizeof (struct cfi_insn_data)); 165218822Sdim struct fde_entry *cur_fde_data = frchain_now->frch_cfi_data->cur_fde_data; 166130561Sobrien 167130561Sobrien *cur_fde_data->last = insn; 168130561Sobrien cur_fde_data->last = &insn->next; 169130561Sobrien 170130561Sobrien return insn; 171130561Sobrien} 172130561Sobrien 173130561Sobrien/* Construct a new FDE structure that begins at LABEL. */ 174130561Sobrien 175130561Sobrienvoid 176130561Sobriencfi_new_fde (symbolS *label) 177130561Sobrien{ 178130561Sobrien struct fde_entry *fde = alloc_fde_entry (); 179130561Sobrien fde->start_address = label; 180218822Sdim frchain_now->frch_cfi_data->last_address = label; 181130561Sobrien} 182130561Sobrien 183130561Sobrien/* End the currently open FDE. */ 184130561Sobrien 185130561Sobrienvoid 186130561Sobriencfi_end_fde (symbolS *label) 187130561Sobrien{ 188218822Sdim frchain_now->frch_cfi_data->cur_fde_data->end_address = label; 189218822Sdim free (frchain_now->frch_cfi_data); 190218822Sdim frchain_now->frch_cfi_data = NULL; 191130561Sobrien} 192130561Sobrien 193130561Sobrien/* Set the return column for the current FDE. */ 194130561Sobrien 195130561Sobrienvoid 196130561Sobriencfi_set_return_column (unsigned regno) 197130561Sobrien{ 198218822Sdim frchain_now->frch_cfi_data->cur_fde_data->return_column = regno; 199130561Sobrien} 200130561Sobrien 201130561Sobrien/* Universal functions to store new instructions. */ 202130561Sobrien 203130561Sobrienstatic void 204130561Sobriencfi_add_CFA_insn(int insn) 205130561Sobrien{ 206130561Sobrien struct cfi_insn_data *insn_ptr = alloc_cfi_insn_data (); 207130561Sobrien 208130561Sobrien insn_ptr->insn = insn; 209130561Sobrien} 210130561Sobrien 211130561Sobrienstatic void 212130561Sobriencfi_add_CFA_insn_reg (int insn, unsigned regno) 213130561Sobrien{ 214130561Sobrien struct cfi_insn_data *insn_ptr = alloc_cfi_insn_data (); 215130561Sobrien 216130561Sobrien insn_ptr->insn = insn; 217130561Sobrien insn_ptr->u.r = regno; 218130561Sobrien} 219130561Sobrien 220130561Sobrienstatic void 221130561Sobriencfi_add_CFA_insn_offset (int insn, offsetT offset) 222130561Sobrien{ 223130561Sobrien struct cfi_insn_data *insn_ptr = alloc_cfi_insn_data (); 224130561Sobrien 225130561Sobrien insn_ptr->insn = insn; 226130561Sobrien insn_ptr->u.i = offset; 227130561Sobrien} 228130561Sobrien 229130561Sobrienstatic void 230130561Sobriencfi_add_CFA_insn_reg_reg (int insn, unsigned reg1, unsigned reg2) 231130561Sobrien{ 232130561Sobrien struct cfi_insn_data *insn_ptr = alloc_cfi_insn_data (); 233130561Sobrien 234130561Sobrien insn_ptr->insn = insn; 235130561Sobrien insn_ptr->u.rr.reg1 = reg1; 236130561Sobrien insn_ptr->u.rr.reg2 = reg2; 237130561Sobrien} 238130561Sobrien 239130561Sobrienstatic void 240130561Sobriencfi_add_CFA_insn_reg_offset (int insn, unsigned regno, offsetT offset) 241130561Sobrien{ 242130561Sobrien struct cfi_insn_data *insn_ptr = alloc_cfi_insn_data (); 243130561Sobrien 244130561Sobrien insn_ptr->insn = insn; 245130561Sobrien insn_ptr->u.ri.reg = regno; 246130561Sobrien insn_ptr->u.ri.offset = offset; 247130561Sobrien} 248130561Sobrien 249130561Sobrien/* Add a CFI insn to advance the PC from the last address to LABEL. */ 250130561Sobrien 251130561Sobrienvoid 252130561Sobriencfi_add_advance_loc (symbolS *label) 253130561Sobrien{ 254130561Sobrien struct cfi_insn_data *insn = alloc_cfi_insn_data (); 255130561Sobrien 256130561Sobrien insn->insn = DW_CFA_advance_loc; 257218822Sdim insn->u.ll.lab1 = frchain_now->frch_cfi_data->last_address; 258130561Sobrien insn->u.ll.lab2 = label; 259130561Sobrien 260218822Sdim frchain_now->frch_cfi_data->last_address = label; 261130561Sobrien} 262130561Sobrien 263130561Sobrien/* Add a DW_CFA_offset record to the CFI data. */ 264130561Sobrien 265130561Sobrienvoid 266130561Sobriencfi_add_CFA_offset (unsigned regno, offsetT offset) 267130561Sobrien{ 268130561Sobrien unsigned int abs_data_align; 269130561Sobrien 270218822Sdim assert (DWARF2_CIE_DATA_ALIGNMENT != 0); 271130561Sobrien cfi_add_CFA_insn_reg_offset (DW_CFA_offset, regno, offset); 272130561Sobrien 273130561Sobrien abs_data_align = (DWARF2_CIE_DATA_ALIGNMENT < 0 274130561Sobrien ? -DWARF2_CIE_DATA_ALIGNMENT : DWARF2_CIE_DATA_ALIGNMENT); 275130561Sobrien if (offset % abs_data_align) 276130561Sobrien as_bad (_("register save offset not a multiple of %u"), abs_data_align); 277130561Sobrien} 278130561Sobrien 279130561Sobrien/* Add a DW_CFA_def_cfa record to the CFI data. */ 280130561Sobrien 281130561Sobrienvoid 282130561Sobriencfi_add_CFA_def_cfa (unsigned regno, offsetT offset) 283130561Sobrien{ 284130561Sobrien cfi_add_CFA_insn_reg_offset (DW_CFA_def_cfa, regno, offset); 285218822Sdim frchain_now->frch_cfi_data->cur_cfa_offset = offset; 286130561Sobrien} 287130561Sobrien 288130561Sobrien/* Add a DW_CFA_register record to the CFI data. */ 289130561Sobrien 290130561Sobrienvoid 291130561Sobriencfi_add_CFA_register (unsigned reg1, unsigned reg2) 292130561Sobrien{ 293130561Sobrien cfi_add_CFA_insn_reg_reg (DW_CFA_register, reg1, reg2); 294130561Sobrien} 295130561Sobrien 296130561Sobrien/* Add a DW_CFA_def_cfa_register record to the CFI data. */ 297130561Sobrien 298130561Sobrienvoid 299130561Sobriencfi_add_CFA_def_cfa_register (unsigned regno) 300130561Sobrien{ 301130561Sobrien cfi_add_CFA_insn_reg (DW_CFA_def_cfa_register, regno); 302130561Sobrien} 303130561Sobrien 304130561Sobrien/* Add a DW_CFA_def_cfa_offset record to the CFI data. */ 305130561Sobrien 306130561Sobrienvoid 307130561Sobriencfi_add_CFA_def_cfa_offset (offsetT offset) 308130561Sobrien{ 309130561Sobrien cfi_add_CFA_insn_offset (DW_CFA_def_cfa_offset, offset); 310218822Sdim frchain_now->frch_cfi_data->cur_cfa_offset = offset; 311130561Sobrien} 312130561Sobrien 313130561Sobrienvoid 314130561Sobriencfi_add_CFA_restore (unsigned regno) 315130561Sobrien{ 316130561Sobrien cfi_add_CFA_insn_reg (DW_CFA_restore, regno); 317130561Sobrien} 318130561Sobrien 319130561Sobrienvoid 320130561Sobriencfi_add_CFA_undefined (unsigned regno) 321130561Sobrien{ 322130561Sobrien cfi_add_CFA_insn_reg (DW_CFA_undefined, regno); 323130561Sobrien} 324130561Sobrien 325130561Sobrienvoid 326130561Sobriencfi_add_CFA_same_value (unsigned regno) 327130561Sobrien{ 328130561Sobrien cfi_add_CFA_insn_reg (DW_CFA_same_value, regno); 329130561Sobrien} 330130561Sobrien 331130561Sobrienvoid 332130561Sobriencfi_add_CFA_remember_state (void) 333130561Sobrien{ 334130561Sobrien struct cfa_save_data *p; 335130561Sobrien 336130561Sobrien cfi_add_CFA_insn (DW_CFA_remember_state); 337130561Sobrien 338130561Sobrien p = xmalloc (sizeof (*p)); 339218822Sdim p->cfa_offset = frchain_now->frch_cfi_data->cur_cfa_offset; 340218822Sdim p->next = frchain_now->frch_cfi_data->cfa_save_stack; 341218822Sdim frchain_now->frch_cfi_data->cfa_save_stack = p; 342130561Sobrien} 343130561Sobrien 344130561Sobrienvoid 345130561Sobriencfi_add_CFA_restore_state (void) 346130561Sobrien{ 347130561Sobrien struct cfa_save_data *p; 348130561Sobrien 349130561Sobrien cfi_add_CFA_insn (DW_CFA_restore_state); 350130561Sobrien 351218822Sdim p = frchain_now->frch_cfi_data->cfa_save_stack; 352130561Sobrien if (p) 353130561Sobrien { 354218822Sdim frchain_now->frch_cfi_data->cur_cfa_offset = p->cfa_offset; 355218822Sdim frchain_now->frch_cfi_data->cfa_save_stack = p->next; 356130561Sobrien free (p); 357130561Sobrien } 358218822Sdim else 359218822Sdim as_bad (_("CFI state restore without previous remember")); 360130561Sobrien} 361130561Sobrien 362130561Sobrien 363130561Sobrien/* Parse CFI assembler directives. */ 364130561Sobrien 365130561Sobrienstatic void dot_cfi (int); 366130561Sobrienstatic void dot_cfi_escape (int); 367130561Sobrienstatic void dot_cfi_startproc (int); 368130561Sobrienstatic void dot_cfi_endproc (int); 369218822Sdimstatic void dot_cfi_personality (int); 370218822Sdimstatic void dot_cfi_lsda (int); 371130561Sobrien 372130561Sobrien/* Fake CFI type; outside the byte range of any real CFI insn. */ 373130561Sobrien#define CFI_adjust_cfa_offset 0x100 374130561Sobrien#define CFI_return_column 0x101 375130561Sobrien#define CFI_rel_offset 0x102 376130561Sobrien#define CFI_escape 0x103 377218822Sdim#define CFI_signal_frame 0x104 378130561Sobrien 379130561Sobrienconst pseudo_typeS cfi_pseudo_table[] = 380130561Sobrien { 381130561Sobrien { "cfi_startproc", dot_cfi_startproc, 0 }, 382130561Sobrien { "cfi_endproc", dot_cfi_endproc, 0 }, 383130561Sobrien { "cfi_def_cfa", dot_cfi, DW_CFA_def_cfa }, 384130561Sobrien { "cfi_def_cfa_register", dot_cfi, DW_CFA_def_cfa_register }, 385130561Sobrien { "cfi_def_cfa_offset", dot_cfi, DW_CFA_def_cfa_offset }, 386130561Sobrien { "cfi_adjust_cfa_offset", dot_cfi, CFI_adjust_cfa_offset }, 387130561Sobrien { "cfi_offset", dot_cfi, DW_CFA_offset }, 388130561Sobrien { "cfi_rel_offset", dot_cfi, CFI_rel_offset }, 389130561Sobrien { "cfi_register", dot_cfi, DW_CFA_register }, 390130561Sobrien { "cfi_return_column", dot_cfi, CFI_return_column }, 391130561Sobrien { "cfi_restore", dot_cfi, DW_CFA_restore }, 392130561Sobrien { "cfi_undefined", dot_cfi, DW_CFA_undefined }, 393130561Sobrien { "cfi_same_value", dot_cfi, DW_CFA_same_value }, 394130561Sobrien { "cfi_remember_state", dot_cfi, DW_CFA_remember_state }, 395130561Sobrien { "cfi_restore_state", dot_cfi, DW_CFA_restore_state }, 396130561Sobrien { "cfi_window_save", dot_cfi, DW_CFA_GNU_window_save }, 397130561Sobrien { "cfi_escape", dot_cfi_escape, 0 }, 398218822Sdim { "cfi_signal_frame", dot_cfi, CFI_signal_frame }, 399218822Sdim { "cfi_personality", dot_cfi_personality, 0 }, 400218822Sdim { "cfi_lsda", dot_cfi_lsda, 0 }, 401130561Sobrien { NULL, NULL, 0 } 402130561Sobrien }; 403130561Sobrien 404130561Sobrienstatic void 405130561Sobriencfi_parse_separator (void) 406130561Sobrien{ 407130561Sobrien SKIP_WHITESPACE (); 408130561Sobrien if (*input_line_pointer == ',') 409130561Sobrien input_line_pointer++; 410130561Sobrien else 411130561Sobrien as_bad (_("missing separator")); 412130561Sobrien} 413130561Sobrien 414130561Sobrienstatic unsigned 415130561Sobriencfi_parse_reg (void) 416130561Sobrien{ 417130561Sobrien int regno; 418130561Sobrien expressionS exp; 419130561Sobrien 420130561Sobrien#ifdef tc_regname_to_dw2regnum 421130561Sobrien SKIP_WHITESPACE (); 422130561Sobrien if (is_name_beginner (*input_line_pointer) 423130561Sobrien || (*input_line_pointer == '%' 424130561Sobrien && is_name_beginner (*++input_line_pointer))) 425130561Sobrien { 426130561Sobrien char *name, c; 427130561Sobrien 428130561Sobrien name = input_line_pointer; 429130561Sobrien c = get_symbol_end (); 430130561Sobrien 431130561Sobrien if ((regno = tc_regname_to_dw2regnum (name)) < 0) 432130561Sobrien { 433130561Sobrien as_bad (_("bad register expression")); 434130561Sobrien regno = 0; 435130561Sobrien } 436130561Sobrien 437130561Sobrien *input_line_pointer = c; 438130561Sobrien return regno; 439130561Sobrien } 440130561Sobrien#endif 441130561Sobrien 442218822Sdim expression_and_evaluate (&exp); 443130561Sobrien switch (exp.X_op) 444130561Sobrien { 445130561Sobrien case O_register: 446130561Sobrien case O_constant: 447130561Sobrien regno = exp.X_add_number; 448130561Sobrien break; 449130561Sobrien 450130561Sobrien default: 451130561Sobrien as_bad (_("bad register expression")); 452130561Sobrien regno = 0; 453130561Sobrien break; 454130561Sobrien } 455130561Sobrien 456130561Sobrien return regno; 457130561Sobrien} 458130561Sobrien 459130561Sobrienstatic offsetT 460130561Sobriencfi_parse_const (void) 461130561Sobrien{ 462130561Sobrien return get_absolute_expression (); 463130561Sobrien} 464130561Sobrien 465130561Sobrienstatic void 466130561Sobriendot_cfi (int arg) 467130561Sobrien{ 468130561Sobrien offsetT offset; 469130561Sobrien unsigned reg1, reg2; 470130561Sobrien 471218822Sdim if (frchain_now->frch_cfi_data == NULL) 472130561Sobrien { 473130561Sobrien as_bad (_("CFI instruction used without previous .cfi_startproc")); 474218822Sdim ignore_rest_of_line (); 475130561Sobrien return; 476130561Sobrien } 477130561Sobrien 478130561Sobrien /* If the last address was not at the current PC, advance to current. */ 479218822Sdim if (symbol_get_frag (frchain_now->frch_cfi_data->last_address) != frag_now 480218822Sdim || S_GET_VALUE (frchain_now->frch_cfi_data->last_address) 481218822Sdim != frag_now_fix ()) 482130561Sobrien cfi_add_advance_loc (symbol_temp_new_now ()); 483130561Sobrien 484130561Sobrien switch (arg) 485130561Sobrien { 486130561Sobrien case DW_CFA_offset: 487130561Sobrien reg1 = cfi_parse_reg (); 488130561Sobrien cfi_parse_separator (); 489130561Sobrien offset = cfi_parse_const (); 490130561Sobrien cfi_add_CFA_offset (reg1, offset); 491130561Sobrien break; 492130561Sobrien 493130561Sobrien case CFI_rel_offset: 494130561Sobrien reg1 = cfi_parse_reg (); 495130561Sobrien cfi_parse_separator (); 496130561Sobrien offset = cfi_parse_const (); 497218822Sdim cfi_add_CFA_offset (reg1, 498218822Sdim offset - frchain_now->frch_cfi_data->cur_cfa_offset); 499130561Sobrien break; 500130561Sobrien 501130561Sobrien case DW_CFA_def_cfa: 502130561Sobrien reg1 = cfi_parse_reg (); 503130561Sobrien cfi_parse_separator (); 504130561Sobrien offset = cfi_parse_const (); 505130561Sobrien cfi_add_CFA_def_cfa (reg1, offset); 506130561Sobrien break; 507130561Sobrien 508130561Sobrien case DW_CFA_register: 509130561Sobrien reg1 = cfi_parse_reg (); 510130561Sobrien cfi_parse_separator (); 511130561Sobrien reg2 = cfi_parse_reg (); 512130561Sobrien cfi_add_CFA_register (reg1, reg2); 513130561Sobrien break; 514130561Sobrien 515130561Sobrien case DW_CFA_def_cfa_register: 516130561Sobrien reg1 = cfi_parse_reg (); 517130561Sobrien cfi_add_CFA_def_cfa_register (reg1); 518130561Sobrien break; 519130561Sobrien 520130561Sobrien case DW_CFA_def_cfa_offset: 521130561Sobrien offset = cfi_parse_const (); 522130561Sobrien cfi_add_CFA_def_cfa_offset (offset); 523130561Sobrien break; 524130561Sobrien 525130561Sobrien case CFI_adjust_cfa_offset: 526130561Sobrien offset = cfi_parse_const (); 527218822Sdim cfi_add_CFA_def_cfa_offset (frchain_now->frch_cfi_data->cur_cfa_offset 528218822Sdim + offset); 529130561Sobrien break; 530130561Sobrien 531130561Sobrien case DW_CFA_restore: 532218822Sdim for (;;) 533218822Sdim { 534218822Sdim reg1 = cfi_parse_reg (); 535218822Sdim cfi_add_CFA_restore (reg1); 536218822Sdim SKIP_WHITESPACE (); 537218822Sdim if (*input_line_pointer != ',') 538218822Sdim break; 539218822Sdim ++input_line_pointer; 540218822Sdim } 541130561Sobrien break; 542130561Sobrien 543130561Sobrien case DW_CFA_undefined: 544218822Sdim for (;;) 545218822Sdim { 546218822Sdim reg1 = cfi_parse_reg (); 547218822Sdim cfi_add_CFA_undefined (reg1); 548218822Sdim SKIP_WHITESPACE (); 549218822Sdim if (*input_line_pointer != ',') 550218822Sdim break; 551218822Sdim ++input_line_pointer; 552218822Sdim } 553130561Sobrien break; 554130561Sobrien 555130561Sobrien case DW_CFA_same_value: 556130561Sobrien reg1 = cfi_parse_reg (); 557130561Sobrien cfi_add_CFA_same_value (reg1); 558130561Sobrien break; 559130561Sobrien 560130561Sobrien case CFI_return_column: 561130561Sobrien reg1 = cfi_parse_reg (); 562130561Sobrien cfi_set_return_column (reg1); 563130561Sobrien break; 564130561Sobrien 565130561Sobrien case DW_CFA_remember_state: 566130561Sobrien cfi_add_CFA_remember_state (); 567130561Sobrien break; 568130561Sobrien 569130561Sobrien case DW_CFA_restore_state: 570130561Sobrien cfi_add_CFA_restore_state (); 571130561Sobrien break; 572130561Sobrien 573130561Sobrien case DW_CFA_GNU_window_save: 574130561Sobrien cfi_add_CFA_insn (DW_CFA_GNU_window_save); 575130561Sobrien break; 576130561Sobrien 577218822Sdim case CFI_signal_frame: 578218822Sdim frchain_now->frch_cfi_data->cur_fde_data->signal_frame = 1; 579218822Sdim break; 580218822Sdim 581130561Sobrien default: 582130561Sobrien abort (); 583130561Sobrien } 584130561Sobrien 585130561Sobrien demand_empty_rest_of_line (); 586130561Sobrien} 587130561Sobrien 588130561Sobrienstatic void 589130561Sobriendot_cfi_escape (int ignored ATTRIBUTE_UNUSED) 590130561Sobrien{ 591130561Sobrien struct cfi_escape_data *head, **tail, *e; 592130561Sobrien struct cfi_insn_data *insn; 593130561Sobrien 594218822Sdim if (frchain_now->frch_cfi_data == NULL) 595130561Sobrien { 596130561Sobrien as_bad (_("CFI instruction used without previous .cfi_startproc")); 597218822Sdim ignore_rest_of_line (); 598130561Sobrien return; 599130561Sobrien } 600130561Sobrien 601130561Sobrien /* If the last address was not at the current PC, advance to current. */ 602218822Sdim if (symbol_get_frag (frchain_now->frch_cfi_data->last_address) != frag_now 603218822Sdim || S_GET_VALUE (frchain_now->frch_cfi_data->last_address) 604218822Sdim != frag_now_fix ()) 605130561Sobrien cfi_add_advance_loc (symbol_temp_new_now ()); 606130561Sobrien 607130561Sobrien tail = &head; 608130561Sobrien do 609130561Sobrien { 610130561Sobrien e = xmalloc (sizeof (*e)); 611130561Sobrien do_parse_cons_expression (&e->exp, 1); 612130561Sobrien *tail = e; 613130561Sobrien tail = &e->next; 614130561Sobrien } 615130561Sobrien while (*input_line_pointer++ == ','); 616130561Sobrien *tail = NULL; 617130561Sobrien 618130561Sobrien insn = alloc_cfi_insn_data (); 619130561Sobrien insn->insn = CFI_escape; 620130561Sobrien insn->u.esc = head; 621218822Sdim 622218822Sdim --input_line_pointer; 623218822Sdim demand_empty_rest_of_line (); 624130561Sobrien} 625130561Sobrien 626130561Sobrienstatic void 627218822Sdimdot_cfi_personality (int ignored ATTRIBUTE_UNUSED) 628218822Sdim{ 629218822Sdim struct fde_entry *fde; 630218822Sdim offsetT encoding; 631218822Sdim 632218822Sdim if (frchain_now->frch_cfi_data == NULL) 633218822Sdim { 634218822Sdim as_bad (_("CFI instruction used without previous .cfi_startproc")); 635218822Sdim ignore_rest_of_line (); 636218822Sdim return; 637218822Sdim } 638218822Sdim 639218822Sdim fde = frchain_now->frch_cfi_data->cur_fde_data; 640218822Sdim encoding = get_absolute_expression (); 641218822Sdim if (encoding == DW_EH_PE_omit) 642218822Sdim { 643218822Sdim demand_empty_rest_of_line (); 644218822Sdim fde->per_encoding = encoding; 645218822Sdim return; 646218822Sdim } 647218822Sdim 648218822Sdim if ((encoding & 0xff) != encoding 649218822Sdim || ((encoding & 0x70) != 0 650218822Sdim#if defined DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr 651218822Sdim && (encoding & 0x70) != DW_EH_PE_pcrel 652218822Sdim#endif 653218822Sdim ) 654218822Sdim /* leb128 can be handled, but does something actually need it? */ 655218822Sdim || (encoding & 7) == DW_EH_PE_uleb128 656218822Sdim || (encoding & 7) > DW_EH_PE_udata8) 657218822Sdim { 658218822Sdim as_bad (_("invalid or unsupported encoding in .cfi_personality")); 659218822Sdim ignore_rest_of_line (); 660218822Sdim return; 661218822Sdim } 662218822Sdim 663218822Sdim if (*input_line_pointer++ != ',') 664218822Sdim { 665218822Sdim as_bad (_(".cfi_personality requires encoding and symbol arguments")); 666218822Sdim ignore_rest_of_line (); 667218822Sdim return; 668218822Sdim } 669218822Sdim 670218822Sdim expression_and_evaluate (&fde->personality); 671218822Sdim switch (fde->personality.X_op) 672218822Sdim { 673218822Sdim case O_symbol: 674218822Sdim break; 675218822Sdim case O_constant: 676218822Sdim if ((encoding & 0x70) == DW_EH_PE_pcrel) 677218822Sdim encoding = DW_EH_PE_omit; 678218822Sdim break; 679218822Sdim default: 680218822Sdim encoding = DW_EH_PE_omit; 681218822Sdim break; 682218822Sdim } 683218822Sdim 684218822Sdim fde->per_encoding = encoding; 685218822Sdim 686218822Sdim if (encoding == DW_EH_PE_omit) 687218822Sdim { 688218822Sdim as_bad (_("wrong second argument to .cfi_personality")); 689218822Sdim ignore_rest_of_line (); 690218822Sdim return; 691218822Sdim } 692218822Sdim 693218822Sdim demand_empty_rest_of_line (); 694218822Sdim} 695218822Sdim 696218822Sdimstatic void 697218822Sdimdot_cfi_lsda (int ignored ATTRIBUTE_UNUSED) 698218822Sdim{ 699218822Sdim struct fde_entry *fde; 700218822Sdim offsetT encoding; 701218822Sdim 702218822Sdim if (frchain_now->frch_cfi_data == NULL) 703218822Sdim { 704218822Sdim as_bad (_("CFI instruction used without previous .cfi_startproc")); 705218822Sdim ignore_rest_of_line (); 706218822Sdim return; 707218822Sdim } 708218822Sdim 709218822Sdim fde = frchain_now->frch_cfi_data->cur_fde_data; 710218822Sdim encoding = get_absolute_expression (); 711218822Sdim if (encoding == DW_EH_PE_omit) 712218822Sdim { 713218822Sdim demand_empty_rest_of_line (); 714218822Sdim fde->lsda_encoding = encoding; 715218822Sdim return; 716218822Sdim } 717218822Sdim 718218822Sdim if ((encoding & 0xff) != encoding 719218822Sdim || ((encoding & 0x70) != 0 720218822Sdim#if defined DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr 721218822Sdim && (encoding & 0x70) != DW_EH_PE_pcrel 722218822Sdim#endif 723218822Sdim ) 724218822Sdim /* leb128 can be handled, but does something actually need it? */ 725218822Sdim || (encoding & 7) == DW_EH_PE_uleb128 726218822Sdim || (encoding & 7) > DW_EH_PE_udata8) 727218822Sdim { 728218822Sdim as_bad (_("invalid or unsupported encoding in .cfi_lsda")); 729218822Sdim ignore_rest_of_line (); 730218822Sdim return; 731218822Sdim } 732218822Sdim 733218822Sdim if (*input_line_pointer++ != ',') 734218822Sdim { 735218822Sdim as_bad (_(".cfi_lsda requires encoding and symbol arguments")); 736218822Sdim ignore_rest_of_line (); 737218822Sdim return; 738218822Sdim } 739218822Sdim 740218822Sdim fde->lsda_encoding = encoding; 741218822Sdim 742218822Sdim expression_and_evaluate (&fde->lsda); 743218822Sdim switch (fde->lsda.X_op) 744218822Sdim { 745218822Sdim case O_symbol: 746218822Sdim break; 747218822Sdim case O_constant: 748218822Sdim if ((encoding & 0x70) == DW_EH_PE_pcrel) 749218822Sdim encoding = DW_EH_PE_omit; 750218822Sdim break; 751218822Sdim default: 752218822Sdim encoding = DW_EH_PE_omit; 753218822Sdim break; 754218822Sdim } 755218822Sdim 756218822Sdim fde->lsda_encoding = encoding; 757218822Sdim 758218822Sdim if (encoding == DW_EH_PE_omit) 759218822Sdim { 760218822Sdim as_bad (_("wrong second argument to .cfi_lsda")); 761218822Sdim ignore_rest_of_line (); 762218822Sdim return; 763218822Sdim } 764218822Sdim 765218822Sdim demand_empty_rest_of_line (); 766218822Sdim} 767218822Sdim 768218822Sdimstatic void 769130561Sobriendot_cfi_startproc (int ignored ATTRIBUTE_UNUSED) 770130561Sobrien{ 771130561Sobrien int simple = 0; 772130561Sobrien 773218822Sdim if (frchain_now->frch_cfi_data != NULL) 774130561Sobrien { 775130561Sobrien as_bad (_("previous CFI entry not closed (missing .cfi_endproc)")); 776218822Sdim ignore_rest_of_line (); 777130561Sobrien return; 778130561Sobrien } 779130561Sobrien 780130561Sobrien cfi_new_fde (symbol_temp_new_now ()); 781130561Sobrien 782130561Sobrien SKIP_WHITESPACE (); 783130561Sobrien if (is_name_beginner (*input_line_pointer)) 784130561Sobrien { 785130561Sobrien char *name, c; 786130561Sobrien 787130561Sobrien name = input_line_pointer; 788130561Sobrien c = get_symbol_end (); 789130561Sobrien 790130561Sobrien if (strcmp (name, "simple") == 0) 791130561Sobrien { 792130561Sobrien simple = 1; 793130561Sobrien *input_line_pointer = c; 794130561Sobrien } 795130561Sobrien else 796130561Sobrien input_line_pointer = name; 797130561Sobrien } 798130561Sobrien demand_empty_rest_of_line (); 799130561Sobrien 800218822Sdim frchain_now->frch_cfi_data->cur_cfa_offset = 0; 801130561Sobrien if (!simple) 802130561Sobrien tc_cfi_frame_initial_instructions (); 803130561Sobrien} 804130561Sobrien 805130561Sobrienstatic void 806130561Sobriendot_cfi_endproc (int ignored ATTRIBUTE_UNUSED) 807130561Sobrien{ 808218822Sdim if (frchain_now->frch_cfi_data == NULL) 809130561Sobrien { 810130561Sobrien as_bad (_(".cfi_endproc without corresponding .cfi_startproc")); 811218822Sdim ignore_rest_of_line (); 812130561Sobrien return; 813130561Sobrien } 814130561Sobrien 815130561Sobrien cfi_end_fde (symbol_temp_new_now ()); 816218822Sdim 817218822Sdim demand_empty_rest_of_line (); 818130561Sobrien} 819130561Sobrien 820130561Sobrien 821130561Sobrien/* Emit a single byte into the current segment. */ 822130561Sobrien 823130561Sobrienstatic inline void 824130561Sobrienout_one (int byte) 825130561Sobrien{ 826130561Sobrien FRAG_APPEND_1_CHAR (byte); 827130561Sobrien} 828130561Sobrien 829130561Sobrien/* Emit a two-byte word into the current segment. */ 830130561Sobrien 831130561Sobrienstatic inline void 832130561Sobrienout_two (int data) 833130561Sobrien{ 834130561Sobrien md_number_to_chars (frag_more (2), data, 2); 835130561Sobrien} 836130561Sobrien 837130561Sobrien/* Emit a four byte word into the current segment. */ 838130561Sobrien 839130561Sobrienstatic inline void 840130561Sobrienout_four (int data) 841130561Sobrien{ 842130561Sobrien md_number_to_chars (frag_more (4), data, 4); 843130561Sobrien} 844130561Sobrien 845130561Sobrien/* Emit an unsigned "little-endian base 128" number. */ 846130561Sobrien 847130561Sobrienstatic void 848130561Sobrienout_uleb128 (addressT value) 849130561Sobrien{ 850130561Sobrien output_leb128 (frag_more (sizeof_leb128 (value, 0)), value, 0); 851130561Sobrien} 852130561Sobrien 853130561Sobrien/* Emit an unsigned "little-endian base 128" number. */ 854130561Sobrien 855130561Sobrienstatic void 856130561Sobrienout_sleb128 (offsetT value) 857130561Sobrien{ 858130561Sobrien output_leb128 (frag_more (sizeof_leb128 (value, 1)), value, 1); 859130561Sobrien} 860130561Sobrien 861130561Sobrienstatic void 862130561Sobrienoutput_cfi_insn (struct cfi_insn_data *insn) 863130561Sobrien{ 864130561Sobrien offsetT offset; 865130561Sobrien unsigned int regno; 866130561Sobrien 867130561Sobrien switch (insn->insn) 868130561Sobrien { 869130561Sobrien case DW_CFA_advance_loc: 870130561Sobrien { 871130561Sobrien symbolS *from = insn->u.ll.lab1; 872130561Sobrien symbolS *to = insn->u.ll.lab2; 873130561Sobrien 874130561Sobrien if (symbol_get_frag (to) == symbol_get_frag (from)) 875130561Sobrien { 876130561Sobrien addressT delta = S_GET_VALUE (to) - S_GET_VALUE (from); 877130561Sobrien addressT scaled = delta / DWARF2_LINE_MIN_INSN_LENGTH; 878130561Sobrien 879130561Sobrien if (scaled <= 0x3F) 880130561Sobrien out_one (DW_CFA_advance_loc + scaled); 881130561Sobrien else if (delta <= 0xFF) 882130561Sobrien { 883218822Sdim out_one (DW_CFA_advance_loc1); 884218822Sdim out_one (delta); 885130561Sobrien } 886130561Sobrien else if (delta <= 0xFFFF) 887130561Sobrien { 888218822Sdim out_one (DW_CFA_advance_loc2); 889218822Sdim out_two (delta); 890130561Sobrien } 891130561Sobrien else 892130561Sobrien { 893218822Sdim out_one (DW_CFA_advance_loc4); 894218822Sdim out_four (delta); 895130561Sobrien } 896130561Sobrien } 897130561Sobrien else 898130561Sobrien { 899130561Sobrien expressionS exp; 900130561Sobrien 901130561Sobrien exp.X_op = O_subtract; 902130561Sobrien exp.X_add_symbol = to; 903130561Sobrien exp.X_op_symbol = from; 904130561Sobrien exp.X_add_number = 0; 905130561Sobrien 906130561Sobrien /* The code in ehopt.c expects that one byte of the encoding 907130561Sobrien is already allocated to the frag. This comes from the way 908130561Sobrien that it scans the .eh_frame section looking first for the 909130561Sobrien .byte DW_CFA_advance_loc4. */ 910130561Sobrien frag_more (1); 911130561Sobrien 912130561Sobrien frag_var (rs_cfa, 4, 0, DWARF2_LINE_MIN_INSN_LENGTH << 3, 913130561Sobrien make_expr_symbol (&exp), frag_now_fix () - 1, 914130561Sobrien (char *) frag_now); 915130561Sobrien } 916130561Sobrien } 917130561Sobrien break; 918130561Sobrien 919130561Sobrien case DW_CFA_def_cfa: 920130561Sobrien offset = insn->u.ri.offset; 921130561Sobrien if (offset < 0) 922130561Sobrien { 923130561Sobrien out_one (DW_CFA_def_cfa_sf); 924130561Sobrien out_uleb128 (insn->u.ri.reg); 925218822Sdim out_sleb128 (offset / DWARF2_CIE_DATA_ALIGNMENT); 926130561Sobrien } 927130561Sobrien else 928130561Sobrien { 929130561Sobrien out_one (DW_CFA_def_cfa); 930130561Sobrien out_uleb128 (insn->u.ri.reg); 931130561Sobrien out_uleb128 (offset); 932130561Sobrien } 933130561Sobrien break; 934130561Sobrien 935130561Sobrien case DW_CFA_def_cfa_register: 936130561Sobrien case DW_CFA_undefined: 937130561Sobrien case DW_CFA_same_value: 938130561Sobrien out_one (insn->insn); 939130561Sobrien out_uleb128 (insn->u.r); 940130561Sobrien break; 941130561Sobrien 942130561Sobrien case DW_CFA_def_cfa_offset: 943130561Sobrien offset = insn->u.i; 944130561Sobrien if (offset < 0) 945130561Sobrien { 946130561Sobrien out_one (DW_CFA_def_cfa_offset_sf); 947218822Sdim out_sleb128 (offset / DWARF2_CIE_DATA_ALIGNMENT); 948130561Sobrien } 949130561Sobrien else 950130561Sobrien { 951130561Sobrien out_one (DW_CFA_def_cfa_offset); 952130561Sobrien out_uleb128 (offset); 953130561Sobrien } 954130561Sobrien break; 955130561Sobrien 956130561Sobrien case DW_CFA_restore: 957130561Sobrien regno = insn->u.r; 958130561Sobrien if (regno <= 0x3F) 959130561Sobrien { 960130561Sobrien out_one (DW_CFA_restore + regno); 961130561Sobrien } 962130561Sobrien else 963130561Sobrien { 964130561Sobrien out_one (DW_CFA_restore_extended); 965130561Sobrien out_uleb128 (regno); 966130561Sobrien } 967130561Sobrien break; 968130561Sobrien 969130561Sobrien case DW_CFA_offset: 970130561Sobrien regno = insn->u.ri.reg; 971130561Sobrien offset = insn->u.ri.offset / DWARF2_CIE_DATA_ALIGNMENT; 972130561Sobrien if (offset < 0) 973130561Sobrien { 974130561Sobrien out_one (DW_CFA_offset_extended_sf); 975130561Sobrien out_uleb128 (regno); 976130561Sobrien out_sleb128 (offset); 977130561Sobrien } 978130561Sobrien else if (regno <= 0x3F) 979130561Sobrien { 980130561Sobrien out_one (DW_CFA_offset + regno); 981130561Sobrien out_uleb128 (offset); 982130561Sobrien } 983130561Sobrien else 984130561Sobrien { 985130561Sobrien out_one (DW_CFA_offset_extended); 986130561Sobrien out_uleb128 (regno); 987130561Sobrien out_uleb128 (offset); 988130561Sobrien } 989130561Sobrien break; 990130561Sobrien 991130561Sobrien case DW_CFA_register: 992130561Sobrien out_one (DW_CFA_register); 993130561Sobrien out_uleb128 (insn->u.rr.reg1); 994130561Sobrien out_uleb128 (insn->u.rr.reg2); 995130561Sobrien break; 996130561Sobrien 997130561Sobrien case DW_CFA_remember_state: 998130561Sobrien case DW_CFA_restore_state: 999130561Sobrien out_one (insn->insn); 1000130561Sobrien break; 1001130561Sobrien 1002130561Sobrien case DW_CFA_GNU_window_save: 1003130561Sobrien out_one (DW_CFA_GNU_window_save); 1004130561Sobrien break; 1005130561Sobrien 1006130561Sobrien case CFI_escape: 1007130561Sobrien { 1008130561Sobrien struct cfi_escape_data *e; 1009130561Sobrien for (e = insn->u.esc; e ; e = e->next) 1010130561Sobrien emit_expr (&e->exp, 1); 1011130561Sobrien break; 1012130561Sobrien } 1013130561Sobrien 1014130561Sobrien default: 1015130561Sobrien abort (); 1016130561Sobrien } 1017130561Sobrien} 1018130561Sobrien 1019218822Sdimstatic offsetT 1020218822Sdimencoding_size (unsigned char encoding) 1021218822Sdim{ 1022218822Sdim if (encoding == DW_EH_PE_omit) 1023218822Sdim return 0; 1024218822Sdim switch (encoding & 0x7) 1025218822Sdim { 1026218822Sdim case 0: 1027218822Sdim return bfd_get_arch_size (stdoutput) == 64 ? 8 : 4; 1028218822Sdim case DW_EH_PE_udata2: 1029218822Sdim return 2; 1030218822Sdim case DW_EH_PE_udata4: 1031218822Sdim return 4; 1032218822Sdim case DW_EH_PE_udata8: 1033218822Sdim return 8; 1034218822Sdim default: 1035218822Sdim abort (); 1036218822Sdim } 1037218822Sdim} 1038218822Sdim 1039130561Sobrienstatic void 1040130561Sobrienoutput_cie (struct cie_entry *cie) 1041130561Sobrien{ 1042130561Sobrien symbolS *after_size_address, *end_address; 1043130561Sobrien expressionS exp; 1044130561Sobrien struct cfi_insn_data *i; 1045218822Sdim offsetT augmentation_size; 1046130561Sobrien 1047130561Sobrien cie->start_address = symbol_temp_new_now (); 1048130561Sobrien after_size_address = symbol_temp_make (); 1049130561Sobrien end_address = symbol_temp_make (); 1050130561Sobrien 1051130561Sobrien exp.X_op = O_subtract; 1052130561Sobrien exp.X_add_symbol = end_address; 1053130561Sobrien exp.X_op_symbol = after_size_address; 1054130561Sobrien exp.X_add_number = 0; 1055130561Sobrien 1056218822Sdim emit_expr (&exp, 4); /* Length. */ 1057130561Sobrien symbol_set_value_now (after_size_address); 1058218822Sdim out_four (0); /* CIE id. */ 1059218822Sdim out_one (DW_CIE_VERSION); /* Version. */ 1060218822Sdim out_one ('z'); /* Augmentation. */ 1061218822Sdim if (cie->per_encoding != DW_EH_PE_omit) 1062218822Sdim out_one ('P'); 1063218822Sdim if (cie->lsda_encoding != DW_EH_PE_omit) 1064218822Sdim out_one ('L'); 1065130561Sobrien out_one ('R'); 1066218822Sdim if (cie->signal_frame) 1067218822Sdim out_one ('S'); 1068130561Sobrien out_one (0); 1069218822Sdim out_uleb128 (DWARF2_LINE_MIN_INSN_LENGTH); /* Code alignment. */ 1070218822Sdim out_sleb128 (DWARF2_CIE_DATA_ALIGNMENT); /* Data alignment. */ 1071218822Sdim if (DW_CIE_VERSION == 1) /* Return column. */ 1072218822Sdim out_one (cie->return_column); 1073218822Sdim else 1074218822Sdim out_uleb128 (cie->return_column); 1075218822Sdim augmentation_size = 1 + (cie->lsda_encoding != DW_EH_PE_omit); 1076218822Sdim if (cie->per_encoding != DW_EH_PE_omit) 1077218822Sdim augmentation_size += 1 + encoding_size (cie->per_encoding); 1078218822Sdim out_uleb128 (augmentation_size); /* Augmentation size. */ 1079218822Sdim if (cie->per_encoding != DW_EH_PE_omit) 1080218822Sdim { 1081218822Sdim offsetT size = encoding_size (cie->per_encoding); 1082218822Sdim out_one (cie->per_encoding); 1083218822Sdim exp = cie->personality; 1084218822Sdim if ((cie->per_encoding & 0x70) == DW_EH_PE_pcrel) 1085218822Sdim { 1086218822Sdim#ifdef DIFF_EXPR_OK 1087218822Sdim exp.X_op = O_subtract; 1088218822Sdim exp.X_op_symbol = symbol_temp_new_now (); 1089218822Sdim emit_expr (&exp, size); 1090218822Sdim#elif defined (tc_cfi_emit_pcrel_expr) 1091218822Sdim tc_cfi_emit_pcrel_expr (&exp, size); 1092218822Sdim#else 1093218822Sdim abort (); 1094218822Sdim#endif 1095218822Sdim } 1096218822Sdim else 1097218822Sdim emit_expr (&exp, size); 1098218822Sdim } 1099218822Sdim if (cie->lsda_encoding != DW_EH_PE_omit) 1100218822Sdim out_one (cie->lsda_encoding); 1101130561Sobrien#if defined DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr 1102130561Sobrien out_one (DW_EH_PE_pcrel | DW_EH_PE_sdata4); 1103130561Sobrien#else 1104130561Sobrien out_one (DW_EH_PE_sdata4); 1105130561Sobrien#endif 1106130561Sobrien 1107130561Sobrien if (cie->first) 1108130561Sobrien for (i = cie->first; i != cie->last; i = i->next) 1109130561Sobrien output_cfi_insn (i); 1110130561Sobrien 1111218822Sdim frag_align (2, DW_CFA_nop, 0); 1112130561Sobrien symbol_set_value_now (end_address); 1113130561Sobrien} 1114130561Sobrien 1115130561Sobrienstatic void 1116130561Sobrienoutput_fde (struct fde_entry *fde, struct cie_entry *cie, 1117130561Sobrien struct cfi_insn_data *first, int align) 1118130561Sobrien{ 1119130561Sobrien symbolS *after_size_address, *end_address; 1120130561Sobrien expressionS exp; 1121218822Sdim offsetT augmentation_size; 1122130561Sobrien 1123130561Sobrien after_size_address = symbol_temp_make (); 1124130561Sobrien end_address = symbol_temp_make (); 1125130561Sobrien 1126130561Sobrien exp.X_op = O_subtract; 1127130561Sobrien exp.X_add_symbol = end_address; 1128130561Sobrien exp.X_op_symbol = after_size_address; 1129130561Sobrien exp.X_add_number = 0; 1130218822Sdim emit_expr (&exp, 4); /* Length. */ 1131130561Sobrien symbol_set_value_now (after_size_address); 1132130561Sobrien 1133130561Sobrien exp.X_add_symbol = after_size_address; 1134130561Sobrien exp.X_op_symbol = cie->start_address; 1135218822Sdim emit_expr (&exp, 4); /* CIE offset. */ 1136130561Sobrien 1137218822Sdim#ifdef DIFF_EXPR_OK 1138130561Sobrien exp.X_add_symbol = fde->start_address; 1139130561Sobrien exp.X_op_symbol = symbol_temp_new_now (); 1140218822Sdim emit_expr (&exp, 4); /* Code offset. */ 1141130561Sobrien#else 1142130561Sobrien exp.X_op = O_symbol; 1143130561Sobrien exp.X_add_symbol = fde->start_address; 1144130561Sobrien exp.X_op_symbol = NULL; 1145130561Sobrien#ifdef tc_cfi_emit_pcrel_expr 1146218822Sdim tc_cfi_emit_pcrel_expr (&exp, 4); /* Code offset. */ 1147130561Sobrien#else 1148218822Sdim emit_expr (&exp, 4); /* Code offset. */ 1149130561Sobrien#endif 1150130561Sobrien exp.X_op = O_subtract; 1151130561Sobrien#endif 1152130561Sobrien 1153130561Sobrien exp.X_add_symbol = fde->end_address; 1154218822Sdim exp.X_op_symbol = fde->start_address; /* Code length. */ 1155130561Sobrien emit_expr (&exp, 4); 1156130561Sobrien 1157218822Sdim augmentation_size = encoding_size (fde->lsda_encoding); 1158218822Sdim out_uleb128 (augmentation_size); /* Augmentation size. */ 1159130561Sobrien 1160218822Sdim if (fde->lsda_encoding != DW_EH_PE_omit) 1161218822Sdim { 1162218822Sdim exp = fde->lsda; 1163218822Sdim if ((fde->lsda_encoding & 0x70) == DW_EH_PE_pcrel) 1164218822Sdim { 1165218822Sdim#ifdef DIFF_EXPR_OK 1166218822Sdim exp.X_op = O_subtract; 1167218822Sdim exp.X_op_symbol = symbol_temp_new_now (); 1168218822Sdim emit_expr (&exp, augmentation_size); 1169218822Sdim#elif defined (tc_cfi_emit_pcrel_expr) 1170218822Sdim tc_cfi_emit_pcrel_expr (&exp, augmentation_size); 1171218822Sdim#else 1172218822Sdim abort (); 1173218822Sdim#endif 1174218822Sdim } 1175218822Sdim else 1176218822Sdim emit_expr (&exp, augmentation_size); 1177218822Sdim } 1178218822Sdim 1179130561Sobrien for (; first; first = first->next) 1180130561Sobrien output_cfi_insn (first); 1181130561Sobrien 1182218822Sdim frag_align (align, DW_CFA_nop, 0); 1183130561Sobrien symbol_set_value_now (end_address); 1184130561Sobrien} 1185130561Sobrien 1186130561Sobrienstatic struct cie_entry * 1187130561Sobrienselect_cie_for_fde (struct fde_entry *fde, struct cfi_insn_data **pfirst) 1188130561Sobrien{ 1189130561Sobrien struct cfi_insn_data *i, *j; 1190130561Sobrien struct cie_entry *cie; 1191130561Sobrien 1192130561Sobrien for (cie = cie_root; cie; cie = cie->next) 1193130561Sobrien { 1194218822Sdim if (cie->return_column != fde->return_column 1195218822Sdim || cie->signal_frame != fde->signal_frame 1196218822Sdim || cie->per_encoding != fde->per_encoding 1197218822Sdim || cie->lsda_encoding != fde->lsda_encoding) 1198130561Sobrien continue; 1199218822Sdim if (cie->per_encoding != DW_EH_PE_omit) 1200218822Sdim { 1201218822Sdim if (cie->personality.X_op != fde->personality.X_op 1202218822Sdim || cie->personality.X_add_number 1203218822Sdim != fde->personality.X_add_number) 1204218822Sdim continue; 1205218822Sdim switch (cie->personality.X_op) 1206218822Sdim { 1207218822Sdim case O_constant: 1208218822Sdim if (cie->personality.X_unsigned != fde->personality.X_unsigned) 1209218822Sdim continue; 1210218822Sdim break; 1211218822Sdim case O_symbol: 1212218822Sdim if (cie->personality.X_add_symbol 1213218822Sdim != fde->personality.X_add_symbol) 1214218822Sdim continue; 1215218822Sdim break; 1216218822Sdim default: 1217218822Sdim abort (); 1218218822Sdim } 1219218822Sdim } 1220130561Sobrien for (i = cie->first, j = fde->data; 1221130561Sobrien i != cie->last && j != NULL; 1222130561Sobrien i = i->next, j = j->next) 1223130561Sobrien { 1224130561Sobrien if (i->insn != j->insn) 1225130561Sobrien goto fail; 1226130561Sobrien switch (i->insn) 1227130561Sobrien { 1228130561Sobrien case DW_CFA_advance_loc: 1229218822Sdim case DW_CFA_remember_state: 1230218822Sdim /* We reached the first advance/remember in the FDE, 1231218822Sdim but did not reach the end of the CIE list. */ 1232130561Sobrien goto fail; 1233130561Sobrien 1234130561Sobrien case DW_CFA_offset: 1235130561Sobrien case DW_CFA_def_cfa: 1236130561Sobrien if (i->u.ri.reg != j->u.ri.reg) 1237130561Sobrien goto fail; 1238130561Sobrien if (i->u.ri.offset != j->u.ri.offset) 1239130561Sobrien goto fail; 1240130561Sobrien break; 1241130561Sobrien 1242130561Sobrien case DW_CFA_register: 1243130561Sobrien if (i->u.rr.reg1 != j->u.rr.reg1) 1244130561Sobrien goto fail; 1245130561Sobrien if (i->u.rr.reg2 != j->u.rr.reg2) 1246130561Sobrien goto fail; 1247130561Sobrien break; 1248130561Sobrien 1249130561Sobrien case DW_CFA_def_cfa_register: 1250130561Sobrien case DW_CFA_restore: 1251130561Sobrien case DW_CFA_undefined: 1252130561Sobrien case DW_CFA_same_value: 1253130561Sobrien if (i->u.r != j->u.r) 1254130561Sobrien goto fail; 1255130561Sobrien break; 1256130561Sobrien 1257130561Sobrien case DW_CFA_def_cfa_offset: 1258130561Sobrien if (i->u.i != j->u.i) 1259130561Sobrien goto fail; 1260130561Sobrien break; 1261130561Sobrien 1262130561Sobrien case CFI_escape: 1263130561Sobrien /* Don't bother matching these for now. */ 1264130561Sobrien goto fail; 1265130561Sobrien 1266130561Sobrien default: 1267130561Sobrien abort (); 1268130561Sobrien } 1269130561Sobrien } 1270130561Sobrien 1271130561Sobrien /* Success if we reached the end of the CIE list, and we've either 1272218822Sdim run out of FDE entries or we've encountered an advance, 1273218822Sdim remember, or escape. */ 1274218822Sdim if (i == cie->last 1275218822Sdim && (!j 1276218822Sdim || j->insn == DW_CFA_advance_loc 1277218822Sdim || j->insn == DW_CFA_remember_state 1278218822Sdim || j->insn == CFI_escape)) 1279130561Sobrien { 1280130561Sobrien *pfirst = j; 1281130561Sobrien return cie; 1282130561Sobrien } 1283130561Sobrien 1284130561Sobrien fail:; 1285130561Sobrien } 1286130561Sobrien 1287130561Sobrien cie = xmalloc (sizeof (struct cie_entry)); 1288130561Sobrien cie->next = cie_root; 1289130561Sobrien cie_root = cie; 1290130561Sobrien cie->return_column = fde->return_column; 1291218822Sdim cie->signal_frame = fde->signal_frame; 1292218822Sdim cie->per_encoding = fde->per_encoding; 1293218822Sdim cie->lsda_encoding = fde->lsda_encoding; 1294218822Sdim cie->personality = fde->personality; 1295130561Sobrien cie->first = fde->data; 1296130561Sobrien 1297130561Sobrien for (i = cie->first; i ; i = i->next) 1298218822Sdim if (i->insn == DW_CFA_advance_loc 1299218822Sdim || i->insn == DW_CFA_remember_state 1300218822Sdim || i->insn == CFI_escape) 1301130561Sobrien break; 1302130561Sobrien 1303130561Sobrien cie->last = i; 1304130561Sobrien *pfirst = i; 1305130561Sobrien 1306130561Sobrien output_cie (cie); 1307130561Sobrien 1308130561Sobrien return cie; 1309130561Sobrien} 1310130561Sobrien 1311130561Sobrienvoid 1312130561Sobriencfi_finish (void) 1313130561Sobrien{ 1314130561Sobrien segT cfi_seg; 1315130561Sobrien struct fde_entry *fde; 1316130561Sobrien int save_flag_traditional_format; 1317130561Sobrien 1318130561Sobrien if (all_fde_data == 0) 1319130561Sobrien return; 1320130561Sobrien 1321130561Sobrien /* Open .eh_frame section. */ 1322130561Sobrien cfi_seg = subseg_new (".eh_frame", 0); 1323130561Sobrien bfd_set_section_flags (stdoutput, cfi_seg, 1324130561Sobrien SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_READONLY); 1325130561Sobrien subseg_set (cfi_seg, 0); 1326130561Sobrien record_alignment (cfi_seg, EH_FRAME_ALIGNMENT); 1327130561Sobrien 1328130561Sobrien /* Make sure check_eh_frame doesn't do anything with our output. */ 1329130561Sobrien save_flag_traditional_format = flag_traditional_format; 1330130561Sobrien flag_traditional_format = 1; 1331130561Sobrien 1332130561Sobrien for (fde = all_fde_data; fde ; fde = fde->next) 1333130561Sobrien { 1334130561Sobrien struct cfi_insn_data *first; 1335130561Sobrien struct cie_entry *cie; 1336130561Sobrien 1337218822Sdim if (fde->end_address == NULL) 1338218822Sdim { 1339218822Sdim as_bad (_("open CFI at the end of file; missing .cfi_endproc directive")); 1340218822Sdim fde->end_address = fde->start_address; 1341218822Sdim } 1342218822Sdim 1343130561Sobrien cie = select_cie_for_fde (fde, &first); 1344130561Sobrien output_fde (fde, cie, first, fde->next == NULL ? EH_FRAME_ALIGNMENT : 2); 1345130561Sobrien } 1346130561Sobrien 1347130561Sobrien flag_traditional_format = save_flag_traditional_format; 1348130561Sobrien} 1349