@ -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 ( strn cmp ( 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 ( f de = 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 ;
}
}