aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/scripts/python/stackcollapse.py
AgeCommit message (Collapse)AuthorFilesLines
2019-03-06perf script python: Remove mixed indentationTony Jones1-1/+1
Remove mixed indentation in Python scripts. Revert to either all tabs (most common form) or all spaces (4 or 8) depending on what was the intent of the original commit. This is necessary to complete Python3 support as it will flag an error if it encounters mixed indentation. Signed-off-by: Tony Jones <tonyj@suse.de> Link: http://lkml.kernel.org/r/20190302011903.2416-2-tonyj@suse.de Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-02-25perf script python: Add Python3 support to stackcollapse.pyTony Jones1-3/+4
Support both Python2 and Python3 in the stackcollapse.py script There may be differences in the ordering of output lines due to differences in dictionary ordering etc. However the format within lines should be unchanged. The use of 'from __future__' implies the minimum supported Python2 version is now v2.6 Signed-off-by: Tony Jones <tonyj@suse.de> Cc: Paolo Bonzini <pbonzini@redhat.com> <pbonzini@redhat.com> Link: http://lkml.kernel.org/r/20190222230619.17887-12-tonyj@suse.de Signed-off-by: Seeteena Thoufeek <s1seetee@linux.vnet.ibm.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-11-02License cleanup: add SPDX GPL-2.0 license identifier to files with no licenseGreg Kroah-Hartman1-0/+1
Many source files in the tree are missing licensing information, which makes it harder for compliance tools to determine the correct license. By default all files without license information are under the default license of the kernel, which is GPL version 2. Update the files which contain no license information with the 'GPL-2.0' SPDX license identifier. The SPDX identifier is a legally binding shorthand, which can be used instead of the full boiler plate text. This patch is based on work done by Thomas Gleixner and Kate Stewart and Philippe Ombredanne. How this work was done: Patches were generated and checked against linux-4.14-rc6 for a subset of the use cases: - file had no licensing information it it. - file was a */uapi/* one with no licensing information in it, - file was a */uapi/* one with existing licensing information, Further patches will be generated in subsequent months to fix up cases where non-standard license headers were used, and references to license had to be inferred by heuristics based on keywords. The analysis to determine which SPDX License Identifier to be applied to a file was done in a spreadsheet of side by side results from of the output of two independent scanners (ScanCode & Windriver) producing SPDX tag:value files created by Philippe Ombredanne. Philippe prepared the base worksheet, and did an initial spot review of a few 1000 files. The 4.13 kernel was the starting point of the analysis with 60,537 files assessed. Kate Stewart did a file by file comparison of the scanner results in the spreadsheet to determine which SPDX license identifier(s) to be applied to the file. She confirmed any determination that was not immediately clear with lawyers working with the Linux Foundation. Criteria used to select files for SPDX license identifier tagging was: - Files considered eligible had to be source code files. - Make and config files were included as candidates if they contained >5 lines of source - File already had some variant of a license header in it (even if <5 lines). All documentation files were explicitly excluded. The following heuristics were used to determine which SPDX license identifiers to apply. - when both scanners couldn't find any license traces, file was considered to have no license information in it, and the top level COPYING file license applied. For non */uapi/* files that summary was: SPDX license identifier # files ---------------------------------------------------|------- GPL-2.0 11139 and resulted in the first patch in this series. If that file was a */uapi/* path one, it was "GPL-2.0 WITH Linux-syscall-note" otherwise it was "GPL-2.0". Results of that was: SPDX license identifier # files ---------------------------------------------------|------- GPL-2.0 WITH Linux-syscall-note 930 and resulted in the second patch in this series. - if a file had some form of licensing information in it, and was one of the */uapi/* ones, it was denoted with the Linux-syscall-note if any GPL family license was found in the file or had no licensing in it (per prior point). Results summary: SPDX license identifier # files ---------------------------------------------------|------ GPL-2.0 WITH Linux-syscall-note 270 GPL-2.0+ WITH Linux-syscall-note 169 ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) 21 ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) 17 LGPL-2.1+ WITH Linux-syscall-note 15 GPL-1.0+ WITH Linux-syscall-note 14 ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) 5 LGPL-2.0+ WITH Linux-syscall-note 4 LGPL-2.1 WITH Linux-syscall-note 3 ((GPL-2.0 WITH Linux-syscall-note) OR MIT) 3 ((GPL-2.0 WITH Linux-syscall-note) AND MIT) 1 and that resulted in the third patch in this series. - when the two scanners agreed on the detected license(s), that became the concluded license(s). - when there was disagreement between the two scanners (one detected a license but the other didn't, or they both detected different licenses) a manual inspection of the file occurred. - In most cases a manual inspection of the information in the file resulted in a clear resolution of the license that should apply (and which scanner probably needed to revisit its heuristics). - When it was not immediately clear, the license identifier was confirmed with lawyers working with the Linux Foundation. - If there was any question as to the appropriate license identifier, the file was flagged for further research and to be revisited later in time. In total, over 70 hours of logged manual review was done on the spreadsheet to determine the SPDX license identifiers to apply to the source files by Kate, Philippe, Thomas and, in some cases, confirmation by lawyers working with the Linux Foundation. Kate also obtained a third independent scan of the 4.13 code base from FOSSology, and compared selected files where the other two scanners disagreed against that SPDX file, to see if there was new insights. The Windriver scanner is based on an older version of FOSSology in part, so they are related. Thomas did random spot checks in about 500 files from the spreadsheets for the uapi headers and agreed with SPDX license identifier in the files he inspected. For the non-uapi files Thomas did random spot checks in about 15000 files. In initial set of patches against 4.14-rc6, 3 files were found to have copy/paste license identifier errors, and have been fixed to reflect the correct identifier. Additionally Philippe spent 10 hours this week doing a detailed manual inspection and review of the 12,461 patched files from the initial patch version early this week with: - a full scancode scan run, collecting the matched texts, detected license ids and scores - reviewing anything where there was a license detected (about 500+ files) to ensure that the applied SPDX license was correct - reviewing anything where there was no detection but the patch license was not GPL-2.0 WITH Linux-syscall-note to ensure that the applied SPDX license was correct This produced a worksheet with 20 files needing minor correction. This worksheet was then exported into 3 different .csv files for the different types of files to be modified. These .csv files were then reviewed by Greg. Thomas wrote a script to parse the csv files and add the proper SPDX tag to the file, in the format that the file expected. This script was further refined by Greg based on the output to detect more types of files automatically and to distinguish between header and source .c files (which need different comment types.) Finally Greg ran the script using the .csv files to generate the patches. Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org> Reviewed-by: Philippe Ombredanne <pombredanne@nexb.com> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-06-22perf script stackcollapse: Remove reference to the perl interpreterArnaldo Carvalho de Melo1-2/+0
It is ignored and this is actually a python script, not a perl one. Reported-by: Brendan Gregg <brendan.d.gregg@gmail.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Milian Wolff <milian.wolff@kdab.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Paolo Bonzini <pbonzini@redhat.com> Link: http://lkml.kernel.org/n/tip-0w4bpbqd79v3sl34jvpr11v0@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-21perf script: Add stackcollapse.py scriptPaolo Bonzini1-0/+127
Add stackcollapse.py script as an example of parsing call chains, and also of using optparse to access command line options. The flame graph tools include a set of scripts that parse output from various tools (including "perf script"), remove the offsets in the function and collapse each stack to a single line. The website also says "perf report could have a report style [...] that output folded stacks directly, obviating the need for stackcollapse-perf.pl", so here it is. This script is a Python rewrite of stackcollapse-perf.pl, using the perf scripting interface to access the perf data directly from Python. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Acked-by: Jiri Olsa <jolsa@kernel.org> Cc: Brendan Gregg <bgregg@netflix.com> Link: http://lkml.kernel.org/r/1460467573-22989-1-git-send-email-pbonzini@redhat.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
447'>447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 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 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 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 660 661 662 663 664 665 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 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
/****************************************************************************/
/*
 *  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, see <http://www.gnu.org/licenses/>.
 *
 *      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 ntohl(x) be32_to_cpu(x)
#include <target_flat.h>

//#define DEBUG

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

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

struct lib_info {
    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 */
    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;

/****************************************************************************/
/*
 * 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.  */
static abi_ulong copy_strings(abi_ulong p, int n, char **s)
{
    int len;

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

    return p;
}

static int target_pread(int fd, abi_ulong ptr, abi_ulong len,
                        abi_ulong offset)
{
    void *buf;
    int ret;

    buf = lock_user(VERIFY_WRITE, 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 */

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

static abi_ulong
calc_reloc(abi_ulong r, struct lib_info *p, int curid, int internalp)
{
    abi_ulong addr;
    int id;
    abi_ulong start_brk;
    abi_ulong start_data;
    abi_ulong text_len;
    abi_ulong start_code;

#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.  */
static void old_reloc(struct lib_info *libinfo, uint32_t rl)
{
#ifdef DEBUG
	const 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 *) ((unsigned long) libinfo->start_code + offset);
#else
	ptr = (uint32_t *) ((unsigned long) 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

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

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

static int load_flat_file(struct linux_binprm * bprm,
		struct lib_info *libinfo, int id, abi_ulong *extra_stack)
{
    struct flat_hdr * hdr;
    abi_ulong textpos = 0, datapos = 0;
    abi_long result;
    abi_ulong realdatastart = 0;
    abi_ulong text_len, data_len, bss_len, stack_len, flags;
    abi_ulong extra;
    abi_ulong reloc = 0, rp;
    int i, rev, relocs = 0;
    abi_ulong fpos;
    abi_ulong start_code;
    abi_ulong indx_len;

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

    /* 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
     */
    extra = relocs * sizeof(abi_ulong);
    if (extra < bss_len + stack_len)
        extra = bss_len + stack_len;

    /* Add space for library base pointers.  Make sure this does not
       misalign the  doesn't misalign the data segment.  */
    indx_len = MAX_SHARED_LIBS * sizeof(abi_ulong);
    indx_len = (indx_len + 15) & ~(abi_ulong)15;

    /*
     * 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);
        if (textpos == -1) {
            fprintf(stderr, "Unable to mmap process text\n");
            return -1;
        }

        realdatastart = target_mmap(0, data_len + extra + indx_len,
                                    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;
        }
        datapos = realdatastart + indx_len;

        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) {
            result = decompress_exec(bprm, fpos, (char *) datapos,
                                     data_len + (relocs * sizeof(abi_ulong)))
        } else
#endif
        {
            result = target_pread(bprm->fd, datapos,
                                  data_len + (relocs * sizeof(abi_ulong)),
                                  fpos);
        }
        if (result < 0) {
            fprintf(stderr, "Unable to read data+bss\n");
            return result;
        }

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

    } else {

        textpos = target_mmap(0, text_len + data_len + extra + indx_len,
                              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);
        datapos = realdatastart + indx_len;
        reloc = (textpos + ntohl(hdr->reloc_start) + indx_len);

#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,
                    data_len + (relocs * sizeof(abi_ulong)),
                    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);

    DBG_FLT("%s %s: TEXT=%x-%x DATA=%x-%x BSS=%x-%x\n",
            id ? "Lib" : "Load", bprm->filename,
            (int) start_code, (int) (textpos + text_len),
            (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);

    /*
     * 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 beginning 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) {
            abi_ulong addr;
            if (get_user_ual(addr, rp))
                return -EFAULT;
            if (addr == -1)
                break;
            if (addr) {
                addr = calc_reloc(addr, libinfo, id, 0);
                if (addr == RELOC_FAILED)
                    return -ENOEXEC;
                if (put_user_ual(addr, rp))
                    return -EFAULT;
            }
            rp += sizeof(abi_ulong);
        }
    }

    /*
     * 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) {
        abi_ulong persistent = 0;
        for (i = 0; i < relocs; i++) {
            abi_ulong addr, relval;

            /* Get the address of the pointer to be
               relocated (of course, the address has to be
               relocated first).  */
            if (get_user_ual(relval, reloc + i * sizeof(abi_ulong)))
                return -EFAULT;
            relval = ntohl(relval);
            if (flat_set_persistent(relval, &persistent))
                continue;
            addr = flat_get_relocate_addr(relval);
            rp = calc_reloc(addr, libinfo, id, 1);
            if (rp == RELOC_FAILED)
                return -ENOEXEC;

            /* Get the pointer's value.  */
            if (get_user_ual(addr, rp))
                return -EFAULT;
            addr = flat_get_addr_from_rp(addr, relval, flags, &persistent);
            if (addr != 0) {
                /*
                 * Do the relocation.  PIC relocs in the data section are
                 * already in target order
                 */
                if ((flags & FLAT_FLAG_GOTPIC) == 0)
                    addr = ntohl(addr);
                addr = calc_reloc(addr, libinfo, id, 0);
                if (addr == RELOC_FAILED)
                    return -ENOEXEC;

                /* Write back the relocated pointer.  */
                if (flat_put_addr_at_rp(rp, addr, relval))
                    return -EFAULT;
            }
        }
    } else {
        for (i = 0; i < relocs; i++) {
            abi_ulong relval;
            if (get_user_ual(relval, reloc + i * sizeof(abi_ulong)))
                return -EFAULT;
            old_reloc(&libinfo[0], relval);
        }
    }

    /* zero the BSS.  */
    memset(g2h(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 image_info *info)
{
    struct lib_info libinfo[MAX_SHARED_LIBS];
    abi_ulong p;
    abi_ulong stack_len;
    abi_ulong start_addr;
    abi_ulong sp;
    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.
     */
    stack_len = 0;
    for (i = 0; i < bprm->argc; ++i) {
        /* the argv strings */
        stack_len += strlen(bprm->argv[i]);
    }
    for (i = 0; i < bprm->envc; ++i) {
        /* the envp strings */
        stack_len += strlen(bprm->envp[i]);
    }
    stack_len += (bprm->argc + 1) * 4; /* the argv array */
    stack_len += (bprm->envc + 1) * 4; /* the envp array */


    res = load_flat_file(bprm, libinfo, 0, &stack_len);
    if (res > (unsigned long)-4096)
            return res;

    /* Update data segment pointers for all libraries */
    for (i=0; i<MAX_SHARED_LIBS; i++) {
        if (libinfo[i].loaded) {
            abi_ulong p;
            p = libinfo[i].start_data;
            for (j=0; j<MAX_SHARED_LIBS; j++) {
                p -= 4;
                /* FIXME - handle put_user() failures */
                if (put_user_ual(libinfo[j].loaded
                                 ? libinfo[j].start_data
                                 : UNLOADED_LIB,
                                 p))
                    return -EFAULT;
            }
        }
    }

    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);
    p = copy_strings(p, bprm->argc, bprm->argv);
    /* Align stack.  */
    sp = p & ~(abi_ulong)(sizeof(abi_ulong) - 1);
    /* 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 */
    stack_len *= sizeof(abi_ulong);
    if ((sp + stack_len) & 15)
        sp -= 16 - ((sp + stack_len) & 15);
    sp = loader_build_argptr(bprm->envc, bprm->argc, sp, p,
                             flat_argvp_envp_on_stack());

    /* 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;
                    if (put_user_ual(start_addr, sp))
                        return -EFAULT;
                    start_addr = libinfo[i].entry;
            }
    }
#endif

    /* 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->stack_limit = libinfo[0].start_brk;
    info->entry = start_addr;
    info->code_offset = info->start_code;
    info->data_offset = info->start_data - libinfo[0].text_len;

    DBG_FLT("start_thread(entry=0x%x, start_stack=0x%x)\n",
            (int)info->entry, (int)info->start_stack);

    return 0;
}