diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 1992b8fe6a2f..d89b94528153 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1532,6 +1532,7 @@ source "fs/Kconfig.binfmt" config IA32_EMULATION bool "IA32 Emulation" depends on X86_64 + select COMPAT_BINFMT_ELF help Include code to run 32-bit programs under a 64-bit kernel. You should likely turn this on, unless you're 100% sure that you don't have any diff --git a/arch/x86/ia32/Makefile b/arch/x86/ia32/Makefile index 93a6fda65f49..52d0ccfcf6ea 100644 --- a/arch/x86/ia32/Makefile +++ b/arch/x86/ia32/Makefile @@ -2,8 +2,7 @@ # Makefile for the ia32 kernel emulation subsystem. # -obj-$(CONFIG_IA32_EMULATION) := ia32entry.o sys_ia32.o ia32_signal.o \ - ia32_binfmt.o +obj-$(CONFIG_IA32_EMULATION) := ia32entry.o sys_ia32.o ia32_signal.o sysv-$(CONFIG_SYSVIPC) := ipc32.o obj-$(CONFIG_IA32_EMULATION) += $(sysv-y) diff --git a/arch/x86/vdso/vdso32-setup.c b/arch/x86/vdso/vdso32-setup.c index e0feb66a2408..348f1341e1c8 100644 --- a/arch/x86/vdso/vdso32-setup.c +++ b/arch/x86/vdso/vdso32-setup.c @@ -377,6 +377,39 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int exstack) __initcall(sysenter_setup); +#ifdef CONFIG_SYSCTL +/* Register vsyscall32 into the ABI table */ +#include + +static ctl_table abi_table2[] = { + { + .procname = "vsyscall32", + .data = &sysctl_vsyscall32, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, + {} +}; + +static ctl_table abi_root_table2[] = { + { + .ctl_name = CTL_ABI, + .procname = "abi", + .mode = 0555, + .child = abi_table2 + }, + {} +}; + +static __init int ia32_binfmt_init(void) +{ + register_sysctl_table(abi_root_table2); + return 0; +} +__initcall(ia32_binfmt_init); +#endif + #else /* CONFIG_X86_32 */ const char *arch_vma_name(struct vm_area_struct *vma) diff --git a/include/asm-x86/elf.h b/include/asm-x86/elf.h index d6bf7421d7b0..dab4744f9586 100644 --- a/include/asm-x86/elf.h +++ b/include/asm-x86/elf.h @@ -73,6 +73,9 @@ typedef struct user_fxsr_struct elf_fpxregset_t; #endif #ifdef __KERNEL__ +#include + +extern unsigned int vdso_enabled; /* * This is used to ensure we don't load something for the wrong architecture. @@ -84,7 +87,6 @@ typedef struct user_fxsr_struct elf_fpxregset_t; #include #include /* for savesegment */ #include -#include #define elf_check_arch(x) elf_check_arch_ia32(x) @@ -106,7 +108,6 @@ typedef struct user_fxsr_struct elf_fpxregset_t; #define ELF_PLATFORM (utsname()->machine) #define set_personality_64bit() do { } while (0) -extern unsigned int vdso_enabled; #else /* CONFIG_X86_32 */ @@ -118,29 +119,57 @@ extern unsigned int vdso_enabled; #define elf_check_arch(x) \ ((x)->e_machine == EM_X86_64) +#define compat_elf_check_arch(x) elf_check_arch_ia32(x) + +static inline void start_ia32_thread(struct pt_regs *regs, u32 ip, u32 sp) +{ + asm volatile("movl %0,%%fs" :: "r" (0)); + asm volatile("movl %0,%%es; movl %0,%%ds" : : "r" (__USER32_DS)); + load_gs_index(0); + regs->ip = ip; + regs->sp = sp; + regs->flags = X86_EFLAGS_IF; + regs->cs = __USER32_CS; + regs->ss = __USER32_DS; +} + +static inline void elf_common_init(struct thread_struct *t, + struct pt_regs *regs, const u16 ds) +{ + regs->ax = regs->bx = regs->cx = regs->dx = 0; + regs->si = regs->di = regs->bp = 0; + regs->r8 = regs->r9 = regs->r10 = regs->r11 = 0; + regs->r12 = regs->r13 = regs->r14 = regs->r15 = 0; + t->fs = t->gs = 0; + t->fsindex = t->gsindex = 0; + t->ds = t->es = ds; +} + #define ELF_PLAT_INIT(_r, load_addr) do { \ - struct task_struct *cur = current; \ - (_r)->bx = 0; (_r)->cx = 0; (_r)->dx = 0; \ - (_r)->si = 0; (_r)->di = 0; (_r)->bp = 0; \ - (_r)->ax = 0; \ - (_r)->r8 = 0; \ - (_r)->r9 = 0; \ - (_r)->r10 = 0; \ - (_r)->r11 = 0; \ - (_r)->r12 = 0; \ - (_r)->r13 = 0; \ - (_r)->r14 = 0; \ - (_r)->r15 = 0; \ - cur->thread.fs = 0; cur->thread.gs = 0; \ - cur->thread.fsindex = 0; cur->thread.gsindex = 0; \ - cur->thread.ds = 0; cur->thread.es = 0; \ + elf_common_init(¤t->thread, _r, 0); \ clear_thread_flag(TIF_IA32); \ } while (0) +#define COMPAT_ELF_PLAT_INIT(regs, load_addr) \ + elf_common_init(¤t->thread, regs, __USER_DS) +#define compat_start_thread(regs, ip, sp) do { \ + start_ia32_thread(regs, ip, sp); \ + set_fs(USER_DS); \ + } while (0) +#define COMPAT_SET_PERSONALITY(ex, ibcs2) do { \ + if (test_thread_flag(TIF_IA32)) \ + clear_thread_flag(TIF_ABI_PENDING); \ + else \ + set_thread_flag(TIF_ABI_PENDING); \ + current->personality |= force_personality32; \ + } while (0) +#define COMPAT_ELF_PLATFORM ("i686") + /* I'm not sure if we can use '-' here */ #define ELF_PLATFORM ("x86_64") extern void set_personality_64bit(void); -extern int vdso_enabled; +extern unsigned int sysctl_vsyscall32; +extern int force_personality32; #endif /* !CONFIG_X86_32 */ @@ -179,18 +208,20 @@ extern int vdso_enabled; struct task_struct; -#ifdef CONFIG_X86_32 - -#define VDSO_HIGH_BASE (__fix_to_virt(FIX_VDSO)) - -/* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT entries changes */ - -#define ARCH_DLINFO \ +#define ARCH_DLINFO_IA32(vdso_enabled) \ do if (vdso_enabled) { \ NEW_AUX_ENT(AT_SYSINFO, VDSO_ENTRY); \ NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_CURRENT_BASE); \ } while (0) +#ifdef CONFIG_X86_32 + +#define VDSO_HIGH_BASE (__fix_to_virt(FIX_VDSO)) + +#define ARCH_DLINFO ARCH_DLINFO_IA32(vdso_enabled) + +/* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT entries changes */ + #else /* CONFIG_X86_32 */ #define VDSO_HIGH_BASE 0xffffe000U /* CONFIG_COMPAT_VDSO address */ @@ -203,6 +234,12 @@ do if (vdso_enabled) { \ NEW_AUX_ENT(AT_SYSINFO_EHDR,(unsigned long)current->mm->context.vdso);\ } while (0) +#define AT_SYSINFO 32 + +#define COMPAT_ARCH_DLINFO ARCH_DLINFO_IA32(sysctl_vsyscall32) + +#define COMPAT_ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE + 0x1000000) + #endif /* !CONFIG_X86_32 */ #define VDSO_CURRENT_BASE ((unsigned long)current->mm->context.vdso) @@ -216,6 +253,9 @@ struct linux_binprm; extern int arch_setup_additional_pages(struct linux_binprm *bprm, int executable_stack); +extern int syscall32_setup_pages(struct linux_binprm *, int exstack); +#define compat_arch_setup_additional_pages syscall32_setup_pages + extern unsigned long arch_randomize_brk(struct mm_struct *mm); #define arch_randomize_brk arch_randomize_brk