On Thu, Dec 30, 2010 at 4:42 PM, H. Peter Anvin <h...@zytor.com> wrote:
> On 12/30/2010 01:08 PM, Robert Millan wrote:
>> Hi folks,
>>
>> I had this unsubmitted patch in my local filesystem.  It makes Linux
>> detect ELF32 AMD64 binaries and sets a flag to restrict them to
>> 32-bit address space.
>>
>> It's not rocket science but can save you some work in case you
>> haven't implemented this already.
>>
>
> I have pushed my old kernel patches to a git tree at:
>
> git://git.kernel.org//pub/scm/linux/kernel/git/hpa/linux-2.6-ilp32.git
>
> They are currently based on 2.6.31 since that was the released version
> when I first did this work; they are not intended to be mergeble but
> rather as a prototype.
>
> Note that we have no intention of supporting this ABI for the kernel
> itself.  The kernel will be a normal x86-64 kernel.
>

Here is the updated ILP32 patch for 2.6.35.


-- 
H.J.
diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c
index 588a7aa..ae915c8 100644
--- a/arch/x86/ia32/ia32_signal.c
+++ b/arch/x86/ia32/ia32_signal.c
@@ -42,6 +42,14 @@
 
 void signal_fault(struct pt_regs *regs, void __user *frame, char *where);
 
+/* We use the standard 64-bit versions of these for the ILP32 variants */
+int
+restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
+                  unsigned long *pax);
+int
+setup_sigcontext(struct sigcontext __user *sc, void __user *fpstate,
+                struct pt_regs *regs, unsigned long mask);
+
 int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
 {
        int err = 0;
@@ -565,3 +573,118 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, 
siginfo_t *info,
 
        return 0;
 }
+
+int ia32_setup_ilp32_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+                          compat_sigset_t *set, struct pt_regs *regs)
+{
+       struct rt_sigframe_ilp32 __user *frame;
+       void __user *restorer;
+       int err = 0;
+       void __user *fpstate = NULL;
+
+       /* __copy_to_user optimizes that into a single 8 byte store */
+       static const struct {
+               u8 movl;
+               u32 val;
+               u16 int80;
+               u8  pad;
+       } __attribute__((packed)) code = {
+               0xb8,
+               __NR_ia32_ilp32_sigreturn,
+               0x80cd,
+               0,
+       };
+
+       frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
+
+       if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+               return -EFAULT;
+
+       put_user_try {
+               put_user_ex(sig, &frame->sig);
+               put_user_ex(ptr_to_compat(&frame->info), &frame->pinfo);
+               put_user_ex(ptr_to_compat(&frame->uc), &frame->puc);
+               err |= copy_siginfo_to_user32(&frame->info, info);
+
+               /* Create the ucontext.  */
+               if (cpu_has_xsave)
+                       put_user_ex(UC_FP_XSTATE, &frame->uc.uc_flags);
+               else
+                       put_user_ex(0, &frame->uc.uc_flags);
+               put_user_ex(0, &frame->uc.uc_link);
+               put_user_ex(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
+               put_user_ex(sas_ss_flags(regs->sp),
+                           &frame->uc.uc_stack.ss_flags);
+               put_user_ex(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
+               err |= setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
+                                       regs, set->sig[0]);
+               err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
+               if (ka->sa.sa_flags & SA_RESTORER)
+                       restorer = ka->sa.sa_restorer;
+               else
+                       restorer = VDSO32_SYMBOL(current->mm->context.vdso,
+                                                rt_sigreturn);
+               put_user_ex(ptr_to_compat(restorer), &frame->pretcode);
+
+               /*
+                * Not actually used anymore, but left because some gdb
+                * versions need it.
+                */
+               put_user_ex(*((u64 *)&code), (u64 *)frame->retcode);
+       } put_user_catch(err);
+
+       if (err)
+               return -EFAULT;
+
+       /* Set up registers for signal handler */
+       regs->sp = (unsigned long) frame;
+       regs->ip = (unsigned long) ka->sa.sa_handler;
+
+       /* We use the 64-bit ILP32 calling convention here... */
+       regs->di = sig;
+       regs->si = (unsigned long) &frame->info;
+       regs->dx = (unsigned long) &frame->uc;
+
+       loadsegment(ds, __USER_DS);
+       loadsegment(es, __USER_DS);
+
+       regs->cs = __USER_CS;
+       regs->ss = __USER_DS;
+
+       return 0;
+}
+
+asmlinkage long sys32_ilp32_sigreturn(struct pt_regs *regs)
+{
+       struct rt_sigframe_ilp32 __user *frame;
+       sigset_t set;
+       unsigned long ax;
+       struct pt_regs tregs;
+
+       frame = (struct rt_sigframe_ilp32 __user *)(regs->sp - 4);
+
+       if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+               goto badframe;
+       if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+               goto badframe;
+
+       sigdelsetmask(&set, ~_BLOCKABLE);
+       spin_lock_irq(&current->sighand->siglock);
+       current->blocked = set;
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+
+       if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
+               goto badframe;
+
+       tregs = *regs;
+       if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT)
+               goto badframe;
+
+       return ax;
+
+badframe:
+       signal_fault(regs, frame, "64bit ilp32 sigreturn");
+       return 0;
+}
diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S
index 4f5f71e..51f9951 100644
--- a/arch/x86/ia32/ia32entry.S
+++ b/arch/x86/ia32/ia32entry.S
@@ -475,6 +475,7 @@ quiet_ni_syscall:
 
        CFI_STARTPROC32
 
+       PTREGSCALL stub32_ilp32_sigreturn, sys32_ilp32_sigreturn, %rdi
        PTREGSCALL stub32_rt_sigreturn, sys32_rt_sigreturn, %rdi
        PTREGSCALL stub32_sigreturn, sys32_sigreturn, %rdi
        PTREGSCALL stub32_sigaltstack, sys32_sigaltstack, %rdx
@@ -761,7 +762,7 @@ ia32_sys_call_table:
        .quad compat_sys_io_submit
        .quad sys_io_cancel
        .quad sys32_fadvise64           /* 250 */
-       .quad quiet_ni_syscall  /* free_huge_pages */
+       .quad stub32_ilp32_sigreturn
        .quad sys_exit_group
        .quad sys32_lookup_dcookie
        .quad sys_epoll_create
diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
index f2ad216..7393347 100644
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -5,6 +5,8 @@
  * ELF register definitions..
  */
 
+#include <linux/thread_info.h>
+
 #include <asm/ptrace.h>
 #include <asm/user.h>
 #include <asm/auxvec.h>
@@ -155,7 +157,12 @@ do {                                               \
 #define elf_check_arch(x)                      \
        ((x)->e_machine == EM_X86_64)
 
-#define compat_elf_check_arch(x)       elf_check_arch_ia32(x)
+#define compat_elf_check_arch(x)               \
+       (elf_check_arch_ia32(x) || (x)->e_machine == EM_X86_64)
+
+#if __USER32_DS != __USER_DS
+# error "The following code assumes __USER32_DS == __USER_DS"
+#endif
 
 static inline void elf_common_init(struct thread_struct *t,
                                   struct pt_regs *regs, const u16 ds)
@@ -178,8 +185,9 @@ static inline void elf_common_init(struct thread_struct *t,
 void start_thread_ia32(struct pt_regs *regs, u32 new_ip, u32 new_sp);
 #define compat_start_thread start_thread_ia32
 
-void set_personality_ia32(void);
-#define COMPAT_SET_PERSONALITY(ex) set_personality_ia32()
+void set_personality_ia32(bool);
+#define COMPAT_SET_PERSONALITY(ex)                     \
+       set_personality_ia32((ex).e_machine == EM_X86_64)
 
 #define COMPAT_ELF_PLATFORM                    ("i686")
 
@@ -277,7 +285,9 @@ do {                                                        
                \
 
 #define VDSO_HIGH_BASE         (__fix_to_virt(FIX_VDSO))
 
-#define ARCH_DLINFO            ARCH_DLINFO_IA32(vdso_enabled)
+#define ARCH_DLINFO                                                    \
+if (!test_thread_flag(TIF_64BIT_ILP32))                                        
\
+       ARCH_DLINFO_IA32(vdso_enabled)
 
 /* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT entries changes */
 
diff --git a/arch/x86/include/asm/ia32.h b/arch/x86/include/asm/ia32.h
index 1f7e625..e19de80 100644
--- a/arch/x86/include/asm/ia32.h
+++ b/arch/x86/include/asm/ia32.h
@@ -43,6 +43,14 @@ struct ucontext_ia32 {
        compat_sigset_t   uc_sigmask;   /* mask last for extensibility */
 };
 
+struct ucontext_ilp32 {
+       unsigned int      uc_flags;
+       unsigned int      uc_link;
+       stack_ia32_t      uc_stack;
+       struct sigcontext uc_mcontext;  /* the 64-bit sigcontext type */
+       compat_sigset_t   uc_sigmask;   /* mask last for extensibility */
+};
+
 /* This matches struct stat64 in glibc2.2, hence the absolutely
  * insane amounts of padding around dev_t's.
  */
diff --git a/arch/x86/include/asm/ia32_unistd.h 
b/arch/x86/include/asm/ia32_unistd.h
index 976f6ec..cc11c64 100644
--- a/arch/x86/include/asm/ia32_unistd.h
+++ b/arch/x86/include/asm/ia32_unistd.h
@@ -8,11 +8,12 @@
  * the number. This should be otherwise in sync with asm-x86/unistd_32.h. -AK
  */
 
-#define __NR_ia32_restart_syscall 0
-#define __NR_ia32_exit           1
-#define __NR_ia32_read           3
-#define __NR_ia32_write                  4
-#define __NR_ia32_sigreturn    119
-#define __NR_ia32_rt_sigreturn 173
+#define __NR_ia32_restart_syscall        0
+#define __NR_ia32_exit                   1
+#define __NR_ia32_read                   3
+#define __NR_ia32_write                          4
+#define __NR_ia32_sigreturn            119
+#define __NR_ia32_rt_sigreturn         173
+#define __NR_ia32_ilp32_sigreturn      251
 
 #endif /* _ASM_X86_IA32_UNISTD_H */
diff --git a/arch/x86/include/asm/sigframe.h b/arch/x86/include/asm/sigframe.h
index 4e0fe26..1e31107 100644
--- a/arch/x86/include/asm/sigframe.h
+++ b/arch/x86/include/asm/sigframe.h
@@ -59,6 +59,21 @@ struct rt_sigframe_ia32 {
 #endif /* defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION) */
 
 #ifdef CONFIG_X86_64
+
+#ifdef CONFIG_IA32_EMULATION
+struct rt_sigframe_ilp32 {
+       u32 pretcode;
+       int sig;
+       u32 pinfo;
+       u32 puc;
+       compat_siginfo_t info;
+       struct ucontext_ilp32 uc;
+       char retcode[8];
+       /* fp state follows here */
+};
+
+#endif /* CONFIG_IA32_EMULATION */
+
 struct rt_sigframe {
        char __user *pretcode;
        struct ucontext uc;
diff --git a/arch/x86/include/asm/signal.h b/arch/x86/include/asm/signal.h
index 598457c..4922358 100644
--- a/arch/x86/include/asm/signal.h
+++ b/arch/x86/include/asm/signal.h
@@ -94,6 +94,10 @@ typedef unsigned long sigset_t;
  *
  * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single
  * Unix names RESETHAND and NODEFER respectively.
+ *
+ * SA_GPR64 dumps the entire 64-bit GPR register set.  This flag is only
+ * applicable to the 32-bit system calls when running on a 64-bit kernel,
+ * and only when SA_SIGINFO is specified.
  */
 #define SA_NOCLDSTOP   0x00000001u
 #define SA_NOCLDWAIT   0x00000002u
@@ -107,6 +111,7 @@ typedef unsigned long sigset_t;
 #define SA_ONESHOT     SA_RESETHAND
 
 #define SA_RESTORER    0x04000000
+#define SA_GPR64       0x02000000
 
 /*
  * sigaltstack controls
diff --git a/arch/x86/include/asm/thread_info.h 
b/arch/x86/include/asm/thread_info.h
index f0b6e5d..7cf9feb 100644
--- a/arch/x86/include/asm/thread_info.h
+++ b/arch/x86/include/asm/thread_info.h
@@ -95,6 +95,7 @@ struct thread_info {
 #define TIF_BLOCKSTEP          25      /* set when we want DEBUGCTLMSR_BTF */
 #define TIF_LAZY_MMU_UPDATES   27      /* task is updating the mmu lazily */
 #define TIF_SYSCALL_TRACEPOINT 28      /* syscall tracepoint instrumentation */
+#define TIF_64BIT_ILP32                29      /* 32-bit native x86-64 binary 
*/
 
 #define _TIF_SYSCALL_TRACE     (1 << TIF_SYSCALL_TRACE)
 #define _TIF_NOTIFY_RESUME     (1 << TIF_NOTIFY_RESUME)
@@ -117,6 +118,7 @@ struct thread_info {
 #define _TIF_BLOCKSTEP         (1 << TIF_BLOCKSTEP)
 #define _TIF_LAZY_MMU_UPDATES  (1 << TIF_LAZY_MMU_UPDATES)
 #define _TIF_SYSCALL_TRACEPOINT        (1 << TIF_SYSCALL_TRACEPOINT)
+#define _TIF_64BIT_ILP32       (1 << TIF_64BIT_ILP32)
 
 /* work to do in syscall_trace_enter() */
 #define _TIF_WORK_SYSCALL_ENTRY        \
diff --git a/arch/x86/include/asm/unistd_32.h b/arch/x86/include/asm/unistd_32.h
index beb9b5f..ac96d72 100644
--- a/arch/x86/include/asm/unistd_32.h
+++ b/arch/x86/include/asm/unistd_32.h
@@ -256,7 +256,7 @@
 #define __NR_io_submit         248
 #define __NR_io_cancel         249
 #define __NR_fadvise64         250
-/* 251 is available for reuse (was briefly sys_set_zone_reclaim) */
+#define __NR_ilp32_sigreturn   251
 #define __NR_exit_group                252
 #define __NR_lookup_dcookie    253
 #define __NR_epoll_create      254
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index 3c2422a..239ce5a 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -355,7 +355,9 @@ start_thread(struct pt_regs *regs, unsigned long new_ip, 
unsigned long new_sp)
 void start_thread_ia32(struct pt_regs *regs, u32 new_ip, u32 new_sp)
 {
        start_thread_common(regs, new_ip, new_sp,
-                           __USER32_CS, __USER32_DS, __USER32_DS);
+                           test_thread_flag(TIF_64BIT_ILP32)
+                           ? __USER_CS : __USER32_CS,
+                           __USER_DS, __USER_DS);
 }
 #endif
 
@@ -507,12 +509,16 @@ void set_personality_64bit(void)
        current->personality &= ~READ_IMPLIES_EXEC;
 }
 
-void set_personality_ia32(void)
+void set_personality_ia32(bool ilp32)
 {
        /* inherit personality from parent */
 
        /* Make sure to be in 32bit mode */
        set_thread_flag(TIF_IA32);
+       if (ilp32)
+               set_thread_flag(TIF_64BIT_ILP32);
+       else
+               clear_thread_flag(TIF_64BIT_ILP32);
        current->personality |= force_personality32;
 
        /* Prepare the first "return" to user space */
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 70c4872..c20f6d1 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -1294,7 +1294,8 @@ void update_regset_xstate_info(unsigned int size, u64 
xstate_mask)
 const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 {
 #ifdef CONFIG_IA32_EMULATION
-       if (test_tsk_thread_flag(task, TIF_IA32))
+       if (test_tsk_thread_flag(task, TIF_IA32) &&
+           !test_tsk_thread_flag(task, TIF_64BIT_ILP32))
 #endif
 #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
                return &user_x86_32_view;
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index 4fd173c..6512444 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -68,7 +68,7 @@
        regs->seg = GET_SEG(seg) | 3;                   \
 } while (0)
 
-static int
+int
 restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
                   unsigned long *pax)
 {
@@ -125,7 +125,7 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext 
__user *sc,
        return err;
 }
 
-static int
+int
 setup_sigcontext(struct sigcontext __user *sc, void __user *fpstate,
                 struct pt_regs *regs, unsigned long mask)
 {
@@ -647,10 +647,12 @@ static int signr_convert(int sig)
 #define is_ia32        0
 #endif /* CONFIG_IA32_EMULATION */
 
+int ia32_setup_ilp32_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+                          sigset_t *set, struct pt_regs *regs);
 int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
-               sigset_t *set, struct pt_regs *regs);
+                       sigset_t *set, struct pt_regs *regs);
 int ia32_setup_frame(int sig, struct k_sigaction *ka,
-               sigset_t *set, struct pt_regs *regs);
+                    sigset_t *set, struct pt_regs *regs);
 
 #endif /* CONFIG_X86_32 */
 
@@ -664,7 +666,14 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t 
*info,
        /* Set up the stack frame */
        if (is_ia32) {
                if (ka->sa.sa_flags & SA_SIGINFO)
-                       ret = ia32_setup_rt_frame(usig, ka, info, set, regs);
+#ifdef CONFIG_X86_64
+                       if (ka->sa.sa_flags & SA_GPR64)
+                               ret = ia32_setup_ilp32_frame(usig, ka, info,
+                                                            set, regs);
+                       else
+#endif
+                               ret = ia32_setup_rt_frame(usig, ka, info,
+                                                         set, regs);
                else
                        ret = ia32_setup_frame(usig, ka, set, regs);
        } else
diff --git a/scripts/checksyscalls.sh b/scripts/checksyscalls.sh
index 66ad375..9a96111 100755
--- a/scripts/checksyscalls.sh
+++ b/scripts/checksyscalls.sh
@@ -145,6 +145,7 @@ cat << EOF
 #define __IGNORE_sysfs
 #define __IGNORE_uselib
 #define __IGNORE__sysctl
+#define __IGNORE_ilp32_sigreturn
 
 /* ... including the "new" 32-bit uid syscalls */
 #define __IGNORE_lchown32

Reply via email to