summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--elf.h19
-rw-r--r--linux-user/elfload.c71
-rw-r--r--linux-user/qemu.h7
3 files changed, 97 insertions, 0 deletions
diff --git a/elf.h b/elf.h
index 7067c90fb0..d2f24f466d 100644
--- a/elf.h
+++ b/elf.h
@@ -1191,6 +1191,25 @@ typedef struct elf64_note {
Elf64_Word n_type; /* Content type */
} Elf64_Nhdr;
+
+/* This data structure represents a PT_LOAD segment. */
+struct elf32_fdpic_loadseg {
+ /* Core address to which the segment is mapped. */
+ Elf32_Addr addr;
+ /* VMA recorded in the program header. */
+ Elf32_Addr p_vaddr;
+ /* Size of this segment in memory. */
+ Elf32_Word p_memsz;
+};
+struct elf32_fdpic_loadmap {
+ /* Protocol version number, must be zero. */
+ Elf32_Half version;
+ /* Number of segments in this map. */
+ Elf32_Half nsegs;
+ /* The actual memory map. */
+ struct elf32_fdpic_loadseg segs[/*nsegs*/];
+};
+
#ifdef ELF_CLASS
#if ELF_CLASS == ELFCLASS32
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 08c44d8933..2de83e4bfb 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1075,6 +1075,33 @@ static void zero_bss(abi_ulong elf_bss, abi_ulong last_bss, int prot)
}
}
+#ifdef CONFIG_USE_FDPIC
+static abi_ulong loader_build_fdpic_loadmap(struct image_info *info, abi_ulong sp)
+{
+ uint16_t n;
+ struct elf32_fdpic_loadseg *loadsegs = info->loadsegs;
+
+ /* elf32_fdpic_loadseg */
+ n = info->nsegs;
+ while (n--) {
+ sp -= 12;
+ put_user_u32(loadsegs[n].addr, sp+0);
+ put_user_u32(loadsegs[n].p_vaddr, sp+4);
+ put_user_u32(loadsegs[n].p_memsz, sp+8);
+ }
+
+ /* elf32_fdpic_loadmap */
+ sp -= 4;
+ put_user_u16(0, sp+0); /* version */
+ put_user_u16(info->nsegs, sp+2); /* nsegs */
+
+ info->personality = PER_LINUX_FDPIC;
+ info->loadmap_addr = sp;
+
+ return sp;
+}
+#endif
+
static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
struct elfhdr *exec,
struct image_info *info,
@@ -1087,6 +1114,21 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
const int n = sizeof(elf_addr_t);
sp = p;
+
+#ifdef CONFIG_USE_FDPIC
+ /* Needs to be before we load the env/argc/... */
+ if (elf_is_fdpic(exec)) {
+ /* Need 4 byte alignment for these structs */
+ sp &= ~3;
+ sp = loader_build_fdpic_loadmap(info, sp);
+ info->other_info = interp_info;
+ if (interp_info) {
+ interp_info->other_info = info;
+ sp = loader_build_fdpic_loadmap(interp_info, sp);
+ }
+ }
+#endif
+
u_platform = 0;
k_platform = ELF_PLATFORM;
if (k_platform) {
@@ -1197,6 +1239,11 @@ static void load_elf_image(const char *image_name, int image_fd,
}
bswap_phdr(phdr, ehdr->e_phnum);
+#ifdef CONFIG_USE_FDPIC
+ info->nsegs = 0;
+ info->pt_dynamic_addr = 0;
+#endif
+
/* Find the maximum size of the image and allocate an appropriate
amount of memory to handle that. */
loaddr = -1, hiaddr = 0;
@@ -1210,6 +1257,9 @@ static void load_elf_image(const char *image_name, int image_fd,
if (a > hiaddr) {
hiaddr = a;
}
+#ifdef CONFIG_USE_FDPIC
+ ++info->nsegs;
+#endif
}
}
@@ -1290,6 +1340,27 @@ static void load_elf_image(const char *image_name, int image_fd,
}
load_bias = load_addr - loaddr;
+#ifdef CONFIG_USE_FDPIC
+ {
+ struct elf32_fdpic_loadseg *loadsegs = info->loadsegs =
+ qemu_malloc(sizeof(*loadsegs) * info->nsegs);
+
+ for (i = 0; i < ehdr->e_phnum; ++i) {
+ switch (phdr[i].p_type) {
+ case PT_DYNAMIC:
+ info->pt_dynamic_addr = phdr[i].p_vaddr + load_bias;
+ break;
+ case PT_LOAD:
+ loadsegs->addr = phdr[i].p_vaddr + load_bias;
+ loadsegs->p_vaddr = phdr[i].p_vaddr;
+ loadsegs->p_memsz = phdr[i].p_memsz;
+ ++loadsegs;
+ break;
+ }
+ }
+ }
+#endif
+
info->load_bias = load_bias;
info->load_addr = load_addr;
info->entry = ehdr->e_entry + load_bias;
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 32de2413f8..250814d9f7 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -51,6 +51,13 @@ struct image_info {
abi_ulong arg_start;
abi_ulong arg_end;
int personality;
+#ifdef CONFIG_USE_FDPIC
+ abi_ulong loadmap_addr;
+ uint16_t nsegs;
+ void *loadsegs;
+ abi_ulong pt_dynamic_addr;
+ struct image_info *other_info;
+#endif
};
#ifdef TARGET_I386