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(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->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