Created
October 22, 2012 01:51
-
-
Save xlab/3929227 to your computer and use it in GitHub Desktop.
Symbols replacer in da ELF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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