diff mbox

[RFC/PATCH] elfload: add FDPIC support

Message ID 1294562938-20097-1-git-send-email-vapier@gentoo.org
State New
Headers show

Commit Message

Mike Frysinger Jan. 9, 2011, 8:48 a.m. UTC
This is a PoC at this point, but it seems to be working for me.  At
least, all the current crashes I'm seeing are due to my Blackfin port
being incomplete.  All of the FDPIC table parsing seems to be OK ...

If someone with a more functional target would like to try this, that'd
be cool.  Or if people want to give feedback on how to approach this
problem so I can adjust the details now.

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 elf.h                |   19 ++++++++++++++
 linux-user/elfload.c |   67 ++++++++++++++++++++++++++++++++++++++++++++++++++
 linux-user/qemu.h    |    8 ++++++
 3 files changed, 94 insertions(+), 0 deletions(-)

Comments

Mike Frysinger Jan. 9, 2011, 6:20 p.m. UTC | #1
On Sun, Jan 9, 2011 at 03:48, Mike Frysinger wrote:
> This is a PoC at this point, but it seems to be working for me.  At
> least, all the current crashes I'm seeing are due to my Blackfin port
> being incomplete.  All of the FDPIC table parsing seems to be OK ...
>
> If someone with a more functional target would like to try this, that'd
> be cool.  Or if people want to give feedback on how to approach this
> problem so I can adjust the details now.

guess i should have CC-ed Riku on this ...
-mike
Stefano Bonifazi Jan. 24, 2011, 1:34 p.m. UTC | #2
On 01/09/2011 09:48 AM, Mike Frysinger wrote:
> This is a PoC at this point, but it seems to be working for me.  At
> least, all the current crashes I'm seeing are due to my Blackfin port
> being incomplete.  All of the FDPIC table parsing seems to be OK ...
>
> If someone with a more functional target would like to try this, that'd
> be cool.  Or if people want to give feedback on how to approach this
> problem so I can adjust the details now.
>
> Signed-off-by: Mike Frysinger<vapier@gentoo.org>
> ---
>   elf.h                |   19 ++++++++++++++
>   linux-user/elfload.c |   67 ++++++++++++++++++++++++++++++++++++++++++++++++++
>   linux-user/qemu.h    |    8 ++++++
>   3 files changed, 94 insertions(+), 0 deletions(-)
>
> diff --git a/elf.h b/elf.h
> index 7067c90..d2f24f4 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 33d776d..8100ffd 100644
> --- a/linux-user/elfload.c
> +++ b/linux-user/elfload.c
> @@ -1075,6 +1075,32 @@ 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 */
> +    for (n = 0; n<  info->nsegs; ++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 +1113,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 +1238,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 +1256,11 @@ static void load_elf_image(const char *image_name, int image_fd,
>               if (a>  hiaddr) {
>                   hiaddr = a;
>               }
> +#ifdef CONFIG_USE_FDPIC
> +            ++info->nsegs;
> +        } else if (phdr[i].p_type == PT_DYNAMIC) {
> +            info->pt_dynamic_addr = phdr[i].p_vaddr;
> +#endif
>           }
>       }
>
> @@ -1290,6 +1341,22 @@ 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) {
> +            if (phdr[i].p_type != PT_LOAD)
> +                continue;
> +            loadsegs->addr = phdr[i].p_vaddr + load_bias;
> +            loadsegs->p_vaddr = phdr[i].p_vaddr;
> +            loadsegs->p_memsz = phdr[i].p_memsz;
> +			++loadsegs;
> +        }
> +    }
> +#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 32de241..0924a1a 100644
> --- a/linux-user/qemu.h
> +++ b/linux-user/qemu.h
> @@ -51,6 +51,14 @@ struct image_info {
>           abi_ulong       arg_start;
>           abi_ulong       arg_end;
>   	int		personality;
> +#ifdef CONFIG_USE_FDPIC
> +#define FDPIC_MAX_LOAD_SEGS 4
> +        abi_ulong       loadmap_addr;
> +        uint16_t        nsegs;
> +        void           *loadsegs;
> +        abi_ulong       pt_dynamic_addr;
> +        struct image_info *other_info;
> +#endif
>   };
>
>   #ifdef TARGET_I386

Hi!
  Could you be so kind of explaining me, or addressing me to what this 
patch would do?
Is FDPIC something different than simply PIC code (position independent 
code)?
I am also trying to fight with the problem of changing the starting 
address of target code for qemu-user, and I was just moving into the 
option of using PIC target code .. but the original qemu-user 
load_elf_binary does not work on them.. and I was just about to try to 
edit it..
You would be more than a bless for me if you managed to do it! :)
You may want to have a look at my post:
http://lists.nongnu.org/archive/html/qemu-devel/2011-01/msg02361.html
Thank you very much in advance!
Stefano B
Mike Frysinger Jan. 24, 2011, 7:11 p.m. UTC | #3
On Mon, Jan 24, 2011 at 08:34, Stefano Bonifazi wrote:
> Is FDPIC something different than simply PIC code (position independent
> code)?

FDPIC ELF is the ELF PIE format used on NOMMU systems so that both the
text and data regions may be located anywhere.  it is the only ELF
format supported under NOMMU systems.

> I am also trying to fight with the problem of changing the starting address
> of target code for qemu-user, and I was just moving into the option of using
> PIC target code .. but the original qemu-user load_elf_binary does not work
> on them.. and I was just about to try to edit it..

i dont believe my patch accomplishes that in any way.  i will need to
force all files to be loaded at a higher address than zero for the
Blackfin arch (as the first ~4KiB is reserved for the ABI), but that
is independent of FDPIC ELF support.
-mike
Stefano Bonifazi Jan. 24, 2011, 9:06 p.m. UTC | #4
On 01/24/2011 08:11 PM, Mike Frysinger wrote:
> On Mon, Jan 24, 2011 at 08:34, Stefano Bonifazi wrote:
>> Is FDPIC something different than simply PIC code (position independent
>> code)?
> FDPIC ELF is the ELF PIE format used on NOMMU systems so that both the
> text and data regions may be located anywhere.  it is the only ELF
> format supported under NOMMU systems.
>
>> I am also trying to fight with the problem of changing the starting address
>> of target code for qemu-user, and I was just moving into the option of using
>> PIC target code .. but the original qemu-user load_elf_binary does not work
>> on them.. and I was just about to try to edit it..
> i dont believe my patch accomplishes that in any way.  i will need to
> force all files to be loaded at a higher address than zero for the
> Blackfin arch (as the first ~4KiB is reserved for the ABI), but that
> is independent of FDPIC ELF support.
> -mike
I don't understand.. what is the difference between pie binary for pcc 
and for your architecture?
As far as I understood pie code is independent from addresses, so it 
should not care if the OS running them would have an mmu at all.. it 
should be just the task of the dynamic linker to relocate it properly, 
am I wrong?
Thank you!
Regards,
Stefano B.
Mike Frysinger Jan. 24, 2011, 9:27 p.m. UTC | #5
On Mon, Jan 24, 2011 at 16:06, Stefano Bonifazi wrote:
> I don't understand.. what is the difference between pie binary for pcc and
> for your architecture?

as i said, i think this is all irrelevant to what you want to do.  but
since you asked and i feel like writing ...

i have no idea what "pcc" is.  there are really two stark differences
between FDPIC ELF and a PIE ELF on say x86.  since the data/text
sections can be relocated independently of each other, the PIC cannot
assume a fixed offset between its text and GOT.  so every PLT entry is
actually two sets of addresses -- the function address and the
function's GOT address.  the other big difference is that an FDPIC ELF
app must first do a little relocation processing of itself as soon as
it starts ... in order to do so, the kernel provides a loadmap (made
up of multiple loadsegs) which describes the executable's PT_LOADs
(the addr encoded in the program header and the addr the chunk was
actually relocated to) as well as the (optional) executable's
interpreter's PT_LOADs.

> As far as I understood pie code is independent from addresses, so it should
> not care if the OS running them would have an mmu at all.. it should be just
> the task of the dynamic linker to relocate it properly, am I wrong?

but the PIE code still has fixed offsets between its text and its
data.  so FDPIC ELF is even more flexible than a PIE ELF.
-mike
Stefano Bonifazi Jan. 24, 2011, 10:07 p.m. UTC | #6
On 01/24/2011 10:27 PM, Mike Frysinger wrote:
> On Mon, Jan 24, 2011 at 16:06, Stefano Bonifazi wrote:
>> I don't understand.. what is the difference between pie binary for pcc and
>> for your architecture?
> as i said, i think this is all irrelevant to what you want to do.  but
> since you asked and i feel like writing ...
>
> i have no idea what "pcc" is.  there are really two stark differences
> between FDPIC ELF and a PIE ELF on say x86.  since the data/text
> sections can be relocated independently of each other, the PIC cannot
> assume a fixed offset between its text and GOT.  so every PLT entry is
> actually two sets of addresses -- the function address and the
> function's GOT address.  the other big difference is that an FDPIC ELF
> app must first do a little relocation processing of itself as soon as
> it starts ... in order to do so, the kernel provides a loadmap (made
> up of multiple loadsegs) which describes the executable's PT_LOADs
> (the addr encoded in the program header and the addr the chunk was
> actually relocated to) as well as the (optional) executable's
> interpreter's PT_LOADs.
>
>> As far as I understood pie code is independent from addresses, so it should
>> not care if the OS running them would have an mmu at all.. it should be just
>> the task of the dynamic linker to relocate it properly, am I wrong?
> but the PIE code still has fixed offsets between its text and its
> data.  so FDPIC ELF is even more flexible than a PIE ELF.
> -mike
Thank you very much!
As a student, understanding how things work is even more precious than 
making things work ;)
You know?  I was just trying to figure out today how the code could 
locate the .got in x86 when relocation is needed..
I am studying ELF and relocation now, reading all possible material 
online, but I did not find yet about the fixed offset!

PPC stands for PowerPC.

Now also your code is much clearer for me, though not useful in my case..

Thank you again!
Best regards!
Stefano B.
diff mbox

Patch

diff --git a/elf.h b/elf.h
index 7067c90..d2f24f4 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 33d776d..8100ffd 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1075,6 +1075,32 @@  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 */
+    for (n = 0; n < info->nsegs; ++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 +1113,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 +1238,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 +1256,11 @@  static void load_elf_image(const char *image_name, int image_fd,
             if (a > hiaddr) {
                 hiaddr = a;
             }
+#ifdef CONFIG_USE_FDPIC
+            ++info->nsegs;
+        } else if (phdr[i].p_type == PT_DYNAMIC) {
+            info->pt_dynamic_addr = phdr[i].p_vaddr;
+#endif
         }
     }
 
@@ -1290,6 +1341,22 @@  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) {
+            if (phdr[i].p_type != PT_LOAD)
+                continue;
+            loadsegs->addr = phdr[i].p_vaddr + load_bias;
+            loadsegs->p_vaddr = phdr[i].p_vaddr;
+            loadsegs->p_memsz = phdr[i].p_memsz;
+			++loadsegs;
+        }
+    }
+#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 32de241..0924a1a 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -51,6 +51,14 @@  struct image_info {
         abi_ulong       arg_start;
         abi_ulong       arg_end;
 	int		personality;
+#ifdef CONFIG_USE_FDPIC
+#define FDPIC_MAX_LOAD_SEGS 4
+        abi_ulong       loadmap_addr;
+        uint16_t        nsegs;
+        void           *loadsegs;
+        abi_ulong       pt_dynamic_addr;
+        struct image_info *other_info;
+#endif
 };
 
 #ifdef TARGET_I386