Skip to content

Instantly share code, notes, and snippets.

@xlab
Created October 22, 2012 01:51
Show Gist options
  • Save xlab/3929227 to your computer and use it in GitHub Desktop.
Save xlab/3929227 to your computer and use it in GitHub Desktop.
Symbols replacer in da ELF
/* Rename symbols in an ELF object file, for use with ocamlopt -pack.
Written by John F. Carr <[email protected]>, September 24 2002.
Do whatever you want with this program.
This program takes two arguments: the name of a file to modify,
and the prefix to prepend to external symbols. A symbol already
beginning with the prefix, or the prefix without a trailng "__",
is not modified. This matches the behavior required by ocamlopt.
See also asmcomp/asmpackager.ml.
Exit code:
0 success
1 usage error
2 I/O error, bad file, or ELF error
3 other error
4 internal error
Tested on Solaris 8 only. Requires libelf. Only supports 32 bit
objects but extension to 64 bits is easy.
*/
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h> /* strerror */
#include <libelf.h>
#include <fcntl.h>
/* These variables are set in main(). */
int verbose = 0;
static char *prefix;
static size_t prefixlen;
static char *oldname;
static size_t oldnamelen;
/* These structures map old string table offsets to new string
table offsets.
Currently new_index = old_index + (index in array) * prefixlen,
but the pair representation allows more complicated name changes
to be easily implemented. */
struct strtab_map_entry { int old_index; int new_index; };
struct strtab_map
{
int size;
struct strtab_map_entry *map;
};
/* callback function for qsort and bsearch */
static int compare_sme(const void *arg0, const void *arg1)
{
struct strtab_map_entry *me0 = (struct strtab_map_entry *)arg0;
struct strtab_map_entry *me1 = (struct strtab_map_entry *)arg1;
return me0->old_index - me1->old_index;
}
/* Find the new string table offset for index _offset_ into old table.
If this is a renamed symbol, the new offset is known by table lookup.
Otherwise the relative adjustment of the next renamed symbol is added
to the original offset. */
static int find_offset(struct strtab_map *map_ptr, int offset)
{
struct strtab_map_entry *sme = map_ptr->map;
int size = map_ptr->size;
int low, high;
/* Make sure the index is in the bounds of the array. */
/* Strings before the first renamed symbol are in the same place. */
if (offset <= sme[0].old_index)
return offset;
/* Strings after the last renamed symbol are displaced by the same
amount as the last renamed symbol, plus an extra (prefixlen) to
account for that symbol expanding. */
if (offset > sme[size-1].old_index)
return offset + (sme[size-1].new_index - sme[size-1].old_index) + prefixlen;
/* Binary search table. */
low = 0;
high = size; /* one greater than max bound */
while (low < high)
{
int mid = (low + high) / 2;
int x = sme[mid].old_index;
/* Check for perfect match. */
if (x == offset)
return sme[mid].new_index;
/* Otherwise search the appropriate half of the remainder. */
if (x > offset)
high = mid;
else
low = mid + 1;
}
/* Apply various sanity checks. */
if (low > high || low > size - 1 ||
sme[low].old_index == offset ||
(low < size - 1 && sme[low+1].old_index < offset) ||
sme[low-1].old_index > offset)
{
/*fprintf(stderr, "(%d %d) %d (%d %d %d) %d\n",
low, high, size, sme[low-1].old_index,
sme[low].old_index, sme[low+1].old_index, offset);*/
fputs("sym-rename: internal error: binary search failure\n", stderr);
exit(4);
}
if (sme[low].old_index < offset)
low++;
return offset + (sme[low].new_index - sme[low].old_index);
}
/* Scan the symbol table looking for externally visible symbols. */
int scan_symtab(Elf *elf, int symtab_index, int strtab_index,
struct strtab_map *map_ptr)
{
Elf_Scn *sym_scn;
Elf32_Shdr *sym_shdr;
Elf_Data *sym_data;
int i, nsym, count;
struct strtab_map_entry *me;
/* The section header is known to exist. */
sym_scn = elf_getscn(elf, symtab_index);
sym_shdr = elf32_getshdr(sym_scn);
sym_data = elf_getdata(sym_scn, NULL);
if (sym_data == NULL)
{
fputs("sym-rename: unable to get .symtab section data\n", stderr);
exit(2);
}
if (sym_data->d_size % sizeof(Elf32_Sym))
{
fprintf(stderr, "sym-rename: .symtab section size, %d, is not a multiple of %d\n",
sym_data->d_size, sizeof(Elf32_Sym));
exit(2);
}
nsym = sym_data->d_size / sizeof(Elf32_Sym);
me = calloc(nsym, sizeof (struct strtab_map_entry));
if (me == NULL)
{
fputs("sym-rename: unable to allocate name mapping table\n", stderr);
exit(3);
}
count = 0;
for (i = 1; i < nsym; i++)
{
Elf32_Sym *sym = (Elf32_Sym *)sym_data->d_buf + i;
const char *name;
if (sym->st_name == 0)
continue;
if (ELF32_ST_BIND(sym->st_info) != STB_GLOBAL || sym->st_shndx == SHN_UNDEF)
continue;
/* Do not rename symbols already starting with the prefix. */
name = elf_strptr(elf, strtab_index, sym->st_name);
if (!memcmp(name, prefix, prefixlen) ||
(prefixlen > 3 &&
prefix[prefixlen-2] == '_' && prefix[prefixlen-1] == '_' &&
name[prefixlen-2] == '\0' &&
!memcmp(name, prefix, prefixlen-2)))
continue;
if(oldnamelen > 0 && memcmp(name, oldname, oldnamelen))
continue;
/* Add this symbol to the list. */
me[count++].old_index = sym->st_name;
}
/* XXX Duplicates should be suppressed, but it is not likely
that there will be any. */
qsort(me, count, sizeof(struct strtab_map_entry), compare_sme);
map_ptr->size = count;
map_ptr->map = me;
return count;
}
/* Modify the symbol table to use the new string offsets. */
void alter_symtab(Elf *elf, int symtab_index, struct strtab_map *map_ptr)
{
Elf_Scn *sym_scn;
Elf32_Shdr *sym_shdr;
Elf_Data *sym_data;
int i, nsym;
/* The section header is known to exist. */
sym_scn = elf_getscn(elf, symtab_index);
sym_shdr = elf32_getshdr(sym_scn);
sym_data = elf_getdata(sym_scn, NULL);
if (sym_data == NULL)
{
fputs("sym-rename: unable to get .symtab section data\n", stderr);
exit(2);
}
if (sym_data->d_size % sizeof(Elf32_Sym))
{
fprintf(stderr, "sym-rename: .symtab section size, %d, is not a multiple of %d\n",
sym_data->d_size, sizeof(Elf32_Sym));
exit(2);
}
nsym = sym_data->d_size / sizeof(Elf32_Sym);
for (i = 1; i < nsym; i++)
{
int delta;
struct strtab_map_entry *sme;
struct strtab_map_entry key;
Elf32_Sym *sym = (Elf32_Sym *)sym_data->d_buf + i;
if (sym->st_name == 0)
continue;
key.old_index = sym->st_name;
sym->st_name = find_offset(map_ptr, sym->st_name);
}
if (elf_getdata(sym_scn, sym_data))
{
fputs("sym-rename: library representation of symbol table is not contiguous\n", stderr);
exit(2);
}
elf_flagdata(sym_data, ELF_C_SET, ELF_F_DIRTY);
}
/* Modify the string table, adding the prefix before each index
specified in the map. */
void alter_strtab(Elf *elf,
int strtab_index,
struct strtab_map *map_ptr)
{
Elf_Scn *str_scn = elf_getscn(elf, strtab_index);
Elf32_Shdr *str_shdr;
Elf_Data *str_data;
int str_size, nsize;
int i;
int n_index, o_index;
char *strings, *nstrings;
struct strtab_map_entry *map;
str_shdr = elf32_getshdr(str_scn);
str_data = elf_getdata(str_scn, NULL);
if (elf_getdata(str_scn, str_data))
{
fputs("sym-rename: library representation of string table is not contiguous\n", stderr);
exit(2);
}
str_size = str_data->d_size;
strings = str_data->d_buf;
/* Allocate a space that is guaranteed to be sufficient.
Each of map_ptr->size strings may grow by at most the
length of the prefix. */
nsize = str_size + (map_ptr->size * prefixlen);
nstrings = malloc(nsize);
if (nstrings == NULL)
{
fprintf(stderr, "sym-rename: unable to allocate %d bytes for new string table\n", nsize);
exit(3);
}
n_index = 0;
o_index = 0;
for (i = 0; i < map_ptr->size; i++)
{
size_t n = map_ptr->map[i].old_index - o_index;
/* Copy from the current position up to the next insertion point. */
memcpy(nstrings + n_index, strings + o_index, n);
n_index += n;
o_index += n;
/* Mark the spot. */
map_ptr->map[i].new_index = n_index;
/* Insert the prefix. */
memcpy(nstrings + n_index, prefix, prefixlen);
n_index += prefixlen;
}
/* Copy the rest of the string table. */
memcpy(nstrings + n_index, strings + o_index, str_size - o_index);
n_index += (str_size - o_index);
if (n_index > nsize)
{
fprintf(stderr, "sym-rename: new string table overflowed! (%d > %d)\n",
n_index, nsize);
exit(3);
}
elf_flagdata(str_data, ELF_C_SET, ELF_F_DIRTY);
{
struct strtab_map_entry *map = map_ptr->map;
for (i = 0; i < map_ptr->size; i++)
{
fprintf(stdout, "%s\n", strings + map[i].old_index);
if (verbose)
if (strcmp(strings + map[i].old_index, nstrings + map[i].new_index))
fprintf(stderr, "%5d -> %5d\t %s -> %s\n",
map[i].old_index, map[i].new_index,
strings + map[i].old_index,
nstrings + map[i].new_index);
else
fprintf(stderr, "%5d -> %5d\t %s\n",
map[i].old_index, map[i].new_index,
strings + map[i].old_index);
}
}
/* Install the new .strtab. */
str_data -> d_buf = nstrings;
str_data -> d_size = n_index;
elf_flagscn(str_scn, ELF_C_SET, ELF_F_DIRTY);
}
static int check_name(Elf *elf, int shstrtab_index, Elf32_Shdr *shdr, char *want)
{
const char *name = elf_strptr(elf, shstrtab_index, shdr->sh_name);
if (name == NULL)
{
if (verbose)
fputs("No name for section!\n", stderr);
return 0;
}
return !strcmp(name, want);
}
int sym_rename(Elf *elf)
{
Elf32_Ehdr *ehdr;
int nsh, i;
long nbytes;
int shstrtab_index, symtab_index = 0, strtab_index = 0;
struct strtab_map m;
ehdr = elf32_getehdr(elf);
if (ehdr == NULL)
{
fputs("sym-rename: unable to get ELF object header\n", stderr);
exit(2);
}
shstrtab_index = ehdr->e_shstrndx;
nsh = ehdr->e_shnum;
for (i = 1; i < nsh; i++)
{
Elf_Scn *scn;
Elf32_Shdr *shdr;
/*const char *name;*/
if (i == shstrtab_index)
continue;
scn = elf_getscn(elf, i);
if (scn == NULL)
continue;
shdr = elf32_getshdr(scn);
if (shdr == NULL)
continue;
/* Choose the sections that hold the symbol table and string table.
If there is only one section with the appropriate tag, use it.
Otherwise use the section with the expected name. */
switch (shdr->sh_type)
{
case SHT_SYMTAB:
if (symtab_index == 0 || check_name(elf, shstrtab_index, shdr, ".symtab"))
symtab_index = i;
case SHT_STRTAB:
if (strtab_index == 0 || check_name(elf, shstrtab_index, shdr, ".strtab"))
strtab_index = i;
}
#if 0
name = elf_strptr(elf, shstrtab_index, shdr->sh_name);
if (name)
fprintf(stderr, "section %d (%s): vaddr=%x offset=%x size=%x\n",
i, name, shdr->sh_addr, shdr->sh_offset, shdr->sh_size);
else
fprintf(stderr, "section %d: vaddr=%x offset=%x size=%x\n",
i, shdr->sh_addr, shdr->sh_offset, shdr->sh_size);
#endif
}
if (symtab_index == 0 || strtab_index == 0)
{
fputs("sym-rename: ELF object does not have .strtab and .symtab sections\n",
stderr);
exit(2);
}
scan_symtab(elf, symtab_index, strtab_index, &m);
alter_strtab(elf, strtab_index, &m);
alter_symtab(elf, symtab_index, &m);
nbytes = elf_update(elf, ELF_C_WRITE);
if (nbytes < 0)
{
fputs("sym-rename: elf_update(ELF_C_WRITE) failed\n", stderr);
exit(2);
}
return 0;
}
Elf *init(const char *filename)
{
int fd;
Elf *elf;
if (elf_version(EV_CURRENT) == EV_NONE)
{
fputs("sym-rename: ELF library version mismatch\n", stderr);
return NULL;
}
fd = open(filename, O_RDWR);
if (fd == -1)
{
fprintf(stderr, "sym-rename: Unable to open %s: %s\n",
filename, strerror(errno));
return NULL;
}
elf = elf_begin(fd, ELF_C_RDWR, (Elf *)NULL);
if (elf_kind(elf) != ELF_K_ELF)
{
fprintf(stderr, "sym-rename: %s is not an ELF object file\n", filename);
elf_end(elf);
return NULL;
}
return elf;
}
static void usage()
{
fputs("Usage: sym-rename [-v] object-file prefix oldname\n", stderr);
exit(1);
}
int main(int argc, char *argv[])
{
Elf *elf;
int base = 1; /* index of first non-flag argument */
if (argc < 3 || argc > 5)
usage();
if (!strcmp(argv[1], "-v"))
{
verbose=1;
base=2;
}
elf = init(argv[base]);
if (elf == NULL)
exit(2);
prefix = argv[base+1];
prefixlen = strlen(argv[base+1]);
if(base+2 < argc) {
oldname = argv[base+2];
oldnamelen = strlen(argv[base+2]);
}else
{
oldnamelen = 0;
}
sym_rename(elf);
elf_end(elf);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment