home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
emxtutor.zip
/
emxsrcd1.zip
/
emx
/
src
/
emxexp
/
emxexp.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-12-19
|
16KB
|
624 lines
/* emxexp.c -- Create export definitions from .o and .obj files
Copyright (c) 1993-1998 Eberhard Mattes
This file is part of emxexp.
emxexp 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, or (at your option)
any later version.
emxexp 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 emxexp; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <ar.h>
#include "defs.h"
#include <sys/omflib.h>
#include "demangle.h"
#define VERSION "0.9d"
struct bss_list
{
char *name;
struct bss_list *next;
};
static int ordinal = 0;
static int noname_flag = FALSE;
static int bss_flag = FALSE;
static FILE *out_file;
static const char *inp_fname;
static const char *mod_name;
static int new_mod = FALSE;
static int last_dem = FALSE;
static struct bss_list *bss_list = NULL;
static void error (const char *fmt, ...) NORETURN2;
static void usage (void) NORETURN2;
static void bad_omf (void) NORETURN2;
static void error (const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
fprintf (stderr, "emxexp: ");
vfprintf (stderr, fmt, arg_ptr);
fputc ('\n', stderr);
exit (2);
}
/* Allocate N bytes of memory. Quit on failure. This function is
used like malloc(), but we don't have to check the return value. */
void *xmalloc (size_t n)
{
void *p;
p = malloc (n);
if (p == NULL)
error ("Out of memory");
return p;
}
/* Change the allocation of PTR to N bytes. Quit on failure. This
function is used like realloc(), but we don't have to check the
return value. */
void *xrealloc (void *ptr, size_t n)
{
void *p;
p = realloc (ptr, n);
if (p == NULL)
error ("Out of memory");
return p;
}
/* How to call this program. */
static void usage (void)
{
fputs ("emxexp " VERSION " -- Copyright (c) 1993-1995 by Eberhard Mattes\n\n"
"Usage: emxexp [-n] [-u] [-o[<ordinal>] <input_file>...\n\n"
"Options:\n"
" -n Output NONAME keyword for each exported symbol\n"
" -o Output ordinal numbers, starting at 1\n"
" -o<ordinal> Output ordinal numbers, starting at <ordinal>\n"
" -u Also export uninitialized variables\n",
stderr);
exit (1);
}
static void export (const char *name)
{
char *dem;
if (new_mod)
{
fprintf (out_file, "\n; From %s", inp_fname);
if (mod_name != NULL)
fprintf (out_file, "(%s)", mod_name);
fputc ('\n', out_file);
new_mod = FALSE;
}
dem = cplus_demangle (name, DMGL_ANSI | DMGL_PARAMS);
if (dem != NULL)
{
fprintf (out_file, "\n ; %s\n", dem);
free (dem);
last_dem = TRUE;
}
else if (last_dem)
{
fputc ('\n', out_file);
last_dem = FALSE;
}
fprintf (out_file, " \"%s\"", name);
if (ordinal != 0)
fprintf (out_file, " @%d", ordinal++);
if (noname_flag)
fprintf (out_file, " NONAME");
fputc ('\n', out_file);
}
static void export_bss (const char *name)
{
struct bss_list *p;
if (bss_flag)
{
for (p = bss_list; p != NULL; p = p->next)
if (strcmp (p->name, name) == 0)
return;
p = xmalloc (sizeof (*p));
p->name = xmalloc (strlen (name) + 1);
strcpy (p->name, name);
p->next = bss_list;
bss_list = p;
export (name);
}
}
static void process_aout (FILE *inp_file, long size)
{
byte *inp_buf;
const struct a_out_header *a_out_h;
const byte *sym;
const struct nlist *sym_ptr;
const byte *str_ptr;
long str_size;
int sym_count, i;
const char *name;
new_mod = TRUE;
inp_buf = xmalloc (size);
size = fread (inp_buf, 1, size, inp_file);
a_out_h = (struct a_out_header *)inp_buf;
if (size < sizeof (struct a_out_header) || a_out_h->magic != 0407)
error ("Malformed input file `%s'", inp_fname);
sym = (inp_buf + sizeof (struct a_out_header) + a_out_h->text_size
+ a_out_h->data_size + a_out_h->trsize + a_out_h->drsize);
str_ptr = sym + a_out_h->sym_size;
if (str_ptr + 4 - inp_buf > size)
error ("Malformed input file `%s'", inp_fname);
str_size = *(long *)str_ptr;
sym_ptr = (const struct nlist *)sym;
sym_count = a_out_h->sym_size / sizeof (struct nlist);
if (str_ptr + str_size - inp_buf > size)
error ("Malformed input file `%s'", inp_fname);
for (i = 0; i < sym_count; ++i)
if (sym_ptr[i].type == (N_TEXT|N_EXT) || sym_ptr[i].type == (N_DATA|N_EXT))
{
name = str_ptr + sym_ptr[i].string;
if (*name == '_')
++name;
export (name);
}
else if (sym_ptr[i].type == N_EXT && sym_ptr[i].value != 0)
{
name = str_ptr + sym_ptr[i].string;
if (*name == '_')
++name;
export_bss (name);
}
free (inp_buf);
}
static byte rec_buf[MAX_REC_SIZE+8];
static int rec_type;
static int rec_len;
static int rec_idx;
static void bad_omf (void)
{
error ("Malformed OMF file `%s'", inp_fname);
}
static void get_mem (void *dst, int len)
{
if (rec_idx + len > rec_len)
bad_omf ();
memcpy (dst, rec_buf + rec_idx, len);
rec_idx += len;
}
static int get_byte (void)
{
if (rec_idx >= rec_len)
bad_omf ();
return rec_buf[rec_idx++];
}
static void get_string (byte *dst)
{
int len;
len = get_byte ();
get_mem (dst, len);
dst[len] = 0;
}
static int get_index (void)
{
int result;
result = get_byte ();
if (result & 0x80)
{
if (rec_idx >= rec_len)
bad_omf ();
result = ((result & 0x7f) << 8) | rec_buf[rec_idx++];
}
return result;
}
static word get_dword (void)
{
dword result;
if (rec_idx + 4 > rec_len)
bad_omf ();
result = rec_buf[rec_idx++];
result |= rec_buf[rec_idx++] << 8;
result |= rec_buf[rec_idx++] << 16;
result |= rec_buf[rec_idx++] << 24;
return result;
}
static word get_word (void)
{
word result;
if (rec_idx + 2 > rec_len)
bad_omf ();
result = rec_buf[rec_idx++];
result |= rec_buf[rec_idx++] << 8;
return result;
}
static dword get_word_or_dword (void)
{
return rec_type & REC32 ? get_dword () : get_word ();
}
static dword get_commlen (void)
{
dword result;
result = get_byte ();
if (result <= 0x80)
return result;
switch (result)
{
case 0x81:
return get_word ();
case 0x84:
result = get_byte ();
result |= get_byte () << 8;
result |= get_byte () << 16;
return result;
case 0x88:
return get_dword ();
default:
bad_omf ();
}
}
static void omf_pubdef (void)
{
int type, group, seg;
word frame;
dword offset;
byte name[256];
group = get_index ();
seg = get_index ();
if (seg == 0)
frame = get_word ();
while (rec_idx < rec_len)
{
get_string (name);
offset = get_word_or_dword ();
type = get_index ();
export (name);
}
}
static void omf_comdef (void)
{
int type_index, data_type;
byte name[256];
while (rec_idx < rec_len)
{
get_string (name);
type_index = get_index ();
data_type = get_byte ();
switch (data_type)
{
case 0x61:
get_commlen ();
get_commlen ();
break;
case 0x62:
get_commlen ();
break;
default:
bad_omf ();
}
export_bss (name);
}
}
static void process_omf (FILE *inp_file)
{
struct omf_rec rec;
new_mod = TRUE;
do
{
if (fread (&rec, sizeof (rec), 1, inp_file) != 1)
error ("Unexpected end of file on input file `%s'", inp_fname);
rec_type = rec.rec_type;
rec_len = rec.rec_len;
rec_idx = 0;
if (rec_len > sizeof (rec_buf))
error ("OMF record too long in `%s'", inp_fname);
if (fread (rec_buf, rec_len, 1, inp_file) != 1)
error ("Unexpected end of file on input file `%s'", inp_fname);
--rec_len; /* Remove checksum */
switch (rec_type)
{
case PUBDEF:
case PUBDEF|REC32:
omf_pubdef ();
break;
case COMDEF:
if (bss_flag)
omf_comdef ();
break;
}
} while (rec.rec_type != MODEND && rec_type != (MODEND|REC32));
}
/* Process one input file. */
static void process (void)
{
static char ar_magic[SARMAG+1] = ARMAG;
char ar_test[SARMAG], *p, *end;
struct ar_hdr ar;
long size, ar_pos, index;
int i, n;
FILE *inp_file;
char *long_names = NULL;
size_t long_names_size = 0;
mod_name = NULL;
inp_file = fopen (inp_fname, "rb");
if (inp_file == NULL)
error ("Cannot open input file `%s'", inp_fname);
/* Read some bytes from the start of the file to find out whether
this is an archive (.a) file or not. */
if (fread (ar_test, sizeof (ar_test), 1, inp_file) != 1)
error ("Cannot read input file `%s'", inp_fname);
if (memcmp (ar_test, ar_magic, SARMAG) == 0)
{
/* The input file is an archive. Loop over all the members of
the archive. */
ar_pos = SARMAG;
for (;;)
{
/* Read the header of the member. */
fseek (inp_file, ar_pos, SEEK_SET);
size = fread (&ar, 1, sizeof (ar), inp_file);
if (size == 0)
break;
else if (size != sizeof (ar))
error ("Malformed archive `%s'", inp_fname);
/* Decode the header. */
errno = 0;
size = strtol (ar.ar_size, &p, 10);
if (p == ar.ar_size || errno != 0 || size <= 0 || *p != ' ')
error ("Malformed archive header in `%s'", inp_fname);
ar_pos += (sizeof (ar) + size + 1) & -2;
/* Remove trailing blanks from the member name. */
i = sizeof (ar.ar_name) - 1;
while (i > 0 && ar.ar_name[i-1] == ' ')
--i;
ar.ar_name[i] = 0;
if (strcmp (ar.ar_name, "ARFILENAMES/") == 0)
{
size_t i;
/* The "ARFILENAMES/" member contains the long file
names, each one is terminated with a newline
character. Member names starting with a space are
also considered long file names because a leading
space is used for names pointing into the
"ARFILENAMES/" table. Read the "ARFILENAMES/" member
to LONG_NAMES. */
if (size != 0)
{
long_names_size = (size_t)size;
long_names = xmalloc (long_names_size);
size = fread (long_names, 1, long_names_size, inp_file);
if (ferror (inp_file))
error ("Cannot read `%s'", inp_fname);
if (size != long_names_size)
error ("%s: ARFILENAMES/ member is truncated", inp_fname);
/* Replace the newlines with nulls to make
processing a bit more convenient. */
for (i = 0; i < long_names_size; ++i)
if (long_names[i] == '\n')
long_names[i] = 0;
if (long_names[long_names_size-1] != 0)
error ("%s: ARFILENAMES/ member corrupt", inp_fname);
}
}
/* Ignore the __.SYMDEF and __.IMPORT members. */
else if (strcmp (ar.ar_name, "__.SYMDEF") != 0
&& strcmp (ar.ar_name, "__.IMPORT") != 0)
{
/* Process the current member. First, fetch the name of
the member. If the ar_name starts with a space, the
decimal number following that space is an offset into
the "ARFILENAMES/" member. The number may be
followed by a space and a substring of the long file
name. */
mod_name = ar.ar_name;
if (mod_name[0] == ' ' && long_names != NULL
&& (index = strtol (mod_name + 1, &end, 10)) >= 0
&& index < long_names_size - 1
&& (*end == 0 || *end == ' ')
&& (index == 0 || long_names[index-1] == 0))
mod_name = long_names + index;
process_aout (inp_file, size);
}
}
}
else
{
if (*(word *)ar_test == 0407)
{
if (fseek (inp_file, 0L, SEEK_END) != 0)
error ("Input file `%s' is not seekable", inp_fname);
size = ftell (inp_file);
fseek (inp_file, 0L, SEEK_SET);
process_aout (inp_file, size);
}
else if (*(byte *)ar_test == LIBHDR)
{
struct omflib *lib;
char errmsg[512], name[257];
int page;
lib = omflib_open (inp_fname, errmsg);
if (lib == NULL)
error ("%s: %s", inp_fname, errmsg);
n = omflib_module_count (lib, errmsg);
if (n == -1)
error ("%s: %s", inp_fname, errmsg);
for (i = 0; i < n; ++i)
{
if (omflib_module_info (lib, i, name, &page, errmsg) != 0)
error ("%s: %s", inp_fname, errmsg);
else
{
fseek (inp_file, omflib_page_pos (lib, page), SEEK_SET);
mod_name = name;
process_omf (inp_file);
}
}
if (omflib_close (lib, errmsg) != 0)
error ("%s: %s", inp_fname, errmsg);
}
else
{
fseek (inp_file, 0L, SEEK_SET);
process_omf (inp_file);
}
}
fclose (inp_file);
}
/* Main line. */
int main (int argc, char **argv)
{
int c, i;
char *p;
_response (&argc, &argv);
_wildcard (&argc, &argv);
opterr = 0;
optswchar = "-";
optind = 0;
while ((c = getopt (argc, argv, "no::u")) != EOF)
{
switch (c)
{
case 'n':
noname_flag = TRUE;
break;
case 'o':
if (optarg == NULL)
ordinal = 1;
else
{
errno = 0;
ordinal = (int)strtol (optarg, &p, 0);
if (p == optarg || errno != 0 || *p != 0
|| ordinal < 1 || ordinal > 65535)
usage ();
}
break;
case 'u':
bss_flag = TRUE;
break;
default:
error ("Invalid option");
}
}
out_file = stdout;
if (optind >= argc)
usage ();
for (i = optind; i < argc; ++i)
{
inp_fname = argv[i];
process ();
}
if (fflush (out_file) != 0 || (out_file != stdout && fclose (out_file) != 0))
error ("Write error");
return 0;
}