summaryrefslogtreecommitdiff
path: root/src/copy/segments.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/copy/segments.c')
-rw-r--r--src/copy/segments.c167
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);
+}