1 /* Program to read the IL symbol table.
2    Copyright (C) 2008 Free Software Foundation, Inc.
3    Contributed by Rafael Avila de Espindola (espindola@google.com).
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
18 
19 #include <fcntl.h>
20 #include <assert.h>
21 #include <dlfcn.h>
22 #include <stdio.h>
23 #include <inttypes.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "plugin-api.h"
28 #include "../gcc/lto/common.h"
29 
30 /* The presence of gelf.h is checked by the toplevel configure script.  */
31 # include <gelf.h>
32 
33 static ld_plugin_claim_file_handler claim_file_handler;
34 static ld_plugin_all_symbols_read_handler all_symbols_read_handler;
35 static ld_plugin_cleanup_handler cleanup_handler;
36 static void *plugin_handle;
37 
38 struct file_handle {
39   unsigned nsyms;
40   struct ld_plugin_symbol *syms;
41 };
42 
43 static struct file_handle **all_file_handles = NULL;
44 static unsigned int num_file_handles;
45 
46 /* Write NSYMS symbols from file HANDLE in SYMS. */
47 
48 static enum ld_plugin_status
49 get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
50 {
51   unsigned i;
52   struct file_handle *h = (struct file_handle *) handle;
53   assert (h->nsyms == nsyms);
54 
55   for (i = 0; i < nsyms; i++)
56     syms[i] = h->syms[i];
57 
58   return LDPS_OK;
59 }
60 
61 /* Register HANDLER as the callback for notifying the plugin that all symbols
62    have been read. */
63 
64 static enum ld_plugin_status
65 register_all_symbols_read (ld_plugin_all_symbols_read_handler handler)
66 {
67   all_symbols_read_handler = handler;
68   return LDPS_OK;
69 }
70 
71 /* Register HANDLER as the callback for claiming a file. */
72 
73 static enum ld_plugin_status
74 register_claim_file(ld_plugin_claim_file_handler handler)
75 {
76   claim_file_handler = handler;
77   return LDPS_OK;
78 }
79 
80 /* Register HANDLER as the callback to removing temporary files. */
81 
82 static enum ld_plugin_status
83 register_cleanup (ld_plugin_cleanup_handler handler)
84 {
85   cleanup_handler = handler;
86   return LDPS_OK;
87 }
88 
89 /* For a file identified by HANDLE, add NSYMS symbols from SYMS. */
90 
91 static enum ld_plugin_status
92 add_symbols (void *handle, int nsyms,
93 	     const struct ld_plugin_symbol *syms)
94 {
95   int i;
96   struct file_handle *h = (struct file_handle *) handle;
97   h->nsyms = nsyms;
98   h->syms = calloc (nsyms, sizeof (struct ld_plugin_symbol));
99   assert (h->syms);
100 
101   for (i = 0; i < nsyms; i++)
102     {
103       h->syms[i] = syms[i];
104       h->syms[i].name = strdup (h->syms[i].name);
105       if (h->syms[i].version)
106 	h->syms[i].version = strdup (h->syms[i].version);
107       if (h->syms[i].comdat_key)
108 	h->syms[i].comdat_key = strdup (h->syms[i].comdat_key);
109     }
110 
111   return LDPS_OK;
112 }
113 
114 struct ld_plugin_tv tv[] = {
115   {LDPT_REGISTER_CLAIM_FILE_HOOK,
116    {.tv_register_claim_file = register_claim_file}
117   },
118   {LDPT_ADD_SYMBOLS,
119    {.tv_add_symbols = add_symbols}
120   },
121 
122   {LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK,
123    {.tv_register_all_symbols_read = register_all_symbols_read}
124   },
125   {LDPT_GET_SYMBOLS,
126    {.tv_get_symbols = get_symbols}
127   },
128   {LDPT_REGISTER_CLEANUP_HOOK,
129    {.tv_register_cleanup = register_cleanup}
130   },
131   {0, {0}}
132 };
133 
134 /* Load a plugin from a file named NAME. */
135 
136 static void
137 load_plugin (const char *name)
138 {
139   ld_plugin_onload onload;
140   plugin_handle = dlopen (name, RTLD_LAZY);
141 
142   assert (plugin_handle != NULL);
143   onload = dlsym (plugin_handle, "onload");
144   assert (onload);
145   onload (tv);
146   assert (claim_file_handler);
147 }
148 
149 /* Send object to the plugin. The file (archive or object) name is NAME.
150    FD is an open file descriptor. The object data starts at OFFSET and is
151    FILESIZE bytes long. */
152 
153 static void
154 register_object (const char *name, int fd, off_t offset, off_t filesize)
155 {
156   int claimed;
157   struct ld_plugin_input_file file;
158   void *handle;
159 
160   num_file_handles++;
161   all_file_handles = realloc (all_file_handles, num_file_handles
162 			      * sizeof (struct file_handle *));
163   assert (all_file_handles);
164 
165   all_file_handles[num_file_handles - 1] = calloc (1,
166 						   sizeof (struct file_handle));
167   handle = all_file_handles[num_file_handles - 1];
168   assert (handle);
169 
170   file.name = (char *) name;
171   file.fd = fd;
172   file.offset = offset;
173   file.filesize = filesize;
174 
175   file.handle = handle;
176 
177   claim_file_handler (&file, &claimed);
178 }
179 
180 /* Send file named NAME to the plugin. */
181 
182 static void
183 register_file (const char *name)
184 {
185  int fd = open (name, O_RDONLY);
186  Elf *elf;
187 
188  assert (fd >= 0);
189 
190  elf = elf_begin (fd, ELF_C_READ, NULL);
191  assert (elf);
192 
193  Elf_Kind kind = elf_kind (elf);
194 
195  assert (kind == ELF_K_ELF || kind == ELF_K_AR);
196 
197  if (kind == ELF_K_AR)
198    {
199      Elf *member = elf_begin (fd, ELF_C_READ, elf);
200      while (member)
201        {
202 	 Elf_Arhdr *h = elf_getarhdr (member);
203 	 assert (h);
204 
205 	 if (h->ar_name[0] != '/')
206 	   {
207 	     off_t offset = elf_getbase (member);
208 	     register_object (name, fd, offset, h->ar_size);
209 	   }
210 
211 	 Elf_Cmd cmd = elf_next (member);
212 	 elf_end (member);
213 	 member = elf_begin (fd, cmd, elf);
214        }
215    }
216  else /* Single File */
217    register_object (name, fd, 0, 0);
218 
219  elf_end (elf);
220 }
221 
222 /* Fake symbol resolution for testing. */
223 
224 static void
225 resolve (void)
226 {
227   unsigned j;
228   for (j = 0; j < num_file_handles; j++)
229     {
230       struct file_handle *handle = all_file_handles[j];
231       unsigned int nsyms = handle->nsyms;
232       struct ld_plugin_symbol *syms = handle->syms;
233       unsigned i;
234       for (i = 0; i < nsyms; i++)
235 	{
236 	  switch (syms[i].def)
237 	    {
238 	    case LDPK_DEF:
239 	    case LDPK_WEAKDEF:
240 	    case LDPK_COMMON:
241 	      syms[i].resolution =  LDPR_PREVAILING_DEF;
242 	      break;
243 	    case LDPK_UNDEF:
244 	    case LDPK_WEAKUNDEF:
245 	      syms[i].resolution =  LDPR_RESOLVED_IR;
246 	      break;
247 	    }
248 	}
249     }
250 }
251 
252 /* Print all symbol information. */
253 
254 static void
255 print (void)
256 {
257   unsigned j;
258   for (j = 0; j < num_file_handles; j++)
259     {
260       struct file_handle *handle = all_file_handles[j];
261       unsigned int nsyms = handle->nsyms;
262       struct ld_plugin_symbol *syms = handle->syms;
263       unsigned i;
264       for (i = 0; i < nsyms; i++)
265 	{
266 	  printf("name: %s; ", syms[i].name);
267 	  if (syms[i].version)
268 	     printf("version: %s;", syms[i].version);
269 	  else
270 	    printf("not versioned; ");
271 	  printf("kind: %s; ", lto_kind_str[syms[i].def]);
272 	  printf("visibility: %s; ", lto_visibility_str[syms[i].visibility]);
273 	  printf("size: %" PRId64 "; ", syms[i].size);
274 	  if (syms[i].comdat_key)
275 	    printf("comdat_key: %s; ", syms[i].comdat_key);
276 	  else
277 	    printf("no comdat_key; ");
278 	  printf ("resolution: %s\n", lto_resolution_str[syms[i].resolution]);
279 	}
280     }
281 }
282 
283 /* Unload the plugin. */
284 
285 static void
286 unload_plugin (void)
287 {
288   unsigned err = dlclose (plugin_handle);
289   assert (err == 0);
290   claim_file_handler = 0;
291   all_symbols_read_handler = 0;
292 }
293 
294 /* Free all memory allocated by us that hasn't been freed yet. */
295 
296 static void
297 free_all (void)
298 {
299   unsigned j;
300   for (j = 0; j < num_file_handles; j++)
301     {
302       struct file_handle *handle = all_file_handles[j];
303       unsigned int nsyms = handle->nsyms;
304       struct ld_plugin_symbol *syms = handle->syms;
305       unsigned i;
306       for (i = 0; i < nsyms; i++)
307 	{
308 	  free (syms[i].name);
309 	  syms[i].name = 0;
310 	  if (syms[i].version)
311 	    {
312 	      free (syms[i].version);
313 	      syms[i].version = 0;
314 	    }
315 	  if (syms[i].comdat_key)
316 	    {
317 	      free (syms[i].comdat_key);
318 	      syms[i].comdat_key = 0;
319 	    }
320 	}
321       free (syms);
322       handle->syms = NULL;
323       handle->nsyms = 0;
324       free (all_file_handles[j]);
325       all_file_handles[j] = NULL;
326     }
327 
328   free (all_file_handles);
329   all_file_handles = NULL;
330   num_file_handles = 0;
331 }
332 
333 int
334 main(int argc, char *argv[])
335 {
336   const char *plugin;
337   unsigned int i;
338   assert (argc >= 3);
339   plugin = argv[1];
340 
341   load_plugin (plugin);
342 
343   for (i = 2; i < argc; i++)
344     register_file (argv[i]);
345 
346   resolve ();
347 
348   print ();
349 
350   all_symbols_read_handler ();
351 
352   free_all ();
353 
354   cleanup_handler ();
355 
356   unload_plugin ();
357 
358   return 0;
359 }
360