ia32_aout.c 13.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/*
 *  a.out loader for x86-64
 *
 *  Copyright (C) 1991, 1992, 1996  Linus Torvalds
 *  Hacked together by Andi Kleen
 */

#include <linux/module.h>

#include <linux/time.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/a.out.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/binfmts.h>
#include <linux/personality.h>
#include <linux/init.h>
27
#include <linux/jiffies.h>
L
Linus Torvalds 已提交
28 29 30 31 32 33 34 35

#include <asm/uaccess.h>
#include <asm/pgalloc.h>
#include <asm/cacheflush.h>
#include <asm/user32.h>
#include <asm/ia32.h>

#undef WARN_OLD
36
#undef CORE_DUMP /* definitely broken */
L
Linus Torvalds 已提交
37

38 39
static int load_aout_binary(struct linux_binprm *, struct pt_regs *regs);
static int load_aout_library(struct file *);
L
Linus Torvalds 已提交
40

41
#ifdef CORE_DUMP
42 43
static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file,
			  unsigned long limit);
L
Linus Torvalds 已提交
44 45 46 47

/*
 * fill in the user structure for a core dump..
 */
48
static void dump_thread32(struct pt_regs *regs, struct user32 *dump)
L
Linus Torvalds 已提交
49
{
50
	u32 fs, gs;
L
Linus Torvalds 已提交
51 52 53 54

/* changed the size calculations - should hopefully work better. lbt */
	dump->magic = CMAGIC;
	dump->start_code = 0;
55
	dump->start_stack = regs->sp & ~(PAGE_SIZE - 1);
L
Linus Torvalds 已提交
56
	dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT;
57 58
	dump->u_dsize = ((unsigned long)
			 (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT;
L
Linus Torvalds 已提交
59 60
	dump->u_dsize -= dump->u_tsize;
	dump->u_ssize = 0;
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
	dump->u_debugreg[0] = current->thread.debugreg0;
	dump->u_debugreg[1] = current->thread.debugreg1;
	dump->u_debugreg[2] = current->thread.debugreg2;
	dump->u_debugreg[3] = current->thread.debugreg3;
	dump->u_debugreg[4] = 0;
	dump->u_debugreg[5] = 0;
	dump->u_debugreg[6] = current->thread.debugreg6;
	dump->u_debugreg[7] = current->thread.debugreg7;

	if (dump->start_stack < 0xc0000000) {
		unsigned long tmp;

		tmp = (unsigned long) (0xc0000000 - dump->start_stack);
		dump->u_ssize = tmp >> PAGE_SHIFT;
	}
L
Linus Torvalds 已提交
76

77 78 79 80 81 82 83
	dump->regs.bx = regs->bx;
	dump->regs.cx = regs->cx;
	dump->regs.dx = regs->dx;
	dump->regs.si = regs->si;
	dump->regs.di = regs->di;
	dump->regs.bp = regs->bp;
	dump->regs.ax = regs->ax;
L
Linus Torvalds 已提交
84 85
	dump->regs.ds = current->thread.ds;
	dump->regs.es = current->thread.es;
86 87 88 89
	savesegment(fs, fs);
	dump->regs.fs = fs;
	savesegment(gs, gs);
	dump->regs.gs = gs;
90 91
	dump->regs.orig_ax = regs->orig_ax;
	dump->regs.ip = regs->ip;
L
Linus Torvalds 已提交
92
	dump->regs.cs = regs->cs;
93 94
	dump->regs.flags = regs->flags;
	dump->regs.sp = regs->sp;
L
Linus Torvalds 已提交
95 96 97 98 99
	dump->regs.ss = regs->ss;

#if 1 /* FIXME */
	dump->u_fpvalid = 0;
#else
100
	dump->u_fpvalid = dump_fpu(regs, &dump->i387);
L
Linus Torvalds 已提交
101 102 103 104 105 106 107 108 109
#endif
}

#endif

static struct linux_binfmt aout_format = {
	.module		= THIS_MODULE,
	.load_binary	= load_aout_binary,
	.load_shlib	= load_aout_library,
110
#ifdef CORE_DUMP
L
Linus Torvalds 已提交
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
	.core_dump	= aout_core_dump,
#endif
	.min_coredump	= PAGE_SIZE
};

static void set_brk(unsigned long start, unsigned long end)
{
	start = PAGE_ALIGN(start);
	end = PAGE_ALIGN(end);
	if (end <= start)
		return;
	down_write(&current->mm->mmap_sem);
	do_brk(start, end - start);
	up_write(&current->mm->mmap_sem);
}

127
#ifdef CORE_DUMP
L
Linus Torvalds 已提交
128 129 130 131 132
/*
 * These are the only things you should do on a core-file: use only these
 * macros to write out all the necessary info.
 */

133
#include <linux/coredump.h>
L
Linus Torvalds 已提交
134

135
#define DUMP_WRITE(addr, nr)			     \
L
Linus Torvalds 已提交
136 137 138
	if (!dump_write(file, (void *)(addr), (nr))) \
		goto end_coredump;

139 140 141
#define DUMP_SEEK(offset)		\
	if (!dump_seek(file, offset))	\
		goto end_coredump;
142 143 144

#define START_DATA()	(u.u_tsize << PAGE_SHIFT)
#define START_STACK(u)	(u.start_stack)
L
Linus Torvalds 已提交
145 146 147 148 149 150 151 152 153 154 155

/*
 * Routine writes a core dump image in the current directory.
 * Currently only a stub-function.
 *
 * Note that setuid/setgid files won't make a core-dump if the uid/gid
 * changed due to the set[u|g]id. It's enforced by the "current->mm->dumpable"
 * field, which also makes sure the core-dumps won't be recursive if the
 * dumping of the process results in another error..
 */

156 157
static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file,
			  unsigned long limit)
L
Linus Torvalds 已提交
158 159 160 161 162 163 164 165 166 167
{
	mm_segment_t fs;
	int has_dumped = 0;
	unsigned long dump_start, dump_size;
	struct user32 dump;

	fs = get_fs();
	set_fs(KERNEL_DS);
	has_dumped = 1;
	current->flags |= PF_DUMPCORE;
168
	strncpy(dump.u_comm, current->comm, sizeof(current->comm));
169
	dump.u_ar0 = offsetof(struct user32, regs);
L
Linus Torvalds 已提交
170 171 172
	dump.signal = signr;
	dump_thread32(regs, &dump);

173 174 175 176 177
	/*
	 * If the size of the dump file exceeds the rlimit, then see
	 * what would happen if we wrote the stack, but not the data
	 * area.
	 */
178
	if ((dump.u_dsize + dump.u_ssize + 1) * PAGE_SIZE > limit)
L
Linus Torvalds 已提交
179 180
		dump.u_dsize = 0;

181
	/* Make sure we have enough room to write the stack and data areas. */
182
	if ((dump.u_ssize + 1) * PAGE_SIZE > limit)
L
Linus Torvalds 已提交
183 184
		dump.u_ssize = 0;

185
	/* make sure we actually have a data and stack area to dump */
L
Linus Torvalds 已提交
186
	set_fs(USER_DS);
187 188
	if (!access_ok(VERIFY_READ, (void *) (unsigned long)START_DATA(dump),
		       dump.u_dsize << PAGE_SHIFT))
L
Linus Torvalds 已提交
189
		dump.u_dsize = 0;
190 191
	if (!access_ok(VERIFY_READ, (void *) (unsigned long)START_STACK(dump),
		       dump.u_ssize << PAGE_SHIFT))
L
Linus Torvalds 已提交
192 193 194
		dump.u_ssize = 0;

	set_fs(KERNEL_DS);
195 196 197
	/* struct user */
	DUMP_WRITE(&dump, sizeof(dump));
	/* Now dump all of the user data.  Include malloced stuff as well */
L
Linus Torvalds 已提交
198
	DUMP_SEEK(PAGE_SIZE);
199
	/* now we start writing out the user space info */
L
Linus Torvalds 已提交
200
	set_fs(USER_DS);
201
	/* Dump the data area */
L
Linus Torvalds 已提交
202 203 204
	if (dump.u_dsize != 0) {
		dump_start = START_DATA(dump);
		dump_size = dump.u_dsize << PAGE_SHIFT;
205
		DUMP_WRITE(dump_start, dump_size);
L
Linus Torvalds 已提交
206
	}
207
	/* Now prepare to dump the stack area */
L
Linus Torvalds 已提交
208 209 210
	if (dump.u_ssize != 0) {
		dump_start = START_STACK(dump);
		dump_size = dump.u_ssize << PAGE_SHIFT;
211
		DUMP_WRITE(dump_start, dump_size);
L
Linus Torvalds 已提交
212 213 214 215 216 217 218 219 220 221 222 223 224 225
	}
end_coredump:
	set_fs(fs);
	return has_dumped;
}
#endif

/*
 * create_aout_tables() parses the env- and arg-strings in new user
 * memory and creates the pointer tables from them, and puts their
 * addresses on the "stack", returning the new stack pointer value.
 */
static u32 __user *create_aout_tables(char __user *p, struct linux_binprm *bprm)
{
226 227
	u32 __user *argv, *envp, *sp;
	int argc = bprm->argc, envc = bprm->envc;
L
Linus Torvalds 已提交
228 229 230 231 232 233

	sp = (u32 __user *) ((-(unsigned long)sizeof(u32)) & (unsigned long) p);
	sp -= envc+1;
	envp = sp;
	sp -= argc+1;
	argv = sp;
234 235 236
	put_user((unsigned long) envp, --sp);
	put_user((unsigned long) argv, --sp);
	put_user(argc, --sp);
L
Linus Torvalds 已提交
237
	current->mm->arg_start = (unsigned long) p;
238
	while (argc-- > 0) {
L
Linus Torvalds 已提交
239
		char c;
240 241

		put_user((u32)(unsigned long)p, argv++);
L
Linus Torvalds 已提交
242
		do {
243
			get_user(c, p++);
L
Linus Torvalds 已提交
244 245
		} while (c);
	}
246
	put_user(0, argv);
L
Linus Torvalds 已提交
247
	current->mm->arg_end = current->mm->env_start = (unsigned long) p;
248
	while (envc-- > 0) {
L
Linus Torvalds 已提交
249
		char c;
250 251

		put_user((u32)(unsigned long)p, envp++);
L
Linus Torvalds 已提交
252
		do {
253
			get_user(c, p++);
L
Linus Torvalds 已提交
254 255
		} while (c);
	}
256
	put_user(0, envp);
L
Linus Torvalds 已提交
257 258 259 260 261 262 263 264
	current->mm->env_end = (unsigned long) p;
	return sp;
}

/*
 * These are the functions used to load a.out style executables and shared
 * libraries.  There is no binary dependent code anywhere else.
 */
265
static int load_aout_binary(struct linux_binprm *bprm, struct pt_regs *regs)
L
Linus Torvalds 已提交
266
{
267
	unsigned long error, fd_offset, rlim;
L
Linus Torvalds 已提交
268 269 270 271 272 273 274
	struct exec ex;
	int retval;

	ex = *((struct exec *) bprm->buf);		/* exec-header */
	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
	     N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) ||
	    N_TRSIZE(ex) || N_DRSIZE(ex) ||
275 276
	    i_size_read(bprm->file->f_path.dentry->d_inode) <
	    ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
L
Linus Torvalds 已提交
277 278 279 280 281 282 283 284 285
		return -ENOEXEC;
	}

	fd_offset = N_TXTOFF(ex);

	/* Check initial limits. This avoids letting people circumvent
	 * size limits imposed on them by creating programs with large
	 * arrays in the data or bss.
	 */
J
Jiri Slaby 已提交
286
	rlim = rlimit(RLIMIT_DATA);
L
Linus Torvalds 已提交
287 288 289 290 291 292 293 294 295 296 297 298
	if (rlim >= RLIM_INFINITY)
		rlim = ~0;
	if (ex.a_data + ex.a_bss > rlim)
		return -ENOMEM;

	/* Flush all traces of the currently running executable */
	retval = flush_old_exec(bprm);
	if (retval)
		return retval;

	/* OK, This is the point of no return */
	set_personality(PER_LINUX);
299
	set_thread_flag(TIF_IA32);
300
	current->mm->context.ia32_compat = 1;
L
Linus Torvalds 已提交
301

302 303 304 305 306 307
	setup_new_exec(bprm);

	regs->cs = __USER32_CS;
	regs->r8 = regs->r9 = regs->r10 = regs->r11 = regs->r12 =
		regs->r13 = regs->r14 = regs->r15 = 0;

L
Linus Torvalds 已提交
308 309 310 311 312 313 314
	current->mm->end_code = ex.a_text +
		(current->mm->start_code = N_TXTADDR(ex));
	current->mm->end_data = ex.a_data +
		(current->mm->start_data = N_DATADDR(ex));
	current->mm->brk = ex.a_bss +
		(current->mm->start_brk = N_BSSADDR(ex));
	current->mm->free_area_cache = TASK_UNMAPPED_BASE;
315
	current->mm->cached_hole_size = 0;
L
Linus Torvalds 已提交
316

317 318 319 320 321 322 323
	retval = setup_arg_pages(bprm, IA32_STACK_TOP, EXSTACK_DEFAULT);
	if (retval < 0) {
		/* Someone check-me: is this error path enough? */
		send_sig(SIGKILL, current, 0);
		return retval;
	}

324
	install_exec_creds(bprm);
L
Linus Torvalds 已提交
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343

	if (N_MAGIC(ex) == OMAGIC) {
		unsigned long text_addr, map_size;
		loff_t pos;

		text_addr = N_TXTADDR(ex);

		pos = 32;
		map_size = ex.a_text+ex.a_data;

		down_write(&current->mm->mmap_sem);
		error = do_brk(text_addr & PAGE_MASK, map_size);
		up_write(&current->mm->mmap_sem);

		if (error != (text_addr & PAGE_MASK)) {
			send_sig(SIGKILL, current, 0);
			return error;
		}

344 345
		error = bprm->file->f_op->read(bprm->file,
			 (char __user *)text_addr,
L
Linus Torvalds 已提交
346 347 348 349 350
			  ex.a_text+ex.a_data, &pos);
		if ((signed long)error < 0) {
			send_sig(SIGKILL, current, 0);
			return error;
		}
351

L
Linus Torvalds 已提交
352 353 354 355 356
		flush_icache_range(text_addr, text_addr+ex.a_text+ex.a_data);
	} else {
#ifdef WARN_OLD
		static unsigned long error_time, error_time2;
		if ((ex.a_text & 0xfff || ex.a_data & 0xfff) &&
357 358
		    (N_MAGIC(ex) != NMAGIC) &&
				time_after(jiffies, error_time2 + 5*HZ)) {
L
Linus Torvalds 已提交
359 360 361 362 363
			printk(KERN_NOTICE "executable not page aligned\n");
			error_time2 = jiffies;
		}

		if ((fd_offset & ~PAGE_MASK) != 0 &&
364
			    time_after(jiffies, error_time + 5*HZ)) {
365 366 367
			printk(KERN_WARNING
			       "fd_offset is not page aligned. Please convert "
			       "program: %s\n",
368
			       bprm->file->f_path.dentry->d_name.name);
L
Linus Torvalds 已提交
369 370 371 372
			error_time = jiffies;
		}
#endif

373
		if (!bprm->file->f_op->mmap || (fd_offset & ~PAGE_MASK) != 0) {
L
Linus Torvalds 已提交
374
			loff_t pos = fd_offset;
375

L
Linus Torvalds 已提交
376 377 378
			down_write(&current->mm->mmap_sem);
			do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data);
			up_write(&current->mm->mmap_sem);
379 380
			bprm->file->f_op->read(bprm->file,
					(char __user *)N_TXTADDR(ex),
L
Linus Torvalds 已提交
381 382 383 384 385 386 387 388 389
					ex.a_text+ex.a_data, &pos);
			flush_icache_range((unsigned long) N_TXTADDR(ex),
					   (unsigned long) N_TXTADDR(ex) +
					   ex.a_text+ex.a_data);
			goto beyond_if;
		}

		down_write(&current->mm->mmap_sem);
		error = do_mmap(bprm->file, N_TXTADDR(ex), ex.a_text,
390 391 392 393
				PROT_READ | PROT_EXEC,
				MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE |
				MAP_EXECUTABLE | MAP_32BIT,
				fd_offset);
L
Linus Torvalds 已提交
394 395 396 397 398 399 400 401
		up_write(&current->mm->mmap_sem);

		if (error != N_TXTADDR(ex)) {
			send_sig(SIGKILL, current, 0);
			return error;
		}

		down_write(&current->mm->mmap_sem);
402
		error = do_mmap(bprm->file, N_DATADDR(ex), ex.a_data,
L
Linus Torvalds 已提交
403
				PROT_READ | PROT_WRITE | PROT_EXEC,
404 405
				MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE |
				MAP_EXECUTABLE | MAP_32BIT,
L
Linus Torvalds 已提交
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
				fd_offset + ex.a_text);
		up_write(&current->mm->mmap_sem);
		if (error != N_DATADDR(ex)) {
			send_sig(SIGKILL, current, 0);
			return error;
		}
	}
beyond_if:
	set_binfmt(&aout_format);

	set_brk(current->mm->start_brk, current->mm->brk);

	current->mm->start_stack =
		(unsigned long)create_aout_tables((char __user *)bprm->p, bprm);
	/* start thread */
421 422 423
	loadsegment(fs, 0);
	loadsegment(ds, __USER32_DS);
	loadsegment(es, __USER32_DS);
424
	load_gs_index(0);
425 426 427
	(regs)->ip = ex.a_entry;
	(regs)->sp = current->mm->start_stack;
	(regs)->flags = 0x200;
L
Linus Torvalds 已提交
428 429
	(regs)->cs = __USER32_CS;
	(regs)->ss = __USER32_DS;
430 431
	regs->r8 = regs->r9 = regs->r10 = regs->r11 =
	regs->r12 = regs->r13 = regs->r14 = regs->r15 = 0;
L
Linus Torvalds 已提交
432 433 434 435 436 437
	set_fs(USER_DS);
	return 0;
}

static int load_aout_library(struct file *file)
{
438 439
	struct inode *inode;
	unsigned long bss, start_addr, len, error;
L
Linus Torvalds 已提交
440 441 442
	int retval;
	struct exec ex;

443
	inode = file->f_path.dentry->d_inode;
L
Linus Torvalds 已提交
444 445 446 447 448 449 450 451 452

	retval = -ENOEXEC;
	error = kernel_read(file, 0, (char *) &ex, sizeof(ex));
	if (error != sizeof(ex))
		goto out;

	/* We come in here for the regular a.out style of shared libraries */
	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
	    N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
453 454
	    i_size_read(inode) <
	    ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
L
Linus Torvalds 已提交
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
		goto out;
	}

	if (N_FLAGS(ex))
		goto out;

	/* For  QMAGIC, the starting address is 0x20 into the page.  We mask
	   this off to get the starting address for the page */

	start_addr =  ex.a_entry & 0xfffff000;

	if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) {
		loff_t pos = N_TXTOFF(ex);

#ifdef WARN_OLD
		static unsigned long error_time;
471
		if (time_after(jiffies, error_time + 5*HZ)) {
472 473 474
			printk(KERN_WARNING
			       "N_TXTOFF is not page aligned. Please convert "
			       "library: %s\n",
475
			       file->f_path.dentry->d_name.name);
L
Linus Torvalds 已提交
476 477 478 479 480 481
			error_time = jiffies;
		}
#endif
		down_write(&current->mm->mmap_sem);
		do_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss);
		up_write(&current->mm->mmap_sem);
482

483
		file->f_op->read(file, (char __user *)start_addr,
L
Linus Torvalds 已提交
484 485
			ex.a_text + ex.a_data, &pos);
		flush_icache_range((unsigned long) start_addr,
486 487
				   (unsigned long) start_addr + ex.a_text +
				   ex.a_data);
L
Linus Torvalds 已提交
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519

		retval = 0;
		goto out;
	}
	/* Now use mmap to map the library into memory. */
	down_write(&current->mm->mmap_sem);
	error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
			PROT_READ | PROT_WRITE | PROT_EXEC,
			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_32BIT,
			N_TXTOFF(ex));
	up_write(&current->mm->mmap_sem);
	retval = error;
	if (error != start_addr)
		goto out;

	len = PAGE_ALIGN(ex.a_text + ex.a_data);
	bss = ex.a_text + ex.a_data + ex.a_bss;
	if (bss > len) {
		down_write(&current->mm->mmap_sem);
		error = do_brk(start_addr + len, bss - len);
		up_write(&current->mm->mmap_sem);
		retval = error;
		if (error != start_addr + len)
			goto out;
	}
	retval = 0;
out:
	return retval;
}

static int __init init_aout_binfmt(void)
{
A
Al Viro 已提交
520 521
	register_binfmt(&aout_format);
	return 0;
L
Linus Torvalds 已提交
522 523 524 525 526 527 528 529 530 531
}

static void __exit exit_aout_binfmt(void)
{
	unregister_binfmt(&aout_format);
}

module_init(init_aout_binfmt);
module_exit(exit_aout_binfmt);
MODULE_LICENSE("GPL");