/*
 *     Copyright (c) 2017, NVIDIA CORPORATION.  All rights reserved.
 *
 * NVIDIA CORPORATION and its licensors retain all intellectual property
 * and proprietary rights in and to this software, related documentation
 * and any modifications thereto.  Any use, reproduction, disclosure or
 * distribution of this software and related documentation without an express
 * license agreement from NVIDIA CORPORATION is strictly prohibited.
 *
 */

/*
 * Extract information section from object file
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gbldefs.h"

#if !defined(WIN64) && !defined(_WIN64) && !defined(_WIN32)
#include <unistd.h>
#endif
#ifdef ZLIB
#include "zlib.h"
#endif

typedef short int16;
typedef unsigned short uint16;
typedef int int32;
typedef unsigned int uint32;
#if __WORDSIZE == 64
typedef long int int64;
typedef unsigned long int uint64;
#define FX64 "%lx"
#define FD64 "%ld"
#else
typedef long long int int64;
typedef unsigned long long int uint64;
#define FX64 "%llx"
#define FD64 "%lld"
#endif

static int extract_debug = 0;

static fpos_t basepos;

static char *xml_tag = NULL;
static char *xml_ns = NULL;

static void
xml_tag_open(FILE *file)
{
  if (xml_tag) {
    fprintf(file, "<?xml version = \"1.0\" encoding = \"utf-8\"?>\n<%s",
            xml_tag);
    if (xml_ns)
      fprintf(file, " xmlns=\"%s\"", xml_ns);
    fprintf(file, ">\n");
  }
} /* xml_tag_open */

static void
xml_tag_close(FILE *file)
{
  if (xml_tag)
    fprintf(file, "</%s>\n", xml_tag);
} /* xml_tag_close */

static void
set_xml_tag(char *tag)
{
  xml_tag = tag;
} /* set_xml_tag */

static void
set_xml_ns(char *ns)
{
  xml_ns = ns;
} /* set_xml_ns */

#define NOTE_TYPE 0x11235813

#define BUFSIZE 1025
static long
copysection(FILE *oldfile, FILE *newfile, int64 nbytes, int compress,
            int elfnote)
{
  unsigned char buf[BUFSIZE];
  int64 oldsize, newsize, total, secttotal, sectbytes;
#ifdef ZLIB
  unsigned char outbuf[BUFSIZE];
  int zret;
  unsigned charsout;
  z_stream ztream;
#endif

#ifdef ZLIB
  if (compress) {
    memset(&ztream, 0, sizeof(ztream));
    ztream.zalloc = Z_NULL;
    ztream.zfree = Z_NULL;
    ztream.opaque = Z_NULL;
    ztream.avail_in = 0;
    ztream.next_in = Z_NULL;
    zret = inflateInit(&ztream);
    if (zret != Z_OK)
      compress = 0;
  }
#endif

  sectbytes = nbytes;
  total = 0;
  while (total < nbytes && !feof(oldfile)) {
    secttotal = 0;
#if defined(TARGET_X86) || defined(TARGET_ARM) || defined(TARGET_POWER)
    /* read next note section header */
    if (elfnote) {
      struct {
        int namesize, descsize, notetype;
        char name[4];
      } elf_note_header;
      struct {
        int64 namesize, descsize, notetype;
        char name[4];
      } elf64_note_header;
      long n;
      if (total & 3) {
        fseek(oldfile, 4 - (total & 3), SEEK_CUR);
        total += (4 - (total & 3));
      }
      n = fread(&elf_note_header, sizeof(elf_note_header), 1, oldfile);
      if (n != 1) {
        return -8;
      }
      if (elf_note_header.namesize == 4 &&
          elf_note_header.notetype == NOTE_TYPE &&
          ((elf_note_header.name[0] == 'P' &&
            elf_note_header.name[1] == 'G' &&
            elf_note_header.name[2] == 'I' &&
            elf_note_header.name[3] == 0) ||
           (elf_note_header.name[0] == 'N' &&
            elf_note_header.name[1] == 'H' &&
            elf_note_header.name[2] == 'P' &&
            elf_note_header.name[3] == 'C'))) {
        /* now we're at the real note information */
        sectbytes = elf_note_header.descsize;
        total += sizeof(elf_note_header);
      } else { /* try a 64-bit header */
        fseek(oldfile, -sizeof(elf_note_header), SEEK_CUR);
        n = fread(&elf64_note_header, sizeof(elf64_note_header), 1, oldfile);
        if (n != 1) {
          return -8;
        }
        if (elf64_note_header.namesize == 4 &&
            elf64_note_header.notetype == NOTE_TYPE &&
          ((elf64_note_header.name[0] == 'P' &&
            elf64_note_header.name[1] == 'G' &&
            elf64_note_header.name[2] == 'I' &&
            elf64_note_header.name[3] == 0) ||
           (elf64_note_header.name[0] == 'N' &&
            elf64_note_header.name[1] == 'H' &&
            elf64_note_header.name[2] == 'P' &&
            elf64_note_header.name[3] == 'C'))) {
          /* now we're at the real note information */
          sectbytes = elf64_note_header.descsize;
          total += sizeof(elf64_note_header);
        } else {
          fseek(oldfile, -sizeof(elf64_note_header), SEEK_CUR);
          sectbytes = nbytes - total;
        }
      }
    }
#endif
    while (secttotal < sectbytes && !feof(oldfile)) {
      oldsize = BUFSIZE - 1;
      if (oldsize > sectbytes - secttotal)
        oldsize = sectbytes - secttotal;
      oldsize = fread(buf, (size_t)sizeof(char), (size_t)oldsize, oldfile);
      if (ferror(oldfile)) {
#ifdef ZLIB
        if (compress) {
          inflateEnd(&ztream);
        }
#endif
        return -8;
      }
#ifdef ZLIB
      if (compress) {
        ztream.avail_in = oldsize;
        ztream.next_in = buf;
        secttotal += oldsize;
        do {
          do {
            ztream.avail_out = BUFSIZE;
            ztream.next_out = outbuf;
            zret = inflate(&ztream, Z_NO_FLUSH);
            if (zret == Z_STREAM_ERROR) {
              return -4;
            }
            switch (zret) {
            case Z_NEED_DICT:
            case Z_DATA_ERROR:
            case Z_MEM_ERROR:
              inflateEnd(&ztream);
              return -4;
            }
            charsout = BUFSIZE - ztream.avail_out;
            if (charsout) {
              newsize = fwrite(outbuf, sizeof(char), charsout, newfile);
              if (ferror(newfile)) {
                perror("copysection");
                inflateEnd(&ztream);
                return -9;
              }
              if (newsize != charsout) {
                inflateEnd(&ztream);
                return -9;
              }
            }
            /* this loop handles the common case where
             * a block of input generates more than one block of
             * output */
          } while (ztream.avail_out == 0);
          /* this loop handles the case where we've appended multiple
           * compressed files into one section.  inflate() will
           * return at the end of the first compressed file,
           * with avail_in and next_in pointing to the start of the next
           * compressed file */
          if (zret == Z_STREAM_END) {
            zret = inflateReset(&ztream);
            if (zret != Z_OK) {
              inflateEnd(&ztream);
              return -9;
            }
          }
        } while (ztream.avail_in != 0);
      } else
#endif
      {
        if (oldsize == 0) {
          /* done successfully */
          return 0;
        }
        newsize = fwrite(buf, sizeof(char), oldsize, newfile);
        if (ferror(newfile)) {
          return -9;
        }
        if (newsize != oldsize) {
          return -9;
        }
        secttotal += newsize;
      }
    }
    total += secttotal;
  }
#ifdef ZLIB
  if (compress) {
    inflateEnd(&ztream);
  }
#endif
  return 0;
} /* copysection */

static int swapbytes;

/*
 * sw16 swaps bytes for a 16-bit quantity if the endianness differs
 */
static uint16
sw16(uint16 i)
{
  int16 j;
  if (!swapbytes)
    return i;
  j = ((i << 8) & 0xff00) | ((i >> 8) & 0x00ff);
  return j;
} /* sw16 */

/*
 * sw32 swaps bytes for a 32-bit quantity if the endianness differs
 */
static uint32
sw32(uint32 i)
{
  int32 j;
  if (!swapbytes)
    return i;
  j = ((i << 24) & 0xff000000) | ((i << 8) & 0x00ff0000) |
      ((i >> 8) & 0x0000ff00) | ((i >> 24) & 0x000000ff);
  return j;
} /* sw32 */

/*
 * sw64 swaps bytes for a 64-bit quantity if the native and guest
 * endianness differ
 */
static uint64
sw64(uint64 i)
{
  int64 il, ih, jl, jh, j;
  if (!swapbytes)
    return i;
  ih = i >> 32;
  il = 0xffff;
  il = il << 16 | il; /* don't want the sign to carry */
  il = i & il;

  jl = ((il << 24) & 0xff000000) | ((il << 8) & 0x00ff0000) |
       ((il >> 8) & 0x0000ff00) | ((il >> 24) & 0x000000ff);
  jh = ((ih << 24) & 0xff000000) | ((ih << 8) & 0x00ff0000) |
       ((ih >> 8) & 0x0000ff00) | ((ih >> 24) & 0x000000ff);
  j = jh << 32 | jl;
  return j;
} /* sw64 */

static int nativeendianness = 0, elfendianness = 0;

/*
 * determine whether we are running on a little-endian or big-endian host
 */
static void
endian(void)
{
  union ff {
    char e[4];
    int32 i;
  } ff;
  ff.i = 0x01020304;
  nativeendianness = 0;
  if (ff.e[0] == 1)
    nativeendianness = 2; /* big endian */
  if (ff.e[0] == 4)
    nativeendianness = 1; /* little endian */
#if 0
    fprintf( stderr, "endian=%2.2i%2.2i%2.2i%2.2i\n", 
		ff.e[0], ff.e[1], ff.e[2], ff.e[3] );
#endif
} /* endian */

/***********************************************************/
/* COFF HEADERS */

typedef struct coffhdr {
  uint16 f_magic;  /* magic number */
  uint16 f_nscns;  /* number of sections */
  int32 f_timdat;  /* time & date stamp */
  int32 f_symptr;  /* file pointer to symtab */
  int32 f_nsyms;   /* number of symtab entries */
  uint16 f_opthdr; /* sizeof(optional hdr) */
  uint16 f_flags;  /* flags */
} coffhdr;

typedef struct coffexehdr {
  uint16 f_magic;    /* magic number */
  uint16 f_cblp;     /* bytes on last page */
  uint16 f_cp;       /* pages in file */
  uint16 f_crlc;     /* relocations */
  uint16 f_cparhdr;  /* size of header on paragraphs */
  uint16 f_minalloc; /* min extra paragraphs */
  uint16 f_maxalloc; /* max extra paragraphs */
  uint16 f_ss;       /* initial SS value */
  uint16 f_sp;       /* initial SP value */
  uint16 f_csum;     /* checksum */
  uint16 f_ip;       /* initial IP value */
  uint16 f_cs;       /* initial CS value */
  uint16 f_lfarlc;   /* file address of relocation table */
  uint16 f_ovno;     /* overlay number */
  uint16 f_res[4];   /* reserved */
  uint16 f_oemid;    /* OEM id */
  uint16 f_oeminfo;  /* OEM info */
  uint16 f_res2[10]; /* reserved */
  uint32 f_lfanew;   /* file address of new (win) EXE header */
} coffexehdr;

typedef struct {
  char s_name[8];       /* section name, or /nnn, offset of int32 name
                         * in string table */
  int32 s_vsize;        /* virtual size */
  int32 s_vaddr;        /* virtual address */
  int32 s_rsize;        /* size of raw data */
  int32 s_roffset;      /* file offset to raw data */
  int32 s_relocate;     /* file offset to relocation entries */
  int32 s_linenumbers;  /* file offset to line number entries */
  int16 s_nrelocate;    /* number of relocation entries */
  int16 s_nlinenumbers; /* number of line number entries */
  int32 s_flags;        /* flags */
} secthdr;

#define SYMESZ 18
#define MSIMCODE2 0x8664
#define MSIMCODE 0x14c
#define MSCODEX 0x5a4d

/*
 * Read portions of the coff file to the
 * output file based on host endian option
 */
static void
swapcoffhdr(coffhdr *hdr)
{
  if (!swapbytes)
    return;
  hdr->f_magic = sw16(hdr->f_magic);
  hdr->f_nscns = sw16(hdr->f_nscns);
  hdr->f_timdat = sw32(hdr->f_timdat);
  hdr->f_symptr = sw32(hdr->f_symptr);
  hdr->f_nsyms = sw32(hdr->f_nsyms);
  hdr->f_opthdr = sw16(hdr->f_opthdr);
  hdr->f_flags = sw16(hdr->f_flags);
} /* swapcoffhdr */

static void
swapsecthdr(secthdr *shdr)
{
  if (!swapbytes)
    return;
  shdr->s_vsize = sw32(shdr->s_vsize);
  shdr->s_vaddr = sw32(shdr->s_vaddr);
  shdr->s_rsize = sw32(shdr->s_rsize);
  shdr->s_roffset = sw32(shdr->s_roffset);
  shdr->s_relocate = sw32(shdr->s_relocate);
  shdr->s_linenumbers = sw32(shdr->s_linenumbers);
  shdr->s_nrelocate = sw32(shdr->s_nrelocate);
  shdr->s_nlinenumbers = sw32(shdr->s_nlinenumbers);
  shdr->s_flags = sw32(shdr->s_flags);
} /* swapsecthdr */

/*
 * from object file 'objectname', find the last section with name 'sectname'
 * and extract to file named 'filename'
 */
int
extract_section_file_mscoff(FILE *objectfile, char *objectname, char *sectname,
                            char *filename, int compress)
{
  long n;
  int s, t;
  FILE *datafile;
  coffhdr hdr;
  coffexehdr ehdr;
  secthdr *shdr;
  uint32 signature;
  int32 sectsize;

  if (objectfile == NULL) {
    return -1;
  }
  fgetpos(objectfile, &basepos);
  /* read header, determine endianness */
  swapbytes = 0;
  n = fread(&hdr, sizeof(coffhdr), 1, objectfile);
  if (n != 1) {
    return -2;
  }

  if (hdr.f_magic != MSIMCODE && hdr.f_magic != MSIMCODE2 &&
      hdr.f_magic != MSCODEX) {
    swapbytes = 1;
    swapcoffhdr(&hdr);
  }
  if (hdr.f_magic != MSIMCODE && hdr.f_magic != MSIMCODE2 &&
      hdr.f_magic != MSCODEX) {
    return -10;
  }

  if (hdr.f_magic == MSCODEX) {
    fsetpos(objectfile, &basepos);
    n = fread(&ehdr, sizeof(coffexehdr), 1, objectfile);
    if (n != 1) {
      return -2;
    }
#if DEBUG
    if (extract_debug & 0x1) {
      printf("PECOFF executable, offset to PECOFF signature is %d\n",
             ehdr.f_lfanew);
    }
#endif
    fsetpos(objectfile, &basepos);
    fseek(objectfile, ehdr.f_lfanew, SEEK_CUR);
    n = fread(&signature, 4, 1, objectfile);
    if (n != 1) {
      return -2;
    }
#if DEBUG
    if (extract_debug & 0x1) {
      printf("PECOFF executable, signature is %x\n", signature);
    }
#endif
    if (signature != 0x00004550) {
      return -2;
    }
    n = fread(&hdr, sizeof(coffhdr), 1, objectfile);
    if (n != 1) {
      return -2;
    }
  }

#if DEBUG
  if (extract_debug & 0x1) {
    printf("header is 0x%hx=magic %hd=number.sections %x=timedate\n"
           "          0x%x=symptr %d=number.symbols %hd=opt.header.size "
           "0x%hx=flags\n",
           hdr.f_magic, hdr.f_nscns, hdr.f_timdat, hdr.f_symptr, hdr.f_nsyms,
           hdr.f_opthdr, hdr.f_flags);

    if (hdr.f_magic == MSIMCODE || hdr.f_magic == MSIMCODE2) {
      int s;
      if (hdr.f_opthdr)
        fseek(objectfile, hdr.f_opthdr, SEEK_CUR);
      for (s = 0; s < hdr.f_nscns; ++s) {
        secthdr shdr;
        n = fread(&shdr, sizeof(secthdr), 1, objectfile);
        if (n != 1) {
          fprintf(stderr, "Reading section header %d: ", s);
          perror(objectname);
          return -8;
        }
        swapsecthdr(&shdr);
        printf("section %d = %8.8s\n", s + 1, shdr.s_name);
        printf("  %d=vsize, 0x%x=vaddr, %d=rsize, 0x%x=roffset\n", shdr.s_vsize,
               shdr.s_vaddr, shdr.s_rsize, shdr.s_roffset);
        printf("  0x%x=relocate, 0x%x=linenum, %d=nrelocate, %d=nlinenum, "
               "0x%x=flags\n",
               shdr.s_relocate, shdr.s_linenumbers, shdr.s_nrelocate,
               shdr.s_nlinenumbers, shdr.s_flags);
      }
    }
    exit(0);
  }
#endif

  /* allocate and read the section table */
  if (hdr.f_opthdr)
    fseek(objectfile, hdr.f_opthdr, SEEK_CUR);
  shdr = (secthdr *)malloc(sizeof(secthdr) * hdr.f_nscns);
  if (shdr == NULL) {
    return -5;
  }
  for (s = 0; s < hdr.f_nscns; ++s) {
    n = fread(shdr + s, sizeof(secthdr), 1, objectfile);
    if (ferror(objectfile)) {
      return -2;
    }
    if (n != 1) {
      return -8;
    }
    if (swapbytes)
      swapsecthdr(shdr + s);
  }

  for (s = hdr.f_nscns - 1; s >= 0; --s) {
    if (strncmp(shdr[s].s_name, sectname, 8) == 0)
      break;
  }
  if (s < 0) {
    /* didn't find it */
    return 1;
  }
  /* copy the section out */
  if (filename == NULL) {
    datafile = stdout;
  } else {
    datafile = fopen(filename, "wb");
    if (datafile == NULL) {
      return -6;
    }
  }

  fsetpos(objectfile, &basepos);
  fseek(objectfile, shdr[s].s_roffset, SEEK_CUR);
  if (ferror(objectfile)) {
    fclose(datafile);
    return -7;
  }
  sectsize = shdr[s].s_rsize;
  if (shdr[s].s_vsize && shdr[s].s_vsize < sectsize)
    sectsize = shdr[s].s_vsize;

  if (xml_tag)
    xml_tag_open(datafile);
  n = copysection(objectfile, datafile, sectsize, compress, 0);
  if (xml_tag)
    xml_tag_close(datafile);

  fclose(datafile);
  return n;
} /* extract_section_file_mscoff */

int
extract_section_mscoff(char *objectname, char *sectname, char *filename,
                       int compress)
{
  FILE *objectfile;
  int r;
  objectfile = fopen(objectname, "rb");
  if (objectfile == NULL) {
    return -1;
  }
  r = extract_section_file_mscoff(objectfile, objectname, sectname, filename,
                                  compress);
  fclose(objectfile);
  return r;
} /* extract_section_mscoff */

/***********************************************************/
/* ELF HEADERS */

#include <stdlib.h>
#ifndef NOELF64
#define __ELF64__
#define __ELF64
#endif
#define HOST_ELFX
#include "elf_x.h"

static void
swapelf32header(Elf32_Ehdr *eh)
{
  if (!swapbytes)
    return;
  eh->e_type = sw16(eh->e_type);
  eh->e_machine = sw16(eh->e_machine);
  eh->e_version = sw32(eh->e_version);
  eh->e_entry = sw32(eh->e_entry);
  eh->e_phoff = sw32(eh->e_phoff);
  eh->e_shoff = sw32(eh->e_shoff);
  eh->e_flags = sw32(eh->e_flags);
  eh->e_ehsize = sw16(eh->e_ehsize);
  eh->e_phentsize = sw16(eh->e_phentsize);
  eh->e_phnum = sw16(eh->e_phnum);
  eh->e_shentsize = sw16(eh->e_shentsize);
  eh->e_shnum = sw16(eh->e_shnum);
  eh->e_shstrndx = sw16(eh->e_shstrndx);
} /* swapelf32header */

static void
swapsect32header(Elf32_Shdr *sh)
{
  if (!swapbytes)
    return;
  sh->sh_name = sw32(sh->sh_name);
  sh->sh_type = sw32(sh->sh_type);
  sh->sh_flags = sw32(sh->sh_flags);
  sh->sh_addr = sw32(sh->sh_addr);
  sh->sh_offset = sw32(sh->sh_offset);
  sh->sh_size = sw32(sh->sh_size);
  sh->sh_link = sw32(sh->sh_link);
  sh->sh_info = sw32(sh->sh_info);
  sh->sh_addralign = sw32(sh->sh_addralign);
  sh->sh_entsize = sw32(sh->sh_entsize);
} /* swapsect32header */

/*
 * readsect32header reads section header 's' into an element of global 'sh'
 */
static int
readsect32header(FILE *F, char *Fname, Elf32_Shdr *sh, int s, int size,
                 uint32 offset)
{
  int n;
  if (sizeof(Elf32_Shdr) < size)
    size = sizeof(Elf32_Shdr);
  fsetpos(F, &basepos);
  fseek(F, offset + size * s, SEEK_CUR);
  n = fread(sh, size, 1, F);
  if (n != 1) {
#if 0
	fprintf( stderr, "Reading section header %d: ", s );
	perror( Fname );
#endif
    return -8;
  }
  swapsect32header(sh);
#if DEBUG
  if (extract_debug & 0x2) {
    printf("SECTION %d:\n", s);
    printf("       type = %d\n", sh->sh_type);
    printf("name offset = 0x%x\n", sh->sh_name);
    printf("       size = 0x%x\n", sh->sh_size);
    printf("  addralign = %d\n", sh->sh_addralign);
    printf("       link = %d\n", sh->sh_link);
    printf("       info = %d\n", sh->sh_info);
    printf("      flags = 0x%x\n", sh->sh_flags);
    printf("       addr = 0x%x\n", sh->sh_addr);
    printf("     offset = 0x%x\n", sh->sh_offset);
    printf("    entsize = %d\n", sh->sh_entsize);
  }
#endif
  return 0;
} /* readsect32header */

/*
 * readstring32sect is called after readsecttable;
 * it checks that the section is a type SHT_STRTAB, and if so,
 * reads that section into a string, returning the string
 */
static int
readstring32sect(FILE *F, Elf32_Shdr *s, int psize, char **psect)
{
  int n;
  char *sect;

  sect = (char *)malloc(psize + 1);
  if (sect == NULL) {
    return -5;
  }
  memset(sect, 0, psize + 1);
  if (s->sh_type != SHT_STRTAB) {
    return 11;
  }

  fsetpos(F, &basepos);
  fseek(F, s->sh_offset, SEEK_CUR);
  n = fread(sect, 1, psize, F);

  if (n != psize) {
    return -8;
  }
  *psect = sect;
  return 0;
} /* readstring32sect */

#ifndef NOELF64
static int extract_section_elf64(char *objectname, char *sectname,
                                 char *filename, FILE *objectfile, int compress,
                                 int donote);
#endif

/*
 * from object file 'objectname', find the last section with name 'sectname'
 * and extract to file named 'filename'
 */
int
extract_section_file_elf(FILE *objectfile, char *objectname, char *sectname,
                         char *filename, int compress, int donote)
{
  Elf32_Ehdr hdr;
  Elf32_Shdr *shdr;
  Elf32_Word ssize;
  char *stringsect;
  int s, notesection = 0;
  long n;
  FILE *datafile;

  if (objectfile == NULL) {
    return -1;
  }

  fgetpos(objectfile, &basepos);
  endian();
  n = fread(&hdr, sizeof(hdr), 1, objectfile);
  if (n != 1) {
    return -2;
  }
  if (hdr.e_ident[0] != ELFMAG0 || hdr.e_ident[1] != ELFMAG1 ||
      hdr.e_ident[2] != ELFMAG2 || hdr.e_ident[3] != ELFMAG3) {
    return -3;
  }

  if (hdr.e_ident[EI_CLASS] == ELFCLASS32) {
/* expected */
#ifndef NOELF64
  } else if (hdr.e_ident[EI_CLASS] == ELFCLASS64) {
    return extract_section_elf64(objectname, sectname, filename, objectfile,
                                 compress, donote);
#endif
  } else {
    return -4;
  }

  if (hdr.e_ident[EI_DATA] == ELFDATA2LSB) {
    /* little endian */
    elfendianness = 1;
  } else if (hdr.e_ident[EI_DATA] == ELFDATA2MSB) {
    /* big endian */
    elfendianness = 2;
  } else {
    return -4;
  }
  swapbytes = 0;
  if (elfendianness != nativeendianness)
    swapbytes = 1;

  if (hdr.e_ident[EI_VERSION] != EV_CURRENT) {
    return -4;
  }

  /* start swapping bytes */
  if (swapbytes)
    swapelf32header(&hdr);

#if DEBUG
  if (extract_debug & 0x1) {
    printf("FILE %s\n", objectname);
    printf("archtype=%d\n", hdr.e_type);
    printf("fileversion=%d\n", hdr.e_version);
    printf("entry=%x\n", hdr.e_entry);
    printf("flags=%x\n", hdr.e_flags);
    printf("headersize=%d\n", hdr.e_ehsize);

    printf("program header position   =%d\n", hdr.e_phoff);
    printf("program header entry size =%d\n", hdr.e_phentsize);
    printf("program header entry count=%d\n", hdr.e_phnum);
    printf("section header position   =%d\n", hdr.e_shoff);
    printf("section header entry size =%d\n", hdr.e_shentsize);
    printf("section header entry count=%d\n", hdr.e_shnum);
    printf("     string section number=%d\n", hdr.e_shstrndx);
  }
#endif

  /* allocate and read the section table;
   * allocate extra slots for our new sections */
  shdr = (Elf32_Shdr *)malloc(sizeof(Elf32_Shdr) * (hdr.e_shnum));
  if (shdr == NULL) {
    return -5;
  }
  for (s = 0; s < hdr.e_shnum; ++s) {
    n = readsect32header(objectfile, objectname, shdr + s, s, hdr.e_shentsize,
                         hdr.e_shoff);
    if (n) {
      return n;
    }
  }

  /* read the string section */
  n = readstring32sect(objectfile, shdr + hdr.e_shstrndx,
                       shdr[hdr.e_shstrndx].sh_size, &stringsect);
  if (n) {
    return n;
  }

  for (s = hdr.e_shnum - 1; s >= 0; --s) {
    /* take the latest section with a matching name */
    char *thissectname;
    thissectname = stringsect + shdr[s].sh_name;
    if (strcmp(thissectname, sectname) == 0)
      break;
  }
  if (s < 0) {
    /* didn't find it */
    return 1;
  }

  /* copy the section out */
  if (filename == NULL) {
    datafile = stdout;
  } else {
    datafile = fopen(filename, "wb");
    if (datafile == NULL) {
      return -6;
    }
  }

  fsetpos(objectfile, &basepos);
  fseek(objectfile, shdr[s].sh_offset, SEEK_CUR);
  if (ferror(objectfile)) {
    fclose(datafile);
    return -7;
  }
  ssize = shdr[s].sh_size;
#if defined(TARGET_X86) || defined(TARGET_ARM) || defined(TARGET_POWER)
  if (shdr[s].sh_type == SHT_NOTE) {
    notesection = donote;
  }
#endif
  if (xml_tag)
    xml_tag_open(datafile);
  n = copysection(objectfile, datafile, ssize, compress, notesection);
  if (xml_tag)
    xml_tag_close(datafile);
  fclose(datafile);
  return n;
} /* extract_section_file_elf */

/*
 * from object file 'objectname', find the last section with name 'sectname'
 * and extract to file named 'filename'
 */
int
extract_section_elf(char *objectname, char *sectname, char *filename,
                    int compress, int donote)
{
  FILE *objectfile;
  int r;

  objectfile = fopen(objectname, "rb");
  if (objectfile == NULL) {
    return -1;
  }
  r = extract_section_file_elf(objectfile, objectname, sectname, filename,
                               compress, donote);
  fclose(objectfile);
  return r;
} /* extract_section_elf */

#ifndef NOELF64
static void
swapelf64header(Elf64_Ehdr *eh)
{
  if (!swapbytes)
    return;
  eh->e_type = sw16(eh->e_type);
  eh->e_machine = sw16(eh->e_machine);
  eh->e_version = sw32(eh->e_version);
  eh->e_entry = sw64(eh->e_entry);
  eh->e_phoff = sw64(eh->e_phoff);
  eh->e_shoff = sw64(eh->e_shoff);
  eh->e_flags = sw32(eh->e_flags);
  eh->e_ehsize = sw16(eh->e_ehsize);
  eh->e_phentsize = sw16(eh->e_phentsize);
  eh->e_phnum = sw16(eh->e_phnum);
  eh->e_shentsize = sw16(eh->e_shentsize);
  eh->e_shnum = sw16(eh->e_shnum);
  eh->e_shstrndx = sw16(eh->e_shstrndx);
} /* swapelf64header */

static void
swapsect64header(Elf64_Shdr *sh)
{
  if (!swapbytes)
    return;
  sh->sh_name = sw32(sh->sh_name);
  sh->sh_type = sw32(sh->sh_type);
  sh->sh_flags = sw64(sh->sh_flags);
  sh->sh_addr = sw64(sh->sh_addr);
  sh->sh_offset = sw64(sh->sh_offset);
  sh->sh_size = sw64(sh->sh_size);
  sh->sh_link = sw32(sh->sh_link);
  sh->sh_info = sw32(sh->sh_info);
  sh->sh_addralign = sw64(sh->sh_addralign);
  sh->sh_entsize = sw64(sh->sh_entsize);
} /* swapsect64header */

/*
 * readsect64header reads section header 's' into an element of global 'sh'
 */
static int
readsect64header(FILE *F, char *Fname, Elf64_Shdr *sh, int s, int size,
                 uint64 offset)
{
  int n;
  if (sizeof(Elf64_Shdr) < size)
    size = sizeof(Elf64_Shdr);
  fsetpos(F, &basepos);
  fseek(F, offset + size * s, SEEK_CUR);
  n = fread(sh, size, 1, F);
  if (n != 1) {
#if 0
	fprintf( stderr, "Reading section header %d: ", s );
	perror( Fname );
#endif
    return -8;
  }
  swapsect64header(sh);
#if DEBUG
  if (extract_debug & 0x2) {
    printf("SECTION %d:\n", s);
    printf("       type = %d\n", sh->sh_type);
    printf("name offset = 0x%x\n", sh->sh_name);
    printf("       size = 0x" FX64 "\n", sh->sh_size);
    printf("  addralign = " FD64 "\n", sh->sh_addralign);
    printf("       link = %d\n", sh->sh_link);
    printf("       info = %d\n", sh->sh_info);
    printf("      flags = 0x" FX64 "\n", sh->sh_flags);
    printf("       addr = 0x" FX64 "\n", sh->sh_addr);
    printf("     offset = 0x" FX64 "\n", sh->sh_offset);
    printf("    entsize = " FD64 "\n", sh->sh_entsize);
  }
#endif
  return 0;
} /* readsect64header */

/*
 * readstring64sect is called after readsecttable;
 * it checks that the section is a type SHT_STRTAB, and if so,
 * reads that section into a string, returning the string
 */
static int
readstring64sect(FILE *F, Elf64_Shdr *s, int psize, char **psect)
{
  int n;
  char *sect;

  sect = (char *)malloc(psize + 1);
  if (sect == NULL) {
    return -5;
  }
  memset(sect, 0, psize + 1);
  if (s->sh_type != SHT_STRTAB) {
    return 11;
  }

  fsetpos(F, &basepos);
  fseek(F, s->sh_offset, SEEK_CUR);
  n = fread(sect, 1, psize, F);

  if (n != psize) {
    return -8;
  }
  *psect = sect;
  return 0;
} /* readstring64sect */

/*
 * from object file 'objectname', find the last section with name 'sectname'
 * and extract to file named 'filename'
 */
static int
extract_section_elf64(char *objectname, char *sectname, char *filename,
                      FILE *objectfile, int compress, int donote)
{
  Elf64_Ehdr hdr;
  Elf64_Shdr *shdr;
  Elf32_Word ssize;
  char *stringsect;
  int s, notesection = 0;
  long n;
  FILE *datafile;

  fsetpos(objectfile, &basepos);
  n = fread(&hdr, sizeof(hdr), 1, objectfile);
  if (n != 1) {
    return -2;
  }
  if (hdr.e_ident[0] != ELFMAG0 || hdr.e_ident[1] != ELFMAG1 ||
      hdr.e_ident[2] != ELFMAG2 || hdr.e_ident[3] != ELFMAG3) {
    return -3;
  }

  if (hdr.e_ident[EI_CLASS] == ELFCLASS32) {
    /* shouldn't have gotten here, then */
    return -4;
  } else if (hdr.e_ident[EI_CLASS] == ELFCLASS64) {
    /* expected */
  } else {
    return -4;
  }

  if (hdr.e_ident[EI_DATA] == ELFDATA2LSB) {
    /* little endian */
    elfendianness = 1;
  } else if (hdr.e_ident[EI_DATA] == ELFDATA2MSB) {
    /* big endian */
    elfendianness = 2;
  } else {
    return -4;
  }
  swapbytes = 0;
  if (elfendianness != nativeendianness)
    swapbytes = 1;

  if (hdr.e_ident[EI_VERSION] != EV_CURRENT) {
    return -4;
  }

  /* start swapping bytes */
  if (swapbytes)
    swapelf64header(&hdr);

#if DEBUG
  if (extract_debug & 0x1) {
    printf("FILE %s\n", objectname);
    printf("archtype=%d\n", hdr.e_type);
    printf("fileversion=%d\n", hdr.e_version);
    printf("entry=" FX64 "\n", hdr.e_entry);
    printf("flags=%x\n", hdr.e_flags);
    printf("headersize=%d\n", hdr.e_ehsize);

    printf("program header position   =" FD64 "\n", hdr.e_phoff);
    printf("program header entry size =%d\n", hdr.e_phentsize);
    printf("program header entry count=%d\n", hdr.e_phnum);
    printf("section header position   =" FD64 "\n", hdr.e_shoff);
    printf("section header entry size =%d\n", hdr.e_shentsize);
    printf("section header entry count=%d\n", hdr.e_shnum);
    printf("     string section number=%d\n", hdr.e_shstrndx);
  }
#endif

  /* allocate and read the section table;
   * allocate extra slots for our new sections */
  shdr = (Elf64_Shdr *)malloc(sizeof(Elf64_Shdr) * (hdr.e_shnum));
  if (shdr == NULL) {
    return -5;
  }
  for (s = 0; s < hdr.e_shnum; ++s) {
    n = readsect64header(objectfile, objectname, shdr + s, s, hdr.e_shentsize,
                         hdr.e_shoff);
    if (n) {
      return n;
    }
  }

  /* read the string section */
  n = readstring64sect(objectfile, shdr + hdr.e_shstrndx,
                       shdr[hdr.e_shstrndx].sh_size, &stringsect);
  if (n) {
    return n;
  }

  for (s = hdr.e_shnum - 1; s >= 0; --s) {
    /* take the latest section with a matching name */
    char *thissectname;
    thissectname = stringsect + shdr[s].sh_name;
    if (strcmp(thissectname, sectname) == 0)
      break;
  }
  if (s < 0) {
    /* didn't find it */
    return 1;
  }

  /* copy the section out */
  if (filename == NULL) {
    datafile = stdout;
  } else {
    datafile = fopen(filename, "wb");
    if (datafile == NULL) {
      return -6;
    }
  }

  fsetpos(objectfile, &basepos);
  fseek(objectfile, shdr[s].sh_offset, SEEK_CUR);
  if (ferror(objectfile)) {
    fclose(datafile);
    return -7;
  }
  ssize = shdr[s].sh_size;
#if defined(TARGET_X86) || defined(TARGET_ARM) || defined(TARGET_POWER)
  if (shdr[s].sh_type == SHT_NOTE) {
    notesection = donote;
  }
#endif
  if (xml_tag)
    xml_tag_open(datafile);
  n = copysection(objectfile, datafile, ssize, compress, notesection);
  if (xml_tag)
    xml_tag_close(datafile);
  fclose(datafile);
  return n;
} /* extract_section_elf64 */
#endif

/***********************************************************/
/* MACH-O HEADERS */

#include "macho_x.h"

static int
extractmacho32(FILE *objectfile, macho_header32_t *header, char *objectname,
               char *sectname, char *filename, int swap, int compress)
{
  macho_uint32_t ncmds;
  macho_uint32_t cmd, c, s;
  int slen, replacedot;
  long n;
  macho_uint32_t macho_offset;
  macho_loadcmd_t loadcmd;
  macho_segment32_t segcmd;
  macho_section32_t sectcmd;
  FILE *datafile;

  slen = strlen(sectname);
  if (sectname[0] != '.') {
    replacedot = 0;
  } else {
    replacedot = 1;
  }
  ncmds = header->ncmds;
  for (c = 0; c < ncmds; ++c) {
    n = fread(&loadcmd, sizeof(macho_loadcmd_t), 1, objectfile);
    if (n != 1) {
      fclose(objectfile);
      return -2;
    }
    cmd = loadcmd.cmd & macho_load_mask;
    if (cmd == macho_load_segment32) {
      /* read the segment to get the sections */
      n = fread(&segcmd, sizeof(macho_segment32_t), 1, objectfile);
      if (n != 1) {
        fclose(objectfile);
        return -2;
      }
      for (s = 0; s < segcmd.nsects; ++s) {
        n = fread(&sectcmd, sizeof(macho_section32_t), 1, objectfile);
        if ((strncmp(sectcmd.segname, "__PGIinfo", 9) == 0 ||
             strncmp(sectcmd.segname, "..PGIinfo", 9) == 0) &&
            ((replacedot && sectcmd.sectname[0] == '_' &&
              sectcmd.sectname[1] == '_' &&
              strncmp(sectcmd.sectname + 2, sectname + 1, slen - 1) == 0) ||
             ((strncmp(sectcmd.sectname, sectname, slen) == 0)))) {
          /* found the section */
          fsetpos(objectfile, &basepos);
          fseek(objectfile, sectcmd.offset, SEEK_CUR);
          if (ferror(objectfile)) {
            return -7;
          }
          if (filename == NULL) {
            datafile = stdout;
          } else {
            datafile = fopen(filename, "wb");
            if (datafile == NULL) {
              return -6;
            }
          }

          if (xml_tag)
            xml_tag_open(datafile);
          n = copysection(objectfile, datafile, sectcmd.size, compress, 0);
          if (xml_tag)
            xml_tag_close(datafile);
          fclose(datafile);
          return n;
        }
      }
    } else {
      /* skip to the next load command */
      if (loadcmd.cmdsize > sizeof(macho_loadcmd_t)) {
        fseek(objectfile, loadcmd.cmdsize - sizeof(macho_loadcmd_t), SEEK_CUR);
      }
    }
  }
  return 1;
} /* extractmacho32 */

static int
extractmacho64(FILE *objectfile, macho_header64_t *header, char *objectname,
               char *sectname, char *filename, int swap, int compress)
{
  macho_uint64_t ncmds;
  macho_uint32_t cmd, c, s;
  int slen, replacedot;
  long n;
  macho_uint64_t macho_offset;
  macho_loadcmd_t loadcmd;
  macho_segment64_t segcmd;
  macho_section64_t sectcmd;
  FILE *datafile;

  slen = strlen(sectname);
  if (sectname[0] != '.') {
    replacedot = 0;
  } else {
    replacedot = 1;
  }
  ncmds = header->ncmds;
  for (c = 0; c < ncmds; ++c) {
    n = fread(&loadcmd, sizeof(macho_loadcmd_t), 1, objectfile);
    if (n != 1) {
      fclose(objectfile);
      return -2;
    }
    cmd = loadcmd.cmd & macho_load_mask;
    if (cmd == macho_load_segment64) {
      /* read the segment to get the sections */
      n = fread(&segcmd, sizeof(macho_segment64_t), 1, objectfile);
      if (n != 1) {
        fclose(objectfile);
        return -2;
      }
      for (s = 0; s < segcmd.nsects; ++s) {
        n = fread(&sectcmd, sizeof(macho_section64_t), 1, objectfile);
        if ((strncmp(sectcmd.segname, "__PGIinfo", 9) == 0 ||
             strncmp(sectcmd.segname, "..PGIinfo", 9) == 0) &&
            ((replacedot && sectcmd.sectname[0] == '_' &&
              sectcmd.sectname[1] == '_' &&
              strncmp(sectcmd.sectname + 2, sectname + 1, slen - 1) == 0) ||
             ((strncmp(sectcmd.sectname, sectname, slen) == 0)))) {
          /* found the section */
          fsetpos(objectfile, &basepos);
          fseek(objectfile, sectcmd.offset, SEEK_CUR);
          if (ferror(objectfile)) {
            return -7;
          }
          if (filename == NULL) {
            datafile = stdout;
          } else {
            datafile = fopen(filename, "wb");
            if (datafile == NULL) {
              return -6;
            }
          }

          if (xml_tag)
            xml_tag_open(datafile);
          n = copysection(objectfile, datafile, sectcmd.size, compress, 0);
          if (xml_tag)
            xml_tag_close(datafile);
          fclose(datafile);
          return n;
        }
      }
    } else {
      /* skip to the next load command */
      if (loadcmd.cmdsize > sizeof(macho_loadcmd_t)) {
        fseek(objectfile, loadcmd.cmdsize - sizeof(macho_loadcmd_t), SEEK_CUR);
      }
    }
  }
  return 1;
} /* extractmacho64 */

/*
 * from object file 'objectname', find the last section with name 'sectname'
 * and extract to file named 'filename'
 */
int
extract_section_file_macho(FILE *objectfile, char *objectname, char *sectname,
                           char *filename, int compress)
{
  macho_uint32_t magic;
  int n, r;

  if (objectfile == NULL) {
    return -1;
  }

  fgetpos(objectfile, &basepos);
  n = fread(&magic, sizeof(macho_uint32_t), 1, objectfile);
  if (n != 1) {
    return -2;
  }

  if (magic == macho_magic_i86) {
    macho_header32_t header32;
    header32.magic = magic;
    n = fread(&header32.cputype,
              sizeof(macho_header32_t) - sizeof(macho_uint32_t), 1, objectfile);
    if (n != 1) {
      fclose(objectfile);
      return -2;
    }
    r = extractmacho32(objectfile, &header32, objectname, sectname, filename, 0,
                       compress);
  } else if (magic == macho_magic_i86_rev) {
    macho_header32_t header32;
    header32.magic = magic;
    n = fread(&header32.cputype,
              sizeof(macho_header32_t) - sizeof(macho_uint32_t), 1, objectfile);
    if (n != 1) {
      fclose(objectfile);
      return -2;
    }
    r = extractmacho32(objectfile, &header32, objectname, sectname, filename, 1,
                       compress);
  } else if (magic == macho_magic_i86_64) {
    macho_header64_t header64;
    header64.magic = magic;
    n = fread(&header64.cputype,
              sizeof(macho_header64_t) - sizeof(macho_uint32_t), 1, objectfile);
    if (n != 1) {
      fclose(objectfile);
      return -2;
    }
    r = extractmacho64(objectfile, &header64, objectname, sectname, filename, 0,
                       compress);
  } else if (magic == macho_magic_i86_64_rev) {
    macho_header64_t header64;
    header64.magic = magic;
    n = fread(&header64.cputype,
              sizeof(macho_header64_t) - sizeof(macho_uint32_t), 1, objectfile);
    if (n != 1) {
      fclose(objectfile);
      return -2;
    }
    r = extractmacho64(objectfile, &header64, objectname, sectname, filename, 1,
                       compress);
  } else {
    fclose(objectfile);
    return -10;
  }
  return r;
} /* extract_section_file_macho */

int
extract_section_macho(char *objectname, char *sectname, char *filename,
                      int compress)
{
  FILE *objectfile;
  int r;
  objectfile = fopen(objectname, "rb");
  if (objectfile == NULL) {
    return -1;
  }
  r = extract_section_file_macho(objectfile, objectname, sectname, filename,
                                 compress);
  fclose(objectfile);
  return r;
} /* extract_section_macho */

int
extract_section_file(FILE *objectfile, char *objectname, char *sectname,
                     char *filename, int compress, int donote)
{
#if defined(TARGET_MSCOFF)
  return extract_section_file_mscoff(objectfile, objectname, sectname, filename,
                                     compress);
#elif defined(TARGET_ELF)
  return extract_section_file_elf(objectfile, objectname, sectname, filename,
                                  compress, donote);
#elif defined(TARGET_MACHO)
  return extract_section_file_macho(objectfile, objectname, sectname, filename,
                                    compress);
#else
  /* by default, assume ELF sections */
  return extract_section_file_elf(objectfile, objectname, sectname, filename,
                                  compress, donote);
#endif
} /* extract_section_file */

int
extract_section(char *objectname, char *sectname, char *filename, int compress,
                int donote)
{
  FILE *objectfile;
  int r;
  objectfile = fopen(objectname, "rb");
  if (objectfile == NULL) {
    return -1;
  }
  r = extract_section_file(objectfile, objectname, sectname, filename, compress,
                           donote);
  fclose(objectfile);
  return r;
} /* extract_section */

#ifndef NOMAIN
/***********************************************************/
/* main routine to part command line */
int
main(int argc, char *argv[])
{
  char *objfile, *section_name, *datafile;
  int a, dofile, usage, n, filetype, quiet, compress = 0, donote = 1;

  objfile = NULL;
  section_name = NULL;
  datafile = NULL;
  dofile = usage = quiet = 0;
  filetype = 0;
  for (a = 1; a < argc; ++a) {
    if (argv[a][0] != '-' || dofile) {
      /* a file name */
      if (objfile == NULL) {
        objfile = argv[a];
      } else if (section_name == NULL) {
        section_name = argv[a];
      } else if (datafile == NULL) {
        datafile = argv[a];
      } else if (!quiet) {
        fprintf(stderr, "too many filenames: %s\n", argv[a]);
        usage = 1;
      }
    } else {
      if (strcmp(argv[a], "-") == 0 && objfile && section_name &&
          datafile == NULL) {
        datafile = argv[a];
      } else if (strcmp(argv[a], "--") == 0) {
        dofile = 1; /* remaining arguments are file names */
      } else if (strcmp(argv[a], "-help") == 0) {
        usage = 1;
      } else if (strcmp(argv[a], "-name") == 0) {
        if (a + 1 < argc) {
          ++a;
          section_name = argv[a];
        } else {
          usage = 1;
        }
      } else if (strcmp(argv[a], "-cz") == 0) {
        compress = 1;
      } else if (strcmp(argv[a], "-nonote") == 0) {
        donote = 0;
      } else if (strcmp(argv[a], "-q") == 0) {
        quiet = 1;
      } else if (strcmp(argv[a], "-usage") == 0) {
        usage = 1;
      } else if (strcmp(argv[a], "-mscoff") == 0) {
        filetype = 2;
      } else if (strcmp(argv[a], "-coff") == 0) {
        filetype = 2;
      } else if (strcmp(argv[a], "-elf") == 0) {
        filetype = 3;
      } else if (strcmp(argv[a], "-macho") == 0) {
        filetype = 4;
      } else if (strcmp(argv[a], "-osx") == 0) {
        filetype = 4;
      } else if (strcmp(argv[a], "-debug") == 0) {
        if (a + 1 < argc && argv[a + 1][0] >= '0' && argv[a + 1][0] <= '9') {
          ++a;
          extract_debug = strtol(argv[a], NULL, 10);
        } else {
          extract_debug = 15;
        }
      } else if (strcmp(argv[a], "-xmltag") == 0) {
        if (a + 1 < argc) {
          ++a;
          set_xml_tag(argv[a]);
        } else {
          set_xml_tag("xml");
        }
      } else if (strcmp(argv[a], "-xmlns") == 0) {
        if (a + 1 < argc) {
          ++a;
          set_xml_ns(argv[a]);
        } else {
          set_xml_ns("http://www.pgroup.com/ccff");
        }
      } else if (!quiet) {
        fprintf(stderr, "unrecognized switch: %s\n", argv[a]);
        usage = 1;
      }
    }
  }
  if (objfile == NULL || section_name == NULL)
    usage = 1;
  if (usage) {
    fprintf(stderr, "Usage: %s", argv[0]);
    fprintf(stderr,
            " <obj file> [-name] <section name> <data file>\n"
            "Flags:\n"
            "  -help|-usage - print this output\n"
            "  [-mscoff|-coff|-elf|-osx|-macho] - select object file type\n"
            "  -xmltag tag  - add <tag> and </tag> before/after section\n"
            "  -xmlns ns    - include namespace defn in opening xml tag\n"
            "  -cz          - decompress section using zlib\n"
            "  -q           - quiet operation\n");
    exit(0);
  }
  if (datafile && datafile[0] == '-' && datafile[1] == '\0')
    datafile = NULL;
  switch (filetype) {
  case 2:
    n = extract_section_mscoff(objfile, section_name, datafile, compress);
    break;
  case 3:
    n = extract_section_elf(objfile, section_name, datafile, compress, donote);
    break;
  case 4:
    n = extract_section_macho(objfile, section_name, datafile, compress);
    break;
  default:
    n = extract_section(objfile, section_name, datafile, compress, donote);
    break;
  }
  switch (n) {
  case 0: /* successful */
    exit(0);
  case 1:
    if (!quiet) {
      fprintf(stderr, "section %s not found in object file %s\n", section_name,
              objfile);
    } else if (xml_tag) {
      FILE *fp;
      /* -q and -xmltag, write an 'empty' XML element */
      if (datafile == NULL) {
        fp = stdout;
      } else {
        fp = fopen(datafile, "wb");
      }
      if (fp != NULL) {
        xml_tag_open(fp);
        xml_tag_close(fp);
        fclose(fp);
      }
    }
    exit(1);
  case -1:
    if (!quiet) {
      fprintf(stderr, "could not open object file\n");
      perror(objfile);
    }
    exit(255);
  case -2:
    if (!quiet) {
      fprintf(stderr, "could not read object file\n");
      perror(objfile);
    }
    exit(255);
  case -3:
    if (!quiet) {
      fprintf(stderr, "object file %s is not a supported object file\n",
              objfile);
    }
    exit(255);
  case -4:
    if (!quiet) {
      fprintf(stderr, "object file %s is an unsupported object file\n",
              objfile);
    }
    exit(255);
  case -5:
    if (!quiet) {
      fprintf(stderr, "memory allocation error, or ran out of memory\n");
    }
    exit(255);
  case -6:
    if (!quiet) {
      fprintf(stderr, "could not open datafile\n");
      perror(objfile);
    }
    exit(255);
  case -7:
    if (!quiet) {
      fprintf(stderr, "could not seek to section %s\n", section_name);
      perror(objfile);
    }
    exit(255);
  case -8:
    if (!quiet) {
      fprintf(stderr, "error reading from object file\n");
      perror(objfile);
    }
    exit(255);
  case -9:
    if (!quiet) {
      fprintf(stderr, "error writing to datafile\n");
      perror(objfile);
    }
    exit(255);
  case -10:
    if (!quiet) {
      fprintf(stderr, "object file %s is not a COFF file\n", objfile);
    }
    exit(255);
  case -11:
    if (!quiet) {
      fprintf(stderr, "ELF string section error in object file %s\n", objfile);
    }
    exit(255);
  }
  exit(0);
} /* main */
#endif
