linux/arch/x86/ia32/ia32_aout.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *  a.out loader for x86-64
   4 *
   5 *  Copyright (C) 1991, 1992, 1996  Linus Torvalds
   6 *  Hacked together by Andi Kleen
   7 */
   8
   9#include <linux/module.h>
  10
  11#include <linux/time.h>
  12#include <linux/kernel.h>
  13#include <linux/mm.h>
  14#include <linux/mman.h>
  15#include <linux/a.out.h>
  16#include <linux/errno.h>
  17#include <linux/signal.h>
  18#include <linux/string.h>
  19#include <linux/fs.h>
  20#include <linux/file.h>
  21#include <linux/stat.h>
  22#include <linux/fcntl.h>
  23#include <linux/ptrace.h>
  24#include <linux/user.h>
  25#include <linux/binfmts.h>
  26#include <linux/personality.h>
  27#include <linux/init.h>
  28#include <linux/jiffies.h>
  29#include <linux/perf_event.h>
  30#include <linux/sched/task_stack.h>
  31
  32#include <linux/uaccess.h>
  33#include <asm/cacheflush.h>
  34#include <asm/user32.h>
  35#include <asm/ia32.h>
  36
  37#undef WARN_OLD
  38
  39static int load_aout_binary(struct linux_binprm *);
  40static int load_aout_library(struct file *);
  41
  42static struct linux_binfmt aout_format = {
  43        .module         = THIS_MODULE,
  44        .load_binary    = load_aout_binary,
  45        .load_shlib     = load_aout_library,
  46};
  47
  48static int set_brk(unsigned long start, unsigned long end)
  49{
  50        start = PAGE_ALIGN(start);
  51        end = PAGE_ALIGN(end);
  52        if (end <= start)
  53                return 0;
  54        return vm_brk(start, end - start);
  55}
  56
  57
  58/*
  59 * create_aout_tables() parses the env- and arg-strings in new user
  60 * memory and creates the pointer tables from them, and puts their
  61 * addresses on the "stack", returning the new stack pointer value.
  62 */
  63static u32 __user *create_aout_tables(char __user *p, struct linux_binprm *bprm)
  64{
  65        u32 __user *argv, *envp, *sp;
  66        int argc = bprm->argc, envc = bprm->envc;
  67
  68        sp = (u32 __user *) ((-(unsigned long)sizeof(u32)) & (unsigned long) p);
  69        sp -= envc+1;
  70        envp = sp;
  71        sp -= argc+1;
  72        argv = sp;
  73        put_user((unsigned long) envp, --sp);
  74        put_user((unsigned long) argv, --sp);
  75        put_user(argc, --sp);
  76        current->mm->arg_start = (unsigned long) p;
  77        while (argc-- > 0) {
  78                char c;
  79
  80                put_user((u32)(unsigned long)p, argv++);
  81                do {
  82                        get_user(c, p++);
  83                } while (c);
  84        }
  85        put_user(0, argv);
  86        current->mm->arg_end = current->mm->env_start = (unsigned long) p;
  87        while (envc-- > 0) {
  88                char c;
  89
  90                put_user((u32)(unsigned long)p, envp++);
  91                do {
  92                        get_user(c, p++);
  93                } while (c);
  94        }
  95        put_user(0, envp);
  96        current->mm->env_end = (unsigned long) p;
  97        return sp;
  98}
  99
 100/*
 101 * These are the functions used to load a.out style executables and shared
 102 * libraries.  There is no binary dependent code anywhere else.
 103 */
 104static int load_aout_binary(struct linux_binprm *bprm)
 105{
 106        unsigned long error, fd_offset, rlim;
 107        struct pt_regs *regs = current_pt_regs();
 108        struct exec ex;
 109        int retval;
 110
 111        ex = *((struct exec *) bprm->buf);              /* exec-header */
 112        if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
 113             N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) ||
 114            N_TRSIZE(ex) || N_DRSIZE(ex) ||
 115            i_size_read(file_inode(bprm->file)) <
 116            ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
 117                return -ENOEXEC;
 118        }
 119
 120        fd_offset = N_TXTOFF(ex);
 121
 122        /* Check initial limits. This avoids letting people circumvent
 123         * size limits imposed on them by creating programs with large
 124         * arrays in the data or bss.
 125         */
 126        rlim = rlimit(RLIMIT_DATA);
 127        if (rlim >= RLIM_INFINITY)
 128                rlim = ~0;
 129        if (ex.a_data + ex.a_bss > rlim)
 130                return -ENOMEM;
 131
 132        /* Flush all traces of the currently running executable */
 133        retval = begin_new_exec(bprm);
 134        if (retval)
 135                return retval;
 136
 137        /* OK, This is the point of no return */
 138        set_personality(PER_LINUX);
 139        set_personality_ia32(false);
 140
 141        setup_new_exec(bprm);
 142
 143        regs->cs = __USER32_CS;
 144        regs->r8 = regs->r9 = regs->r10 = regs->r11 = regs->r12 =
 145                regs->r13 = regs->r14 = regs->r15 = 0;
 146
 147        current->mm->end_code = ex.a_text +
 148                (current->mm->start_code = N_TXTADDR(ex));
 149        current->mm->end_data = ex.a_data +
 150                (current->mm->start_data = N_DATADDR(ex));
 151        current->mm->brk = ex.a_bss +
 152                (current->mm->start_brk = N_BSSADDR(ex));
 153
 154        retval = setup_arg_pages(bprm, IA32_STACK_TOP, EXSTACK_DEFAULT);
 155        if (retval < 0)
 156                return retval;
 157
 158        if (N_MAGIC(ex) == OMAGIC) {
 159                unsigned long text_addr, map_size;
 160
 161                text_addr = N_TXTADDR(ex);
 162                map_size = ex.a_text+ex.a_data;
 163
 164                error = vm_brk(text_addr & PAGE_MASK, map_size);
 165
 166                if (error)
 167                        return error;
 168
 169                error = read_code(bprm->file, text_addr, 32,
 170                                  ex.a_text + ex.a_data);
 171                if ((signed long)error < 0)
 172                        return error;
 173        } else {
 174#ifdef WARN_OLD
 175                static unsigned long error_time, error_time2;
 176                if ((ex.a_text & 0xfff || ex.a_data & 0xfff) &&
 177                    (N_MAGIC(ex) != NMAGIC) &&
 178                                time_after(jiffies, error_time2 + 5*HZ)) {
 179                        printk(KERN_NOTICE "executable not page aligned\n");
 180                        error_time2 = jiffies;
 181                }
 182
 183                if ((fd_offset & ~PAGE_MASK) != 0 &&
 184                            time_after(jiffies, error_time + 5*HZ)) {
 185                        printk(KERN_WARNING
 186                               "fd_offset is not page aligned. Please convert "
 187                               "program: %pD\n",
 188                               bprm->file);
 189                        error_time = jiffies;
 190                }
 191#endif
 192
 193                if (!bprm->file->f_op->mmap || (fd_offset & ~PAGE_MASK) != 0) {
 194                        error = vm_brk(N_TXTADDR(ex), ex.a_text+ex.a_data);
 195                        if (error)
 196                                return error;
 197
 198                        read_code(bprm->file, N_TXTADDR(ex), fd_offset,
 199                                        ex.a_text+ex.a_data);
 200                        goto beyond_if;
 201                }
 202
 203                error = vm_mmap(bprm->file, N_TXTADDR(ex), ex.a_text,
 204                                PROT_READ | PROT_EXEC,
 205                                MAP_FIXED | MAP_PRIVATE | MAP_32BIT,
 206                                fd_offset);
 207
 208                if (error != N_TXTADDR(ex))
 209                        return error;
 210
 211                error = vm_mmap(bprm->file, N_DATADDR(ex), ex.a_data,
 212                                PROT_READ | PROT_WRITE | PROT_EXEC,
 213                                MAP_FIXED | MAP_PRIVATE | MAP_32BIT,
 214                                fd_offset + ex.a_text);
 215                if (error != N_DATADDR(ex))
 216                        return error;
 217        }
 218
 219beyond_if:
 220        error = set_brk(current->mm->start_brk, current->mm->brk);
 221        if (error)
 222                return error;
 223
 224        set_binfmt(&aout_format);
 225
 226        current->mm->start_stack =
 227                (unsigned long)create_aout_tables((char __user *)bprm->p, bprm);
 228        /* start thread */
 229        loadsegment(fs, 0);
 230        loadsegment(ds, __USER32_DS);
 231        loadsegment(es, __USER32_DS);
 232        load_gs_index(0);
 233        (regs)->ip = ex.a_entry;
 234        (regs)->sp = current->mm->start_stack;
 235        (regs)->flags = 0x200;
 236        (regs)->cs = __USER32_CS;
 237        (regs)->ss = __USER32_DS;
 238        regs->r8 = regs->r9 = regs->r10 = regs->r11 =
 239        regs->r12 = regs->r13 = regs->r14 = regs->r15 = 0;
 240        return 0;
 241}
 242
 243static int load_aout_library(struct file *file)
 244{
 245        unsigned long bss, start_addr, len, error;
 246        int retval;
 247        struct exec ex;
 248        loff_t pos = 0;
 249
 250        retval = -ENOEXEC;
 251        error = kernel_read(file, &ex, sizeof(ex), &pos);
 252        if (error != sizeof(ex))
 253                goto out;
 254
 255        /* We come in here for the regular a.out style of shared libraries */
 256        if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
 257            N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
 258            i_size_read(file_inode(file)) <
 259            ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
 260                goto out;
 261        }
 262
 263        if (N_FLAGS(ex))
 264                goto out;
 265
 266        /* For  QMAGIC, the starting address is 0x20 into the page.  We mask
 267           this off to get the starting address for the page */
 268
 269        start_addr =  ex.a_entry & 0xfffff000;
 270
 271        if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) {
 272#ifdef WARN_OLD
 273                static unsigned long error_time;
 274                if (time_after(jiffies, error_time + 5*HZ)) {
 275                        printk(KERN_WARNING
 276                               "N_TXTOFF is not page aligned. Please convert "
 277                               "library: %pD\n",
 278                               file);
 279                        error_time = jiffies;
 280                }
 281#endif
 282                retval = vm_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss);
 283                if (retval)
 284                        goto out;
 285
 286                read_code(file, start_addr, N_TXTOFF(ex),
 287                          ex.a_text + ex.a_data);
 288                retval = 0;
 289                goto out;
 290        }
 291        /* Now use mmap to map the library into memory. */
 292        error = vm_mmap(file, start_addr, ex.a_text + ex.a_data,
 293                        PROT_READ | PROT_WRITE | PROT_EXEC,
 294                        MAP_FIXED | MAP_PRIVATE | MAP_32BIT,
 295                        N_TXTOFF(ex));
 296        retval = error;
 297        if (error != start_addr)
 298                goto out;
 299
 300        len = PAGE_ALIGN(ex.a_text + ex.a_data);
 301        bss = ex.a_text + ex.a_data + ex.a_bss;
 302        if (bss > len) {
 303                retval = vm_brk(start_addr + len, bss - len);
 304                if (retval)
 305                        goto out;
 306        }
 307        retval = 0;
 308out:
 309        return retval;
 310}
 311
 312static int __init init_aout_binfmt(void)
 313{
 314        register_binfmt(&aout_format);
 315        return 0;
 316}
 317
 318static void __exit exit_aout_binfmt(void)
 319{
 320        unregister_binfmt(&aout_format);
 321}
 322
 323module_init(init_aout_binfmt);
 324module_exit(exit_aout_binfmt);
 325MODULE_LICENSE("GPL");
 326