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
5This program is free software; you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation; either version 3 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, 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
33static ld_plugin_claim_file_handler claim_file_handler;
34static ld_plugin_all_symbols_read_handler all_symbols_read_handler;
35static ld_plugin_cleanup_handler cleanup_handler;
36static void *plugin_handle;
37
38struct file_handle {
39  unsigned nsyms;
40  struct ld_plugin_symbol *syms;
41};
42
43static struct file_handle **all_file_handles = NULL;
44static unsigned int num_file_handles;
45
46/* Write NSYMS symbols from file HANDLE in SYMS. */
47
48static enum ld_plugin_status
49get_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
64static enum ld_plugin_status
65register_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
73static enum ld_plugin_status
74register_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
82static enum ld_plugin_status
83register_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
91static enum ld_plugin_status
92add_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
114struct 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
136static void
137load_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
153static void
154register_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
182static void
183register_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
224static void
225resolve (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
254static void
255print (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
285static void
286unload_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
296static void
297free_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
333int
334main(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