summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornorly <ny-git@enpas.org>2013-06-18 08:47:33 +0100
committernorly <ny-git@enpas.org>2013-06-18 08:49:07 +0100
commitc98d704a812502c34d82e34949f37c8b87ae6018 (patch)
treeab0ea9890143ab40ead5e07bb9be29ac874e8ccd
parentf88e1ad7d9db8a41abecc795200f21138af65c74 (diff)
Basic detour support
-rw-r--r--include/libelfu/modelops.h3
-rw-r--r--src/elfucli.c38
-rw-r--r--src/modelops/detour.c51
-rw-r--r--src/modelops/symtab.c46
4 files changed, 138 insertions, 0 deletions
diff --git a/include/libelfu/modelops.h b/include/libelfu/modelops.h
index cf11b20..fec6821 100644
--- a/include/libelfu/modelops.h
+++ b/include/libelfu/modelops.h
@@ -11,6 +11,7 @@
GElf_Word elfu_mSymtabLookupVal(ElfuElf *me, ElfuScn *msst, GElf_Word entry);
+GElf_Word elfu_mSymtabLookupAddrByName(ElfuElf *me, ElfuScn *msst, char *name);
void elfu_mSymtabFlatten(ElfuElf *me);
@@ -54,4 +55,6 @@ ElfuElf* elfu_mFromElf(Elf *e);
void elfu_mReladd(ElfuElf *me, const ElfuElf *mrel);
+void elfu_mDetour(ElfuElf *me, GElf_Addr from, GElf_Addr to);
+
#endif
diff --git a/src/elfucli.c b/src/elfucli.c
index 0fd5a2d..cd8f0ba 100644
--- a/src/elfucli.c
+++ b/src/elfucli.c
@@ -1,5 +1,6 @@
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <getopt.h>
#include <libelfu/libelfu.h>
@@ -19,6 +20,7 @@ static void printUsage(char *progname)
" -c, --check Do a few sanity checks and print any errors\n"
" -d, --dump Dump current model state (debug only)\n"
" --reladd obj.o Insert object file contents\n"
+ " --detour from,to Write a jump to <to> at <from>\n"
" -o, --output outfile Where to write the modified ELF file to\n"
"\n");
}
@@ -42,6 +44,7 @@ int main(int argc, char **argv)
{"input", 1, 0, 'i'},
{"output", 1, 0, 'o'},
{"reladd", 1, 0, 10001},
+ {"detour", 1, 0, 10002},
{NULL, 0, NULL, 0}
};
@@ -148,6 +151,41 @@ int main(int argc, char **argv)
}
}
break;
+ case 10002:
+ if (!me) {
+ goto ERR_NO_INPUT;
+ } else {
+ GElf_Addr from;
+ GElf_Addr to;
+ char *second;
+
+ strtok_r(optarg, ",", &second);
+ printf("--detour: From '%s' to '%s'\n", optarg, second);
+
+
+ from = strtoul(optarg, NULL, 0);
+ if (from == 0) {
+ from = elfu_mSymtabLookupAddrByName(me, me->symtab, optarg);
+ }
+ if (from == 0) {
+ printf("--detour: Cannot parse argument 1, aborting.\n");
+ goto ERR;
+ }
+ printf("--detour: From %x\n", (unsigned)from);
+
+ to = strtoul(second, NULL, 0);
+ if (to == 0) {
+ to = elfu_mSymtabLookupAddrByName(me, me->symtab, second);
+ }
+ if (to == 0) {
+ printf("--detour: Cannot parse argument 2, aborting.\n");
+ goto ERR;
+ }
+ printf("--detour: To %x\n", (unsigned)to);
+
+ elfu_mDetour(me, from, to);
+ }
+ break;
case '?':
default:
printUsage(progname);
diff --git a/src/modelops/detour.c b/src/modelops/detour.c
new file mode 100644
index 0000000..075d945
--- /dev/null
+++ b/src/modelops/detour.c
@@ -0,0 +1,51 @@
+#include <libelfu/libelfu.h>
+#include <string.h>
+
+static void* subFindByAddr(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
+{
+ GElf_Addr a = *(GElf_Addr*)aux1;
+
+ if (OVERLAPPING(ms->shdr.sh_addr, ms->shdr.sh_size, a, 1)) {
+ return ms;
+ }
+
+ /* Continue */
+ return NULL;
+}
+
+
+void elfu_mDetour(ElfuElf *me, GElf_Addr from, GElf_Addr to)
+{
+ ElfuScn *ms;
+ GElf_Word scnoffset;
+ unsigned char detourcode[] = {0xe9, 0xfc, 0xff, 0xff, 0xff};
+
+ ms = elfu_mScnForall(me, subFindByAddr, &from, NULL);
+
+ if (!ms) {
+ ELFU_WARN("mDetour: Cannot find address %x in any section.\n",
+ (unsigned)from);
+ return;
+ }
+
+ if (ms->shdr.sh_type != SHT_PROGBITS) {
+ ELFU_WARN("mDetour: Cannot detour in non-PROGBITS section %s.\n",
+ elfu_mScnName(me, ms));
+ return;
+ }
+
+ scnoffset = from - ms->shdr.sh_addr;
+
+ if (ms->shdr.sh_size - scnoffset < 5) {
+ ELFU_WARN("mDetour: Not enough space to insert a detour.\n");
+ return;
+ }
+
+ ELFU_DEBUG("mDetour: Detouring at address %x in section %s to %x.\n",
+ (unsigned)from,
+ elfu_mScnName(me, ms),
+ (unsigned)to);
+
+ *(Elf32_Word*)(detourcode + 1) = to - from - 5;
+ memcpy((char*)ms->data.d_buf + scnoffset, detourcode, 5);
+}
diff --git a/src/modelops/symtab.c b/src/modelops/symtab.c
index e62871f..ef8443f 100644
--- a/src/modelops/symtab.c
+++ b/src/modelops/symtab.c
@@ -141,6 +141,52 @@ GElf_Word elfu_mSymtabLookupVal(ElfuElf *me, ElfuScn *msst, GElf_Word entry)
}
+/* Look up a value in the symbol table section *msst which is in *me. */
+GElf_Word elfu_mSymtabLookupAddrByName(ElfuElf *me, ElfuScn *msst, char *name)
+{
+ ElfuSym *sym;
+
+ assert(me);
+ assert(msst);
+ assert(name);
+ assert(strlen(name) > 0);
+ assert(!CIRCLEQ_EMPTY(&msst->symtab.syms));
+
+ CIRCLEQ_FOREACH(sym, &msst->symtab.syms, elem) {
+ char *symname = ELFU_SYMSTR(msst, sym->name);
+
+ if (!strcmp(symname, name)) {
+ goto SYMBOL_FOUND;
+ }
+ }
+ return 0;
+
+
+ SYMBOL_FOUND:
+
+ switch (sym->type) {
+ case STT_NOTYPE:
+ case STT_OBJECT:
+ case STT_FUNC:
+ if (sym->scnptr) {
+ GElf_Addr a = sym->value;
+ a += me->ehdr.e_type == ET_REL ? sym->scnptr->shdr.sh_addr : 0;
+ return a;
+ } else if (sym->shndx == SHN_UNDEF) {
+ return 0;
+ } else if (sym->shndx == SHN_ABS) {
+ return sym->value;
+ } else {
+ ELFU_WARN("elfu_mSymtabLookupAddrByName: Symbol binding COMMON is not supported, using 0.\n");
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+ }
+}
+
+
/* Convert symtab from memory model to elfclass specific format */
void elfu_mSymtabFlatten(ElfuElf *me)