Commit | Line | Data |
---|---|---|
2c310d1a ILT |
1 | /* ldcref.c -- output a cross reference table |
2 | Copyright (C) 1996 Free Software Foundation, Inc. | |
3 | Written by Ian Lance Taylor <ian@cygnus.com> | |
4 | ||
5 | This file is part of GLD, the Gnu Linker. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 2 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program; if not, write to the Free Software | |
19 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |
20 | ||
21 | /* This file holds routines that manage the cross reference table. */ | |
22 | ||
23 | #include "bfd.h" | |
24 | #include "sysdep.h" | |
25 | #include "bfdlink.h" | |
26 | #include "libiberty.h" | |
27 | ||
28 | #include "ld.h" | |
29 | #include "ldmain.h" | |
30 | #include "ldmisc.h" | |
31 | ||
32 | /* We keep an instance of this structure for each reference to a | |
33 | symbol from a given object. */ | |
34 | ||
35 | struct cref_ref | |
36 | { | |
37 | /* The next reference. */ | |
38 | struct cref_ref *next; | |
39 | /* The object. */ | |
40 | bfd *abfd; | |
41 | /* True if the symbol is defined. */ | |
42 | unsigned int def : 1; | |
43 | /* True if the symbol is common. */ | |
44 | unsigned int common : 1; | |
45 | /* True if the symbol is undefined. */ | |
46 | unsigned int undef : 1; | |
47 | }; | |
48 | ||
49 | /* We keep a hash table of symbols. Each entry looks like this. */ | |
50 | ||
51 | struct cref_hash_entry | |
52 | { | |
53 | struct bfd_hash_entry root; | |
54 | /* The demangled name. */ | |
55 | char *demangled; | |
56 | /* References to and definitions of this symbol. */ | |
57 | struct cref_ref *refs; | |
58 | }; | |
59 | ||
60 | /* This is what the hash table looks like. */ | |
61 | ||
62 | struct cref_hash_table | |
63 | { | |
64 | struct bfd_hash_table root; | |
65 | }; | |
66 | ||
67 | /* Local functions. */ | |
68 | ||
69 | static struct bfd_hash_entry *cref_hash_newfunc | |
70 | PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); | |
71 | static boolean cref_fill_array PARAMS ((struct cref_hash_entry *, PTR)); | |
72 | static int cref_sort_array PARAMS ((const PTR, const PTR)); | |
73 | static void output_one_cref PARAMS ((FILE *, struct cref_hash_entry *)); | |
74 | ||
75 | /* Look up an entry in the cref hash table. */ | |
76 | ||
77 | #define cref_hash_lookup(table, string, create, copy) \ | |
78 | ((struct cref_hash_entry *) \ | |
79 | bfd_hash_lookup (&(table)->root, (string), (create), (copy))) | |
80 | ||
81 | /* Traverse the cref hash table. */ | |
82 | ||
83 | #define cref_hash_traverse(table, func, info) \ | |
84 | (bfd_hash_traverse \ | |
85 | (&(table)->root, \ | |
86 | (boolean (*) PARAMS ((struct bfd_hash_entry *, PTR))) (func), \ | |
87 | (info))) | |
88 | ||
89 | /* The cref hash table. */ | |
90 | ||
91 | static struct cref_hash_table cref_table; | |
92 | ||
93 | /* Whether the cref hash table has been initialized. */ | |
94 | ||
95 | static boolean cref_initialized; | |
96 | ||
97 | /* The number of symbols seen so far. */ | |
98 | ||
99 | static size_t cref_symcount; | |
100 | ||
101 | /* Create an entry in a cref hash table. */ | |
102 | ||
103 | static struct bfd_hash_entry * | |
104 | cref_hash_newfunc (entry, table, string) | |
105 | struct bfd_hash_entry *entry; | |
106 | struct bfd_hash_table *table; | |
107 | const char *string; | |
108 | { | |
109 | struct cref_hash_entry *ret = (struct cref_hash_entry *) entry; | |
110 | ||
111 | /* Allocate the structure if it has not already been allocated by a | |
112 | subclass. */ | |
113 | if (ret == NULL) | |
114 | ret = ((struct cref_hash_entry *) | |
115 | bfd_hash_allocate (table, sizeof (struct cref_hash_entry))); | |
116 | if (ret == NULL) | |
117 | return (struct bfd_hash_entry *) ret; | |
118 | ||
119 | /* Call the allocation method of the superclass. */ | |
120 | ret = ((struct cref_hash_entry *) | |
121 | bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); | |
122 | if (ret != NULL) | |
123 | { | |
124 | /* Set local fields. */ | |
125 | ret->demangled = NULL; | |
126 | ret->refs = NULL; | |
127 | ||
128 | /* Keep a count of the number of entries created in the hash | |
129 | table. */ | |
130 | ++cref_symcount; | |
131 | } | |
132 | ||
133 | return (struct bfd_hash_entry *) ret; | |
134 | } | |
135 | ||
136 | /* Add a symbol to the cref hash table. This is called for every | |
137 | symbol that is seen during the link. */ | |
138 | ||
139 | /*ARGSUSED*/ | |
140 | void | |
141 | add_cref (name, abfd, section, value) | |
142 | const char *name; | |
143 | bfd *abfd; | |
144 | asection *section; | |
145 | bfd_vma value; | |
146 | { | |
147 | struct cref_hash_entry *h; | |
148 | struct cref_ref *r; | |
149 | ||
150 | if (! cref_initialized) | |
151 | { | |
152 | if (! bfd_hash_table_init (&cref_table.root, cref_hash_newfunc)) | |
153 | einfo ("%X%P: bfd_hash_table_init of cref table failed: %E\n"); | |
154 | cref_initialized = true; | |
155 | } | |
156 | ||
157 | h = cref_hash_lookup (&cref_table, name, true, false); | |
158 | if (h == NULL) | |
159 | einfo ("%X%P: cref_hash_lookup failed: %E\n"); | |
160 | ||
161 | for (r = h->refs; r != NULL; r = r->next) | |
162 | if (r->abfd == abfd) | |
163 | break; | |
164 | ||
165 | if (r == NULL) | |
166 | { | |
167 | r = (struct cref_ref *) xmalloc (sizeof *r); | |
168 | r->next = h->refs; | |
169 | h->refs = r; | |
170 | r->abfd = abfd; | |
171 | r->def = false; | |
172 | r->common = false; | |
173 | r->undef = false; | |
174 | } | |
175 | ||
176 | if (bfd_is_und_section (section)) | |
177 | r->undef = true; | |
178 | else if (bfd_is_com_section (section)) | |
179 | r->common = true; | |
180 | else | |
181 | r->def = true; | |
182 | } | |
183 | ||
184 | /* Copy the addresses of the hash table entries into an array. This | |
185 | is called via cref_hash_traverse. We also fill in the demangled | |
186 | name. */ | |
187 | ||
188 | static boolean | |
189 | cref_fill_array (h, data) | |
190 | struct cref_hash_entry *h; | |
191 | PTR data; | |
192 | { | |
193 | struct cref_hash_entry ***pph = (struct cref_hash_entry ***) data; | |
194 | ||
195 | ASSERT (h->demangled == NULL); | |
196 | h->demangled = demangle (h->root.string); | |
197 | ||
198 | **pph = h; | |
199 | ||
200 | ++*pph; | |
201 | ||
202 | return true; | |
203 | } | |
204 | ||
205 | /* Sort an array of cref hash table entries by name. */ | |
206 | ||
207 | static int | |
208 | cref_sort_array (a1, a2) | |
209 | const PTR a1; | |
210 | const PTR a2; | |
211 | { | |
212 | const struct cref_hash_entry **p1 = (const struct cref_hash_entry **) a1; | |
213 | const struct cref_hash_entry **p2 = (const struct cref_hash_entry **) a2; | |
214 | ||
215 | return strcmp ((*p1)->demangled, (*p2)->demangled); | |
216 | } | |
217 | ||
218 | /* Write out the cref table. */ | |
219 | ||
220 | #define FILECOL (50) | |
221 | ||
222 | void | |
223 | output_cref (fp) | |
224 | FILE *fp; | |
225 | { | |
226 | int len; | |
227 | struct cref_hash_entry **csyms, **csym_fill, **csym, **csym_end; | |
228 | ||
229 | fprintf (fp, "\nCross Reference Table\n\n"); | |
230 | fprintf (fp, "Symbol"); | |
231 | len = sizeof "Symbol" - 1; | |
232 | while (len < FILECOL) | |
233 | { | |
234 | putc (' ' , fp); | |
235 | ++len; | |
236 | } | |
237 | fprintf (fp, "File\n"); | |
238 | ||
239 | if (! cref_initialized) | |
240 | { | |
241 | fprintf (fp, "No symbols\n"); | |
242 | return; | |
243 | } | |
244 | ||
245 | csyms = ((struct cref_hash_entry **) | |
246 | xmalloc (cref_symcount * sizeof (*csyms))); | |
247 | ||
248 | csym_fill = csyms; | |
249 | cref_hash_traverse (&cref_table, cref_fill_array, &csym_fill); | |
250 | ASSERT (csym_fill - csyms == cref_symcount); | |
251 | ||
252 | qsort (csyms, cref_symcount, sizeof (*csyms), cref_sort_array); | |
253 | ||
254 | csym_end = csyms + cref_symcount; | |
255 | for (csym = csyms; csym < csym_end; csym++) | |
256 | output_one_cref (fp, *csym); | |
257 | } | |
258 | ||
259 | /* Output one entry in the cross reference table. */ | |
260 | ||
261 | static void | |
262 | output_one_cref (fp, h) | |
263 | FILE *fp; | |
264 | struct cref_hash_entry *h; | |
265 | { | |
266 | int len; | |
267 | struct bfd_link_hash_entry *hl; | |
268 | struct cref_ref *r; | |
269 | ||
270 | hl = bfd_link_hash_lookup (link_info.hash, h->root.string, false, | |
271 | false, true); | |
272 | if (hl == NULL) | |
273 | einfo ("%P: symbol `%T' missing from main hash table\n", | |
274 | h->root.string); | |
275 | else | |
276 | { | |
277 | /* If this symbol is defined in a dynamic object but never | |
278 | referenced by a normal object, then don't print it. */ | |
279 | if (hl->type == bfd_link_hash_defined) | |
280 | { | |
281 | if (hl->u.def.section->output_section == NULL) | |
282 | return; | |
283 | if (hl->u.def.section->owner != NULL | |
284 | && (hl->u.def.section->owner->flags & DYNAMIC) != 0) | |
285 | { | |
286 | for (r = h->refs; r != NULL; r = r->next) | |
287 | if ((r->abfd->flags & DYNAMIC) == 0) | |
288 | break; | |
289 | if (r == NULL) | |
290 | return; | |
291 | } | |
292 | } | |
293 | } | |
294 | ||
295 | fprintf (fp, "%s ", h->demangled); | |
296 | len = strlen (h->demangled) + 1; | |
297 | ||
298 | for (r = h->refs; r != NULL; r = r->next) | |
299 | { | |
300 | if (r->def) | |
301 | { | |
302 | while (len < FILECOL) | |
303 | { | |
304 | putc (' ', fp); | |
305 | ++len; | |
306 | } | |
307 | finfo (fp, "%B\n", r->abfd); | |
308 | len = 0; | |
309 | } | |
310 | } | |
311 | ||
312 | for (r = h->refs; r != NULL; r = r->next) | |
313 | { | |
314 | if (! r->def) | |
315 | { | |
316 | while (len < FILECOL) | |
317 | { | |
318 | putc (' ', fp); | |
319 | ++len; | |
320 | } | |
321 | finfo (fp, "%B\n", r->abfd); | |
322 | len = 0; | |
323 | } | |
324 | } | |
325 | ||
326 | ASSERT (len == 0); | |
327 | } |