flatload.c 24.9 KB
Newer Older
pbrook's avatar
pbrook committed
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
/****************************************************************************/
/*
 *  QEMU bFLT binary loader.  Based on linux/fs/binfmt_flat.c
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *      Copyright (C) 2006 CodeSourcery.
 *	Copyright (C) 2000-2003 David McCullough <davidm@snapgear.com>
 *	Copyright (C) 2002 Greg Ungerer <gerg@snapgear.com>
 *	Copyright (C) 2002 SnapGear, by Paul Dale <pauli@snapgear.com>
 *	Copyright (C) 2000, 2001 Lineo, by David McCullough <davidm@lineo.com>
 *  based heavily on:
 *
 *  linux/fs/binfmt_aout.c:
 *      Copyright (C) 1991, 1992, 1996  Linus Torvalds
 *  linux/fs/binfmt_flat.c for 2.0 kernel
 *	    Copyright (C) 1998  Kenneth Albanowski <kjahds@kjahds.com>
 *	JAN/99 -- coded full program relocation (gerg@snapgear.com)
 */

/* ??? ZFLAT and shared library support is currently disabled.  */

/****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>
#include <unistd.h>

#include "qemu.h"
#include "flat.h"

//#define DEBUG

#ifdef DEBUG
#define	DBG_FLT(a...)	printf(a)
#else
#define	DBG_FLT(a...)
#endif

#define flat_reloc_valid(reloc, size)             ((reloc) <= (size))
#define flat_old_ram_flag(flag)                   (flag)
#ifdef TARGET_WORDS_BIGENDIAN
#define flat_get_relocate_addr(relval)            (relval)
#else
#define flat_get_relocate_addr(relval)            bswap32(relval)
#endif

#define RELOC_FAILED 0xff00ff01		/* Relocation incorrect somewhere */
#define UNLOADED_LIB 0x7ff000ff		/* Placeholder for unused library */

struct lib_info {
66 67 68 69 70 71 72
    abi_ulong start_code;       /* Start of text segment */
    abi_ulong start_data;       /* Start of data segment */
    abi_ulong end_data;         /* Start of bss section */
    abi_ulong start_brk;        /* End of data segment */
    abi_ulong text_len;	        /* Length of text segment */
    abi_ulong entry;	        /* Start address for this module */
    abi_ulong build_date;       /* When this one was compiled */
pbrook's avatar
pbrook committed
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
    short loaded;		/* Has this library been loaded? */
};

#ifdef CONFIG_BINFMT_SHARED_FLAT
static int load_flat_shared_library(int id, struct lib_info *p);
#endif

struct linux_binprm;

#define ntohl(x) be32_to_cpu(x)

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

/* Push a block of strings onto the guest stack.  */
92
static abi_ulong copy_strings(abi_ulong p, int n, char **s)
pbrook's avatar
pbrook committed
93 94 95 96 97 98 99 100 101 102 103 104
{
    int len;

    while (n-- > 0) {
        len = strlen(s[n]) + 1;
        p -= len;
        memcpy_to_target(p, s[n], len);
    }

    return p;
}

105 106
int target_pread(int fd, abi_ulong ptr, abi_ulong len,
                 abi_ulong offset)
pbrook's avatar
pbrook committed
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
{
    void *buf;
    int ret;

    buf = lock_user(ptr, len, 0);
    ret = pread(fd, buf, len, offset);
    unlock_user(buf, ptr, len);
    return ret;
}
/****************************************************************************/

#ifdef CONFIG_BINFMT_ZFLAT

#include <linux/zlib.h>

#define LBUFSIZE	4000

/* gzip flag byte */
#define ASCII_FLAG   0x01 /* bit 0 set: file probably ASCII text */
#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
#define COMMENT      0x10 /* bit 4 set: file comment present */
#define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
#define RESERVED     0xC0 /* bit 6,7:   reserved */

static int decompress_exec(
	struct linux_binprm *bprm,
	unsigned long offset,
	char *dst,
	long len,
	int fd)
{
	unsigned char *buf;
	z_stream strm;
	loff_t fpos;
	int ret, retval;

	DBG_FLT("decompress_exec(offset=%x,buf=%x,len=%x)\n",(int)offset, (int)dst, (int)len);

	memset(&strm, 0, sizeof(strm));
	strm.workspace = kmalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
	if (strm.workspace == NULL) {
		DBG_FLT("binfmt_flat: no memory for decompress workspace\n");
		return -ENOMEM;
	}
	buf = kmalloc(LBUFSIZE, GFP_KERNEL);
	if (buf == NULL) {
		DBG_FLT("binfmt_flat: no memory for read buffer\n");
		retval = -ENOMEM;
		goto out_free;
	}

	/* Read in first chunk of data and parse gzip header. */
	fpos = offset;
	ret = bprm->file->f_op->read(bprm->file, buf, LBUFSIZE, &fpos);

	strm.next_in = buf;
	strm.avail_in = ret;
	strm.total_in = 0;

	retval = -ENOEXEC;

	/* Check minimum size -- gzip header */
	if (ret < 10) {
		DBG_FLT("binfmt_flat: file too small?\n");
		goto out_free_buf;
	}

	/* Check gzip magic number */
	if ((buf[0] != 037) || ((buf[1] != 0213) && (buf[1] != 0236))) {
		DBG_FLT("binfmt_flat: unknown compression magic?\n");
		goto out_free_buf;
	}

	/* Check gzip method */
	if (buf[2] != 8) {
		DBG_FLT("binfmt_flat: unknown compression method?\n");
		goto out_free_buf;
	}
	/* Check gzip flags */
	if ((buf[3] & ENCRYPTED) || (buf[3] & CONTINUATION) ||
	    (buf[3] & RESERVED)) {
		DBG_FLT("binfmt_flat: unknown flags?\n");
		goto out_free_buf;
	}

	ret = 10;
	if (buf[3] & EXTRA_FIELD) {
		ret += 2 + buf[10] + (buf[11] << 8);
		if (unlikely(LBUFSIZE == ret)) {
			DBG_FLT("binfmt_flat: buffer overflow (EXTRA)?\n");
			goto out_free_buf;
		}
	}
	if (buf[3] & ORIG_NAME) {
		for (; ret < LBUFSIZE && (buf[ret] != 0); ret++)
			;
		if (unlikely(LBUFSIZE == ret)) {
			DBG_FLT("binfmt_flat: buffer overflow (ORIG_NAME)?\n");
			goto out_free_buf;
		}
	}
	if (buf[3] & COMMENT) {
		for (;  ret < LBUFSIZE && (buf[ret] != 0); ret++)
			;
		if (unlikely(LBUFSIZE == ret)) {
			DBG_FLT("binfmt_flat: buffer overflow (COMMENT)?\n");
			goto out_free_buf;
		}
	}

	strm.next_in += ret;
	strm.avail_in -= ret;

	strm.next_out = dst;
	strm.avail_out = len;
	strm.total_out = 0;

	if (zlib_inflateInit2(&strm, -MAX_WBITS) != Z_OK) {
		DBG_FLT("binfmt_flat: zlib init failed?\n");
		goto out_free_buf;
	}

	while ((ret = zlib_inflate(&strm, Z_NO_FLUSH)) == Z_OK) {
		ret = bprm->file->f_op->read(bprm->file, buf, LBUFSIZE, &fpos);
		if (ret <= 0)
			break;
		if (ret >= (unsigned long) -4096)
			break;
		len -= ret;

		strm.next_in = buf;
		strm.avail_in = ret;
		strm.total_in = 0;
	}

	if (ret < 0) {
		DBG_FLT("binfmt_flat: decompression failed (%d), %s\n",
			ret, strm.msg);
		goto out_zlib;
	}

	retval = 0;
out_zlib:
	zlib_inflateEnd(&strm);
out_free_buf:
	kfree(buf);
out_free:
	kfree(strm.workspace);
out:
	return retval;
}

#endif /* CONFIG_BINFMT_ZFLAT */

/****************************************************************************/

265 266
static abi_ulong
calc_reloc(abi_ulong r, struct lib_info *p, int curid, int internalp)
pbrook's avatar
pbrook committed
267
{
268
    abi_ulong addr;
pbrook's avatar
pbrook committed
269
    int id;
270 271 272 273
    abi_ulong start_brk;
    abi_ulong start_data;
    abi_ulong text_len;
    abi_ulong start_code;
pbrook's avatar
pbrook committed
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361

#ifdef CONFIG_BINFMT_SHARED_FLAT
#error needs checking
    if (r == 0)
        id = curid;	/* Relocs of 0 are always self referring */
    else {
        id = (r >> 24) & 0xff;	/* Find ID for this reloc */
        r &= 0x00ffffff;	/* Trim ID off here */
    }
    if (id >= MAX_SHARED_LIBS) {
        fprintf(stderr, "BINFMT_FLAT: reference 0x%x to shared library %d\n",
                (unsigned) r, id);
        goto failed;
    }
    if (curid != id) {
        if (internalp) {
            fprintf(stderr, "BINFMT_FLAT: reloc address 0x%x not "
                    "in same module (%d != %d)\n",
                    (unsigned) r, curid, id);
            goto failed;
        } else if ( ! p[id].loaded &&
                    load_flat_shared_library(id, p) > (unsigned long) -4096) {
            fprintf(stderr, "BINFMT_FLAT: failed to load library %d\n", id);
            goto failed;
        }
        /* Check versioning information (i.e. time stamps) */
        if (p[id].build_date && p[curid].build_date
            && p[curid].build_date < p[id].build_date) {
            fprintf(stderr, "BINFMT_FLAT: library %d is younger than %d\n",
                    id, curid);
            goto failed;
        }
    }
#else
    id = 0;
#endif

    start_brk = p[id].start_brk;
    start_data = p[id].start_data;
    start_code = p[id].start_code;
    text_len = p[id].text_len;

    if (!flat_reloc_valid(r, start_brk - start_data + text_len)) {
        fprintf(stderr, "BINFMT_FLAT: reloc outside program 0x%x "
                "(0 - 0x%x/0x%x)\n",
               (int) r,(int)(start_brk-start_code),(int)text_len);
        goto failed;
    }

    if (r < text_len)			/* In text segment */
        addr = r + start_code;
    else					/* In data segment */
        addr = r - text_len + start_data;

    /* Range checked already above so doing the range tests is redundant...*/
    return(addr);

failed:
    abort();
    return RELOC_FAILED;
}

/****************************************************************************/

/* ??? This does not handle endianness correctly.  */
void old_reloc(struct lib_info *libinfo, uint32_t rl)
{
#ifdef DEBUG
	char *segment[] = { "TEXT", "DATA", "BSS", "*UNKNOWN*" };
#endif
	uint32_t *ptr;
        uint32_t offset;
        int reloc_type;

        offset = rl & 0x3fffffff;
        reloc_type = rl >> 30;
        /* ??? How to handle this?  */
#if defined(CONFIG_COLDFIRE)
	ptr = (uint32_t *) (libinfo->start_code + offset);
#else
	ptr = (uint32_t *) (libinfo->start_data + offset);
#endif

#ifdef DEBUG
	fprintf(stderr, "Relocation of variable at DATASEG+%x "
		"(address %p, currently %x) into segment %s\n",
		offset, ptr, (int)*ptr, segment[reloc_type]);
#endif
362

pbrook's avatar
pbrook committed
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
	switch (reloc_type) {
	case OLD_FLAT_RELOC_TYPE_TEXT:
		*ptr += libinfo->start_code;
		break;
	case OLD_FLAT_RELOC_TYPE_DATA:
		*ptr += libinfo->start_data;
		break;
	case OLD_FLAT_RELOC_TYPE_BSS:
		*ptr += libinfo->end_data;
		break;
	default:
		fprintf(stderr, "BINFMT_FLAT: Unknown relocation type=%x\n",
                        reloc_type);
		break;
	}
	DBG_FLT("Relocation became %x\n", (int)*ptr);
379
}
pbrook's avatar
pbrook committed
380 381 382 383

/****************************************************************************/

static int load_flat_file(struct linux_binprm * bprm,
384
		struct lib_info *libinfo, int id, abi_ulong *extra_stack)
pbrook's avatar
pbrook committed
385 386
{
    struct flat_hdr * hdr;
387 388 389 390 391 392
    abi_ulong textpos = 0, datapos = 0, result;
    abi_ulong realdatastart = 0;
    abi_ulong text_len, data_len, bss_len, stack_len, flags;
    abi_ulong memp = 0; /* for finding the brk area */
    abi_ulong extra;
    abi_ulong reloc = 0, rp;
pbrook's avatar
pbrook committed
393
    int i, rev, relocs = 0;
394 395 396
    abi_ulong fpos;
    abi_ulong start_code, end_code;
    abi_ulong indx_len;
pbrook's avatar
pbrook committed
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418

    hdr = ((struct flat_hdr *) bprm->buf);		/* exec-header */

    text_len  = ntohl(hdr->data_start);
    data_len  = ntohl(hdr->data_end) - ntohl(hdr->data_start);
    bss_len   = ntohl(hdr->bss_end) - ntohl(hdr->data_end);
    stack_len = ntohl(hdr->stack_size);
    if (extra_stack) {
        stack_len += *extra_stack;
        *extra_stack = stack_len;
    }
    relocs    = ntohl(hdr->reloc_count);
    flags     = ntohl(hdr->flags);
    rev       = ntohl(hdr->rev);

    DBG_FLT("BINFMT_FLAT: Loading file: %s\n", bprm->filename);

    if (rev != FLAT_VERSION && rev != OLD_FLAT_VERSION) {
        fprintf(stderr, "BINFMT_FLAT: bad magic/rev (0x%x, need 0x%x)\n",
                rev, (int) FLAT_VERSION);
        return -ENOEXEC;
    }
419

pbrook's avatar
pbrook committed
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
    /* Don't allow old format executables to use shared libraries */
    if (rev == OLD_FLAT_VERSION && id != 0) {
        fprintf(stderr, "BINFMT_FLAT: shared libraries are not available\n");
        return -ENOEXEC;
    }

    /*
     * fix up the flags for the older format,  there were all kinds
     * of endian hacks,  this only works for the simple cases
     */
    if (rev == OLD_FLAT_VERSION && flat_old_ram_flag(flags))
        flags = FLAT_FLAG_RAM;

#ifndef CONFIG_BINFMT_ZFLAT
    if (flags & (FLAT_FLAG_GZIP|FLAT_FLAG_GZDATA)) {
        fprintf(stderr, "Support for ZFLAT executables is not enabled\n");
        return -ENOEXEC;
    }
#endif

    /*
     * calculate the extra space we need to map in
     */
443
    extra = relocs * sizeof(abi_ulong);
pbrook's avatar
pbrook committed
444 445 446
    if (extra < bss_len + stack_len)
        extra = bss_len + stack_len;

pbrook's avatar
pbrook committed
447 448
    /* Add space for library base pointers.  Make sure this does not
       misalign the  doesn't misalign the data segment.  */
449 450
    indx_len = MAX_SHARED_LIBS * sizeof(abi_ulong);
    indx_len = (indx_len + 15) & ~(abi_ulong)15;
pbrook's avatar
pbrook committed
451

pbrook's avatar
pbrook committed
452 453 454 455 456 457 458 459 460 461 462 463 464 465
    /*
     * there are a couple of cases here,  the separate code/data
     * case,  and then the fully copied to RAM case which lumps
     * it all together.
     */
    if ((flags & (FLAT_FLAG_RAM|FLAT_FLAG_GZIP)) == 0) {
        /*
         * this should give us a ROM ptr,  but if it doesn't we don't
         * really care
         */
        DBG_FLT("BINFMT_FLAT: ROM mapping of file (we hope)\n");

        textpos = target_mmap(0, text_len, PROT_READ|PROT_EXEC,
                              MAP_PRIVATE, bprm->fd, 0);
466
        if (textpos == -1) {
pbrook's avatar
pbrook committed
467 468 469 470
            fprintf(stderr, "Unable to mmap process text\n");
            return -1;
        }

pbrook's avatar
pbrook committed
471
        realdatastart = target_mmap(0, data_len + extra + indx_len,
pbrook's avatar
pbrook committed
472 473 474 475 476 477 478
                                    PROT_READ|PROT_WRITE|PROT_EXEC,
                                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

        if (realdatastart == -1) {
            fprintf(stderr, "Unable to allocate RAM for process data\n");
            return realdatastart;
        }
pbrook's avatar
pbrook committed
479
        datapos = realdatastart + indx_len;
pbrook's avatar
pbrook committed
480 481 482 483 484 485 486

        DBG_FLT("BINFMT_FLAT: Allocated data+bss+stack (%d bytes): %x\n",
                        (int)(data_len + bss_len + stack_len), (int)datapos);

        fpos = ntohl(hdr->data_start);
#ifdef CONFIG_BINFMT_ZFLAT
        if (flags & FLAT_FLAG_GZDATA) {
487
            result = decompress_exec(bprm, fpos, (char *) datapos,
488
                                     data_len + (relocs * sizeof(abi_ulong)))
pbrook's avatar
pbrook committed
489 490 491 492
        } else
#endif
        {
            result = target_pread(bprm->fd, datapos,
493
                                  data_len + (relocs * sizeof(abi_ulong)),
pbrook's avatar
pbrook committed
494 495 496 497 498 499 500 501 502 503 504 505
                                  fpos);
        }
        if (result < 0) {
            fprintf(stderr, "Unable to read data+bss\n");
            return result;
        }

        reloc = datapos + (ntohl(hdr->reloc_start) - text_len);
        memp = realdatastart;

    } else {

pbrook's avatar
pbrook committed
506
        textpos = target_mmap(0, text_len + data_len + extra + indx_len,
pbrook's avatar
pbrook committed
507 508 509 510 511 512 513 514
                              PROT_READ | PROT_EXEC | PROT_WRITE,
                              MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
        if (textpos == -1 ) {
            fprintf(stderr, "Unable to allocate RAM for process text/data\n");
            return -1;
        }

        realdatastart = textpos + ntohl(hdr->data_start);
pbrook's avatar
pbrook committed
515 516
        datapos = realdatastart + indx_len;
        reloc = (textpos + ntohl(hdr->reloc_start) + indx_len);
pbrook's avatar
pbrook committed
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
        memp = textpos;

#ifdef CONFIG_BINFMT_ZFLAT
#error code needs checking
        /*
         * load it all in and treat it like a RAM load from now on
         */
        if (flags & FLAT_FLAG_GZIP) {
                result = decompress_exec(bprm, sizeof (struct flat_hdr),
                                 (((char *) textpos) + sizeof (struct flat_hdr)),
                                 (text_len + data_len + (relocs * sizeof(unsigned long))
                                          - sizeof (struct flat_hdr)),
                                 0);
                memmove((void *) datapos, (void *) realdatastart,
                                data_len + (relocs * sizeof(unsigned long)));
        } else if (flags & FLAT_FLAG_GZDATA) {
                fpos = 0;
                result = bprm->file->f_op->read(bprm->file,
                                (char *) textpos, text_len, &fpos);
                if (result < (unsigned long) -4096)
                        result = decompress_exec(bprm, text_len, (char *) datapos,
                                         data_len + (relocs * sizeof(unsigned long)), 0);
        }
        else
#endif
        {
            result = target_pread(bprm->fd, textpos,
                                  text_len, 0);
            if (result >= 0) {
                result = target_pread(bprm->fd, datapos,
547
                    data_len + (relocs * sizeof(abi_ulong)),
pbrook's avatar
pbrook committed
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
                    ntohl(hdr->data_start));
            }
        }
        if (result < 0) {
            fprintf(stderr, "Unable to read code+data+bss\n");
            return result;
        }
    }

    DBG_FLT("Mapping is 0x%x, Entry point is 0x%x, data_start is 0x%x\n",
            (int)textpos, 0x00ffffff&ntohl(hdr->entry),
            ntohl(hdr->data_start));

    /* The main program needs a little extra setup in the task structure */
    start_code = textpos + sizeof (struct flat_hdr);
    end_code = textpos + text_len;

    DBG_FLT("%s %s: TEXT=%x-%x DATA=%x-%x BSS=%x-%x\n",
            id ? "Lib" : "Load", bprm->filename,
            (int) start_code, (int) end_code,
            (int) datapos,
            (int) (datapos + data_len),
            (int) (datapos + data_len),
            (int) (((datapos + data_len + bss_len) + 3) & ~3));

    text_len -= sizeof(struct flat_hdr); /* the real code len */

    /* Store the current module values into the global library structure */
    libinfo[id].start_code = start_code;
    libinfo[id].start_data = datapos;
    libinfo[id].end_data = datapos + data_len;
    libinfo[id].start_brk = datapos + data_len + bss_len;
    libinfo[id].text_len = text_len;
    libinfo[id].loaded = 1;
    libinfo[id].entry = (0x00ffffff & ntohl(hdr->entry)) + textpos;
    libinfo[id].build_date = ntohl(hdr->build_date);
584

pbrook's avatar
pbrook committed
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
    /*
     * We just load the allocations into some temporary memory to
     * help simplify all this mumbo jumbo
     *
     * We've got two different sections of relocation entries.
     * The first is the GOT which resides at the begining of the data segment
     * and is terminated with a -1.  This one can be relocated in place.
     * The second is the extra relocation entries tacked after the image's
     * data segment. These require a little more processing as the entry is
     * really an offset into the image which contains an offset into the
     * image.
     */
    if (flags & FLAT_FLAG_GOTPIC) {
        rp = datapos;
        while (1) {
600
            abi_ulong addr;
pbrook's avatar
pbrook committed
601 602 603 604 605 606 607 608 609
            addr = tgetl(rp);
            if (addr == -1)
                break;
            if (addr) {
                addr = calc_reloc(addr, libinfo, id, 0);
                if (addr == RELOC_FAILED)
                    return -ENOEXEC;
                tputl(rp, addr);
            }
610
            rp += sizeof(abi_ulong);
pbrook's avatar
pbrook committed
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
        }
    }

    /*
     * Now run through the relocation entries.
     * We've got to be careful here as C++ produces relocatable zero
     * entries in the constructor and destructor tables which are then
     * tested for being not zero (which will always occur unless we're
     * based from address zero).  This causes an endless loop as __start
     * is at zero.  The solution used is to not relocate zero addresses.
     * This has the negative side effect of not allowing a global data
     * reference to be statically initialised to _stext (I've moved
     * __start to address 4 so that is okay).
     */
    if (rev > OLD_FLAT_VERSION) {
        for (i = 0; i < relocs; i++) {
627
            abi_ulong addr, relval;
pbrook's avatar
pbrook committed
628 629 630 631

            /* Get the address of the pointer to be
               relocated (of course, the address has to be
               relocated first).  */
632
            relval = tgetl(reloc + i * sizeof (abi_ulong));
pbrook's avatar
pbrook committed
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
            addr = flat_get_relocate_addr(relval);
            rp = calc_reloc(addr, libinfo, id, 1);
            if (rp == RELOC_FAILED)
                return -ENOEXEC;

            /* Get the pointer's value.  */
            addr = tgetl(rp);
            if (addr != 0) {
                /*
                 * Do the relocation.  PIC relocs in the data section are
                 * already in target order
                 */

#ifndef TARGET_WORDS_BIGENDIAN
                if ((flags & FLAT_FLAG_GOTPIC) == 0)
                    addr = bswap32(addr);
#endif
                addr = calc_reloc(addr, libinfo, id, 0);
                if (addr == RELOC_FAILED)
                    return -ENOEXEC;

                /* Write back the relocated pointer.  */
                tputl(rp, addr);
            }
        }
    } else {
        for (i = 0; i < relocs; i++) {
660 661
            abi_ulong relval;
            relval = tgetl(reloc + i * sizeof (abi_ulong));
pbrook's avatar
pbrook committed
662 663 664
            old_reloc(&libinfo[0], relval);
        }
    }
665

pbrook's avatar
pbrook committed
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
    /* zero the BSS.  */
    memset((void*)(datapos + data_len), 0, bss_len);

    return 0;
}


/****************************************************************************/
#ifdef CONFIG_BINFMT_SHARED_FLAT

/*
 * Load a shared library into memory.  The library gets its own data
 * segment (including bss) but not argv/argc/environ.
 */

static int load_flat_shared_library(int id, struct lib_info *libs)
{
	struct linux_binprm bprm;
	int res;
	char buf[16];

	/* Create the file name */
	sprintf(buf, "/lib/lib%d.so", id);

	/* Open the file up */
	bprm.filename = buf;
	bprm.file = open_exec(bprm.filename);
	res = PTR_ERR(bprm.file);
	if (IS_ERR(bprm.file))
		return res;

	res = prepare_binprm(&bprm);

	if (res <= (unsigned long)-4096)
		res = load_flat_file(&bprm, libs, id, NULL);
	if (bprm.file) {
		allow_write_access(bprm.file);
		fput(bprm.file);
		bprm.file = NULL;
	}
	return(res);
}

#endif /* CONFIG_BINFMT_SHARED_FLAT */

int load_flt_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
                    struct image_info * info)
{
    struct lib_info libinfo[MAX_SHARED_LIBS];
715 716 717 718
    abi_ulong p = bprm->p;
    abi_ulong stack_len;
    abi_ulong start_addr;
    abi_ulong sp;
pbrook's avatar
pbrook committed
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
    int res;
    int i, j;

    memset(libinfo, 0, sizeof(libinfo));
    /*
     * We have to add the size of our arguments to our stack size
     * otherwise it's too easy for users to create stack overflows
     * by passing in a huge argument list.  And yes,  we have to be
     * pedantic and include space for the argv/envp array as it may have
     * a lot of entries.
     */
#define TOP_OF_ARGS (TARGET_PAGE_SIZE * MAX_ARG_PAGES - sizeof(void *))
    stack_len = TOP_OF_ARGS - bprm->p;             /* the strings */
    stack_len += (bprm->argc + 1) * 4; /* the argv array */
    stack_len += (bprm->envc + 1) * 4; /* the envp array */

735

pbrook's avatar
pbrook committed
736 737 738
    res = load_flat_file(bprm, libinfo, 0, &stack_len);
    if (res > (unsigned long)-4096)
            return res;
739

pbrook's avatar
pbrook committed
740 741 742
    /* Update data segment pointers for all libraries */
    for (i=0; i<MAX_SHARED_LIBS; i++) {
        if (libinfo[i].loaded) {
743
            abi_ulong p;
pbrook's avatar
pbrook committed
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
            p = libinfo[i].start_data;
            for (j=0; j<MAX_SHARED_LIBS; j++) {
                p -= 4;
                tput32(p, libinfo[j].loaded
                          ? libinfo[j].start_data
                          : UNLOADED_LIB);
            }
        }
    }

    p = ((libinfo[0].start_brk + stack_len + 3) & ~3) - 4;
    DBG_FLT("p=%x\n", (int)p);

    /* Copy argv/envp.  */
    p = copy_strings(p, bprm->envc, bprm->envp);
pbrook's avatar
pbrook committed
759
    p = copy_strings(p, bprm->argc, bprm->argv);
pbrook's avatar
pbrook committed
760
    /* Align stack.  */
761
    sp = p & ~(abi_ulong)(sizeof(abi_ulong) - 1);
762 763 764 765
    /* Enforce final stack alignment of 16 bytes.  This is sufficient
       for all current targets, and excess alignment is harmless.  */
    stack_len = bprm->envc + bprm->argc + 2;
    stack_len += 3;	/* argc, arvg, argp */
766
    stack_len *= sizeof(abi_ulong);
767 768
    if ((sp + stack_len) & 15)
        sp -= 16 - ((sp + stack_len) & 15);
pbrook's avatar
pbrook committed
769
    sp = loader_build_argptr(bprm->envc, bprm->argc, sp, p, 1);
770

pbrook's avatar
pbrook committed
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
    /* Fake some return addresses to ensure the call chain will
     * initialise library in order for us.  We are required to call
     * lib 1 first, then 2, ... and finally the main program (id 0).
     */
    start_addr = libinfo[0].entry;

#ifdef CONFIG_BINFMT_SHARED_FLAT
#error here
    for (i = MAX_SHARED_LIBS-1; i>0; i--) {
            if (libinfo[i].loaded) {
                    /* Push previos first to call address */
                    --sp;	put_user(start_addr, sp);
                    start_addr = libinfo[i].entry;
            }
    }
#endif
787

pbrook's avatar
pbrook committed
788 789 790 791 792 793 794 795
    /* Stash our initial stack pointer into the mm structure */
    info->start_code = libinfo[0].start_code;
    info->end_code = libinfo[0].start_code = libinfo[0].text_len;
    info->start_data = libinfo[0].start_data;
    info->end_data = libinfo[0].end_data;
    info->start_brk = libinfo[0].start_brk;
    info->start_stack = sp;
    info->entry = start_addr;
796 797 798
    info->code_offset = info->start_code;
    info->data_offset = info->start_data - libinfo[0].text_len;

pbrook's avatar
pbrook committed
799 800
    DBG_FLT("start_thread(entry=0x%x, start_stack=0x%x)\n",
            (int)info->entry, (int)info->start_stack);
801

pbrook's avatar
pbrook committed
802 803
    return 0;
}