summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analysis/segment-contains-section.c26
-rw-r--r--src/elfhandle.c74
-rw-r--r--src/lookup/first-section-in-segment.c34
-rw-r--r--src/lookup/last-section-in-segment.c49
-rw-r--r--src/lookup/section-by-name.c39
-rw-r--r--src/lookup/section-name.c22
-rw-r--r--src/main.c74
-rw-r--r--src/options.c81
-rw-r--r--src/printing/header.c62
-rw-r--r--src/printing/sections.c60
-rw-r--r--src/printing/segments.c76
11 files changed, 597 insertions, 0 deletions
diff --git a/src/analysis/segment-contains-section.c b/src/analysis/segment-contains-section.c
new file mode 100644
index 0000000..dc3269d
--- /dev/null
+++ b/src/analysis/segment-contains-section.c
@@ -0,0 +1,26 @@
+#include <libelf.h>
+#include <gelf.h>
+
+#include <libelfu/libelfu.h>
+
+
+ELFU_BOOL elfu_segmentContainsSection(GElf_Phdr *phdr, Elf_Scn *scn)
+{
+ GElf_Shdr shdr;
+
+
+ if (gelf_getshdr(scn, &shdr) != &shdr) {
+ return ELFU_ERROR;
+ }
+
+ size_t secStart = shdr.sh_offset;
+ size_t secEnd = shdr.sh_offset + shdr.sh_size;
+ size_t segStart = phdr->p_offset;
+ size_t segEnd = phdr->p_offset + phdr->p_memsz;
+
+ if (secStart < segStart || secEnd > segEnd) {
+ return ELFU_FALSE;
+ }
+
+ return ELFU_TRUE;
+}
diff --git a/src/elfhandle.c b/src/elfhandle.c
new file mode 100644
index 0000000..a17216d
--- /dev/null
+++ b/src/elfhandle.c
@@ -0,0 +1,74 @@
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libelf.h>
+
+#include "elfhandle.h"
+
+
+void openElf(ELFHandles *h, char *fn, Elf_Cmd elfmode)
+{
+ int openflags = 0;
+ int openmode = 0;
+
+ h->e = NULL;
+
+ switch(elfmode) {
+ case ELF_C_READ:
+ openflags = O_RDONLY;
+ break;
+ case ELF_C_WRITE:
+ openflags = O_WRONLY | O_CREAT;
+ openmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+ break;
+ case ELF_C_RDWR:
+ openflags = O_RDWR;
+ break;
+ default:
+ return;
+ }
+
+
+ h->fd = open(fn, openflags, openmode);
+ if (h->fd < 0) {
+ fprintf(stderr, "Cannot open %s: %s\n", fn, strerror(errno));
+ return;
+ }
+
+ h->e = elf_begin(h->fd, elfmode, NULL);
+ if (!h->e) {
+ fprintf(stderr, "elf_begin() failed on %s: %s\n", fn, elf_errmsg(-1));
+ goto ERR;
+ }
+
+ if (elfmode == ELF_C_READ || elfmode == ELF_C_RDWR) {
+ if (elf_kind(h->e) != ELF_K_ELF) {
+ fprintf(stderr, "Not an ELF object: %s", fn);
+ goto ERR;
+ }
+ }
+
+ return;
+
+ERR:
+ if (h->e) {
+ elf_end(h->e);
+ h->e = NULL;
+ }
+ close(h->fd);
+}
+
+
+void closeElf(ELFHandles *h)
+{
+ if (h->e) {
+ elf_end(h->e);
+ close(h->fd);
+ h->e = NULL;
+ }
+}
diff --git a/src/lookup/first-section-in-segment.c b/src/lookup/first-section-in-segment.c
new file mode 100644
index 0000000..b66c518
--- /dev/null
+++ b/src/lookup/first-section-in-segment.c
@@ -0,0 +1,34 @@
+#include <libelf.h>
+#include <gelf.h>
+
+#include <libelfu/libelfu.h>
+
+
+/*
+ * Returns the section that starts at the same point in the file as
+ * the segment AND is wholly contained in the memory image.
+ *
+ * If no section fits, NULL is returned.
+ */
+Elf_Scn* elfu_firstSectionInSegment(Elf *e, GElf_Phdr *phdr)
+{
+ Elf_Scn *scn;
+
+ scn = elf_getscn(e, 1);
+ while (scn) {
+ GElf_Shdr shdr;
+
+ if (gelf_getshdr(scn, &shdr) != &shdr) {
+ return NULL;
+ }
+
+ if (shdr.sh_offset == phdr->p_offset
+ && elfu_segmentContainsSection(phdr, scn) == ELFU_TRUE) {
+ return scn;
+ }
+
+ scn = elf_nextscn(e, scn);
+ }
+
+ return NULL;
+}
diff --git a/src/lookup/last-section-in-segment.c b/src/lookup/last-section-in-segment.c
new file mode 100644
index 0000000..0e78263
--- /dev/null
+++ b/src/lookup/last-section-in-segment.c
@@ -0,0 +1,49 @@
+#include <libelf.h>
+#include <gelf.h>
+
+#include <libelfu/libelfu.h>
+
+
+/*
+ * Returns the first section that is contained in the segment and
+ * ends as close to its memory image of as possible (the "last"
+ * section in the segment).
+ *
+ * If no section fits, NULL is returned.
+ */
+Elf_Scn* elfu_lastSectionInSegment(Elf *e, GElf_Phdr *phdr)
+{
+ Elf_Scn *last = NULL;
+ Elf_Scn *scn;
+
+
+ scn = elf_getscn(e, 1);
+ while (scn) {
+ if (elfu_segmentContainsSection(phdr, scn) == ELFU_TRUE) {
+ if (!last) {
+ last = scn;
+ } else {
+ GElf_Shdr shdrOld;
+ GElf_Shdr shdrNew;
+
+ if (gelf_getshdr(last, &shdrOld) != &shdrOld) {
+ continue;
+ }
+
+ if (gelf_getshdr(scn, &shdrNew) != &shdrNew) {
+ continue;
+ }
+
+ if (shdrNew.sh_offset + shdrNew.sh_size
+ > shdrOld.sh_offset + shdrOld.sh_size) {
+ // TODO: Check (leftover space in memory image) < (p_align)
+ last = scn;
+ }
+ }
+ }
+
+ scn = elf_nextscn(e, scn);
+ }
+
+ return last;
+}
diff --git a/src/lookup/section-by-name.c b/src/lookup/section-by-name.c
new file mode 100644
index 0000000..1697140
--- /dev/null
+++ b/src/lookup/section-by-name.c
@@ -0,0 +1,39 @@
+#include <string.h>
+
+#include <libelf.h>
+#include <gelf.h>
+
+#include <libelfu/libelfu.h>
+
+
+Elf_Scn* elfu_sectionByName(Elf *e, char *name)
+{
+ size_t shstrndx;
+ Elf_Scn *scn;
+
+ if (elf_getshdrstrndx(e, &shstrndx) != 0) {
+ return NULL;
+ }
+
+ scn = elf_getscn(e, 1);
+ while (scn) {
+ GElf_Shdr shdr;
+ char *curname;
+
+ if (gelf_getshdr(scn, &shdr) != &shdr) {
+ return NULL;
+ }
+
+ /* elf_strptr returns NULL if there was an error */
+ curname = elf_strptr(e, shstrndx, shdr.sh_name);
+
+ /* strcmp... but we really have no bounds on the lengths here */
+ if (!strcmp(curname, name)) {
+ return scn;
+ }
+
+ scn = elf_nextscn(e, scn);
+ }
+
+ return NULL;
+}
diff --git a/src/lookup/section-name.c b/src/lookup/section-name.c
new file mode 100644
index 0000000..d3748f9
--- /dev/null
+++ b/src/lookup/section-name.c
@@ -0,0 +1,22 @@
+#include <libelf.h>
+#include <gelf.h>
+
+#include <libelfu/libelfu.h>
+
+
+char* elfu_sectionName(Elf *e, Elf_Scn *scn)
+{
+ size_t shstrndx;
+ GElf_Shdr shdr;
+
+ if (elf_getshdrstrndx(e, &shstrndx) != 0) {
+ return NULL;
+ }
+
+ if (gelf_getshdr(scn, &shdr) != &shdr) {
+ return NULL;
+ }
+
+ /* elf_strptr returns NULL if there was an error */
+ return elf_strptr(e, shstrndx, shdr.sh_name);
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..9791feb
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,74 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <getopt.h>
+#include <libelf.h>
+#include <gelf.h>
+
+#include "elfhandle.h"
+#include "options.h"
+#include "printing.h"
+
+
+int main(int argc, char **argv)
+{
+ CLIOpts opts = { 0 };
+ ELFHandles hIn = { 0 };
+ ELFHandles hOut = { 0 };
+ int exitval = EXIT_SUCCESS;
+
+ /* Is libelf alive and well? */
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ fprintf(stderr, "libelf init error: %s\n", elf_errmsg(-1));
+ }
+
+
+ /* Parse and validate user input */
+ parseOptions(&opts, argc, argv);
+
+
+ /* Open input/output files */
+ openElf(&hIn, opts.fnInput, ELF_C_READ);
+ if (!hIn.e) {
+ exitval = EXIT_FAILURE;
+ goto EXIT;
+ }
+
+ if (opts.fnOutput) {
+ openElf(&hOut, opts.fnOutput, ELF_C_WRITE);
+ if (!hOut.e) {
+ exitval = EXIT_FAILURE;
+ goto EXIT;
+ }
+ }
+
+
+ /* Now that we have a (hopefully) sane environment, execute commands */
+ if (opts.printHeader) {
+ printHeader(hIn.e);
+ }
+
+ if (opts.printSegments) {
+ printSegments(hIn.e);
+ }
+
+ if (opts.printSections) {
+ printSections(hIn.e);
+ }
+
+
+
+EXIT:
+ if (hOut.e) {
+ if (elf_update(hOut.e, ELF_C_WRITE) < 0) {
+ fprintf(stderr, "elf_update() failed: %s\n", elf_errmsg(-1));
+ }
+ closeElf(&hOut);
+ }
+
+ if (hIn.e) {
+ closeElf(&hIn);
+ }
+
+ return (exitval);
+}
diff --git a/src/options.c b/src/options.c
new file mode 100644
index 0000000..014a1f2
--- /dev/null
+++ b/src/options.c
@@ -0,0 +1,81 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "options.h"
+
+
+static void printUsage(char *progname)
+{
+ printf("Usage: %s [OPTIONS] <elf-file>\n", progname);
+ printf("\n"
+ "Options:\n"
+ " -h, --help Print this help message\n"
+ " -o, --output Where to write the modified ELF file to\n"
+ "\n"
+ " --print-header Print ELF header\n"
+ " --print-segments Print program headers\n"
+ " --print-sections Print sections\n"
+ "\n");
+}
+
+
+
+void parseOptions(CLIOpts *opts, int argc, char **argv)
+{
+ char *progname = argv[0];
+ int c;
+ int option_index = 0;
+
+ static struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+ {"output", 1, 0, 'o'},
+ {"print-header", 0, 0, 10001},
+ {"print-segments", 0, 0, 10002},
+ {"print-sections", 0, 0, 10003},
+ {NULL, 0, NULL, 0}
+ };
+
+ while ((c = getopt_long(argc, argv, "e:ho:",
+ long_options, &option_index)) != -1) {
+ switch (c) {
+ case 'h':
+ printUsage(progname);
+ exit(EXIT_SUCCESS);
+ case 'o':
+ opts->fnOutput = optarg;
+ break;
+ case 10001:
+ opts->printHeader = 1;
+ break;
+ case 10002:
+ opts->printSegments = 1;
+ break;
+ case 10003:
+ opts->printSections = 1;
+ break;
+ case '?':
+ default:
+ goto USAGE;
+ }
+ }
+
+ while (optind < argc) {
+ if (!opts->fnInput) {
+ opts->fnInput = argv[optind];
+ }
+ optind++;
+ }
+
+ if (!opts->fnInput) {
+ fprintf(stderr, "Error: No input file specified.\n\n");
+ goto USAGE;
+ }
+
+ return;
+
+USAGE:
+ printUsage(progname);
+ exit(1);
+}
diff --git a/src/printing/header.c b/src/printing/header.c
new file mode 100644
index 0000000..33f7bdb
--- /dev/null
+++ b/src/printing/header.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+
+#include <libelf.h>
+#include <gelf.h>
+
+
+void printHeader(Elf *e)
+{
+ GElf_Ehdr ehdr;
+ int elfclass;
+ size_t shdrnum, shdrstrndx, phdrnum;
+
+
+ printf("ELF header:\n");
+
+
+ elfclass = gelf_getclass(e);
+ if (elfclass == ELFCLASSNONE) {
+ fprintf(stderr, "getclass() failed: %s\n", elf_errmsg(-1));
+ }
+ printf(" * %d-bit ELF object\n", elfclass == ELFCLASS32 ? 32 : 64);
+
+
+ if (!gelf_getehdr(e, &ehdr)) {
+ fprintf(stderr, "getehdr() failed: %s.", elf_errmsg(-1));
+ return;
+ }
+
+ printf(" * type machine version entry phoff shoff flags ehsize phentsize shentsize\n");
+ printf(" %8jx %8jx %8jx %8jx %8jx %8jx %8jx %8jx %9jx %9jx\n",
+ (uintmax_t) ehdr.e_type,
+ (uintmax_t) ehdr.e_machine,
+ (uintmax_t) ehdr.e_version,
+ (uintmax_t) ehdr.e_entry,
+ (uintmax_t) ehdr.e_phoff,
+ (uintmax_t) ehdr.e_shoff,
+ (uintmax_t) ehdr.e_flags,
+ (uintmax_t) ehdr.e_ehsize,
+ (uintmax_t) ehdr.e_phentsize,
+ (uintmax_t) ehdr.e_shentsize);
+
+
+ if (elf_getshdrnum(e, &shdrnum) != 0) {
+ fprintf(stderr, "getshdrnum() failed: %s\n", elf_errmsg(-1));
+ } else {
+ printf(" * (shnum): %u\n", shdrnum);
+ }
+
+ if (elf_getshdrstrndx(e, &shdrstrndx) != 0) {
+ fprintf(stderr, "getshdrstrndx() failed: %s\n", elf_errmsg(-1));
+ } else {
+ printf(" * (shstrndx): %u\n", shdrstrndx);
+ }
+
+ if (elf_getphdrnum(e, &phdrnum) != 0) {
+ fprintf(stderr, "getphdrnum() failed: %s\n", elf_errmsg(-1));
+ } else {
+ printf(" * (phnum): %u\n", phdrnum);
+ }
+
+ printf("\n");
+}
diff --git a/src/printing/sections.c b/src/printing/sections.c
new file mode 100644
index 0000000..6241a73
--- /dev/null
+++ b/src/printing/sections.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+
+#include <libelf.h>
+#include <gelf.h>
+
+#include <libelfu/libelfu.h>
+#include "printing.h"
+
+
+void printSegmentsWithSection(Elf *e, Elf_Scn *scn)
+{
+ GElf_Phdr phdr;
+ int i;
+ size_t n;
+
+
+ if (elf_getphdrnum(e, &n)) {
+ fprintf(stderr, "elf_getphdrnum() failed: %s\n", elf_errmsg(-1));
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ ELFU_BOOL isInSeg;
+
+ if (gelf_getphdr(e, i, &phdr) != &phdr) {
+ fprintf(stderr, "getphdr() failed for #%d: %s\n", i, elf_errmsg(-1));
+ continue;
+ }
+
+ isInSeg = elfu_segmentContainsSection(&phdr, scn);
+ if (isInSeg == ELFU_TRUE) {
+ printf(" %d %s\n", i, segmentTypeStr(phdr.p_type));
+ }
+ }
+}
+
+
+void printSection(Elf *e, Elf_Scn *scn)
+{
+ printf(" %jd: %s\n",
+ (uintmax_t) elf_ndxscn(scn),
+ elfu_sectionName(e, scn));
+}
+
+
+void printSections(Elf *e)
+{
+ Elf_Scn *scn;
+
+ printf("Sections:\n");
+
+ scn = elf_getscn(e, 0);
+
+ while (scn) {
+ printSection(e, scn);
+ //printSegmentsWithSection(e, scn);
+
+ scn = elf_nextscn(e, scn);
+ }
+}
diff --git a/src/printing/segments.c b/src/printing/segments.c
new file mode 100644
index 0000000..6d8cac7
--- /dev/null
+++ b/src/printing/segments.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+
+#include <libelf.h>
+#include <gelf.h>
+
+#include <libelfu/libelfu.h>
+
+
+static char *ptstr[] = {"NULL", "LOAD", "DYNAMIC", "INTERP", "NOTE", "SHLIB", "PHDR", "TLS", "NUM"};
+
+
+char* segmentTypeStr(size_t pt)
+{
+ if (pt >= 0 && pt <= PT_NUM) {
+ return ptstr[pt];
+ }
+
+ return "-?-";
+}
+
+
+void printSectionsInSegment(Elf *e, GElf_Phdr *phdr)
+{
+ Elf_Scn *scn;
+
+ scn = elf_getscn(e, 0);
+
+ while (scn) {
+ ELFU_BOOL isInSeg;
+
+ isInSeg = elfu_segmentContainsSection(phdr, scn);
+ if (isInSeg == ELFU_TRUE) {
+ printf(" %10u %s\n", elf_ndxscn(scn), elfu_sectionName(e, scn));
+ }
+
+ scn = elf_nextscn(e, scn);
+ }
+}
+
+
+void printSegments(Elf *e)
+{
+ size_t i, n;
+
+ if (elf_getphdrnum(e, &n)) {
+ fprintf(stderr, "elf_getphdrnum() failed: %s.", elf_errmsg(-1));
+ }
+
+ printf("Segments:\n");
+ printf(" # typeStr type offset vaddr paddr filesz memsz flags align\n");
+
+ for (i = 0; i < n; i++) {
+ GElf_Phdr phdr;
+
+ if (gelf_getphdr(e, i, &phdr) != &phdr) {
+ fprintf(stderr, "getphdr() failed for #%d: %s.", i, elf_errmsg(-1));
+ continue;
+ }
+
+ printf(" * %4d: %8s ", i, segmentTypeStr(phdr.p_type));
+
+ printf("%8jx %8jx %8jx %8jx %8jx %8jx %8jx %8jx\n",
+ (uintmax_t) phdr.p_type,
+ (uintmax_t) phdr.p_offset,
+ (uintmax_t) phdr.p_vaddr,
+ (uintmax_t) phdr.p_paddr,
+ (uintmax_t) phdr.p_filesz,
+ (uintmax_t) phdr.p_memsz,
+ (uintmax_t) phdr.p_flags,
+ (uintmax_t) phdr.p_align);
+
+ printSectionsInSegment(e, &phdr);
+ }
+
+ printf("\n");
+}