binfmt_elf32.c 6.37 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4
/*
 * IA-32 ELF support.
 *
 * Copyright (C) 1999 Arun Sharma <arun.sharma@intel.com>
Linus Torvalds's avatar
Linus Torvalds committed
5
 * Copyright (C) 2001 Hewlett-Packard Co
Linus Torvalds's avatar
Linus Torvalds committed
6
 *	David Mosberger-Tang <davidm@hpl.hp.com>
Linus Torvalds's avatar
Linus Torvalds committed
7 8
 *
 * 06/16/00	A. Mallick	initialize csd/ssd/tssd/cflg for ia32_load_state
Linus Torvalds's avatar
Linus Torvalds committed
9
 * 04/13/01	D. Mosberger	dropped saving tssd in ar.k1---it's not needed
Linus Torvalds's avatar
Linus Torvalds committed
10
 * 09/14/01	D. Mosberger	fixed memory management for gdt/tss page
Linus Torvalds's avatar
Linus Torvalds committed
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
 */
#include <linux/config.h>

#include <linux/types.h>

#include <asm/param.h>
#include <asm/signal.h>
#include <asm/ia32.h>

#define CONFIG_BINFMT_ELF32

/* Override some function names */
#undef start_thread
#define start_thread			ia32_start_thread
#define elf_format			elf32_format
#define init_elf_binfmt			init_elf32_binfmt
#define exit_elf_binfmt			exit_elf32_binfmt

#undef CONFIG_BINFMT_ELF
#ifdef CONFIG_BINFMT_ELF32
# define CONFIG_BINFMT_ELF		CONFIG_BINFMT_ELF32
#endif

#undef CONFIG_BINFMT_ELF_MODULE
#ifdef CONFIG_BINFMT_ELF32_MODULE
# define CONFIG_BINFMT_ELF_MODULE	CONFIG_BINFMT_ELF32_MODULE
#endif

#undef CLOCKS_PER_SEC
#define CLOCKS_PER_SEC	IA32_CLOCKS_PER_SEC

Linus Torvalds's avatar
Linus Torvalds committed
42 43
extern void ia64_elf32_init (struct pt_regs *regs);
extern void put_dirty_page (struct task_struct * tsk, struct page *page, unsigned long address);
Linus Torvalds's avatar
Linus Torvalds committed
44

Linus Torvalds's avatar
Linus Torvalds committed
45 46
static void elf32_set_personality (void);

Linus Torvalds's avatar
Linus Torvalds committed
47 48
#define ELF_PLAT_INIT(_r)		ia64_elf32_init(_r)
#define setup_arg_pages(bprm)		ia32_setup_arg_pages(bprm)
Linus Torvalds's avatar
Linus Torvalds committed
49
#define elf_map				elf32_map
50 51

#undef SET_PERSONALITY
Linus Torvalds's avatar
Linus Torvalds committed
52
#define SET_PERSONALITY(ex, ibcs2)	elf32_set_personality()
Linus Torvalds's avatar
Linus Torvalds committed
53 54 55 56

/* Ugly but avoids duplication */
#include "../../../fs/binfmt_elf.c"

Linus Torvalds's avatar
Linus Torvalds committed
57 58
extern struct page *ia32_shared_page[];
extern unsigned long *ia32_gdt;
Linus Torvalds's avatar
Linus Torvalds committed
59 60

struct page *
Linus Torvalds's avatar
Linus Torvalds committed
61
ia32_install_shared_page (struct vm_area_struct *vma, unsigned long address, int no_share)
Linus Torvalds's avatar
Linus Torvalds committed
62
{
Linus Torvalds's avatar
Linus Torvalds committed
63
	struct page *pg = ia32_shared_page[(address - vma->vm_start)/PAGE_SIZE];
Linus Torvalds's avatar
Linus Torvalds committed
64

Linus Torvalds's avatar
Linus Torvalds committed
65 66
	get_page(pg);
	return pg;
Linus Torvalds's avatar
Linus Torvalds committed
67 68
}

Linus Torvalds's avatar
Linus Torvalds committed
69 70 71 72
static struct vm_operations_struct ia32_shared_page_vm_ops = {
	nopage:	ia32_install_shared_page
};

Linus Torvalds's avatar
Linus Torvalds committed
73 74
void
ia64_elf32_init (struct pt_regs *regs)
Linus Torvalds's avatar
Linus Torvalds committed
75
{
Linus Torvalds's avatar
Linus Torvalds committed
76
	struct vm_area_struct *vma;
Linus Torvalds's avatar
Linus Torvalds committed
77

Linus Torvalds's avatar
Linus Torvalds committed
78 79 80 81 82
	/*
	 * Map GDT and TSS below 4GB, where the processor can find them.  We need to map
	 * it with privilege level 3 because the IVE uses non-privileged accesses to these
	 * tables.  IA-32 segmentation is used to protect against IA-32 accesses to them.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
	vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
	if (vma) {
		vma->vm_mm = current->mm;
		vma->vm_start = IA32_GDT_OFFSET;
		vma->vm_end = vma->vm_start + max(PAGE_SIZE, 2*IA32_PAGE_SIZE);
		vma->vm_page_prot = PAGE_SHARED;
		vma->vm_flags = VM_READ|VM_MAYREAD;
		vma->vm_ops = &ia32_shared_page_vm_ops;
		vma->vm_pgoff = 0;
		vma->vm_file = NULL;
		vma->vm_private_data = NULL;
		down_write(&current->mm->mmap_sem);
		{
			insert_vm_struct(current->mm, vma);
		}
		up_write(&current->mm->mmap_sem);
	}
Linus Torvalds's avatar
Linus Torvalds committed
100

Linus Torvalds's avatar
Linus Torvalds committed
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
	/*
	 * Install LDT as anonymous memory.  This gives us all-zero segment descriptors
	 * until a task modifies them via modify_ldt().
	 */
	vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
	if (vma) {
		vma->vm_mm = current->mm;
		vma->vm_start = IA32_LDT_OFFSET;
		vma->vm_end = vma->vm_start + PAGE_ALIGN(IA32_LDT_ENTRIES*IA32_LDT_ENTRY_SIZE);
		vma->vm_page_prot = PAGE_SHARED;
		vma->vm_flags = VM_READ|VM_WRITE|VM_MAYREAD|VM_MAYWRITE;
		vma->vm_ops = NULL;
		vma->vm_pgoff = 0;
		vma->vm_file = NULL;
		vma->vm_private_data = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
116 117 118 119 120
		down_write(&current->mm->mmap_sem);
		{
			insert_vm_struct(current->mm, vma);
		}
		up_write(&current->mm->mmap_sem);
Linus Torvalds's avatar
Linus Torvalds committed
121
	}
Linus Torvalds's avatar
Linus Torvalds committed
122

Linus Torvalds's avatar
Linus Torvalds committed
123
	ia64_psr(regs)->ac = 0;		/* turn off alignment checking */
Linus Torvalds's avatar
Linus Torvalds committed
124
	regs->loadrs = 0;
Linus Torvalds's avatar
Linus Torvalds committed
125 126 127 128 129 130 131 132 133 134 135 136
	/*
	 *  According to the ABI %edx points to an `atexit' handler.  Since we don't have
	 *  one we'll set it to 0 and initialize all the other registers just to make
	 *  things more deterministic, ala the i386 implementation.
	 */
	regs->r8 = 0;	/* %eax */
	regs->r11 = 0;	/* %ebx */
	regs->r9 = 0;	/* %ecx */
	regs->r10 = 0;	/* %edx */
	regs->r13 = 0;	/* %ebp */
	regs->r14 = 0;	/* %esi */
	regs->r15 = 0;	/* %edi */
Linus Torvalds's avatar
Linus Torvalds committed
137

Linus Torvalds's avatar
Linus Torvalds committed
138 139 140 141 142 143
	current->thread.eflag = IA32_EFLAG;
	current->thread.fsr = IA32_FSR_DEFAULT;
	current->thread.fcr = IA32_FCR_DEFAULT;
	current->thread.fir = 0;
	current->thread.fdr = 0;

Linus Torvalds's avatar
Linus Torvalds committed
144 145 146
	/*
	 * Setup GDTD.  Note: GDTD is the descrambled version of the pseudo-descriptor
	 * format defined by Figure 3-11 "Pseudo-Descriptor Format" in the IA-32
David Mosberger's avatar
David Mosberger committed
147 148
	 * architecture manual. Also note that the only fields that are not ignored are
	 * `base', `limit', 'G', `P' (must be 1) and `S' (must be 0).
Linus Torvalds's avatar
Linus Torvalds committed
149
	 */
David Mosberger's avatar
David Mosberger committed
150 151
	regs->r31 = IA32_SEG_UNSCRAMBLE(IA32_SEG_DESCRIPTOR(IA32_GDT_OFFSET, IA32_PAGE_SIZE - 1,
							    0, 0, 0, 1, 0, 0, 0));
Linus Torvalds's avatar
Linus Torvalds committed
152 153 154 155 156
	/* Setup the segment selectors */
	regs->r16 = (__USER_DS << 16) | __USER_DS; /* ES == DS, GS, FS are zero */
	regs->r17 = (__USER_DS << 16) | __USER_CS; /* SS, CS; ia32_load_state() sets TSS and LDT */

	ia32_load_segment_descriptors(current);
Linus Torvalds's avatar
Linus Torvalds committed
157 158
	ia32_load_state(current);
}
Linus Torvalds's avatar
Linus Torvalds committed
159

Linus Torvalds's avatar
Linus Torvalds committed
160 161
int
ia32_setup_arg_pages (struct linux_binprm *bprm)
Linus Torvalds's avatar
Linus Torvalds committed
162 163 164 165 166
{
	unsigned long stack_base;
	struct vm_area_struct *mpnt;
	int i;

Linus Torvalds's avatar
Linus Torvalds committed
167
	stack_base = IA32_STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE;
Linus Torvalds's avatar
Linus Torvalds committed
168 169 170 171 172 173 174

	bprm->p += stack_base;
	if (bprm->loader)
		bprm->loader += stack_base;
	bprm->exec += stack_base;

	mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
175 176 177
	if (!mpnt)
		return -ENOMEM;

Linus Torvalds's avatar
Linus Torvalds committed
178
	down_write(&current->mm->mmap_sem);
Linus Torvalds's avatar
Linus Torvalds committed
179 180 181
	{
		mpnt->vm_mm = current->mm;
		mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;
Linus Torvalds's avatar
Linus Torvalds committed
182
		mpnt->vm_end = IA32_STACK_TOP;
Linus Torvalds's avatar
Linus Torvalds committed
183 184 185 186 187 188 189 190
		mpnt->vm_page_prot = PAGE_COPY;
		mpnt->vm_flags = VM_STACK_FLAGS;
		mpnt->vm_ops = NULL;
		mpnt->vm_pgoff = 0;
		mpnt->vm_file = NULL;
		mpnt->vm_private_data = 0;
		insert_vm_struct(current->mm, mpnt);
		current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
Linus Torvalds's avatar
Linus Torvalds committed
191
	}
Linus Torvalds's avatar
Linus Torvalds committed
192 193

	for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
194 195 196 197
		struct page *page = bprm->page[i];
		if (page) {
			bprm->page[i] = NULL;
			put_dirty_page(current, page, stack_base);
Linus Torvalds's avatar
Linus Torvalds committed
198 199 200
		}
		stack_base += PAGE_SIZE;
	}
Linus Torvalds's avatar
Linus Torvalds committed
201
	up_write(&current->mm->mmap_sem);
Linus Torvalds's avatar
Linus Torvalds committed
202

Linus Torvalds's avatar
Linus Torvalds committed
203 204 205
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
206 207
static void
elf32_set_personality (void)
Linus Torvalds's avatar
Linus Torvalds committed
208
{
Linus Torvalds's avatar
Linus Torvalds committed
209 210 211
	set_personality(PER_LINUX32);
	current->thread.map_base  = IA32_PAGE_OFFSET/3;
	current->thread.task_size = IA32_PAGE_OFFSET;	/* use what Linux/x86 uses... */
David Mosberger's avatar
David Mosberger committed
212
	current->thread.flags |= IA64_THREAD_XSTACK;	/* data must be executable */
Linus Torvalds's avatar
Linus Torvalds committed
213
	set_fs(USER_DS);				/* set addr limit for new TASK_SIZE */
Linus Torvalds's avatar
Linus Torvalds committed
214 215 216
}

static unsigned long
Linus Torvalds's avatar
Linus Torvalds committed
217
elf32_map (struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, int type)
Linus Torvalds's avatar
Linus Torvalds committed
218
{
Linus Torvalds's avatar
Linus Torvalds committed
219
	unsigned long pgoff = (eppnt->p_vaddr) & ~IA32_PAGE_MASK;
Linus Torvalds's avatar
Linus Torvalds committed
220

Linus Torvalds's avatar
Linus Torvalds committed
221 222
	return ia32_do_mmap(filep, (addr & IA32_PAGE_MASK), eppnt->p_filesz + pgoff, prot, type,
			    eppnt->p_offset - pgoff);
Linus Torvalds's avatar
Linus Torvalds committed
223
}