/* Program to read the IL symbol table.
|
/* Program to read the IL symbol table.
|
Copyright (C) 2008 Free Software Foundation, Inc.
|
Copyright (C) 2008 Free Software Foundation, Inc.
|
Contributed by Rafael Avila de Espindola (espindola@google.com).
|
Contributed by Rafael Avila de Espindola (espindola@google.com).
|
|
|
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
the Free Software Foundation; either version 3 of the License, or
|
the Free Software Foundation; either version 3 of the License, or
|
(at your option) any later version.
|
(at your option) any later version.
|
|
|
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
GNU General Public License for more details.
|
GNU General Public License for more details.
|
|
|
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
|
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
|
|
|
#include <fcntl.h>
|
#include <fcntl.h>
|
#include <assert.h>
|
#include <assert.h>
|
#include <dlfcn.h>
|
#include <dlfcn.h>
|
#include <stdio.h>
|
#include <stdio.h>
|
#include <inttypes.h>
|
#include <inttypes.h>
|
#include <stdlib.h>
|
#include <stdlib.h>
|
#include <string.h>
|
#include <string.h>
|
|
|
#include "plugin-api.h"
|
#include "plugin-api.h"
|
#include "../gcc/lto/common.h"
|
#include "../gcc/lto/common.h"
|
|
|
/* The presence of gelf.h is checked by the toplevel configure script. */
|
/* The presence of gelf.h is checked by the toplevel configure script. */
|
# include <gelf.h>
|
# include <gelf.h>
|
|
|
static ld_plugin_claim_file_handler claim_file_handler;
|
static ld_plugin_claim_file_handler claim_file_handler;
|
static ld_plugin_all_symbols_read_handler all_symbols_read_handler;
|
static ld_plugin_all_symbols_read_handler all_symbols_read_handler;
|
static ld_plugin_cleanup_handler cleanup_handler;
|
static ld_plugin_cleanup_handler cleanup_handler;
|
static void *plugin_handle;
|
static void *plugin_handle;
|
|
|
struct file_handle {
|
struct file_handle {
|
unsigned nsyms;
|
unsigned nsyms;
|
struct ld_plugin_symbol *syms;
|
struct ld_plugin_symbol *syms;
|
};
|
};
|
|
|
static struct file_handle **all_file_handles = NULL;
|
static struct file_handle **all_file_handles = NULL;
|
static unsigned int num_file_handles;
|
static unsigned int num_file_handles;
|
|
|
/* Write NSYMS symbols from file HANDLE in SYMS. */
|
/* Write NSYMS symbols from file HANDLE in SYMS. */
|
|
|
static enum ld_plugin_status
|
static enum ld_plugin_status
|
get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
|
get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
|
{
|
{
|
unsigned i;
|
unsigned i;
|
struct file_handle *h = (struct file_handle *) handle;
|
struct file_handle *h = (struct file_handle *) handle;
|
assert (h->nsyms == nsyms);
|
assert (h->nsyms == nsyms);
|
|
|
for (i = 0; i < nsyms; i++)
|
for (i = 0; i < nsyms; i++)
|
syms[i] = h->syms[i];
|
syms[i] = h->syms[i];
|
|
|
return LDPS_OK;
|
return LDPS_OK;
|
}
|
}
|
|
|
/* Register HANDLER as the callback for notifying the plugin that all symbols
|
/* Register HANDLER as the callback for notifying the plugin that all symbols
|
have been read. */
|
have been read. */
|
|
|
static enum ld_plugin_status
|
static enum ld_plugin_status
|
register_all_symbols_read (ld_plugin_all_symbols_read_handler handler)
|
register_all_symbols_read (ld_plugin_all_symbols_read_handler handler)
|
{
|
{
|
all_symbols_read_handler = handler;
|
all_symbols_read_handler = handler;
|
return LDPS_OK;
|
return LDPS_OK;
|
}
|
}
|
|
|
/* Register HANDLER as the callback for claiming a file. */
|
/* Register HANDLER as the callback for claiming a file. */
|
|
|
static enum ld_plugin_status
|
static enum ld_plugin_status
|
register_claim_file(ld_plugin_claim_file_handler handler)
|
register_claim_file(ld_plugin_claim_file_handler handler)
|
{
|
{
|
claim_file_handler = handler;
|
claim_file_handler = handler;
|
return LDPS_OK;
|
return LDPS_OK;
|
}
|
}
|
|
|
/* Register HANDLER as the callback to removing temporary files. */
|
/* Register HANDLER as the callback to removing temporary files. */
|
|
|
static enum ld_plugin_status
|
static enum ld_plugin_status
|
register_cleanup (ld_plugin_cleanup_handler handler)
|
register_cleanup (ld_plugin_cleanup_handler handler)
|
{
|
{
|
cleanup_handler = handler;
|
cleanup_handler = handler;
|
return LDPS_OK;
|
return LDPS_OK;
|
}
|
}
|
|
|
/* For a file identified by HANDLE, add NSYMS symbols from SYMS. */
|
/* For a file identified by HANDLE, add NSYMS symbols from SYMS. */
|
|
|
static enum ld_plugin_status
|
static enum ld_plugin_status
|
add_symbols (void *handle, int nsyms,
|
add_symbols (void *handle, int nsyms,
|
const struct ld_plugin_symbol *syms)
|
const struct ld_plugin_symbol *syms)
|
{
|
{
|
int i;
|
int i;
|
struct file_handle *h = (struct file_handle *) handle;
|
struct file_handle *h = (struct file_handle *) handle;
|
h->nsyms = nsyms;
|
h->nsyms = nsyms;
|
h->syms = calloc (nsyms, sizeof (struct ld_plugin_symbol));
|
h->syms = calloc (nsyms, sizeof (struct ld_plugin_symbol));
|
assert (h->syms);
|
assert (h->syms);
|
|
|
for (i = 0; i < nsyms; i++)
|
for (i = 0; i < nsyms; i++)
|
{
|
{
|
h->syms[i] = syms[i];
|
h->syms[i] = syms[i];
|
h->syms[i].name = strdup (h->syms[i].name);
|
h->syms[i].name = strdup (h->syms[i].name);
|
if (h->syms[i].version)
|
if (h->syms[i].version)
|
h->syms[i].version = strdup (h->syms[i].version);
|
h->syms[i].version = strdup (h->syms[i].version);
|
if (h->syms[i].comdat_key)
|
if (h->syms[i].comdat_key)
|
h->syms[i].comdat_key = strdup (h->syms[i].comdat_key);
|
h->syms[i].comdat_key = strdup (h->syms[i].comdat_key);
|
}
|
}
|
|
|
return LDPS_OK;
|
return LDPS_OK;
|
}
|
}
|
|
|
struct ld_plugin_tv tv[] = {
|
struct ld_plugin_tv tv[] = {
|
{LDPT_REGISTER_CLAIM_FILE_HOOK,
|
{LDPT_REGISTER_CLAIM_FILE_HOOK,
|
{.tv_register_claim_file = register_claim_file}
|
{.tv_register_claim_file = register_claim_file}
|
},
|
},
|
{LDPT_ADD_SYMBOLS,
|
{LDPT_ADD_SYMBOLS,
|
{.tv_add_symbols = add_symbols}
|
{.tv_add_symbols = add_symbols}
|
},
|
},
|
|
|
{LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK,
|
{LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK,
|
{.tv_register_all_symbols_read = register_all_symbols_read}
|
{.tv_register_all_symbols_read = register_all_symbols_read}
|
},
|
},
|
{LDPT_GET_SYMBOLS,
|
{LDPT_GET_SYMBOLS,
|
{.tv_get_symbols = get_symbols}
|
{.tv_get_symbols = get_symbols}
|
},
|
},
|
{LDPT_REGISTER_CLEANUP_HOOK,
|
{LDPT_REGISTER_CLEANUP_HOOK,
|
{.tv_register_cleanup = register_cleanup}
|
{.tv_register_cleanup = register_cleanup}
|
},
|
},
|
{0, {0}}
|
{0, {0}}
|
};
|
};
|
|
|
/* Load a plugin from a file named NAME. */
|
/* Load a plugin from a file named NAME. */
|
|
|
static void
|
static void
|
load_plugin (const char *name)
|
load_plugin (const char *name)
|
{
|
{
|
ld_plugin_onload onload;
|
ld_plugin_onload onload;
|
plugin_handle = dlopen (name, RTLD_LAZY);
|
plugin_handle = dlopen (name, RTLD_LAZY);
|
|
|
assert (plugin_handle != NULL);
|
assert (plugin_handle != NULL);
|
onload = dlsym (plugin_handle, "onload");
|
onload = dlsym (plugin_handle, "onload");
|
assert (onload);
|
assert (onload);
|
onload (tv);
|
onload (tv);
|
assert (claim_file_handler);
|
assert (claim_file_handler);
|
}
|
}
|
|
|
/* Send object to the plugin. The file (archive or object) name is NAME.
|
/* Send object to the plugin. The file (archive or object) name is NAME.
|
FD is an open file descriptor. The object data starts at OFFSET and is
|
FD is an open file descriptor. The object data starts at OFFSET and is
|
FILESIZE bytes long. */
|
FILESIZE bytes long. */
|
|
|
static void
|
static void
|
register_object (const char *name, int fd, off_t offset, off_t filesize)
|
register_object (const char *name, int fd, off_t offset, off_t filesize)
|
{
|
{
|
int claimed;
|
int claimed;
|
struct ld_plugin_input_file file;
|
struct ld_plugin_input_file file;
|
void *handle;
|
void *handle;
|
|
|
num_file_handles++;
|
num_file_handles++;
|
all_file_handles = realloc (all_file_handles, num_file_handles
|
all_file_handles = realloc (all_file_handles, num_file_handles
|
* sizeof (struct file_handle *));
|
* sizeof (struct file_handle *));
|
assert (all_file_handles);
|
assert (all_file_handles);
|
|
|
all_file_handles[num_file_handles - 1] = calloc (1,
|
all_file_handles[num_file_handles - 1] = calloc (1,
|
sizeof (struct file_handle));
|
sizeof (struct file_handle));
|
handle = all_file_handles[num_file_handles - 1];
|
handle = all_file_handles[num_file_handles - 1];
|
assert (handle);
|
assert (handle);
|
|
|
file.name = (char *) name;
|
file.name = (char *) name;
|
file.fd = fd;
|
file.fd = fd;
|
file.offset = offset;
|
file.offset = offset;
|
file.filesize = filesize;
|
file.filesize = filesize;
|
|
|
file.handle = handle;
|
file.handle = handle;
|
|
|
claim_file_handler (&file, &claimed);
|
claim_file_handler (&file, &claimed);
|
}
|
}
|
|
|
/* Send file named NAME to the plugin. */
|
/* Send file named NAME to the plugin. */
|
|
|
static void
|
static void
|
register_file (const char *name)
|
register_file (const char *name)
|
{
|
{
|
int fd = open (name, O_RDONLY);
|
int fd = open (name, O_RDONLY);
|
Elf *elf;
|
Elf *elf;
|
|
|
assert (fd >= 0);
|
assert (fd >= 0);
|
|
|
elf = elf_begin (fd, ELF_C_READ, NULL);
|
elf = elf_begin (fd, ELF_C_READ, NULL);
|
assert (elf);
|
assert (elf);
|
|
|
Elf_Kind kind = elf_kind (elf);
|
Elf_Kind kind = elf_kind (elf);
|
|
|
assert (kind == ELF_K_ELF || kind == ELF_K_AR);
|
assert (kind == ELF_K_ELF || kind == ELF_K_AR);
|
|
|
if (kind == ELF_K_AR)
|
if (kind == ELF_K_AR)
|
{
|
{
|
Elf *member = elf_begin (fd, ELF_C_READ, elf);
|
Elf *member = elf_begin (fd, ELF_C_READ, elf);
|
while (member)
|
while (member)
|
{
|
{
|
Elf_Arhdr *h = elf_getarhdr (member);
|
Elf_Arhdr *h = elf_getarhdr (member);
|
assert (h);
|
assert (h);
|
|
|
if (h->ar_name[0] != '/')
|
if (h->ar_name[0] != '/')
|
{
|
{
|
off_t offset = elf_getbase (member);
|
off_t offset = elf_getbase (member);
|
register_object (name, fd, offset, h->ar_size);
|
register_object (name, fd, offset, h->ar_size);
|
}
|
}
|
|
|
Elf_Cmd cmd = elf_next (member);
|
Elf_Cmd cmd = elf_next (member);
|
elf_end (member);
|
elf_end (member);
|
member = elf_begin (fd, cmd, elf);
|
member = elf_begin (fd, cmd, elf);
|
}
|
}
|
}
|
}
|
else /* Single File */
|
else /* Single File */
|
register_object (name, fd, 0, 0);
|
register_object (name, fd, 0, 0);
|
|
|
elf_end (elf);
|
elf_end (elf);
|
}
|
}
|
|
|
/* Fake symbol resolution for testing. */
|
/* Fake symbol resolution for testing. */
|
|
|
static void
|
static void
|
resolve (void)
|
resolve (void)
|
{
|
{
|
unsigned j;
|
unsigned j;
|
for (j = 0; j < num_file_handles; j++)
|
for (j = 0; j < num_file_handles; j++)
|
{
|
{
|
struct file_handle *handle = all_file_handles[j];
|
struct file_handle *handle = all_file_handles[j];
|
unsigned int nsyms = handle->nsyms;
|
unsigned int nsyms = handle->nsyms;
|
struct ld_plugin_symbol *syms = handle->syms;
|
struct ld_plugin_symbol *syms = handle->syms;
|
unsigned i;
|
unsigned i;
|
for (i = 0; i < nsyms; i++)
|
for (i = 0; i < nsyms; i++)
|
{
|
{
|
switch (syms[i].def)
|
switch (syms[i].def)
|
{
|
{
|
case LDPK_DEF:
|
case LDPK_DEF:
|
case LDPK_WEAKDEF:
|
case LDPK_WEAKDEF:
|
case LDPK_COMMON:
|
case LDPK_COMMON:
|
syms[i].resolution = LDPR_PREVAILING_DEF;
|
syms[i].resolution = LDPR_PREVAILING_DEF;
|
break;
|
break;
|
case LDPK_UNDEF:
|
case LDPK_UNDEF:
|
case LDPK_WEAKUNDEF:
|
case LDPK_WEAKUNDEF:
|
syms[i].resolution = LDPR_RESOLVED_IR;
|
syms[i].resolution = LDPR_RESOLVED_IR;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Print all symbol information. */
|
/* Print all symbol information. */
|
|
|
static void
|
static void
|
print (void)
|
print (void)
|
{
|
{
|
unsigned j;
|
unsigned j;
|
for (j = 0; j < num_file_handles; j++)
|
for (j = 0; j < num_file_handles; j++)
|
{
|
{
|
struct file_handle *handle = all_file_handles[j];
|
struct file_handle *handle = all_file_handles[j];
|
unsigned int nsyms = handle->nsyms;
|
unsigned int nsyms = handle->nsyms;
|
struct ld_plugin_symbol *syms = handle->syms;
|
struct ld_plugin_symbol *syms = handle->syms;
|
unsigned i;
|
unsigned i;
|
for (i = 0; i < nsyms; i++)
|
for (i = 0; i < nsyms; i++)
|
{
|
{
|
printf("name: %s; ", syms[i].name);
|
printf("name: %s; ", syms[i].name);
|
if (syms[i].version)
|
if (syms[i].version)
|
printf("version: %s;", syms[i].version);
|
printf("version: %s;", syms[i].version);
|
else
|
else
|
printf("not versioned; ");
|
printf("not versioned; ");
|
printf("kind: %s; ", lto_kind_str[syms[i].def]);
|
printf("kind: %s; ", lto_kind_str[syms[i].def]);
|
printf("visibility: %s; ", lto_visibility_str[syms[i].visibility]);
|
printf("visibility: %s; ", lto_visibility_str[syms[i].visibility]);
|
printf("size: %" PRId64 "; ", syms[i].size);
|
printf("size: %" PRId64 "; ", syms[i].size);
|
if (syms[i].comdat_key)
|
if (syms[i].comdat_key)
|
printf("comdat_key: %s; ", syms[i].comdat_key);
|
printf("comdat_key: %s; ", syms[i].comdat_key);
|
else
|
else
|
printf("no comdat_key; ");
|
printf("no comdat_key; ");
|
printf ("resolution: %s\n", lto_resolution_str[syms[i].resolution]);
|
printf ("resolution: %s\n", lto_resolution_str[syms[i].resolution]);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Unload the plugin. */
|
/* Unload the plugin. */
|
|
|
static void
|
static void
|
unload_plugin (void)
|
unload_plugin (void)
|
{
|
{
|
unsigned err = dlclose (plugin_handle);
|
unsigned err = dlclose (plugin_handle);
|
assert (err == 0);
|
assert (err == 0);
|
claim_file_handler = 0;
|
claim_file_handler = 0;
|
all_symbols_read_handler = 0;
|
all_symbols_read_handler = 0;
|
}
|
}
|
|
|
/* Free all memory allocated by us that hasn't been freed yet. */
|
/* Free all memory allocated by us that hasn't been freed yet. */
|
|
|
static void
|
static void
|
free_all (void)
|
free_all (void)
|
{
|
{
|
unsigned j;
|
unsigned j;
|
for (j = 0; j < num_file_handles; j++)
|
for (j = 0; j < num_file_handles; j++)
|
{
|
{
|
struct file_handle *handle = all_file_handles[j];
|
struct file_handle *handle = all_file_handles[j];
|
unsigned int nsyms = handle->nsyms;
|
unsigned int nsyms = handle->nsyms;
|
struct ld_plugin_symbol *syms = handle->syms;
|
struct ld_plugin_symbol *syms = handle->syms;
|
unsigned i;
|
unsigned i;
|
for (i = 0; i < nsyms; i++)
|
for (i = 0; i < nsyms; i++)
|
{
|
{
|
free (syms[i].name);
|
free (syms[i].name);
|
syms[i].name = 0;
|
syms[i].name = 0;
|
if (syms[i].version)
|
if (syms[i].version)
|
{
|
{
|
free (syms[i].version);
|
free (syms[i].version);
|
syms[i].version = 0;
|
syms[i].version = 0;
|
}
|
}
|
if (syms[i].comdat_key)
|
if (syms[i].comdat_key)
|
{
|
{
|
free (syms[i].comdat_key);
|
free (syms[i].comdat_key);
|
syms[i].comdat_key = 0;
|
syms[i].comdat_key = 0;
|
}
|
}
|
}
|
}
|
free (syms);
|
free (syms);
|
handle->syms = NULL;
|
handle->syms = NULL;
|
handle->nsyms = 0;
|
handle->nsyms = 0;
|
free (all_file_handles[j]);
|
free (all_file_handles[j]);
|
all_file_handles[j] = NULL;
|
all_file_handles[j] = NULL;
|
}
|
}
|
|
|
free (all_file_handles);
|
free (all_file_handles);
|
all_file_handles = NULL;
|
all_file_handles = NULL;
|
num_file_handles = 0;
|
num_file_handles = 0;
|
}
|
}
|
|
|
int
|
int
|
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
{
|
{
|
const char *plugin;
|
const char *plugin;
|
unsigned int i;
|
unsigned int i;
|
assert (argc >= 3);
|
assert (argc >= 3);
|
plugin = argv[1];
|
plugin = argv[1];
|
|
|
load_plugin (plugin);
|
load_plugin (plugin);
|
|
|
for (i = 2; i < argc; i++)
|
for (i = 2; i < argc; i++)
|
register_file (argv[i]);
|
register_file (argv[i]);
|
|
|
resolve ();
|
resolve ();
|
|
|
print ();
|
print ();
|
|
|
all_symbols_read_handler ();
|
all_symbols_read_handler ();
|
|
|
free_all ();
|
free_all ();
|
|
|
cleanup_handler ();
|
cleanup_handler ();
|
|
|
unload_plugin ();
|
unload_plugin ();
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|