From: norly Date: Mon, 28 Jan 2013 02:14:24 +0000 (+0000) Subject: Initial work on copying an ELF file X-Git-Url: https://git.enpas.org/?a=commitdiff_plain;h=a78f477fc5a2327118bb129026e18ba8302ec7aa;p=centaur.git Initial work on copying an ELF file --- diff --git a/Makefile b/Makefile index df5f59f..726b92a 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,10 @@ LDFLAGS := -lelf default: $(EXE) +.PHONY: debug +debug: $(EXE) + gdb $(EXE) $(shell ps -e | sed "s/^ *\([0-9]\+\) .*$(PROJ).*$$/\1/g;te;d;:e") + $(EXE): $(OBJS) @if [ ! -d $(BUILDDIR) ] ; then echo "Error: Build dir '$(BUILDDIR)' does not exist." ; false ; fi diff --git a/include/libelfu/copy.h b/include/libelfu/copy.h new file mode 100644 index 0000000..341df88 --- /dev/null +++ b/include/libelfu/copy.h @@ -0,0 +1,9 @@ +#ifndef __LIBELFU_COPY_H_ +#define __LIBELFU_COPY_H_ + +#include +#include + +void elfu_copyElf(Elf *eo, Elf *ei); + +#endif diff --git a/include/libelfu/fixup.h b/include/libelfu/fixup.h new file mode 100644 index 0000000..6fb6620 --- /dev/null +++ b/include/libelfu/fixup.h @@ -0,0 +1,9 @@ +#ifndef __LIBELFU_FIXUP_H__ +#define __LIBELFU_FIXUP_H__ + +#include +#include + +void elfu_fixupPhdrSelfRef(Elf *e); + +#endif diff --git a/include/libelfu/libelfu.h b/include/libelfu/libelfu.h index 1c40541..ac9812b 100644 --- a/include/libelfu/libelfu.h +++ b/include/libelfu/libelfu.h @@ -5,6 +5,8 @@ #include #include +#include +#include #include diff --git a/src/copy/ehdr.c b/src/copy/ehdr.c new file mode 100644 index 0000000..b7c6232 --- /dev/null +++ b/src/copy/ehdr.c @@ -0,0 +1,52 @@ +#include + +#include +#include + +#include + + +void elfu_copyEhdr(Elf *eo, Elf *ei) +{ + int elfclass; + GElf_Ehdr ehdr, ehdrOut; + + elfclass = gelf_getclass(ei); + if (elfclass == ELFCLASSNONE) { + fprintf(stderr, "getclass() failed: %s\n", elf_errmsg(-1)); + } + + if (!gelf_getehdr(ei, &ehdr)) { + fprintf(stderr, "gelf_getehdr() failed: %s\n", elf_errmsg(-1)); + return; + } + + if (!gelf_newehdr(eo, elfclass)) { + fprintf(stderr, "gelf_newehdr() failed: %s\n", elf_errmsg(-1)); + } + + if (!gelf_getehdr(ei, &ehdrOut)) { + fprintf(stderr, "gelf_getehdr() failed: %s\n", elf_errmsg(-1)); + return; + } + + ehdrOut.e_ident[EI_DATA] = ehdr.e_ident[EI_DATA]; + ehdrOut.e_type = ehdr.e_type; + ehdrOut.e_machine = ehdr.e_machine; + ehdrOut.e_version = ehdr.e_version; + ehdrOut.e_entry = ehdr.e_entry; // FIXME + /* e_phoff */ + /* e_shoff */ + ehdrOut.e_flags = ehdr.e_flags; + /* e_ehsize */ + /* e_phentsize */ + /* e_phnum */ + /* e_shentsize */ + /* s_shnum */ + ehdrOut.e_shstrndx = ehdr.e_shstrndx; // FIXME + + + if (!gelf_update_ehdr(eo, &ehdrOut)) { + fprintf(stderr, "gelf_update_ehdr() failed: %s\n", elf_errmsg(-1)); + } +} diff --git a/src/copy/elf.c b/src/copy/elf.c new file mode 100644 index 0000000..445f98c --- /dev/null +++ b/src/copy/elf.c @@ -0,0 +1,30 @@ +#include + +#include +#include + +#include + + +void elfu_copyEhdr(Elf *eo, Elf *ei); +void elfu_copySections(Elf *eo, Elf *ei); +void elfu_copySegments1(Elf *eo, Elf *ei); +void elfu_copySegments2(Elf *eo, Elf *ei); + + +void elfu_copyElf(Elf *eo, Elf *ei) +{ + elfu_copyEhdr(eo, ei); + elfu_copySections(eo, ei); + elfu_copySegments1(eo, ei); + + /* Calculate file structure so we have section offsets */ + if (elf_update(eo, ELF_C_NULL) < 0) { + fprintf(stderr, "elf_update(NULL) failed: %s\n", elf_errmsg(-1)); + } + + /* Update the segment offsets and lengths */ + elfu_copySegments2(eo, ei); + + elfu_fixupPhdrSelfRef(eo); +} diff --git a/src/copy/section.c b/src/copy/section.c new file mode 100644 index 0000000..5a62593 --- /dev/null +++ b/src/copy/section.c @@ -0,0 +1,75 @@ +#include + +#include +#include + +#include + + +void elfu_copySection(Elf *eo, Elf_Scn *scn) +{ + Elf_Scn *scnOut; + Elf_Data *data; + GElf_Shdr shdr, shdrOut; + + scnOut = elf_newscn(eo); + if (!scnOut) { + fprintf(stderr, "elf_newscn() failed: %s\n", elf_errmsg(-1)); + return; + } + + if (gelf_getshdr(scn, &shdr) != &shdr) { + fprintf(stderr, "gelf_getshdr() failed: %s\n", elf_errmsg(-1)); + } + + + /* Copy section header */ + if (gelf_getshdr(scnOut, &shdrOut) != &shdrOut) { + fprintf(stderr, "gelf_getshdr() failed: %s\n", elf_errmsg(-1)); + } + + shdrOut.sh_name = shdr.sh_name; + shdrOut.sh_type = shdr.sh_type; + shdrOut.sh_flags = shdr.sh_flags; + /* sh_addr */ + shdrOut.sh_addr = shdr.sh_addr; + /* sh_offset */ + /* sh_size */ + shdrOut.sh_link = shdr.sh_link; + shdrOut.sh_info = shdr.sh_info; + shdrOut.sh_addralign = shdr.sh_addralign; + //shdrOut.sh_entsize = shdr.sh_entsize; + + if (!gelf_update_shdr(scnOut, &shdrOut)) { + fprintf(stderr, "gelf_update_shdr() failed: %s\n", elf_errmsg(-1)); + } + + + //elf_flagshdr(scnOut, ELF_C_SET, ELF_F_DIRTY); + //elf_flagscn(scnOut, ELF_C_SET, ELF_F_DIRTY); + + + /* Copy each data part in source segment */ + data = elf_rawdata(scn, NULL); + while (data) { + Elf_Data *dataOut = elf_newdata(scnOut); + if (!dataOut) { + fprintf(stderr, "elf_newdata() failed: %s\n", elf_errmsg(-1)); + } + + dataOut->d_align = data->d_align; + dataOut->d_buf = data->d_buf; + /* dataOut->d_off = data->d_off; */ + dataOut->d_type = data->d_type; + dataOut->d_size = data->d_size; + dataOut->d_version = data->d_version; + + //elf_flagdata(dataOut, ELF_C_SET, ELF_F_DIRTY); + + data = elf_rawdata(scn, data); + } + + // ehf_newdata() should flag the entire section dirty + //elf_flagshdr(scnOut, ELF_C_SET, ELF_F_DIRTY); + //elf_flagscn(scnOut, ELF_C_SET, ELF_F_DIRTY); +} diff --git a/src/copy/sections.c b/src/copy/sections.c new file mode 100644 index 0000000..046b590 --- /dev/null +++ b/src/copy/sections.c @@ -0,0 +1,25 @@ +#include + +#include +#include + +#include + + + +void elfu_copySection(Elf *eo, Elf_Scn *scn); + + + +void elfu_copySections(Elf *eo, Elf *ei) +{ + Elf_Scn *scn; + + scn = elf_getscn(ei, 1); + + while (scn) { + elfu_copySection(eo, scn); + + scn = elf_nextscn(ei, scn); + } +} diff --git a/src/copy/segments.c b/src/copy/segments.c new file mode 100644 index 0000000..0928de6 --- /dev/null +++ b/src/copy/segments.c @@ -0,0 +1,167 @@ +#include + +#include +#include + +#include + + + +void elfu_copySegments1(Elf *eo, Elf *ei) +{ + size_t i, n; + + if (elf_getphdrnum(ei, &n)) { + fprintf(stderr, "elf_getphdrnum() failed: %s\n", elf_errmsg(-1)); + } + + if (!gelf_newphdr(eo, n)) { + fprintf(stderr, "gelf_newphdr() failed: %s\n", elf_errmsg(-1)); + } + + for (i = 0; i < n; i++) { + GElf_Phdr phdr, phdrOut; + + if (gelf_getphdr(ei, i, &phdr) != &phdr) { + fprintf(stderr, "gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1)); + break; + } + + if (gelf_getphdr(eo, i, &phdrOut) != &phdrOut) { + fprintf(stderr, "gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1)); + break; + } + + phdrOut.p_type = phdr.p_type; + /* p_offset */ + phdrOut.p_vaddr = phdr.p_vaddr; + phdrOut.p_paddr = phdr.p_paddr; + /* p_filesz */ + /* p_memsz */ + phdrOut.p_flags = phdr.p_flags; + phdrOut.p_align = phdr.p_align; + + if (!gelf_update_phdr (eo, i, &phdrOut)) { + fprintf(stderr, "gelf_update_ehdr() failed: %s\n", elf_errmsg(-1)); + } + } + + /* Tell libelf that phdrs have changed */ + elf_flagphdr(eo, ELF_C_SET, ELF_F_DIRTY); +} + + +void elfu_copySegments2(Elf *eo, Elf *ei) +{ + size_t i, n; + + if (elf_getphdrnum(ei, &n)) { + fprintf(stderr, "elf_getphdrnum() failed: %s\n", elf_errmsg(-1)); + } + + for (i = 0; i < n; i++) { + GElf_Phdr phdr, phdrOut; + + if (gelf_getphdr(ei, i, &phdr) != &phdr) { + fprintf(stderr, "gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1)); + break; + } + + if (gelf_getphdr(eo, i, &phdrOut) != &phdrOut) { + fprintf(stderr, "gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1)); + break; + } + + /* Start of segment */ + if (phdr.p_type == PT_PHDR) { + /* Skip PHDR entries and fix them up later */ + } + else if (phdr.p_offset == 0) { + /* The text segment usually loads the whole ELF header */ + phdrOut.p_offset = 0; + } else { + /* Try to guess the start of the segment */ + Elf_Scn *scn, *scnOut; + GElf_Shdr shdr, shdrOut; + + scn = elfu_firstSectionInSegment(ei, &phdr); + if (!scn) { + fprintf(stderr, "elfu_firstSectionInSegment() failed for segment #%d\n", i); + continue; + } + + if (gelf_getshdr(scn, &shdr) != &shdr) { + fprintf(stderr, "gelf_getshdr() failed: %s\n", elf_errmsg(-1)); + } + + if (phdr.p_offset < shdr.sh_offset) { + fprintf(stderr, "elfu_copySegments2: Segment #%d starts before first contained section.\n", i); + } + + scnOut = elf_getscn(eo, elf_ndxscn(scn)); + if (!scnOut) { + fprintf(stderr, "elf_getscn() failed: %s\n", elf_errmsg(-1)); + } + + if (gelf_getshdr(scnOut, &shdrOut) != &shdrOut) { + fprintf(stderr, "gelf_getshdr() failed: %s\n", elf_errmsg(-1)); + } + + phdrOut.p_offset = shdrOut.sh_offset; + } + + /* File length of segment */ + if (phdr.p_type == PT_PHDR) { + /* Skip PHDR entries and fix them up later */ + } + else if (phdr.p_filesz == 0) { + phdrOut.p_filesz = 0; + } else { + /* Try to guess the end of the segment */ + Elf_Scn *scn, *scnOut; + GElf_Shdr shdr, shdrOut; + + scn = elfu_lastSectionInSegment(ei, &phdr); + if (!scn) { + fprintf(stderr, "elfu_lastSectionInSegment() failed for segment #%d\n", i); + continue; + } + + if (gelf_getshdr(scn, &shdr) != &shdr) { + fprintf(stderr, "gelf_getshdr() failed: %s\n", elf_errmsg(-1)); + } + + if (phdr.p_offset + phdr.p_filesz > shdr.sh_offset + shdr.sh_size) { + fprintf(stderr, "elfu_copySegments2: Segment #%d ends after last contained section.\n", i); + } + + scnOut = elf_getscn(eo, elf_ndxscn(scn)); + if (!scnOut) { + fprintf(stderr, "elf_getscn() failed: %s\n", elf_errmsg(-1)); + } + + if (gelf_getshdr(scnOut, &shdrOut) != &shdrOut) { + fprintf(stderr, "gelf_getshdr() failed: %s\n", elf_errmsg(-1)); + } + + phdrOut.p_filesz = shdrOut.sh_offset - phdrOut.p_offset + shdrOut.sh_size; + } + + if (phdr.p_type == PT_PHDR) { + /* Skip PHDR entries and fix them up later */ + } + else if (phdr.p_memsz == 0) { + phdrOut.p_memsz = 0; + } else { + // TODO: Calculate memory size (depends on .bss, etc.) + phdrOut.p_memsz = phdrOut.p_filesz; // FIXME + } + + if (!gelf_update_phdr (eo, i, &phdrOut)) { + fprintf(stderr, "gelf_update_ehdr() failed: %s\n", elf_errmsg(-1)); + } + } + + /* Tell libelf that phdrs have changed */ + elf_flagphdr(eo, ELF_C_SET, ELF_F_DIRTY); +} diff --git a/src/fixup/phdr.c b/src/fixup/phdr.c new file mode 100644 index 0000000..ccdc021 --- /dev/null +++ b/src/fixup/phdr.c @@ -0,0 +1,41 @@ +#include + +#include +#include + +void elfu_fixupPhdrSelfRef(Elf *e) +{ + GElf_Ehdr ehdr; + size_t i, n; + + if (!gelf_getehdr(e, &ehdr)) { + fprintf(stderr, "gelf_getehdr() failed: %s.", elf_errmsg(-1)); + return; + } + + if (elf_getphdrnum(e, &n)) { + fprintf(stderr, "elf_getphdrnum() failed: %s\n", elf_errmsg(-1)); + } + + for (i = 0; i < n; i++) { + GElf_Phdr phdr; + + if (gelf_getphdr(e, i, &phdr) != &phdr) { + fprintf(stderr, "gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1)); + continue; + } + + if (phdr.p_type == PT_PHDR) { + phdr.p_offset = ehdr.e_phoff; + phdr.p_filesz = elf32_fsize(ELF_T_PHDR, n, EV_CURRENT); + phdr.p_memsz = phdr.p_filesz; + + if (!gelf_update_phdr (e, i, &phdr)) { + fprintf(stderr, "gelf_update_ehdr() failed: %s\n", elf_errmsg(-1)); + } + } + } + + /* Tell libelf that phdrs have changed */ + elf_flagphdr(e, ELF_C_SET, ELF_F_DIRTY); +} diff --git a/src/main.c b/src/main.c index 9791feb..4ed0059 100644 --- a/src/main.c +++ b/src/main.c @@ -56,6 +56,11 @@ int main(int argc, char **argv) printSections(hIn.e); } + /* Copy the input ELF to the output file */ + if (opts.fnOutput) { + elfu_copyElf(hOut.e, hIn.e); + } + EXIT: