@@ -27,6 +27,7 @@ static void process_linebreak(struct die *cache, int n)
2727 !dwarf_form##attr(&da, value); \
2828 }
2929
30+ DEFINE_GET_ATTR (flag , bool )
3031DEFINE_GET_ATTR (udata , Dwarf_Word )
3132
3233static bool get_ref_die_attr (Dwarf_Die * die , unsigned int id , Dwarf_Die * value )
@@ -80,6 +81,55 @@ static bool match_export_symbol(struct state *state, Dwarf_Die *die)
8081 return !!state -> sym ;
8182}
8283
84+ /* DW_AT_decl_file -> struct srcfile */
85+ static struct cache srcfile_cache ;
86+
87+ static bool is_definition_private (Dwarf_Die * die )
88+ {
89+ Dwarf_Word filenum ;
90+ Dwarf_Files * files ;
91+ Dwarf_Die cudie ;
92+ const char * s ;
93+ int res ;
94+
95+ /*
96+ * Definitions in .c files cannot change the public ABI,
97+ * so consider them private.
98+ */
99+ if (!get_udata_attr (die , DW_AT_decl_file , & filenum ))
100+ return false;
101+
102+ res = cache_get (& srcfile_cache , filenum );
103+ if (res >= 0 )
104+ return !!res ;
105+
106+ if (!dwarf_cu_die (die -> cu , & cudie , NULL , NULL , NULL , NULL , NULL , NULL ))
107+ error ("dwarf_cu_die failed: '%s'" , dwarf_errmsg (-1 ));
108+
109+ if (dwarf_getsrcfiles (& cudie , & files , NULL ))
110+ error ("dwarf_getsrcfiles failed: '%s'" , dwarf_errmsg (-1 ));
111+
112+ s = dwarf_filesrc (files , filenum , NULL , NULL );
113+ if (!s )
114+ error ("dwarf_filesrc failed: '%s'" , dwarf_errmsg (-1 ));
115+
116+ s = strrchr (s , '.' );
117+ res = s && !strcmp (s , ".c" );
118+ cache_set (& srcfile_cache , filenum , res );
119+
120+ return !!res ;
121+ }
122+
123+ static bool is_kabi_definition (Dwarf_Die * die )
124+ {
125+ bool value ;
126+
127+ if (get_flag_attr (die , DW_AT_declaration , & value ) && value )
128+ return false;
129+
130+ return !is_definition_private (die );
131+ }
132+
83133/*
84134 * Type string processing
85135 */
@@ -456,19 +506,27 @@ static void __process_structure_type(struct state *state, struct die *cache,
456506 die_callback_t process_func ,
457507 die_match_callback_t match_func )
458508{
509+ bool expand ;
510+
459511 process (cache , type );
460512 process_fqn (cache , die );
461513 process (cache , " {" );
462514 process_linebreak (cache , 1 );
463515
464- check (process_die_container (state , cache , die , process_func ,
465- match_func ));
516+ expand = state -> expand .expand && is_kabi_definition (die );
517+
518+ if (expand ) {
519+ check (process_die_container (state , cache , die , process_func ,
520+ match_func ));
521+ }
466522
467523 process_linebreak (cache , -1 );
468524 process (cache , "}" );
469525
470- process_byte_size_attr (cache , die );
471- process_alignment_attr (cache , die );
526+ if (expand ) {
527+ process_byte_size_attr (cache , die );
528+ process_alignment_attr (cache , die );
529+ }
472530}
473531
474532#define DEFINE_PROCESS_STRUCTURE_TYPE (structure ) \
@@ -553,25 +611,70 @@ static void process_cached(struct state *state, struct die *cache,
553611 }
554612}
555613
614+ static void state_init (struct state * state )
615+ {
616+ state -> expand .expand = true;
617+ cache_init (& state -> expansion_cache );
618+ }
619+
620+ static void expansion_state_restore (struct expansion_state * state ,
621+ struct expansion_state * saved )
622+ {
623+ state -> expand = saved -> expand ;
624+ }
625+
626+ static void expansion_state_save (struct expansion_state * state ,
627+ struct expansion_state * saved )
628+ {
629+ expansion_state_restore (saved , state );
630+ }
631+
632+ static bool is_expanded_type (int tag )
633+ {
634+ return tag == DW_TAG_class_type || tag == DW_TAG_structure_type ||
635+ tag == DW_TAG_union_type || tag == DW_TAG_enumeration_type ;
636+ }
637+
556638#define PROCESS_TYPE (type ) \
557639 case DW_TAG_##type##_type: \
558640 process_##type##_type(state, cache, die); \
559641 break;
560642
561643static int process_type (struct state * state , struct die * parent , Dwarf_Die * die )
562644{
645+ enum die_state want_state = DIE_COMPLETE ;
563646 struct die * cache ;
647+ struct expansion_state saved ;
564648 int tag = dwarf_tag (die );
565649
650+ expansion_state_save (& state -> expand , & saved );
651+
566652 /*
567- * If we have the DIE already cached, use it instead of walking
653+ * Structures and enumeration types are expanded only once per
654+ * exported symbol. This is sufficient for detecting ABI changes
655+ * within the structure.
656+ */
657+ if (is_expanded_type (tag )) {
658+ if (cache_was_expanded (& state -> expansion_cache , die -> addr ))
659+ state -> expand .expand = false;
660+
661+ if (state -> expand .expand )
662+ cache_mark_expanded (& state -> expansion_cache , die -> addr );
663+ else
664+ want_state = DIE_UNEXPANDED ;
665+ }
666+
667+ /*
668+ * If we have want_state already cached, use it instead of walking
568669 * through DWARF.
569670 */
570- cache = die_map_get (die , DIE_COMPLETE );
671+ cache = die_map_get (die , want_state );
571672
572- if (cache -> state == DIE_COMPLETE ) {
673+ if (cache -> state == want_state ) {
573674 process_cached (state , cache , die );
574675 die_map_add_die (parent , cache );
676+
677+ expansion_state_restore (& state -> expand , & saved );
575678 return 0 ;
576679 }
577680
@@ -612,9 +715,10 @@ static int process_type(struct state *state, struct die *parent, Dwarf_Die *die)
612715
613716 /* Update cache state and append to the parent (if any) */
614717 cache -> tag = tag ;
615- cache -> state = DIE_COMPLETE ;
718+ cache -> state = want_state ;
616719 die_map_add_die (parent , cache );
617720
721+ expansion_state_restore (& state -> expand , & saved );
618722 return 0 ;
619723}
620724
@@ -676,11 +780,14 @@ static int process_exported_symbols(struct state *unused, struct die *cache,
676780 if (!match_export_symbol (& state , die ))
677781 return 0 ;
678782
783+ state_init (& state );
784+
679785 if (tag == DW_TAG_subprogram )
680786 process_subprogram (& state , & state .die );
681787 else
682788 process_variable (& state , & state .die );
683789
790+ cache_free (& state .expansion_cache );
684791 return 0 ;
685792 }
686793 default :
@@ -692,4 +799,6 @@ void process_cu(Dwarf_Die *cudie)
692799{
693800 check (process_die_container (NULL , NULL , cudie , process_exported_symbols ,
694801 match_all ));
802+
803+ cache_free (& srcfile_cache );
695804}
0 commit comments