| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /*  | 
 | 2 |  * Written 2000,2002 by Andi Kleen.  | 
 | 3 |  *  | 
 | 4 |  * Loosely based on the sparc64 and IA64 32bit emulation loaders. | 
 | 5 |  * This tricks binfmt_elf.c into loading 32bit binaries using lots  | 
 | 6 |  * of ugly preprocessor tricks. Talk about very very poor man's inheritance. | 
 | 7 |  */  | 
| Ralf Baechle | 9f7290e | 2007-05-02 19:27:08 +0200 | [diff] [blame] | 8 | #define __ASM_X86_64_ELF_H 1 | 
 | 9 |  | 
 | 10 | #undef ELF_CLASS | 
 | 11 | #define ELF_CLASS ELFCLASS32 | 
 | 12 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 13 | #include <linux/types.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 14 | #include <linux/stddef.h> | 
 | 15 | #include <linux/rwsem.h> | 
 | 16 | #include <linux/sched.h> | 
 | 17 | #include <linux/compat.h> | 
 | 18 | #include <linux/string.h> | 
 | 19 | #include <linux/binfmts.h> | 
 | 20 | #include <linux/mm.h> | 
 | 21 | #include <linux/security.h> | 
 | 22 |  | 
 | 23 | #include <asm/segment.h>  | 
 | 24 | #include <asm/ptrace.h> | 
 | 25 | #include <asm/processor.h> | 
 | 26 | #include <asm/user32.h> | 
 | 27 | #include <asm/sigcontext32.h> | 
 | 28 | #include <asm/fpu32.h> | 
 | 29 | #include <asm/i387.h> | 
 | 30 | #include <asm/uaccess.h> | 
 | 31 | #include <asm/ia32.h> | 
 | 32 | #include <asm/vsyscall32.h> | 
 | 33 |  | 
 | 34 | #define ELF_NAME "elf/i386" | 
 | 35 |  | 
 | 36 | #define AT_SYSINFO 32 | 
 | 37 | #define AT_SYSINFO_EHDR		33 | 
 | 38 |  | 
 | 39 | int sysctl_vsyscall32 = 1; | 
 | 40 |  | 
| Andi Kleen | 2aae950 | 2007-07-21 17:10:01 +0200 | [diff] [blame] | 41 | #undef ARCH_DLINFO | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 42 | #define ARCH_DLINFO do {  \ | 
 | 43 | 	if (sysctl_vsyscall32) { \ | 
| Roland McGrath | 2ebc3cc | 2007-07-26 10:41:12 -0700 | [diff] [blame] | 44 | 		current->mm->context.vdso = (void *)VSYSCALL32_BASE;	\ | 
 | 45 | 		NEW_AUX_ENT(AT_SYSINFO, (u32)(u64)VSYSCALL32_VSYSCALL); \ | 
 | 46 | 		NEW_AUX_ENT(AT_SYSINFO_EHDR, VSYSCALL32_BASE);    \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 47 | 	}	\ | 
 | 48 | } while(0) | 
 | 49 |  | 
 | 50 | struct file; | 
 | 51 | struct elf_phdr;  | 
 | 52 |  | 
 | 53 | #define IA32_EMULATOR 1 | 
 | 54 |  | 
| Suresh Siddha | 8492980 | 2005-06-21 17:14:32 -0700 | [diff] [blame] | 55 | #define ELF_ET_DYN_BASE		(TASK_UNMAPPED_BASE + 0x1000000) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 56 |  | 
 | 57 | #undef ELF_ARCH | 
 | 58 | #define ELF_ARCH EM_386 | 
 | 59 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 60 | #define ELF_DATA	ELFDATA2LSB | 
 | 61 |  | 
 | 62 | #define USE_ELF_CORE_DUMP 1 | 
 | 63 |  | 
| Dave Jones | e6fc99c | 2006-03-25 16:30:16 +0100 | [diff] [blame] | 64 | /* Override elfcore.h */  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 65 | #define _LINUX_ELFCORE_H 1 | 
 | 66 | typedef unsigned int elf_greg_t; | 
 | 67 |  | 
 | 68 | #define ELF_NGREG (sizeof (struct user_regs_struct32) / sizeof(elf_greg_t)) | 
 | 69 | typedef elf_greg_t elf_gregset_t[ELF_NGREG]; | 
 | 70 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 71 | struct elf_siginfo | 
 | 72 | { | 
 | 73 | 	int	si_signo;			/* signal number */ | 
 | 74 | 	int	si_code;			/* extra code */ | 
 | 75 | 	int	si_errno;			/* errno */ | 
 | 76 | }; | 
 | 77 |  | 
 | 78 | #define jiffies_to_timeval(a,b) do { (b)->tv_usec = 0; (b)->tv_sec = (a)/HZ; }while(0) | 
 | 79 |  | 
 | 80 | struct elf_prstatus | 
 | 81 | { | 
 | 82 | 	struct elf_siginfo pr_info;	/* Info associated with signal */ | 
 | 83 | 	short	pr_cursig;		/* Current signal */ | 
 | 84 | 	unsigned int pr_sigpend;	/* Set of pending signals */ | 
 | 85 | 	unsigned int pr_sighold;	/* Set of held signals */ | 
 | 86 | 	pid_t	pr_pid; | 
 | 87 | 	pid_t	pr_ppid; | 
 | 88 | 	pid_t	pr_pgrp; | 
 | 89 | 	pid_t	pr_sid; | 
 | 90 | 	struct compat_timeval pr_utime;	/* User time */ | 
 | 91 | 	struct compat_timeval pr_stime;	/* System time */ | 
 | 92 | 	struct compat_timeval pr_cutime;	/* Cumulative user time */ | 
 | 93 | 	struct compat_timeval pr_cstime;	/* Cumulative system time */ | 
 | 94 | 	elf_gregset_t pr_reg;	/* GP registers */ | 
 | 95 | 	int pr_fpvalid;		/* True if math co-processor being used.  */ | 
 | 96 | }; | 
 | 97 |  | 
 | 98 | #define ELF_PRARGSZ	(80)	/* Number of chars for args */ | 
 | 99 |  | 
 | 100 | struct elf_prpsinfo | 
 | 101 | { | 
 | 102 | 	char	pr_state;	/* numeric process state */ | 
 | 103 | 	char	pr_sname;	/* char for pr_state */ | 
 | 104 | 	char	pr_zomb;	/* zombie */ | 
 | 105 | 	char	pr_nice;	/* nice val */ | 
 | 106 | 	unsigned int pr_flag;	/* flags */ | 
 | 107 | 	__u16	pr_uid; | 
 | 108 | 	__u16	pr_gid; | 
 | 109 | 	pid_t	pr_pid, pr_ppid, pr_pgrp, pr_sid; | 
 | 110 | 	/* Lots missing */ | 
 | 111 | 	char	pr_fname[16];	/* filename of executable */ | 
 | 112 | 	char	pr_psargs[ELF_PRARGSZ];	/* initial part of arg list */ | 
 | 113 | }; | 
 | 114 |  | 
 | 115 | #define __STR(x) #x | 
 | 116 | #define STR(x) __STR(x) | 
 | 117 |  | 
 | 118 | #define _GET_SEG(x) \ | 
 | 119 | 	({ __u32 seg; asm("movl %%" STR(x) ",%0" : "=r"(seg)); seg; }) | 
 | 120 |  | 
 | 121 | /* Assumes current==process to be dumped */ | 
 | 122 | #define ELF_CORE_COPY_REGS(pr_reg, regs)       		\ | 
 | 123 | 	pr_reg[0] = regs->rbx;				\ | 
 | 124 | 	pr_reg[1] = regs->rcx;				\ | 
 | 125 | 	pr_reg[2] = regs->rdx;				\ | 
 | 126 | 	pr_reg[3] = regs->rsi;				\ | 
 | 127 | 	pr_reg[4] = regs->rdi;				\ | 
 | 128 | 	pr_reg[5] = regs->rbp;				\ | 
 | 129 | 	pr_reg[6] = regs->rax;				\ | 
 | 130 | 	pr_reg[7] = _GET_SEG(ds);   			\ | 
 | 131 | 	pr_reg[8] = _GET_SEG(es);			\ | 
 | 132 | 	pr_reg[9] = _GET_SEG(fs);			\ | 
 | 133 | 	pr_reg[10] = _GET_SEG(gs);			\ | 
 | 134 | 	pr_reg[11] = regs->orig_rax;			\ | 
 | 135 | 	pr_reg[12] = regs->rip;				\ | 
 | 136 | 	pr_reg[13] = regs->cs;				\ | 
 | 137 | 	pr_reg[14] = regs->eflags;			\ | 
 | 138 | 	pr_reg[15] = regs->rsp;				\ | 
 | 139 | 	pr_reg[16] = regs->ss; | 
 | 140 |  | 
 | 141 | #define user user32 | 
 | 142 |  | 
| Ralf Baechle | 9f7290e | 2007-05-02 19:27:08 +0200 | [diff] [blame] | 143 | #undef elf_read_implies_exec | 
| Markus Schoder | 3391c22 | 2006-07-10 17:06:06 +0200 | [diff] [blame] | 144 | #define elf_read_implies_exec(ex, executable_stack)     (executable_stack != EXSTACK_DISABLE_X) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 145 | //#include <asm/ia32.h> | 
 | 146 | #include <linux/elf.h> | 
 | 147 |  | 
 | 148 | typedef struct user_i387_ia32_struct elf_fpregset_t; | 
 | 149 | typedef struct user32_fxsr_struct elf_fpxregset_t; | 
 | 150 |  | 
 | 151 |  | 
 | 152 | static inline void elf_core_copy_regs(elf_gregset_t *elfregs, struct pt_regs *regs) | 
 | 153 | { | 
 | 154 | 	ELF_CORE_COPY_REGS((*elfregs), regs) | 
 | 155 | } | 
 | 156 |  | 
 | 157 | static inline int elf_core_copy_task_regs(struct task_struct *t, elf_gregset_t* elfregs) | 
 | 158 | {	 | 
| Al Viro | bb04923 | 2006-01-12 01:05:38 -0800 | [diff] [blame] | 159 | 	struct pt_regs *pp = task_pt_regs(t); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 160 | 	ELF_CORE_COPY_REGS((*elfregs), pp); | 
 | 161 | 	/* fix wrong segments */  | 
 | 162 | 	(*elfregs)[7] = t->thread.ds;  | 
 | 163 | 	(*elfregs)[9] = t->thread.fsindex;  | 
 | 164 | 	(*elfregs)[10] = t->thread.gsindex;  | 
 | 165 | 	(*elfregs)[8] = t->thread.es; 	 | 
 | 166 | 	return 1;  | 
 | 167 | } | 
 | 168 |  | 
 | 169 | static inline int  | 
 | 170 | elf_core_copy_task_fpregs(struct task_struct *tsk, struct pt_regs *regs, elf_fpregset_t *fpu) | 
 | 171 | { | 
 | 172 | 	struct _fpstate_ia32 *fpstate = (void*)fpu;  | 
 | 173 | 	mm_segment_t oldfs = get_fs(); | 
 | 174 |  | 
 | 175 | 	if (!tsk_used_math(tsk)) | 
 | 176 | 		return 0; | 
 | 177 | 	if (!regs) | 
| Al Viro | bb04923 | 2006-01-12 01:05:38 -0800 | [diff] [blame] | 178 | 		regs = task_pt_regs(tsk); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 179 | 	if (tsk == current) | 
 | 180 | 		unlazy_fpu(tsk); | 
 | 181 | 	set_fs(KERNEL_DS);  | 
 | 182 | 	save_i387_ia32(tsk, fpstate, regs, 1); | 
 | 183 | 	/* Correct for i386 bug. It puts the fop into the upper 16bits of  | 
 | 184 | 	   the tag word (like FXSAVE), not into the fcs*/  | 
 | 185 | 	fpstate->cssel |= fpstate->tag & 0xffff0000;  | 
 | 186 | 	set_fs(oldfs);  | 
 | 187 | 	return 1;  | 
 | 188 | } | 
 | 189 |  | 
 | 190 | #define ELF_CORE_COPY_XFPREGS 1 | 
| Mark Nelson | 5b20cd8 | 2007-10-16 23:25:39 -0700 | [diff] [blame] | 191 | #define ELF_CORE_XFPREG_TYPE NT_PRXFPREG | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 192 | static inline int  | 
 | 193 | elf_core_copy_task_xfpregs(struct task_struct *t, elf_fpxregset_t *xfpu) | 
 | 194 | { | 
| Al Viro | bb04923 | 2006-01-12 01:05:38 -0800 | [diff] [blame] | 195 | 	struct pt_regs *regs = task_pt_regs(t); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 196 | 	if (!tsk_used_math(t)) | 
 | 197 | 		return 0; | 
 | 198 | 	if (t == current) | 
 | 199 | 		unlazy_fpu(t);  | 
 | 200 | 	memcpy(xfpu, &t->thread.i387.fxsave, sizeof(elf_fpxregset_t)); | 
 | 201 | 	xfpu->fcs = regs->cs;  | 
 | 202 | 	xfpu->fos = t->thread.ds; /* right? */  | 
 | 203 | 	return 1; | 
 | 204 | } | 
 | 205 |  | 
 | 206 | #undef elf_check_arch | 
 | 207 | #define elf_check_arch(x) \ | 
 | 208 | 	((x)->e_machine == EM_386) | 
 | 209 |  | 
 | 210 | extern int force_personality32; | 
 | 211 |  | 
 | 212 | #define ELF_EXEC_PAGESIZE PAGE_SIZE | 
 | 213 | #define ELF_HWCAP (boot_cpu_data.x86_capability[0]) | 
 | 214 | #define ELF_PLATFORM  ("i686") | 
 | 215 | #define SET_PERSONALITY(ex, ibcs2)			\ | 
 | 216 | do {							\ | 
 | 217 | 	unsigned long new_flags = 0;				\ | 
 | 218 | 	if ((ex).e_ident[EI_CLASS] == ELFCLASS32)		\ | 
 | 219 | 		new_flags = _TIF_IA32;				\ | 
 | 220 | 	if ((current_thread_info()->flags & _TIF_IA32)		\ | 
 | 221 | 	    != new_flags)					\ | 
 | 222 | 		set_thread_flag(TIF_ABI_PENDING);		\ | 
 | 223 | 	else							\ | 
 | 224 | 		clear_thread_flag(TIF_ABI_PENDING);		\ | 
 | 225 | 	/* XXX This overwrites the user set personality */	\ | 
 | 226 | 	current->personality |= force_personality32;		\ | 
 | 227 | } while (0) | 
 | 228 |  | 
 | 229 | /* Override some function names */ | 
 | 230 | #define elf_format			elf32_format | 
 | 231 |  | 
 | 232 | #define init_elf_binfmt			init_elf32_binfmt | 
 | 233 | #define exit_elf_binfmt			exit_elf32_binfmt | 
 | 234 |  | 
 | 235 | #define load_elf_binary load_elf32_binary | 
 | 236 |  | 
 | 237 | #define ELF_PLAT_INIT(r, load_addr)	elf32_init(r) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 238 |  | 
 | 239 | #undef start_thread | 
 | 240 | #define start_thread(regs,new_rip,new_rsp) do { \ | 
 | 241 | 	asm volatile("movl %0,%%fs" :: "r" (0)); \ | 
 | 242 | 	asm volatile("movl %0,%%es; movl %0,%%ds": :"r" (__USER32_DS)); \ | 
 | 243 | 	load_gs_index(0); \ | 
 | 244 | 	(regs)->rip = (new_rip); \ | 
 | 245 | 	(regs)->rsp = (new_rsp); \ | 
 | 246 | 	(regs)->eflags = 0x200; \ | 
 | 247 | 	(regs)->cs = __USER32_CS; \ | 
 | 248 | 	(regs)->ss = __USER32_DS; \ | 
 | 249 | 	set_fs(USER_DS); \ | 
 | 250 | } while(0)  | 
 | 251 |  | 
 | 252 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 253 | #include <linux/module.h> | 
 | 254 |  | 
 | 255 | MODULE_DESCRIPTION("Binary format loader for compatibility with IA32 ELF binaries.");  | 
 | 256 | MODULE_AUTHOR("Eric Youngdale, Andi Kleen"); | 
 | 257 |  | 
 | 258 | #undef MODULE_DESCRIPTION | 
 | 259 | #undef MODULE_AUTHOR | 
 | 260 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 261 | static void elf32_init(struct pt_regs *); | 
 | 262 |  | 
| Andi Kleen | 1e01441 | 2005-04-16 15:24:55 -0700 | [diff] [blame] | 263 | #define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1 | 
 | 264 | #define arch_setup_additional_pages syscall32_setup_pages | 
 | 265 | extern int syscall32_setup_pages(struct linux_binprm *, int exstack); | 
 | 266 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 267 | #include "../../../fs/binfmt_elf.c"  | 
 | 268 |  | 
 | 269 | static void elf32_init(struct pt_regs *regs) | 
 | 270 | { | 
 | 271 | 	struct task_struct *me = current;  | 
 | 272 | 	regs->rdi = 0; | 
 | 273 | 	regs->rsi = 0; | 
 | 274 | 	regs->rdx = 0; | 
 | 275 | 	regs->rcx = 0; | 
 | 276 | 	regs->rax = 0; | 
 | 277 | 	regs->rbx = 0;  | 
 | 278 | 	regs->rbp = 0;  | 
 | 279 | 	regs->r8 = regs->r9 = regs->r10 = regs->r11 = regs->r12 = | 
 | 280 | 		regs->r13 = regs->r14 = regs->r15 = 0;  | 
 | 281 |     me->thread.fs = 0;  | 
 | 282 | 	me->thread.gs = 0; | 
 | 283 | 	me->thread.fsindex = 0;  | 
 | 284 | 	me->thread.gsindex = 0; | 
 | 285 |     me->thread.ds = __USER_DS;  | 
 | 286 | 	me->thread.es = __USER_DS; | 
 | 287 | } | 
 | 288 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 289 | #ifdef CONFIG_SYSCTL | 
 | 290 | /* Register vsyscall32 into the ABI table */ | 
 | 291 | #include <linux/sysctl.h> | 
 | 292 |  | 
 | 293 | static ctl_table abi_table2[] = { | 
| Eric W. Biederman | 306421f | 2007-02-14 00:33:49 -0800 | [diff] [blame] | 294 | 	{ | 
 | 295 | 		.ctl_name	= 99, | 
 | 296 | 		.procname	= "vsyscall32", | 
 | 297 | 		.data		= &sysctl_vsyscall32, | 
 | 298 | 		.maxlen		= sizeof(int), | 
 | 299 | 		.mode		= 0644, | 
 | 300 | 		.proc_handler	= proc_dointvec | 
 | 301 | 	}, | 
 | 302 | 	{} | 
 | 303 | }; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 304 |  | 
| Eric W. Biederman | 306421f | 2007-02-14 00:33:49 -0800 | [diff] [blame] | 305 | static ctl_table abi_root_table2[] = { | 
 | 306 | 	{ | 
 | 307 | 		.ctl_name = CTL_ABI, | 
 | 308 | 		.procname = "abi", | 
 | 309 | 		.mode = 0555, | 
 | 310 | 		.child = abi_table2 | 
 | 311 | 	}, | 
 | 312 | 	{} | 
 | 313 | }; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 314 |  | 
 | 315 | static __init int ia32_binfmt_init(void) | 
 | 316 | {  | 
| Eric W. Biederman | 0b4d414 | 2007-02-14 00:34:09 -0800 | [diff] [blame] | 317 | 	register_sysctl_table(abi_root_table2); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 318 | 	return 0; | 
 | 319 | } | 
 | 320 | __initcall(ia32_binfmt_init); | 
 | 321 | #endif |