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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		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");