diff mbox

linux-user: MIPS set cpu to r6 CPU if binary is R6

Message ID 20171219115041.6611-1-syq@debian.org (mailing list archive)
State New, archived
Headers show

Commit Message

YunQiang Su Dec. 19, 2017, 11:50 a.m. UTC
MIPS r6 is not just simple super set for pre-R6,
it also drops some instruction and even changes encoding for some.
But r6 binary has the same header for binfmt_misc.

So here we need to detect the version of binaries and set
cpu_model for it.
---
 include/elf.h        |  4 ++++
 linux-user/elfload.c | 36 ++++++++++++++++++++++++++++++++++++
 linux-user/main.c    | 15 +++++++++++++++
 linux-user/qemu.h    |  1 +
 4 files changed, 56 insertions(+)

Comments

Laurent Vivier Dec. 23, 2017, 4:34 p.m. UTC | #1
Le 19/12/2017 à 12:50, YunQiang Su a écrit :
> MIPS r6 is not just simple super set for pre-R6,
> it also drops some instruction and even changes encoding for some.
> But r6 binary has the same header for binfmt_misc.
> So here we need to detect the version of binaries and set
> cpu_model for it.
> ---
>  include/elf.h        |  4 ++++
>  linux-user/elfload.c | 36 ++++++++++++++++++++++++++++++++++++
>  linux-user/main.c    | 15 +++++++++++++++
>  linux-user/qemu.h    |  1 +
>  4 files changed, 56 insertions(+)
> 
> diff --git a/include/elf.h b/include/elf.h
> index e8a515ce3d..f2104809b1 100644
> --- a/include/elf.h
> +++ b/include/elf.h
> @@ -40,6 +40,10 @@ typedef int64_t  Elf64_Sxword;
>  #define EF_MIPS_ARCH_5		0x40000000	/* -mips5 code.  */
>  #define EF_MIPS_ARCH_32		0x50000000	/* MIPS32 code.  */
>  #define EF_MIPS_ARCH_64		0x60000000	/* MIPS64 code.  */
> +#define EF_MIPS_ARCH_32R2       0x70000000      /* MIPS32r2 code.  */
> +#define EF_MIPS_ARCH_64R2       0x80000000      /* MIPS64r2 code.  */
> +#define EF_MIPS_ARCH_32R6       0x90000000      /* MIPS32r6 code.  */
> +#define EF_MIPS_ARCH_64R6       0xa0000000      /* MIPS64r6 code.  */
>  
>  /* The ABI of a file. */
>  #define EF_MIPS_ABI_O32		0x00001000	/* O32 ABI.  */
> diff --git a/linux-user/elfload.c b/linux-user/elfload.c
> index 20f3d8c2c3..f9b8e028ca 100644
> --- a/linux-user/elfload.c
> +++ b/linux-user/elfload.c
> @@ -2224,6 +2224,42 @@ static void load_elf_interp(const char *filename, struct image_info *info,
>      exit(-1);
>  }
>  
> +uint32_t get_elf_eflags(const char *filename)
> +{
> +    int fd, retval;
> +    char bprm_buf[BPRM_BUF_SIZE];
> +
> +    fd = open(path(filename), O_RDONLY);

You can't do that with binfmt and credential ('C' flag) enabled (it
implies 'O' flag, open-binary), because in this case the kernel opens
the file and provides the file descriptor to QEMU. We need the 'C' flags
to allow to execute binaries with the setuid flag (like "sudo") [1]

See linux-user/main.c:

   4446     execfd = qemu_getauxval(AT_EXECFD);
   4447     if (execfd == 0) {
   4448         execfd = open(filename, O_RDONLY);
   4449         if (execfd < 0) {
   4450             printf("Error while loading %s: %s\n", filename,
strerror(errno));
   4451             _exit(EXIT_FAILURE);
   4452         }
   4453     }

Thanks,
Laurent
[1]
https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/binfmt-misc.rst
YunQiang Su Dec. 24, 2017, 2:43 p.m. UTC | #2
should something like:

+    int fd, retval;
+    off_t cur_pos;
+    bool is_execfd = true;
+    char bprm_buf[BPRM_BUF_SIZE];
+
+    fd = qemu_getauxval(AT_EXECFD);
+    if (fd == 0){
+        is_execfd = false;
+        fd = open(path(filename), O_RDONLY);
+        if (fd < 0) {
+            return 0;
+        }
+    }else{
+        cur_pos = lseek(fd, 0, SEEK_CUR);
+       if ((cur_pos < 0) || (lseek(fd, 0, SEEK_SET) != 0)){
+            printf("Error while lseek in %s: %s\n", filename, strerror(errno));
+            _exit(EXIT_FAILURE);
+       }
+    }
+    retval = read(fd, bprm_buf, BPRM_BUF_SIZE);
+    if (!is_execfd)
+        close(fd);
+    else {
+       if (lseek(fd, cur_pos, SEEK_SET) != cur_pos){
+            printf("Error while lseek in %s: %s\n", filename, strerror(errno));
+            _exit(EXIT_FAILURE);
+        }
+    }
+
+    if (retval < 0) {
+        return 0;
+    }


works?

On Sun, Dec 24, 2017 at 12:34 AM, Laurent Vivier <laurent@vivier.eu> wrote:
> Le 19/12/2017 à 12:50, YunQiang Su a écrit :
>> MIPS r6 is not just simple super set for pre-R6,
>> it also drops some instruction and even changes encoding for some.
>> But r6 binary has the same header for binfmt_misc.
>> So here we need to detect the version of binaries and set
>> cpu_model for it.
>> ---
>>  include/elf.h        |  4 ++++
>>  linux-user/elfload.c | 36 ++++++++++++++++++++++++++++++++++++
>>  linux-user/main.c    | 15 +++++++++++++++
>>  linux-user/qemu.h    |  1 +
>>  4 files changed, 56 insertions(+)
>>
>> diff --git a/include/elf.h b/include/elf.h
>> index e8a515ce3d..f2104809b1 100644
>> --- a/include/elf.h
>> +++ b/include/elf.h
>> @@ -40,6 +40,10 @@ typedef int64_t  Elf64_Sxword;
>>  #define EF_MIPS_ARCH_5               0x40000000      /* -mips5 code.  */
>>  #define EF_MIPS_ARCH_32              0x50000000      /* MIPS32 code.  */
>>  #define EF_MIPS_ARCH_64              0x60000000      /* MIPS64 code.  */
>> +#define EF_MIPS_ARCH_32R2       0x70000000      /* MIPS32r2 code.  */
>> +#define EF_MIPS_ARCH_64R2       0x80000000      /* MIPS64r2 code.  */
>> +#define EF_MIPS_ARCH_32R6       0x90000000      /* MIPS32r6 code.  */
>> +#define EF_MIPS_ARCH_64R6       0xa0000000      /* MIPS64r6 code.  */
>>
>>  /* The ABI of a file. */
>>  #define EF_MIPS_ABI_O32              0x00001000      /* O32 ABI.  */
>> diff --git a/linux-user/elfload.c b/linux-user/elfload.c
>> index 20f3d8c2c3..f9b8e028ca 100644
>> --- a/linux-user/elfload.c
>> +++ b/linux-user/elfload.c
>> @@ -2224,6 +2224,42 @@ static void load_elf_interp(const char *filename, struct image_info *info,
>>      exit(-1);
>>  }
>>
>> +uint32_t get_elf_eflags(const char *filename)
>> +{
>> +    int fd, retval;
>> +    char bprm_buf[BPRM_BUF_SIZE];
>> +
>> +    fd = open(path(filename), O_RDONLY);
>
> You can't do that with binfmt and credential ('C' flag) enabled (it
> implies 'O' flag, open-binary), because in this case the kernel opens
> the file and provides the file descriptor to QEMU. We need the 'C' flags
> to allow to execute binaries with the setuid flag (like "sudo") [1]
>
> See linux-user/main.c:
>
>    4446     execfd = qemu_getauxval(AT_EXECFD);
>    4447     if (execfd == 0) {
>    4448         execfd = open(filename, O_RDONLY);
>    4449         if (execfd < 0) {
>    4450             printf("Error while loading %s: %s\n", filename,
> strerror(errno));
>    4451             _exit(EXIT_FAILURE);
>    4452         }
>    4453     }
>
> Thanks,
> Laurent
> [1]
> https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/binfmt-misc.rst
Laurent Vivier Jan. 8, 2018, 11:34 p.m. UTC | #3
Peter, Riku,

what do you think of the idea of using the ELF header to select the CPU
to emulate?

Thanks,
Laurent

Le 19/12/2017 à 12:50, YunQiang Su a écrit :
> MIPS r6 is not just simple super set for pre-R6,
> it also drops some instruction and even changes encoding for some.
> But r6 binary has the same header for binfmt_misc.
> 
> So here we need to detect the version of binaries and set
> cpu_model for it.
> ---
>  include/elf.h        |  4 ++++
>  linux-user/elfload.c | 36 ++++++++++++++++++++++++++++++++++++
>  linux-user/main.c    | 15 +++++++++++++++
>  linux-user/qemu.h    |  1 +
>  4 files changed, 56 insertions(+)
> 
> diff --git a/include/elf.h b/include/elf.h
> index e8a515ce3d..f2104809b1 100644
> --- a/include/elf.h
> +++ b/include/elf.h
> @@ -40,6 +40,10 @@ typedef int64_t  Elf64_Sxword;
>  #define EF_MIPS_ARCH_5		0x40000000	/* -mips5 code.  */
>  #define EF_MIPS_ARCH_32		0x50000000	/* MIPS32 code.  */
>  #define EF_MIPS_ARCH_64		0x60000000	/* MIPS64 code.  */
> +#define EF_MIPS_ARCH_32R2       0x70000000      /* MIPS32r2 code.  */
> +#define EF_MIPS_ARCH_64R2       0x80000000      /* MIPS64r2 code.  */
> +#define EF_MIPS_ARCH_32R6       0x90000000      /* MIPS32r6 code.  */
> +#define EF_MIPS_ARCH_64R6       0xa0000000      /* MIPS64r6 code.  */
>  
>  /* The ABI of a file. */
>  #define EF_MIPS_ABI_O32		0x00001000	/* O32 ABI.  */
> diff --git a/linux-user/elfload.c b/linux-user/elfload.c
> index 20f3d8c2c3..f9b8e028ca 100644
> --- a/linux-user/elfload.c
> +++ b/linux-user/elfload.c
> @@ -2224,6 +2224,42 @@ static void load_elf_interp(const char *filename, struct image_info *info,
>      exit(-1);
>  }
>  
> +uint32_t get_elf_eflags(const char *filename)
> +{
> +    int fd, retval;
> +    char bprm_buf[BPRM_BUF_SIZE];
> +
> +    fd = open(path(filename), O_RDONLY);
> +    if (fd < 0) {
> +        return 0;
> +    }
> +    retval = read(fd, bprm_buf, BPRM_BUF_SIZE);
> +    close(fd);
> +    if (retval < 0) {
> +        return 0;
> +    }
> +    if (retval < BPRM_BUF_SIZE) {
> +        memset(bprm_buf + retval, 0, BPRM_BUF_SIZE - retval);
> +    }
> +
> +    if (bprm_buf[0] != 0x7f
> +             || bprm_buf[1] != 'E'
> +             || bprm_buf[2] != 'L'
> +             || bprm_buf[3] != 'F') {
> +        return 0;
> +    }
> +
> +    struct elfhdr *ehdr = (struct elfhdr *)bprm_buf;
> +    if (!elf_check_ident(ehdr)) {
> +        return 0;
> +    }
> +    bswap_ehdr(ehdr);
> +    if (!elf_check_ehdr(ehdr)) {
> +        return 0;
> +    }
> +    return ehdr->e_flags;
> +}
> +
>  static int symfind(const void *s0, const void *s1)
>  {
>      target_ulong addr = *(target_ulong *)s0;
> diff --git a/linux-user/main.c b/linux-user/main.c
> index 7c0bffeff6..b4626e5aa0 100644
> --- a/linux-user/main.c
> +++ b/linux-user/main.c
> @@ -4287,6 +4287,21 @@ int main(int argc, char **argv, char **envp)
>      }
>      trace_init_file(trace_file);
>  
> +#if defined(TARGET_MIPS)
> +    if (cpu_model == NULL) {
> +        uint32_t eflags = get_elf_eflags(filename);
> +#if defined(TARGET_ABI_MIPSN32) || defined(TARGET_ABI_MIPSN64)
> +        if ((eflags & EF_MIPS_ARCH_64R6) != 0) {
> +            cpu_model = "I6400";
> +        }
> +#else
> +        if ((eflags & EF_MIPS_ARCH_32R6) != 0) {
> +            cpu_model = "mips32r6-generic";
> +        }
> +#endif
> +    }
> +#endif
> +
>      /* Zero out regs */
>      memset(regs, 0, sizeof(struct target_pt_regs));
>  
> diff --git a/linux-user/qemu.h b/linux-user/qemu.h
> index 4edd7d0c08..cf09110bf9 100644
> --- a/linux-user/qemu.h
> +++ b/linux-user/qemu.h
> @@ -190,6 +190,7 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp,
>  
>  int load_elf_binary(struct linux_binprm *bprm, struct image_info *info);
>  int load_flt_binary(struct linux_binprm *bprm, struct image_info *info);
> +uint32_t get_elf_eflags(const char *filename);
>  
>  abi_long memcpy_to_target(abi_ulong dest, const void *src,
>                            unsigned long len);
>
Richard Henderson Jan. 9, 2018, 3:39 p.m. UTC | #4
On 01/08/2018 03:34 PM, Laurent Vivier wrote:
> Peter, Riku,
> 
> what do you think of the idea of using the ELF header to select the CPU
> to emulate?

I think it's a good way to cut down on mistakes and reduce -- or at least not
increase -- the number of binaries we build.


r~
YunQiang Su Jan. 24, 2018, 1 p.m. UTC | #5
Signed-off-by: YunQiang Su <syq@debian.org>

On Tue, Jan 9, 2018 at 11:39 PM, Richard Henderson <richard.henderson@linaro.org> wrote:
> On 01/08/2018 03:34 PM, Laurent Vivier wrote:
>> Peter, Riku,
>> 
>> what do you think of the idea of using the ELF header to select the CPU
>> to emulate?
> 
> I think it's a good way to cut down on mistakes and reduce -- or at least not
> increase -- the number of binaries we build.
> 
> 
> r~
diff mbox

Patch

diff --git a/include/elf.h b/include/elf.h
index e8a515ce3d..f2104809b1 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -40,6 +40,10 @@  typedef int64_t  Elf64_Sxword;
 #define EF_MIPS_ARCH_5		0x40000000	/* -mips5 code.  */
 #define EF_MIPS_ARCH_32		0x50000000	/* MIPS32 code.  */
 #define EF_MIPS_ARCH_64		0x60000000	/* MIPS64 code.  */
+#define EF_MIPS_ARCH_32R2       0x70000000      /* MIPS32r2 code.  */
+#define EF_MIPS_ARCH_64R2       0x80000000      /* MIPS64r2 code.  */
+#define EF_MIPS_ARCH_32R6       0x90000000      /* MIPS32r6 code.  */
+#define EF_MIPS_ARCH_64R6       0xa0000000      /* MIPS64r6 code.  */
 
 /* The ABI of a file. */
 #define EF_MIPS_ABI_O32		0x00001000	/* O32 ABI.  */
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 20f3d8c2c3..f9b8e028ca 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -2224,6 +2224,42 @@  static void load_elf_interp(const char *filename, struct image_info *info,
     exit(-1);
 }
 
+uint32_t get_elf_eflags(const char *filename)
+{
+    int fd, retval;
+    char bprm_buf[BPRM_BUF_SIZE];
+
+    fd = open(path(filename), O_RDONLY);
+    if (fd < 0) {
+        return 0;
+    }
+    retval = read(fd, bprm_buf, BPRM_BUF_SIZE);
+    close(fd);
+    if (retval < 0) {
+        return 0;
+    }
+    if (retval < BPRM_BUF_SIZE) {
+        memset(bprm_buf + retval, 0, BPRM_BUF_SIZE - retval);
+    }
+
+    if (bprm_buf[0] != 0x7f
+             || bprm_buf[1] != 'E'
+             || bprm_buf[2] != 'L'
+             || bprm_buf[3] != 'F') {
+        return 0;
+    }
+
+    struct elfhdr *ehdr = (struct elfhdr *)bprm_buf;
+    if (!elf_check_ident(ehdr)) {
+        return 0;
+    }
+    bswap_ehdr(ehdr);
+    if (!elf_check_ehdr(ehdr)) {
+        return 0;
+    }
+    return ehdr->e_flags;
+}
+
 static int symfind(const void *s0, const void *s1)
 {
     target_ulong addr = *(target_ulong *)s0;
diff --git a/linux-user/main.c b/linux-user/main.c
index 7c0bffeff6..b4626e5aa0 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -4287,6 +4287,21 @@  int main(int argc, char **argv, char **envp)
     }
     trace_init_file(trace_file);
 
+#if defined(TARGET_MIPS)
+    if (cpu_model == NULL) {
+        uint32_t eflags = get_elf_eflags(filename);
+#if defined(TARGET_ABI_MIPSN32) || defined(TARGET_ABI_MIPSN64)
+        if ((eflags & EF_MIPS_ARCH_64R6) != 0) {
+            cpu_model = "I6400";
+        }
+#else
+        if ((eflags & EF_MIPS_ARCH_32R6) != 0) {
+            cpu_model = "mips32r6-generic";
+        }
+#endif
+    }
+#endif
+
     /* Zero out regs */
     memset(regs, 0, sizeof(struct target_pt_regs));
 
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 4edd7d0c08..cf09110bf9 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -190,6 +190,7 @@  int loader_exec(int fdexec, const char *filename, char **argv, char **envp,
 
 int load_elf_binary(struct linux_binprm *bprm, struct image_info *info);
 int load_flt_binary(struct linux_binprm *bprm, struct image_info *info);
+uint32_t get_elf_eflags(const char *filename);
 
 abi_long memcpy_to_target(abi_ulong dest, const void *src,
                           unsigned long len);