diff options
Diffstat (limited to 'src/copy/segments.c')
-rw-r--r-- | src/copy/segments.c | 167 |
1 files changed, 167 insertions, 0 deletions
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 <stdio.h> + +#include <libelf.h> +#include <gelf.h> + +#include <libelfu/libelfu.h> + + + +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); +} |