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 36

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

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

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

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

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

/* changed the size calculations - should hopefully work better. lbt */
	dump->magic = CMAGIC;
	dump->start_code = 0;
56
	dump->start_stack = regs->sp & ~(PAGE_SIZE - 1);
L
Linus Torvalds 已提交
57
	dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT;
58 59
	dump->u_dsize = ((unsigned long)
			 (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT;
L
Linus Torvalds 已提交
60 61
	dump->u_dsize -= dump->u_tsize;
	dump->u_ssize = 0;
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
	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 已提交
77

78 79 80 81 82 83 84
	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 已提交
85 86
	dump->regs.ds = current->thread.ds;
	dump->regs.es = current->thread.es;
87 88 89 90
	savesegment(fs, fs);
	dump->regs.fs = fs;
	savesegment(gs, gs);
	dump->regs.gs = gs;
91 92
	dump->regs.orig_ax = regs->orig_ax;
	dump->regs.ip = regs->ip;
L
Linus Torvalds 已提交
93
	dump->regs.cs = regs->cs;
94 95
	dump->regs.flags = regs->flags;
	dump->regs.sp = regs->sp;
L
Linus Torvalds 已提交
96 97 98 99 100
	dump->regs.ss = regs->ss;

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

#endif

static struct linux_binfmt aout_format = {
	.module		= THIS_MODULE,
	.load_binary	= load_aout_binary,
	.load_shlib	= load_aout_library,
111
#ifdef CORE_DUMP
L
Linus Torvalds 已提交
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
	.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);
}

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

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

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

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

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

/*
 * 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..
 */

157 158
static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file,
			  unsigned long limit)
L
Linus Torvalds 已提交
159 160 161 162 163 164 165 166 167 168
{
	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;
169
	strncpy(dump.u_comm, current->comm, sizeof(current->comm));
170
	dump.u_ar0 = offsetof(struct user32, regs);
L
Linus Torvalds 已提交
171 172 173
	dump.signal = signr;
	dump_thread32(regs, &dump);

174 175 176 177 178
	/*
	 * 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.
	 */
179
	if ((dump.u_dsize + dump.u_ssize + 1) * PAGE_SIZE > limit)
L
Linus Torvalds 已提交
180 181
		dump.u_dsize = 0;

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

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

	set_fs(KERNEL_DS);
196 197 198
	/* struct user */
	DUMP_WRITE(&dump, sizeof(dump));
	/* Now dump all of the user data.  Include malloced stuff as well */
L
Linus Torvalds 已提交
199
	DUMP_SEEK(PAGE_SIZE);
200
	/* now we start writing out the user space info */
L
Linus Torvalds 已提交
201
	set_fs(USER_DS);
202
	/* Dump the data area */
L
Linus Torvalds 已提交
203 204 205
	if (dump.u_dsize != 0) {
		dump_start = START_DATA(dump);
		dump_size = dump.u_dsize << PAGE_SHIFT;
206
		DUMP_WRITE(dump_start, dump_size);
L
Linus Torvalds 已提交
207
	}
208
	/* Now prepare to dump the stack area */
L
Linus Torvalds 已提交
209 210 211
	if (dump.u_ssize != 0) {
		dump_start = START_STACK(dump);
		dump_size = dump.u_ssize << PAGE_SHIFT;
212
		DUMP_WRITE(dump_start, dump_size);
L
Linus Torvalds 已提交
213 214 215 216 217 218 219 220 221 222 223 224 225 226
	}
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)
{
227 228
	u32 __user *argv, *envp, *sp;
	int argc = bprm->argc, envc = bprm->envc;
L
Linus Torvalds 已提交
229 230 231 232 233 234

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

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

		put_user((u32)(unsigned long)p, envp++);
L
Linus Torvalds 已提交
253
		do {
254
			get_user(c, p++);
L
Linus Torvalds 已提交
255 256
		} while (c);
	}
257
	put_user(0, envp);
L
Linus Torvalds 已提交
258 259 260 261 262 263 264 265
	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.
 */
266
static int load_aout_binary(struct linux_binprm *bprm, struct pt_regs *regs)
L
Linus Torvalds 已提交
267
{
268
	unsigned long error, fd_offset, rlim;
L
Linus Torvalds 已提交
269 270 271 272 273 274 275
	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) ||
276 277
	    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 已提交
278 279 280 281 282 283 284 285 286
		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 已提交
287
	rlim = rlimit(RLIMIT_DATA);
L
Linus Torvalds 已提交
288 289 290 291 292 293 294 295 296 297 298 299
	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);
300
	set_thread_flag(TIF_IA32);
301
	current->mm->context.ia32_compat = 1;
L
Linus Torvalds 已提交
302

303 304 305 306 307 308
	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 已提交
309 310 311 312 313 314 315
	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;
316
	current->mm->cached_hole_size = 0;
L
Linus Torvalds 已提交
317

318
	install_exec_creds(bprm);
319
	current->flags &= ~PF_FORKNOEXEC;
L
Linus Torvalds 已提交
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338

	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;
		}

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

L
Linus Torvalds 已提交
347 348 349 350 351
		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) &&
352 353
		    (N_MAGIC(ex) != NMAGIC) &&
				time_after(jiffies, error_time2 + 5*HZ)) {
L
Linus Torvalds 已提交
354 355 356 357 358
			printk(KERN_NOTICE "executable not page aligned\n");
			error_time2 = jiffies;
		}

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

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

L
Linus Torvalds 已提交
371 372 373
			down_write(&current->mm->mmap_sem);
			do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data);
			up_write(&current->mm->mmap_sem);
374 375
			bprm->file->f_op->read(bprm->file,
					(char __user *)N_TXTADDR(ex),
L
Linus Torvalds 已提交
376 377 378 379 380 381 382 383 384
					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,
385 386 387 388
				PROT_READ | PROT_EXEC,
				MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE |
				MAP_EXECUTABLE | MAP_32BIT,
				fd_offset);
L
Linus Torvalds 已提交
389 390 391 392 393 394 395 396
		up_write(&current->mm->mmap_sem);

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

		down_write(&current->mm->mmap_sem);
397
		error = do_mmap(bprm->file, N_DATADDR(ex), ex.a_data,
L
Linus Torvalds 已提交
398
				PROT_READ | PROT_WRITE | PROT_EXEC,
399 400
				MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE |
				MAP_EXECUTABLE | MAP_32BIT,
L
Linus Torvalds 已提交
401 402 403 404 405 406 407 408 409 410 411 412
				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);

413
	retval = setup_arg_pages(bprm, IA32_STACK_TOP, EXSTACK_DEFAULT);
414 415 416
	if (retval < 0) {
		/* Someone check-me: is this error path enough? */
		send_sig(SIGKILL, current, 0);
L
Linus Torvalds 已提交
417 418 419 420 421 422
		return retval;
	}

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

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

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

	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) ||
455 456
	    i_size_read(inode) <
	    ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
L
Linus Torvalds 已提交
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
		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;
473
		if (time_after(jiffies, error_time + 5*HZ)) {
474 475 476
			printk(KERN_WARNING
			       "N_TXTOFF is not page aligned. Please convert "
			       "library: %s\n",
477
			       file->f_path.dentry->d_name.name);
L
Linus Torvalds 已提交
478 479 480 481 482 483
			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);
484

485
		file->f_op->read(file, (char __user *)start_addr,
L
Linus Torvalds 已提交
486 487
			ex.a_text + ex.a_data, &pos);
		flush_icache_range((unsigned long) start_addr,
488 489
				   (unsigned long) start_addr + ex.a_text +
				   ex.a_data);
L
Linus Torvalds 已提交
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 520 521 522 523 524 525 526 527 528 529 530 531 532

		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)
{
	return register_binfmt(&aout_format);
}

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

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