162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/****************************************************************************/
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci *  linux/fs/binfmt_flat.c
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *	Copyright (C) 2000-2003 David McCullough <davidm@snapgear.com>
762306a36Sopenharmony_ci *	Copyright (C) 2002 Greg Ungerer <gerg@snapgear.com>
862306a36Sopenharmony_ci *	Copyright (C) 2002 SnapGear, by Paul Dale <pauli@snapgear.com>
962306a36Sopenharmony_ci *	Copyright (C) 2000, 2001 Lineo, by David McCullough <davidm@lineo.com>
1062306a36Sopenharmony_ci *  based heavily on:
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *  linux/fs/binfmt_aout.c:
1362306a36Sopenharmony_ci *      Copyright (C) 1991, 1992, 1996  Linus Torvalds
1462306a36Sopenharmony_ci *  linux/fs/binfmt_flat.c for 2.0 kernel
1562306a36Sopenharmony_ci *	    Copyright (C) 1998  Kenneth Albanowski <kjahds@kjahds.com>
1662306a36Sopenharmony_ci *	JAN/99 -- coded full program relocation (gerg@snapgear.com)
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/kernel.h>
2262306a36Sopenharmony_ci#include <linux/sched.h>
2362306a36Sopenharmony_ci#include <linux/sched/task_stack.h>
2462306a36Sopenharmony_ci#include <linux/mm.h>
2562306a36Sopenharmony_ci#include <linux/mman.h>
2662306a36Sopenharmony_ci#include <linux/errno.h>
2762306a36Sopenharmony_ci#include <linux/signal.h>
2862306a36Sopenharmony_ci#include <linux/string.h>
2962306a36Sopenharmony_ci#include <linux/fs.h>
3062306a36Sopenharmony_ci#include <linux/file.h>
3162306a36Sopenharmony_ci#include <linux/ptrace.h>
3262306a36Sopenharmony_ci#include <linux/user.h>
3362306a36Sopenharmony_ci#include <linux/slab.h>
3462306a36Sopenharmony_ci#include <linux/binfmts.h>
3562306a36Sopenharmony_ci#include <linux/personality.h>
3662306a36Sopenharmony_ci#include <linux/init.h>
3762306a36Sopenharmony_ci#include <linux/flat.h>
3862306a36Sopenharmony_ci#include <linux/uaccess.h>
3962306a36Sopenharmony_ci#include <linux/vmalloc.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include <asm/byteorder.h>
4262306a36Sopenharmony_ci#include <asm/unaligned.h>
4362306a36Sopenharmony_ci#include <asm/cacheflush.h>
4462306a36Sopenharmony_ci#include <asm/page.h>
4562306a36Sopenharmony_ci#include <asm/flat.h>
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#ifndef flat_get_relocate_addr
4862306a36Sopenharmony_ci#define flat_get_relocate_addr(rel)	(rel)
4962306a36Sopenharmony_ci#endif
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/****************************************************************************/
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/*
5462306a36Sopenharmony_ci * User data (data section and bss) needs to be aligned.
5562306a36Sopenharmony_ci * We pick 0x20 here because it is the max value elf2flt has always
5662306a36Sopenharmony_ci * used in producing FLAT files, and because it seems to be large
5762306a36Sopenharmony_ci * enough to make all the gcc alignment related tests happy.
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_ci#define FLAT_DATA_ALIGN	(0x20)
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/*
6262306a36Sopenharmony_ci * User data (stack) also needs to be aligned.
6362306a36Sopenharmony_ci * Here we can be a bit looser than the data sections since this
6462306a36Sopenharmony_ci * needs to only meet arch ABI requirements.
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_ci#define FLAT_STACK_ALIGN	max_t(unsigned long, sizeof(void *), ARCH_SLAB_MINALIGN)
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define RELOC_FAILED 0xff00ff01		/* Relocation incorrect somewhere */
6962306a36Sopenharmony_ci#define UNLOADED_LIB 0x7ff000ff		/* Placeholder for unused library */
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#define MAX_SHARED_LIBS			(1)
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#ifdef CONFIG_BINFMT_FLAT_NO_DATA_START_OFFSET
7462306a36Sopenharmony_ci#define DATA_START_OFFSET_WORDS		(0)
7562306a36Sopenharmony_ci#else
7662306a36Sopenharmony_ci#define DATA_START_OFFSET_WORDS		(MAX_SHARED_LIBS)
7762306a36Sopenharmony_ci#endif
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistruct lib_info {
8062306a36Sopenharmony_ci	struct {
8162306a36Sopenharmony_ci		unsigned long start_code;		/* Start of text segment */
8262306a36Sopenharmony_ci		unsigned long start_data;		/* Start of data segment */
8362306a36Sopenharmony_ci		unsigned long start_brk;		/* End of data segment */
8462306a36Sopenharmony_ci		unsigned long text_len;			/* Length of text segment */
8562306a36Sopenharmony_ci		unsigned long entry;			/* Start address for this module */
8662306a36Sopenharmony_ci		unsigned long build_date;		/* When this one was compiled */
8762306a36Sopenharmony_ci		bool loaded;				/* Has this library been loaded? */
8862306a36Sopenharmony_ci	} lib_list[MAX_SHARED_LIBS];
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int load_flat_binary(struct linux_binprm *);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic struct linux_binfmt flat_format = {
9462306a36Sopenharmony_ci	.module		= THIS_MODULE,
9562306a36Sopenharmony_ci	.load_binary	= load_flat_binary,
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/****************************************************************************/
10062306a36Sopenharmony_ci/*
10162306a36Sopenharmony_ci * create_flat_tables() parses the env- and arg-strings in new user
10262306a36Sopenharmony_ci * memory and creates the pointer tables from them, and puts their
10362306a36Sopenharmony_ci * addresses on the "stack", recording the new stack pointer value.
10462306a36Sopenharmony_ci */
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int create_flat_tables(struct linux_binprm *bprm, unsigned long arg_start)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	char __user *p;
10962306a36Sopenharmony_ci	unsigned long __user *sp;
11062306a36Sopenharmony_ci	long i, len;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	p = (char __user *)arg_start;
11362306a36Sopenharmony_ci	sp = (unsigned long __user *)current->mm->start_stack;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	sp -= bprm->envc + 1;
11662306a36Sopenharmony_ci	sp -= bprm->argc + 1;
11762306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_BINFMT_FLAT_ARGVP_ENVP_ON_STACK))
11862306a36Sopenharmony_ci		sp -= 2; /* argvp + envp */
11962306a36Sopenharmony_ci	sp -= 1;  /* &argc */
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	current->mm->start_stack = (unsigned long)sp & -FLAT_STACK_ALIGN;
12262306a36Sopenharmony_ci	sp = (unsigned long __user *)current->mm->start_stack;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (put_user(bprm->argc, sp++))
12562306a36Sopenharmony_ci		return -EFAULT;
12662306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_BINFMT_FLAT_ARGVP_ENVP_ON_STACK)) {
12762306a36Sopenharmony_ci		unsigned long argv, envp;
12862306a36Sopenharmony_ci		argv = (unsigned long)(sp + 2);
12962306a36Sopenharmony_ci		envp = (unsigned long)(sp + 2 + bprm->argc + 1);
13062306a36Sopenharmony_ci		if (put_user(argv, sp++) || put_user(envp, sp++))
13162306a36Sopenharmony_ci			return -EFAULT;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	current->mm->arg_start = (unsigned long)p;
13562306a36Sopenharmony_ci	for (i = bprm->argc; i > 0; i--) {
13662306a36Sopenharmony_ci		if (put_user((unsigned long)p, sp++))
13762306a36Sopenharmony_ci			return -EFAULT;
13862306a36Sopenharmony_ci		len = strnlen_user(p, MAX_ARG_STRLEN);
13962306a36Sopenharmony_ci		if (!len || len > MAX_ARG_STRLEN)
14062306a36Sopenharmony_ci			return -EINVAL;
14162306a36Sopenharmony_ci		p += len;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci	if (put_user(0, sp++))
14462306a36Sopenharmony_ci		return -EFAULT;
14562306a36Sopenharmony_ci	current->mm->arg_end = (unsigned long)p;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	current->mm->env_start = (unsigned long) p;
14862306a36Sopenharmony_ci	for (i = bprm->envc; i > 0; i--) {
14962306a36Sopenharmony_ci		if (put_user((unsigned long)p, sp++))
15062306a36Sopenharmony_ci			return -EFAULT;
15162306a36Sopenharmony_ci		len = strnlen_user(p, MAX_ARG_STRLEN);
15262306a36Sopenharmony_ci		if (!len || len > MAX_ARG_STRLEN)
15362306a36Sopenharmony_ci			return -EINVAL;
15462306a36Sopenharmony_ci		p += len;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci	if (put_user(0, sp++))
15762306a36Sopenharmony_ci		return -EFAULT;
15862306a36Sopenharmony_ci	current->mm->env_end = (unsigned long)p;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	return 0;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/****************************************************************************/
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci#ifdef CONFIG_BINFMT_ZFLAT
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci#include <linux/zlib.h>
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci#define LBUFSIZE	4000
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/* gzip flag byte */
17262306a36Sopenharmony_ci#define ASCII_FLAG   0x01 /* bit 0 set: file probably ASCII text */
17362306a36Sopenharmony_ci#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
17462306a36Sopenharmony_ci#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
17562306a36Sopenharmony_ci#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
17662306a36Sopenharmony_ci#define COMMENT      0x10 /* bit 4 set: file comment present */
17762306a36Sopenharmony_ci#define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
17862306a36Sopenharmony_ci#define RESERVED     0xC0 /* bit 6,7:   reserved */
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int decompress_exec(struct linux_binprm *bprm, loff_t fpos, char *dst,
18162306a36Sopenharmony_ci		long len, int fd)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	unsigned char *buf;
18462306a36Sopenharmony_ci	z_stream strm;
18562306a36Sopenharmony_ci	int ret, retval;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	pr_debug("decompress_exec(offset=%llx,buf=%p,len=%lx)\n", fpos, dst, len);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	memset(&strm, 0, sizeof(strm));
19062306a36Sopenharmony_ci	strm.workspace = kmalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
19162306a36Sopenharmony_ci	if (!strm.workspace)
19262306a36Sopenharmony_ci		return -ENOMEM;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	buf = kmalloc(LBUFSIZE, GFP_KERNEL);
19562306a36Sopenharmony_ci	if (!buf) {
19662306a36Sopenharmony_ci		retval = -ENOMEM;
19762306a36Sopenharmony_ci		goto out_free;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* Read in first chunk of data and parse gzip header. */
20162306a36Sopenharmony_ci	ret = kernel_read(bprm->file, buf, LBUFSIZE, &fpos);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	strm.next_in = buf;
20462306a36Sopenharmony_ci	strm.avail_in = ret;
20562306a36Sopenharmony_ci	strm.total_in = 0;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	retval = -ENOEXEC;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* Check minimum size -- gzip header */
21062306a36Sopenharmony_ci	if (ret < 10) {
21162306a36Sopenharmony_ci		pr_debug("file too small?\n");
21262306a36Sopenharmony_ci		goto out_free_buf;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Check gzip magic number */
21662306a36Sopenharmony_ci	if ((buf[0] != 037) || ((buf[1] != 0213) && (buf[1] != 0236))) {
21762306a36Sopenharmony_ci		pr_debug("unknown compression magic?\n");
21862306a36Sopenharmony_ci		goto out_free_buf;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/* Check gzip method */
22262306a36Sopenharmony_ci	if (buf[2] != 8) {
22362306a36Sopenharmony_ci		pr_debug("unknown compression method?\n");
22462306a36Sopenharmony_ci		goto out_free_buf;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci	/* Check gzip flags */
22762306a36Sopenharmony_ci	if ((buf[3] & ENCRYPTED) || (buf[3] & CONTINUATION) ||
22862306a36Sopenharmony_ci	    (buf[3] & RESERVED)) {
22962306a36Sopenharmony_ci		pr_debug("unknown flags?\n");
23062306a36Sopenharmony_ci		goto out_free_buf;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	ret = 10;
23462306a36Sopenharmony_ci	if (buf[3] & EXTRA_FIELD) {
23562306a36Sopenharmony_ci		ret += 2 + buf[10] + (buf[11] << 8);
23662306a36Sopenharmony_ci		if (unlikely(ret >= LBUFSIZE)) {
23762306a36Sopenharmony_ci			pr_debug("buffer overflow (EXTRA)?\n");
23862306a36Sopenharmony_ci			goto out_free_buf;
23962306a36Sopenharmony_ci		}
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci	if (buf[3] & ORIG_NAME) {
24262306a36Sopenharmony_ci		while (ret < LBUFSIZE && buf[ret++] != 0)
24362306a36Sopenharmony_ci			;
24462306a36Sopenharmony_ci		if (unlikely(ret == LBUFSIZE)) {
24562306a36Sopenharmony_ci			pr_debug("buffer overflow (ORIG_NAME)?\n");
24662306a36Sopenharmony_ci			goto out_free_buf;
24762306a36Sopenharmony_ci		}
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci	if (buf[3] & COMMENT) {
25062306a36Sopenharmony_ci		while (ret < LBUFSIZE && buf[ret++] != 0)
25162306a36Sopenharmony_ci			;
25262306a36Sopenharmony_ci		if (unlikely(ret == LBUFSIZE)) {
25362306a36Sopenharmony_ci			pr_debug("buffer overflow (COMMENT)?\n");
25462306a36Sopenharmony_ci			goto out_free_buf;
25562306a36Sopenharmony_ci		}
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	strm.next_in += ret;
25962306a36Sopenharmony_ci	strm.avail_in -= ret;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	strm.next_out = dst;
26262306a36Sopenharmony_ci	strm.avail_out = len;
26362306a36Sopenharmony_ci	strm.total_out = 0;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (zlib_inflateInit2(&strm, -MAX_WBITS) != Z_OK) {
26662306a36Sopenharmony_ci		pr_debug("zlib init failed?\n");
26762306a36Sopenharmony_ci		goto out_free_buf;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	while ((ret = zlib_inflate(&strm, Z_NO_FLUSH)) == Z_OK) {
27162306a36Sopenharmony_ci		ret = kernel_read(bprm->file, buf, LBUFSIZE, &fpos);
27262306a36Sopenharmony_ci		if (ret <= 0)
27362306a36Sopenharmony_ci			break;
27462306a36Sopenharmony_ci		len -= ret;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci		strm.next_in = buf;
27762306a36Sopenharmony_ci		strm.avail_in = ret;
27862306a36Sopenharmony_ci		strm.total_in = 0;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (ret < 0) {
28262306a36Sopenharmony_ci		pr_debug("decompression failed (%d), %s\n",
28362306a36Sopenharmony_ci			ret, strm.msg);
28462306a36Sopenharmony_ci		goto out_zlib;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	retval = 0;
28862306a36Sopenharmony_ciout_zlib:
28962306a36Sopenharmony_ci	zlib_inflateEnd(&strm);
29062306a36Sopenharmony_ciout_free_buf:
29162306a36Sopenharmony_ci	kfree(buf);
29262306a36Sopenharmony_ciout_free:
29362306a36Sopenharmony_ci	kfree(strm.workspace);
29462306a36Sopenharmony_ci	return retval;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci#endif /* CONFIG_BINFMT_ZFLAT */
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci/****************************************************************************/
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic unsigned long
30262306a36Sopenharmony_cicalc_reloc(unsigned long r, struct lib_info *p)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	unsigned long addr;
30562306a36Sopenharmony_ci	unsigned long start_brk;
30662306a36Sopenharmony_ci	unsigned long start_data;
30762306a36Sopenharmony_ci	unsigned long text_len;
30862306a36Sopenharmony_ci	unsigned long start_code;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	start_brk = p->lib_list[0].start_brk;
31162306a36Sopenharmony_ci	start_data = p->lib_list[0].start_data;
31262306a36Sopenharmony_ci	start_code = p->lib_list[0].start_code;
31362306a36Sopenharmony_ci	text_len = p->lib_list[0].text_len;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (r > start_brk - start_data + text_len) {
31662306a36Sopenharmony_ci		pr_err("reloc outside program 0x%lx (0 - 0x%lx/0x%lx)",
31762306a36Sopenharmony_ci		       r, start_brk-start_data+text_len, text_len);
31862306a36Sopenharmony_ci		goto failed;
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	if (r < text_len)			/* In text segment */
32262306a36Sopenharmony_ci		addr = r + start_code;
32362306a36Sopenharmony_ci	else					/* In data segment */
32462306a36Sopenharmony_ci		addr = r - text_len + start_data;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	/* Range checked already above so doing the range tests is redundant...*/
32762306a36Sopenharmony_ci	return addr;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cifailed:
33062306a36Sopenharmony_ci	pr_cont(", killing %s!\n", current->comm);
33162306a36Sopenharmony_ci	send_sig(SIGSEGV, current, 0);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	return RELOC_FAILED;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci/****************************************************************************/
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci#ifdef CONFIG_BINFMT_FLAT_OLD
33962306a36Sopenharmony_cistatic void old_reloc(unsigned long rl)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	static const char *segment[] = { "TEXT", "DATA", "BSS", "*UNKNOWN*" };
34262306a36Sopenharmony_ci	flat_v2_reloc_t	r;
34362306a36Sopenharmony_ci	unsigned long __user *ptr;
34462306a36Sopenharmony_ci	unsigned long val;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	r.value = rl;
34762306a36Sopenharmony_ci#if defined(CONFIG_COLDFIRE)
34862306a36Sopenharmony_ci	ptr = (unsigned long __user *)(current->mm->start_code + r.reloc.offset);
34962306a36Sopenharmony_ci#else
35062306a36Sopenharmony_ci	ptr = (unsigned long __user *)(current->mm->start_data + r.reloc.offset);
35162306a36Sopenharmony_ci#endif
35262306a36Sopenharmony_ci	get_user(val, ptr);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	pr_debug("Relocation of variable at DATASEG+%x "
35562306a36Sopenharmony_ci		 "(address %p, currently %lx) into segment %s\n",
35662306a36Sopenharmony_ci		 r.reloc.offset, ptr, val, segment[r.reloc.type]);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	switch (r.reloc.type) {
35962306a36Sopenharmony_ci	case OLD_FLAT_RELOC_TYPE_TEXT:
36062306a36Sopenharmony_ci		val += current->mm->start_code;
36162306a36Sopenharmony_ci		break;
36262306a36Sopenharmony_ci	case OLD_FLAT_RELOC_TYPE_DATA:
36362306a36Sopenharmony_ci		val += current->mm->start_data;
36462306a36Sopenharmony_ci		break;
36562306a36Sopenharmony_ci	case OLD_FLAT_RELOC_TYPE_BSS:
36662306a36Sopenharmony_ci		val += current->mm->end_data;
36762306a36Sopenharmony_ci		break;
36862306a36Sopenharmony_ci	default:
36962306a36Sopenharmony_ci		pr_err("Unknown relocation type=%x\n", r.reloc.type);
37062306a36Sopenharmony_ci		break;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci	put_user(val, ptr);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	pr_debug("Relocation became %lx\n", val);
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci#endif /* CONFIG_BINFMT_FLAT_OLD */
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci/****************************************************************************/
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic inline u32 __user *skip_got_header(u32 __user *rp)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_RISCV)) {
38362306a36Sopenharmony_ci		/*
38462306a36Sopenharmony_ci		 * RISC-V has a 16 byte GOT PLT header for elf64-riscv
38562306a36Sopenharmony_ci		 * and 8 byte GOT PLT header for elf32-riscv.
38662306a36Sopenharmony_ci		 * Skip the whole GOT PLT header, since it is reserved
38762306a36Sopenharmony_ci		 * for the dynamic linker (ld.so).
38862306a36Sopenharmony_ci		 */
38962306a36Sopenharmony_ci		u32 rp_val0, rp_val1;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci		if (get_user(rp_val0, rp))
39262306a36Sopenharmony_ci			return rp;
39362306a36Sopenharmony_ci		if (get_user(rp_val1, rp + 1))
39462306a36Sopenharmony_ci			return rp;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci		if (rp_val0 == 0xffffffff && rp_val1 == 0xffffffff)
39762306a36Sopenharmony_ci			rp += 4;
39862306a36Sopenharmony_ci		else if (rp_val0 == 0xffffffff)
39962306a36Sopenharmony_ci			rp += 2;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci	return rp;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic int load_flat_file(struct linux_binprm *bprm,
40562306a36Sopenharmony_ci		struct lib_info *libinfo, unsigned long *extra_stack)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	struct flat_hdr *hdr;
40862306a36Sopenharmony_ci	unsigned long textpos, datapos, realdatastart;
40962306a36Sopenharmony_ci	u32 text_len, data_len, bss_len, stack_len, full_data, flags;
41062306a36Sopenharmony_ci	unsigned long len, memp, memp_size, extra, rlim;
41162306a36Sopenharmony_ci	__be32 __user *reloc;
41262306a36Sopenharmony_ci	u32 __user *rp;
41362306a36Sopenharmony_ci	int i, rev, relocs;
41462306a36Sopenharmony_ci	loff_t fpos;
41562306a36Sopenharmony_ci	unsigned long start_code, end_code;
41662306a36Sopenharmony_ci	ssize_t result;
41762306a36Sopenharmony_ci	int ret;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	hdr = ((struct flat_hdr *) bprm->buf);		/* exec-header */
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	text_len  = ntohl(hdr->data_start);
42262306a36Sopenharmony_ci	data_len  = ntohl(hdr->data_end) - ntohl(hdr->data_start);
42362306a36Sopenharmony_ci	bss_len   = ntohl(hdr->bss_end) - ntohl(hdr->data_end);
42462306a36Sopenharmony_ci	stack_len = ntohl(hdr->stack_size);
42562306a36Sopenharmony_ci	if (extra_stack) {
42662306a36Sopenharmony_ci		stack_len += *extra_stack;
42762306a36Sopenharmony_ci		*extra_stack = stack_len;
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci	relocs    = ntohl(hdr->reloc_count);
43062306a36Sopenharmony_ci	flags     = ntohl(hdr->flags);
43162306a36Sopenharmony_ci	rev       = ntohl(hdr->rev);
43262306a36Sopenharmony_ci	full_data = data_len + relocs * sizeof(unsigned long);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (strncmp(hdr->magic, "bFLT", 4)) {
43562306a36Sopenharmony_ci		/*
43662306a36Sopenharmony_ci		 * Previously, here was a printk to tell people
43762306a36Sopenharmony_ci		 *   "BINFMT_FLAT: bad header magic".
43862306a36Sopenharmony_ci		 * But for the kernel which also use ELF FD-PIC format, this
43962306a36Sopenharmony_ci		 * error message is confusing.
44062306a36Sopenharmony_ci		 * because a lot of people do not manage to produce good
44162306a36Sopenharmony_ci		 */
44262306a36Sopenharmony_ci		ret = -ENOEXEC;
44362306a36Sopenharmony_ci		goto err;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (flags & FLAT_FLAG_KTRACE)
44762306a36Sopenharmony_ci		pr_info("Loading file: %s\n", bprm->filename);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci#ifdef CONFIG_BINFMT_FLAT_OLD
45062306a36Sopenharmony_ci	if (rev != FLAT_VERSION && rev != OLD_FLAT_VERSION) {
45162306a36Sopenharmony_ci		pr_err("bad flat file version 0x%x (supported 0x%lx and 0x%lx)\n",
45262306a36Sopenharmony_ci		       rev, FLAT_VERSION, OLD_FLAT_VERSION);
45362306a36Sopenharmony_ci		ret = -ENOEXEC;
45462306a36Sopenharmony_ci		goto err;
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/*
45862306a36Sopenharmony_ci	 * fix up the flags for the older format,  there were all kinds
45962306a36Sopenharmony_ci	 * of endian hacks,  this only works for the simple cases
46062306a36Sopenharmony_ci	 */
46162306a36Sopenharmony_ci	if (rev == OLD_FLAT_VERSION &&
46262306a36Sopenharmony_ci	   (flags || IS_ENABLED(CONFIG_BINFMT_FLAT_OLD_ALWAYS_RAM)))
46362306a36Sopenharmony_ci		flags = FLAT_FLAG_RAM;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci#else /* CONFIG_BINFMT_FLAT_OLD */
46662306a36Sopenharmony_ci	if (rev != FLAT_VERSION) {
46762306a36Sopenharmony_ci		pr_err("bad flat file version 0x%x (supported 0x%lx)\n",
46862306a36Sopenharmony_ci		       rev, FLAT_VERSION);
46962306a36Sopenharmony_ci		ret = -ENOEXEC;
47062306a36Sopenharmony_ci		goto err;
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci#endif /* !CONFIG_BINFMT_FLAT_OLD */
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/*
47562306a36Sopenharmony_ci	 * Make sure the header params are sane.
47662306a36Sopenharmony_ci	 * 28 bits (256 MB) is way more than reasonable in this case.
47762306a36Sopenharmony_ci	 * If some top bits are set we have probable binary corruption.
47862306a36Sopenharmony_ci	*/
47962306a36Sopenharmony_ci	if ((text_len | data_len | bss_len | stack_len | full_data) >> 28) {
48062306a36Sopenharmony_ci		pr_err("bad header\n");
48162306a36Sopenharmony_ci		ret = -ENOEXEC;
48262306a36Sopenharmony_ci		goto err;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci#ifndef CONFIG_BINFMT_ZFLAT
48662306a36Sopenharmony_ci	if (flags & (FLAT_FLAG_GZIP|FLAT_FLAG_GZDATA)) {
48762306a36Sopenharmony_ci		pr_err("Support for ZFLAT executables is not enabled.\n");
48862306a36Sopenharmony_ci		ret = -ENOEXEC;
48962306a36Sopenharmony_ci		goto err;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci#endif
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/*
49462306a36Sopenharmony_ci	 * Check initial limits. This avoids letting people circumvent
49562306a36Sopenharmony_ci	 * size limits imposed on them by creating programs with large
49662306a36Sopenharmony_ci	 * arrays in the data or bss.
49762306a36Sopenharmony_ci	 */
49862306a36Sopenharmony_ci	rlim = rlimit(RLIMIT_DATA);
49962306a36Sopenharmony_ci	if (rlim >= RLIM_INFINITY)
50062306a36Sopenharmony_ci		rlim = ~0;
50162306a36Sopenharmony_ci	if (data_len + bss_len > rlim) {
50262306a36Sopenharmony_ci		ret = -ENOMEM;
50362306a36Sopenharmony_ci		goto err;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	/* Flush all traces of the currently running executable */
50762306a36Sopenharmony_ci	ret = begin_new_exec(bprm);
50862306a36Sopenharmony_ci	if (ret)
50962306a36Sopenharmony_ci		goto err;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	/* OK, This is the point of no return */
51262306a36Sopenharmony_ci	set_personality(PER_LINUX_32BIT);
51362306a36Sopenharmony_ci	setup_new_exec(bprm);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	/*
51662306a36Sopenharmony_ci	 * calculate the extra space we need to map in
51762306a36Sopenharmony_ci	 */
51862306a36Sopenharmony_ci	extra = max_t(unsigned long, bss_len + stack_len,
51962306a36Sopenharmony_ci			relocs * sizeof(unsigned long));
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	/*
52262306a36Sopenharmony_ci	 * there are a couple of cases here,  the separate code/data
52362306a36Sopenharmony_ci	 * case,  and then the fully copied to RAM case which lumps
52462306a36Sopenharmony_ci	 * it all together.
52562306a36Sopenharmony_ci	 */
52662306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_MMU) && !(flags & (FLAT_FLAG_RAM|FLAT_FLAG_GZIP))) {
52762306a36Sopenharmony_ci		/*
52862306a36Sopenharmony_ci		 * this should give us a ROM ptr,  but if it doesn't we don't
52962306a36Sopenharmony_ci		 * really care
53062306a36Sopenharmony_ci		 */
53162306a36Sopenharmony_ci		pr_debug("ROM mapping of file (we hope)\n");
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci		textpos = vm_mmap(bprm->file, 0, text_len, PROT_READ|PROT_EXEC,
53462306a36Sopenharmony_ci				  MAP_PRIVATE, 0);
53562306a36Sopenharmony_ci		if (!textpos || IS_ERR_VALUE(textpos)) {
53662306a36Sopenharmony_ci			ret = textpos;
53762306a36Sopenharmony_ci			if (!textpos)
53862306a36Sopenharmony_ci				ret = -ENOMEM;
53962306a36Sopenharmony_ci			pr_err("Unable to mmap process text, errno %d\n", ret);
54062306a36Sopenharmony_ci			goto err;
54162306a36Sopenharmony_ci		}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci		len = data_len + extra +
54462306a36Sopenharmony_ci			DATA_START_OFFSET_WORDS * sizeof(unsigned long);
54562306a36Sopenharmony_ci		len = PAGE_ALIGN(len);
54662306a36Sopenharmony_ci		realdatastart = vm_mmap(NULL, 0, len,
54762306a36Sopenharmony_ci			PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, 0);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		if (realdatastart == 0 || IS_ERR_VALUE(realdatastart)) {
55062306a36Sopenharmony_ci			ret = realdatastart;
55162306a36Sopenharmony_ci			if (!realdatastart)
55262306a36Sopenharmony_ci				ret = -ENOMEM;
55362306a36Sopenharmony_ci			pr_err("Unable to allocate RAM for process data, "
55462306a36Sopenharmony_ci			       "errno %d\n", ret);
55562306a36Sopenharmony_ci			vm_munmap(textpos, text_len);
55662306a36Sopenharmony_ci			goto err;
55762306a36Sopenharmony_ci		}
55862306a36Sopenharmony_ci		datapos = ALIGN(realdatastart +
55962306a36Sopenharmony_ci				DATA_START_OFFSET_WORDS * sizeof(unsigned long),
56062306a36Sopenharmony_ci				FLAT_DATA_ALIGN);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		pr_debug("Allocated data+bss+stack (%u bytes): %lx\n",
56362306a36Sopenharmony_ci			 data_len + bss_len + stack_len, datapos);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci		fpos = ntohl(hdr->data_start);
56662306a36Sopenharmony_ci#ifdef CONFIG_BINFMT_ZFLAT
56762306a36Sopenharmony_ci		if (flags & FLAT_FLAG_GZDATA) {
56862306a36Sopenharmony_ci			result = decompress_exec(bprm, fpos, (char *)datapos,
56962306a36Sopenharmony_ci						 full_data, 0);
57062306a36Sopenharmony_ci		} else
57162306a36Sopenharmony_ci#endif
57262306a36Sopenharmony_ci		{
57362306a36Sopenharmony_ci			result = read_code(bprm->file, datapos, fpos,
57462306a36Sopenharmony_ci					full_data);
57562306a36Sopenharmony_ci		}
57662306a36Sopenharmony_ci		if (IS_ERR_VALUE(result)) {
57762306a36Sopenharmony_ci			ret = result;
57862306a36Sopenharmony_ci			pr_err("Unable to read data+bss, errno %d\n", ret);
57962306a36Sopenharmony_ci			vm_munmap(textpos, text_len);
58062306a36Sopenharmony_ci			vm_munmap(realdatastart, len);
58162306a36Sopenharmony_ci			goto err;
58262306a36Sopenharmony_ci		}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci		reloc = (__be32 __user *)
58562306a36Sopenharmony_ci			(datapos + (ntohl(hdr->reloc_start) - text_len));
58662306a36Sopenharmony_ci		memp = realdatastart;
58762306a36Sopenharmony_ci		memp_size = len;
58862306a36Sopenharmony_ci	} else {
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci		len = text_len + data_len + extra +
59162306a36Sopenharmony_ci			DATA_START_OFFSET_WORDS * sizeof(u32);
59262306a36Sopenharmony_ci		len = PAGE_ALIGN(len);
59362306a36Sopenharmony_ci		textpos = vm_mmap(NULL, 0, len,
59462306a36Sopenharmony_ci			PROT_READ | PROT_EXEC | PROT_WRITE, MAP_PRIVATE, 0);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci		if (!textpos || IS_ERR_VALUE(textpos)) {
59762306a36Sopenharmony_ci			ret = textpos;
59862306a36Sopenharmony_ci			if (!textpos)
59962306a36Sopenharmony_ci				ret = -ENOMEM;
60062306a36Sopenharmony_ci			pr_err("Unable to allocate RAM for process text/data, "
60162306a36Sopenharmony_ci			       "errno %d\n", ret);
60262306a36Sopenharmony_ci			goto err;
60362306a36Sopenharmony_ci		}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci		realdatastart = textpos + ntohl(hdr->data_start);
60662306a36Sopenharmony_ci		datapos = ALIGN(realdatastart +
60762306a36Sopenharmony_ci				DATA_START_OFFSET_WORDS * sizeof(u32),
60862306a36Sopenharmony_ci				FLAT_DATA_ALIGN);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci		reloc = (__be32 __user *)
61162306a36Sopenharmony_ci			(datapos + (ntohl(hdr->reloc_start) - text_len));
61262306a36Sopenharmony_ci		memp = textpos;
61362306a36Sopenharmony_ci		memp_size = len;
61462306a36Sopenharmony_ci#ifdef CONFIG_BINFMT_ZFLAT
61562306a36Sopenharmony_ci		/*
61662306a36Sopenharmony_ci		 * load it all in and treat it like a RAM load from now on
61762306a36Sopenharmony_ci		 */
61862306a36Sopenharmony_ci		if (flags & FLAT_FLAG_GZIP) {
61962306a36Sopenharmony_ci#ifndef CONFIG_MMU
62062306a36Sopenharmony_ci			result = decompress_exec(bprm, sizeof(struct flat_hdr),
62162306a36Sopenharmony_ci					 (((char *)textpos) + sizeof(struct flat_hdr)),
62262306a36Sopenharmony_ci					 (text_len + full_data
62362306a36Sopenharmony_ci						  - sizeof(struct flat_hdr)),
62462306a36Sopenharmony_ci					 0);
62562306a36Sopenharmony_ci			memmove((void *) datapos, (void *) realdatastart,
62662306a36Sopenharmony_ci					full_data);
62762306a36Sopenharmony_ci#else
62862306a36Sopenharmony_ci			/*
62962306a36Sopenharmony_ci			 * This is used on MMU systems mainly for testing.
63062306a36Sopenharmony_ci			 * Let's use a kernel buffer to simplify things.
63162306a36Sopenharmony_ci			 */
63262306a36Sopenharmony_ci			long unz_text_len = text_len - sizeof(struct flat_hdr);
63362306a36Sopenharmony_ci			long unz_len = unz_text_len + full_data;
63462306a36Sopenharmony_ci			char *unz_data = vmalloc(unz_len);
63562306a36Sopenharmony_ci			if (!unz_data) {
63662306a36Sopenharmony_ci				result = -ENOMEM;
63762306a36Sopenharmony_ci			} else {
63862306a36Sopenharmony_ci				result = decompress_exec(bprm, sizeof(struct flat_hdr),
63962306a36Sopenharmony_ci							 unz_data, unz_len, 0);
64062306a36Sopenharmony_ci				if (result == 0 &&
64162306a36Sopenharmony_ci				    (copy_to_user((void __user *)textpos + sizeof(struct flat_hdr),
64262306a36Sopenharmony_ci						  unz_data, unz_text_len) ||
64362306a36Sopenharmony_ci				     copy_to_user((void __user *)datapos,
64462306a36Sopenharmony_ci						  unz_data + unz_text_len, full_data)))
64562306a36Sopenharmony_ci					result = -EFAULT;
64662306a36Sopenharmony_ci				vfree(unz_data);
64762306a36Sopenharmony_ci			}
64862306a36Sopenharmony_ci#endif
64962306a36Sopenharmony_ci		} else if (flags & FLAT_FLAG_GZDATA) {
65062306a36Sopenharmony_ci			result = read_code(bprm->file, textpos, 0, text_len);
65162306a36Sopenharmony_ci			if (!IS_ERR_VALUE(result)) {
65262306a36Sopenharmony_ci#ifndef CONFIG_MMU
65362306a36Sopenharmony_ci				result = decompress_exec(bprm, text_len, (char *) datapos,
65462306a36Sopenharmony_ci						 full_data, 0);
65562306a36Sopenharmony_ci#else
65662306a36Sopenharmony_ci				char *unz_data = vmalloc(full_data);
65762306a36Sopenharmony_ci				if (!unz_data) {
65862306a36Sopenharmony_ci					result = -ENOMEM;
65962306a36Sopenharmony_ci				} else {
66062306a36Sopenharmony_ci					result = decompress_exec(bprm, text_len,
66162306a36Sopenharmony_ci						       unz_data, full_data, 0);
66262306a36Sopenharmony_ci					if (result == 0 &&
66362306a36Sopenharmony_ci					    copy_to_user((void __user *)datapos,
66462306a36Sopenharmony_ci							 unz_data, full_data))
66562306a36Sopenharmony_ci						result = -EFAULT;
66662306a36Sopenharmony_ci					vfree(unz_data);
66762306a36Sopenharmony_ci				}
66862306a36Sopenharmony_ci#endif
66962306a36Sopenharmony_ci			}
67062306a36Sopenharmony_ci		} else
67162306a36Sopenharmony_ci#endif /* CONFIG_BINFMT_ZFLAT */
67262306a36Sopenharmony_ci		{
67362306a36Sopenharmony_ci			result = read_code(bprm->file, textpos, 0, text_len);
67462306a36Sopenharmony_ci			if (!IS_ERR_VALUE(result))
67562306a36Sopenharmony_ci				result = read_code(bprm->file, datapos,
67662306a36Sopenharmony_ci						   ntohl(hdr->data_start),
67762306a36Sopenharmony_ci						   full_data);
67862306a36Sopenharmony_ci		}
67962306a36Sopenharmony_ci		if (IS_ERR_VALUE(result)) {
68062306a36Sopenharmony_ci			ret = result;
68162306a36Sopenharmony_ci			pr_err("Unable to read code+data+bss, errno %d\n", ret);
68262306a36Sopenharmony_ci			vm_munmap(textpos, text_len + data_len + extra +
68362306a36Sopenharmony_ci				  DATA_START_OFFSET_WORDS * sizeof(u32));
68462306a36Sopenharmony_ci			goto err;
68562306a36Sopenharmony_ci		}
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	start_code = textpos + sizeof(struct flat_hdr);
68962306a36Sopenharmony_ci	end_code = textpos + text_len;
69062306a36Sopenharmony_ci	text_len -= sizeof(struct flat_hdr); /* the real code len */
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	/* The main program needs a little extra setup in the task structure */
69362306a36Sopenharmony_ci	current->mm->start_code = start_code;
69462306a36Sopenharmony_ci	current->mm->end_code = end_code;
69562306a36Sopenharmony_ci	current->mm->start_data = datapos;
69662306a36Sopenharmony_ci	current->mm->end_data = datapos + data_len;
69762306a36Sopenharmony_ci	/*
69862306a36Sopenharmony_ci	 * set up the brk stuff, uses any slack left in data/bss/stack
69962306a36Sopenharmony_ci	 * allocation.  We put the brk after the bss (between the bss
70062306a36Sopenharmony_ci	 * and stack) like other platforms.
70162306a36Sopenharmony_ci	 * Userspace code relies on the stack pointer starting out at
70262306a36Sopenharmony_ci	 * an address right at the end of a page.
70362306a36Sopenharmony_ci	 */
70462306a36Sopenharmony_ci	current->mm->start_brk = datapos + data_len + bss_len;
70562306a36Sopenharmony_ci	current->mm->brk = (current->mm->start_brk + 3) & ~3;
70662306a36Sopenharmony_ci#ifndef CONFIG_MMU
70762306a36Sopenharmony_ci	current->mm->context.end_brk = memp + memp_size - stack_len;
70862306a36Sopenharmony_ci#endif
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	if (flags & FLAT_FLAG_KTRACE) {
71162306a36Sopenharmony_ci		pr_info("Mapping is %lx, Entry point is %x, data_start is %x\n",
71262306a36Sopenharmony_ci			textpos, 0x00ffffff&ntohl(hdr->entry), ntohl(hdr->data_start));
71362306a36Sopenharmony_ci		pr_info("%s %s: TEXT=%lx-%lx DATA=%lx-%lx BSS=%lx-%lx\n",
71462306a36Sopenharmony_ci			"Load", bprm->filename,
71562306a36Sopenharmony_ci			start_code, end_code, datapos, datapos + data_len,
71662306a36Sopenharmony_ci			datapos + data_len, (datapos + data_len + bss_len + 3) & ~3);
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	/* Store the current module values into the global library structure */
72062306a36Sopenharmony_ci	libinfo->lib_list[0].start_code = start_code;
72162306a36Sopenharmony_ci	libinfo->lib_list[0].start_data = datapos;
72262306a36Sopenharmony_ci	libinfo->lib_list[0].start_brk = datapos + data_len + bss_len;
72362306a36Sopenharmony_ci	libinfo->lib_list[0].text_len = text_len;
72462306a36Sopenharmony_ci	libinfo->lib_list[0].loaded = 1;
72562306a36Sopenharmony_ci	libinfo->lib_list[0].entry = (0x00ffffff & ntohl(hdr->entry)) + textpos;
72662306a36Sopenharmony_ci	libinfo->lib_list[0].build_date = ntohl(hdr->build_date);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	/*
72962306a36Sopenharmony_ci	 * We just load the allocations into some temporary memory to
73062306a36Sopenharmony_ci	 * help simplify all this mumbo jumbo
73162306a36Sopenharmony_ci	 *
73262306a36Sopenharmony_ci	 * We've got two different sections of relocation entries.
73362306a36Sopenharmony_ci	 * The first is the GOT which resides at the beginning of the data segment
73462306a36Sopenharmony_ci	 * and is terminated with a -1.  This one can be relocated in place.
73562306a36Sopenharmony_ci	 * The second is the extra relocation entries tacked after the image's
73662306a36Sopenharmony_ci	 * data segment. These require a little more processing as the entry is
73762306a36Sopenharmony_ci	 * really an offset into the image which contains an offset into the
73862306a36Sopenharmony_ci	 * image.
73962306a36Sopenharmony_ci	 */
74062306a36Sopenharmony_ci	if (flags & FLAT_FLAG_GOTPIC) {
74162306a36Sopenharmony_ci		rp = skip_got_header((u32 __user *) datapos);
74262306a36Sopenharmony_ci		for (; ; rp++) {
74362306a36Sopenharmony_ci			u32 addr, rp_val;
74462306a36Sopenharmony_ci			if (get_user(rp_val, rp))
74562306a36Sopenharmony_ci				return -EFAULT;
74662306a36Sopenharmony_ci			if (rp_val == 0xffffffff)
74762306a36Sopenharmony_ci				break;
74862306a36Sopenharmony_ci			if (rp_val) {
74962306a36Sopenharmony_ci				addr = calc_reloc(rp_val, libinfo);
75062306a36Sopenharmony_ci				if (addr == RELOC_FAILED) {
75162306a36Sopenharmony_ci					ret = -ENOEXEC;
75262306a36Sopenharmony_ci					goto err;
75362306a36Sopenharmony_ci				}
75462306a36Sopenharmony_ci				if (put_user(addr, rp))
75562306a36Sopenharmony_ci					return -EFAULT;
75662306a36Sopenharmony_ci			}
75762306a36Sopenharmony_ci		}
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	/*
76162306a36Sopenharmony_ci	 * Now run through the relocation entries.
76262306a36Sopenharmony_ci	 * We've got to be careful here as C++ produces relocatable zero
76362306a36Sopenharmony_ci	 * entries in the constructor and destructor tables which are then
76462306a36Sopenharmony_ci	 * tested for being not zero (which will always occur unless we're
76562306a36Sopenharmony_ci	 * based from address zero).  This causes an endless loop as __start
76662306a36Sopenharmony_ci	 * is at zero.  The solution used is to not relocate zero addresses.
76762306a36Sopenharmony_ci	 * This has the negative side effect of not allowing a global data
76862306a36Sopenharmony_ci	 * reference to be statically initialised to _stext (I've moved
76962306a36Sopenharmony_ci	 * __start to address 4 so that is okay).
77062306a36Sopenharmony_ci	 */
77162306a36Sopenharmony_ci	if (rev > OLD_FLAT_VERSION) {
77262306a36Sopenharmony_ci		for (i = 0; i < relocs; i++) {
77362306a36Sopenharmony_ci			u32 addr, relval;
77462306a36Sopenharmony_ci			__be32 tmp;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci			/*
77762306a36Sopenharmony_ci			 * Get the address of the pointer to be
77862306a36Sopenharmony_ci			 * relocated (of course, the address has to be
77962306a36Sopenharmony_ci			 * relocated first).
78062306a36Sopenharmony_ci			 */
78162306a36Sopenharmony_ci			if (get_user(tmp, reloc + i))
78262306a36Sopenharmony_ci				return -EFAULT;
78362306a36Sopenharmony_ci			relval = ntohl(tmp);
78462306a36Sopenharmony_ci			addr = flat_get_relocate_addr(relval);
78562306a36Sopenharmony_ci			rp = (u32 __user *)calc_reloc(addr, libinfo);
78662306a36Sopenharmony_ci			if (rp == (u32 __user *)RELOC_FAILED) {
78762306a36Sopenharmony_ci				ret = -ENOEXEC;
78862306a36Sopenharmony_ci				goto err;
78962306a36Sopenharmony_ci			}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci			/* Get the pointer's value.  */
79262306a36Sopenharmony_ci			ret = flat_get_addr_from_rp(rp, relval, flags, &addr);
79362306a36Sopenharmony_ci			if (unlikely(ret))
79462306a36Sopenharmony_ci				goto err;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci			if (addr != 0) {
79762306a36Sopenharmony_ci				/*
79862306a36Sopenharmony_ci				 * Do the relocation.  PIC relocs in the data section are
79962306a36Sopenharmony_ci				 * already in target order
80062306a36Sopenharmony_ci				 */
80162306a36Sopenharmony_ci				if ((flags & FLAT_FLAG_GOTPIC) == 0) {
80262306a36Sopenharmony_ci					/*
80362306a36Sopenharmony_ci					 * Meh, the same value can have a different
80462306a36Sopenharmony_ci					 * byte order based on a flag..
80562306a36Sopenharmony_ci					 */
80662306a36Sopenharmony_ci					addr = ntohl((__force __be32)addr);
80762306a36Sopenharmony_ci				}
80862306a36Sopenharmony_ci				addr = calc_reloc(addr, libinfo);
80962306a36Sopenharmony_ci				if (addr == RELOC_FAILED) {
81062306a36Sopenharmony_ci					ret = -ENOEXEC;
81162306a36Sopenharmony_ci					goto err;
81262306a36Sopenharmony_ci				}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci				/* Write back the relocated pointer.  */
81562306a36Sopenharmony_ci				ret = flat_put_addr_at_rp(rp, addr, relval);
81662306a36Sopenharmony_ci				if (unlikely(ret))
81762306a36Sopenharmony_ci					goto err;
81862306a36Sopenharmony_ci			}
81962306a36Sopenharmony_ci		}
82062306a36Sopenharmony_ci#ifdef CONFIG_BINFMT_FLAT_OLD
82162306a36Sopenharmony_ci	} else {
82262306a36Sopenharmony_ci		for (i = 0; i < relocs; i++) {
82362306a36Sopenharmony_ci			__be32 relval;
82462306a36Sopenharmony_ci			if (get_user(relval, reloc + i))
82562306a36Sopenharmony_ci				return -EFAULT;
82662306a36Sopenharmony_ci			old_reloc(ntohl(relval));
82762306a36Sopenharmony_ci		}
82862306a36Sopenharmony_ci#endif /* CONFIG_BINFMT_FLAT_OLD */
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	flush_icache_user_range(start_code, end_code);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	/* zero the BSS,  BRK and stack areas */
83462306a36Sopenharmony_ci	if (clear_user((void __user *)(datapos + data_len), bss_len +
83562306a36Sopenharmony_ci		       (memp + memp_size - stack_len -		/* end brk */
83662306a36Sopenharmony_ci		       libinfo->lib_list[0].start_brk) +	/* start brk */
83762306a36Sopenharmony_ci		       stack_len))
83862306a36Sopenharmony_ci		return -EFAULT;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	return 0;
84162306a36Sopenharmony_cierr:
84262306a36Sopenharmony_ci	return ret;
84362306a36Sopenharmony_ci}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci/****************************************************************************/
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci/*
84962306a36Sopenharmony_ci * These are the functions used to load flat style executables and shared
85062306a36Sopenharmony_ci * libraries.  There is no binary dependent code anywhere else.
85162306a36Sopenharmony_ci */
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_cistatic int load_flat_binary(struct linux_binprm *bprm)
85462306a36Sopenharmony_ci{
85562306a36Sopenharmony_ci	struct lib_info libinfo;
85662306a36Sopenharmony_ci	struct pt_regs *regs = current_pt_regs();
85762306a36Sopenharmony_ci	unsigned long stack_len = 0;
85862306a36Sopenharmony_ci	unsigned long start_addr;
85962306a36Sopenharmony_ci	int res;
86062306a36Sopenharmony_ci	int i, j;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	memset(&libinfo, 0, sizeof(libinfo));
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	/*
86562306a36Sopenharmony_ci	 * We have to add the size of our arguments to our stack size
86662306a36Sopenharmony_ci	 * otherwise it's too easy for users to create stack overflows
86762306a36Sopenharmony_ci	 * by passing in a huge argument list.  And yes,  we have to be
86862306a36Sopenharmony_ci	 * pedantic and include space for the argv/envp array as it may have
86962306a36Sopenharmony_ci	 * a lot of entries.
87062306a36Sopenharmony_ci	 */
87162306a36Sopenharmony_ci#ifndef CONFIG_MMU
87262306a36Sopenharmony_ci	stack_len += PAGE_SIZE * MAX_ARG_PAGES - bprm->p; /* the strings */
87362306a36Sopenharmony_ci#endif
87462306a36Sopenharmony_ci	stack_len += (bprm->argc + 1) * sizeof(char *);   /* the argv array */
87562306a36Sopenharmony_ci	stack_len += (bprm->envc + 1) * sizeof(char *);   /* the envp array */
87662306a36Sopenharmony_ci	stack_len = ALIGN(stack_len, FLAT_STACK_ALIGN);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	res = load_flat_file(bprm, &libinfo, &stack_len);
87962306a36Sopenharmony_ci	if (res < 0)
88062306a36Sopenharmony_ci		return res;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	/* Update data segment pointers for all libraries */
88362306a36Sopenharmony_ci	for (i = 0; i < MAX_SHARED_LIBS; i++) {
88462306a36Sopenharmony_ci		if (!libinfo.lib_list[i].loaded)
88562306a36Sopenharmony_ci			continue;
88662306a36Sopenharmony_ci		for (j = 0; j < MAX_SHARED_LIBS; j++) {
88762306a36Sopenharmony_ci			unsigned long val = libinfo.lib_list[j].loaded ?
88862306a36Sopenharmony_ci				libinfo.lib_list[j].start_data : UNLOADED_LIB;
88962306a36Sopenharmony_ci			unsigned long __user *p = (unsigned long __user *)
89062306a36Sopenharmony_ci				libinfo.lib_list[i].start_data;
89162306a36Sopenharmony_ci			p -= j + 1;
89262306a36Sopenharmony_ci			if (put_user(val, p))
89362306a36Sopenharmony_ci				return -EFAULT;
89462306a36Sopenharmony_ci		}
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	set_binfmt(&flat_format);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci#ifdef CONFIG_MMU
90062306a36Sopenharmony_ci	res = setup_arg_pages(bprm, STACK_TOP, EXSTACK_DEFAULT);
90162306a36Sopenharmony_ci	if (!res)
90262306a36Sopenharmony_ci		res = create_flat_tables(bprm, bprm->p);
90362306a36Sopenharmony_ci#else
90462306a36Sopenharmony_ci	/* Stash our initial stack pointer into the mm structure */
90562306a36Sopenharmony_ci	current->mm->start_stack =
90662306a36Sopenharmony_ci		((current->mm->context.end_brk + stack_len + 3) & ~3) - 4;
90762306a36Sopenharmony_ci	pr_debug("sp=%lx\n", current->mm->start_stack);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	/* copy the arg pages onto the stack */
91062306a36Sopenharmony_ci	res = transfer_args_to_stack(bprm, &current->mm->start_stack);
91162306a36Sopenharmony_ci	if (!res)
91262306a36Sopenharmony_ci		res = create_flat_tables(bprm, current->mm->start_stack);
91362306a36Sopenharmony_ci#endif
91462306a36Sopenharmony_ci	if (res)
91562306a36Sopenharmony_ci		return res;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	/* Fake some return addresses to ensure the call chain will
91862306a36Sopenharmony_ci	 * initialise library in order for us.  We are required to call
91962306a36Sopenharmony_ci	 * lib 1 first, then 2, ... and finally the main program (id 0).
92062306a36Sopenharmony_ci	 */
92162306a36Sopenharmony_ci	start_addr = libinfo.lib_list[0].entry;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci#ifdef FLAT_PLAT_INIT
92462306a36Sopenharmony_ci	FLAT_PLAT_INIT(regs);
92562306a36Sopenharmony_ci#endif
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	finalize_exec(bprm);
92862306a36Sopenharmony_ci	pr_debug("start_thread(regs=0x%p, entry=0x%lx, start_stack=0x%lx)\n",
92962306a36Sopenharmony_ci		 regs, start_addr, current->mm->start_stack);
93062306a36Sopenharmony_ci	start_thread(regs, start_addr, current->mm->start_stack);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	return 0;
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci/****************************************************************************/
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_cistatic int __init init_flat_binfmt(void)
93862306a36Sopenharmony_ci{
93962306a36Sopenharmony_ci	register_binfmt(&flat_format);
94062306a36Sopenharmony_ci	return 0;
94162306a36Sopenharmony_ci}
94262306a36Sopenharmony_cicore_initcall(init_flat_binfmt);
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci/****************************************************************************/
945