old-cross-binutils/binutils/dlltool.c

1017 lines
22 KiB
C
Raw Normal View History

/* dlltool.c -- tool to generate stuff for PE style DLLs
Copyright (C) 1995 Free Software Foundation, Inc.
This file is part of GNU Binutils.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
This program allows you to build the files necessary to create
DLLs to run on a system which understands PE format image files.
(eg, Windows NT)
A DLL contains an export table which contains the information
which the runtime loader needs to tie up references from a
referencing program.
The export table is generated by this program by reading
in a .DEF file or scanning the .a and .o files which will be in the
DLL. A .o file can contain information in special ".drective" sections
with export information.
A DEF file contains any number of the following commands:
NAME <name> [ , <base> ]
The result is going to be <name>.EXE
LIBRARY <name> [ , <base> ]
The result is going to be <name>.DLL
EXPORTS ( <name1> [ = <name2> ] [ @ <integer> ] [ NONAME ] [CONSTANT] ) *
Declares name1 as an exported symbol from the
DLL, with optional ordinal number <integer>
IMPORTS ( [ <name> = ] <name> . <name> ) *
Ignored for compatibility
DESCRIPTION <string>
Puts <string> into output .exp file in the .rdata section
[STACKSIZE|HEAPSIZE] <number-reserve> [ , <number-commit> ]
Generates --stack|--heap <number-reserve>,<number-commit>
in the output .drective section. The linker will
see this and act upon it.
[CODE|DATA] <attr>+
SECTIONS ( <sectionname> <attr>+ )*
<attr> = READ | WRITE | EXECUTE | SHARED
Generates --attr <sectionname> <attr> in the output
.drective section. The linker will see this and act
upon it.
A -export:<name> in a .drective section in an input .o or .a
file to this program is equivalent to a EXPORTS <name>
in a .DEF file.
The program generates output files with the prefix supplied
on the command line, or in the def file, or taken from the first
supplied argument.
The output files are <prefix>.exp.s and <prefix>.lib.s
The .exp.s file contains the information necessary to export
the routines in the DLL. The .lib.s file contains the information
necessary to use the DLL's routines from a referencing program.
Example:
file1.c:
asm (".section .drectve");
asm (".ascii \"-export:adef\"");
adef(char *s)
{
printf("hello from the dll %s\n",s);
}
bdef(char *s)
{
printf("hello from the dll and the other entry point %s\n",s);
}
file2.c:
asm (".section .drectve");
asm (".ascii \"-export:cdef\"");
asm (".ascii \"-export:ddef\"");
cdef(char *s)
{
printf("hello from the dll %s\n",s);
}
ddef(char *s)
{
printf("hello from the dll and the other entry point %s\n",s);
}
printf()
{
return 9;
}
main.c
main()
{
cdef();
}
thedll.def
LIBRARY thedll
HEAPSIZE 0x40000, 0x2000
EXPORTS bdef @ 20
cdef @ 30 NONAME
SECTIONS donkey READ WRITE
aardvark EXECUTE
# compile up the parts of the dll
gcc -c file1.c
gcc -c file2.c
# put them in a library (you don't have to, you
# could name all the .os on the dlltool line)
ar qcv thedll.in file1.o file2.o
ranlib thedll.in
# run this tool over the library and the def file
./dlltool -o thedll -d thedll.def thedll.in
# build the export table for the dll
as -o thedll.exp thedll.exp.s
# build the dll with the library with file1.o, file2.o and the export table
ld -o thedll.dll thedll.exp thedll.in
# build the import table for the executable
as -o thedll.lib thedll.lib.s
# build the mainline
gcc -c themain.c
# link the executable with the import library
ld -e main -Tthemain.ld -o themain.exe themain.o thedll.lib
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "getopt.h"
#include "bfd.h"
int yydebug;
char *def_file;
char *program_name;
char *strrchr ();
char *outfile_prefix;
char *xmalloc ();
char *strdup ();
static int machine;
#ifdef DLLTOOL_ARM
static char *mname = "arm";
#endif
#ifdef DLLTOOL_I386
static char *mname = "i386";
#endif
struct mac
{
char *type;
char *how_byte;
char *how_short;
char *how_long;
char *how_asciz;
char *how_comment;
char *how_jump;
char *how_global;
char *how_space;
} mtable[]
= {{"arm",".byte",".short",".long",".asciz","@","bl",".global",".space"},
{"i386",".byte",".short",".long",".asciz",";","jmp",".global",".space"},
0};
#define ASM_BYTE mtable[machine].how_byte
#define ASM_SHORT mtable[machine].how_short
#define ASM_LONG mtable[machine].how_long
#define ASM_TEXT mtable[machine].how_asciz
#define ASM_C mtable[machine].how_comment
#define ASM_JUMP mtable[machine].how_jump
#define ASM_GLOBAL mtable[machine].how_global
#define ASM_SPACE mtable[machine].how_space
#define PATHMAX 250 /* What's the right name for this ? */
static char **oav;
int i;
FILE *yyin; /* communications with flex */
extern int linenumber;
void
process_def_file (name)
char *name;
{
FILE *f = fopen (name, "r");
if (!f)
{
fprintf (stderr, "Can't open def file %s\n", name);
exit (1);
}
yyin = f;
yyparse ();
}
/**********************************************************************/
/* Communications with the parser */
typedef struct dlist
{
char *text;
struct dlist *next;
} dlist_type;
typedef struct export
{
char *name;
char *equal;
int ordinal;
int constant;
int noname;
struct export *next;
}
export_type;
static char *d_name; /* Arg to NAME or LIBRARY */
static int d_nfuncs; /* Number of functions exported */
static int d_ord; /* Base ordinal index */
static export_type *d_exports; /*list of exported functions */
static char *d_suffix = "dll";
static dlist_type *d_list; /* Descriptions */
static dlist_type *a_list; /* Stuff to go in directives */
static int d_is_dll;
static int d_is_exe;
yyerror ()
{
fprintf (stderr, "Syntax error in def file %s:%d\n",
def_file, linenumber);
}
void
def_exports (name, equal, ordinal, noname, constant)
char *name;
char *equal;
int ordinal;
int noname;
int constant;
{
struct export *p = (struct export *) xmalloc (sizeof (*p));
p->name = name;
p->equal = equal;
p->ordinal = ordinal;
p->constant = constant;
p->noname = noname;
p->next = d_exports;
d_exports = p;
d_nfuncs++;
printf ("EXPORTS %s", name);
if (equal)
printf ("=%s", equal);
if (ordinal > 0)
printf ("@%d", ordinal);
if (constant)
printf (" CONSTANT");
printf ("\n");
}
void
def_name (name, base)
char *name;
int base;
{
printf ("NAME %s base %x\n", name, base);
if (d_is_dll)
{
fprintf (stderr, "Can't have LIBRARY and NAME\n");
}
d_name = name;
if (strchr (d_name, '.'))
d_suffix = strdup (strchr (d_name, '.') + 1);
d_is_exe = 1;
}
void
def_library (name, base)
char *name;
int base;
{
printf ("LIBRARY %s base %x\n", name, base);
if (d_is_exe)
{
fprintf (stderr, "Can't have LIBRARY and NAME\n");
}
d_name = name;
if (strchr (d_name, '.'))
d_suffix = strdup (strchr (d_name, '.') + 1);
d_is_dll = 1;
}
void
def_description (desc)
char *desc;
{
dlist_type *d = (dlist_type *)xmalloc(sizeof(dlist_type));
d->text = strdup (desc);
d->next = d_list;
d_list = d;
}
void new_directive(dir)
char *dir;
{
dlist_type *d = (dlist_type *)xmalloc(sizeof(dlist_type));
d->text = strdup (dir);
d->next = a_list;
a_list = d;
}
void
def_stacksize (reserve, commit)
int reserve;
int commit;
{
char b[200];
if (commit>0)
sprintf (b,"-stack 0x%x,0x%x ", reserve, commit);
else
sprintf (b,"-stack 0x%x ", reserve);
new_directive (strdup(b));
}
void
def_heapsize (reserve, commit)
int reserve;
int commit;
{
char b[200];
if (commit>0)
sprintf (b,"-heap 0x%x,0x%x ", reserve, commit);
else
sprintf (b,"-heap 0x%x ", reserve);
new_directive (strdup(b));
}
void
def_import (internal, module, entry)
char *internal;
char *module;
char *entry;
{
fprintf (stderr, "IMPORTS are ignored");
}
void
def_version (major, minor)
{
printf ("VERSION %d.%d\n", major, minor);
}
void
def_section (name, attr)
char *name;
int attr;
{
char buf[200];
char atts[5];
char *d = atts;
if (attr & 1)
*d++= 'R';
if (attr & 2)
*d++ = 'W';
if (attr & 4)
*d++ = 'X';
if (attr & 8)
*d++ = 'S';
*d++ = 0;
sprintf (buf, "-attr %s %s", name, atts);
new_directive (strdup(buf));
}
void
def_code (attr)
int attr;
{
def_section ("CODE", attr);
}
void
def_data (attr)
int attr;
{
def_section ("DATA",attr);
}
/**********************************************************************/
void
scan_open_obj_file (abfd)
bfd *abfd;
{
/* Look for .drectives */
asection *s = bfd_get_section_by_name (abfd, ".drectve");
if (s)
{
int size = bfd_get_section_size_before_reloc (s);
char *buf = xmalloc (size);
char *p;
char *e;
bfd_get_section_contents (abfd, s, buf, 0, size);
printf ("Sucking in info from %s\n",
bfd_get_filename (abfd));
/* Search for -export: strings */
p = buf;
e = buf + size;
while (p < e)
{
if (p[0] == '-'
&& strncmp (p, "-export:", 8) == 0)
{
char *name;
char *c;
p += 8;
name = p;
while (*p != ' ' && *p != '-' && p < e)
p++;
c = xmalloc (p - name + 1);
memcpy (c, name, p - name);
c[p - name] = 0;
def_exports (c, 0, -1, 0);
}
else
p++;
}
free (buf);
}
}
void
scan_obj_file (filename)
char *filename;
{
bfd *f = bfd_openr (filename, 0);
if (!f)
{
fprintf (stderr, "Unable to open object file %s\n", filename);
exit (1);
}
if (bfd_check_format (f, bfd_archive))
{
bfd *arfile = bfd_openr_next_archived_file (f, 0);
while (arfile)
{
if (bfd_check_format (arfile, bfd_object))
scan_open_obj_file (arfile);
bfd_close (arfile);
arfile = bfd_openr_next_archived_file (f, arfile);
}
}
if (bfd_check_format (f, bfd_object))
{
scan_open_obj_file (f);
}
bfd_close (f);
}
/**********************************************************************/
/* return the bit of the name before the last . */
static
char *
prefix (name)
char *name;
{
char *res = strdup (name);
char *p = strrchr (res, '.');
if (p)
*p = 0;
return res;
}
void
dump_def_info (f)
FILE *f;
{
int i;
export_type *exp;
fprintf(f,"%s ", ASM_C);
for (i= 0; oav[i]; i++)
fprintf(f,"%s ", oav[i]);
fprintf(f,"\n");
for (i = 0, exp = d_exports; exp; i++, exp = exp->next)
{
fprintf (f, "%s %d %s @ %d %s%s\n",
ASM_C,
i,
exp->name, exp->ordinal,
exp->noname ? "NONAME " : "",
exp->constant ? "CONSTANT" : "");
}
}
/* Generate the .exp file */
void
gen_exp_file ()
{
FILE *f;
char outfile[PATHMAX];
int i;
export_type *exp;
dlist_type *dl;
sprintf (outfile, "%s.exp.s", outfile_prefix);
f = fopen (outfile, "w");
if (!f)
{
fprintf (stderr, "Unable to open output file %s\n", outfile);
exit (1);
}
dump_def_info (f);
fprintf (f, "\t.section .edata\n\n");
fprintf (f, "\t%s 0 %s Allways 0\n", ASM_LONG, ASM_C);
fprintf (f, "\t%s %d %s Time and date\n", ASM_LONG, time (0), ASM_C);
fprintf (f, "\t%s 0 %s Major and Minor version\n", ASM_LONG, ASM_C);
fprintf (f, "\t%s name %s Ptr to name of dll\n", ASM_LONG, ASM_C);
fprintf (f, "\t%s %d %s Starting ordinal of exports\n", ASM_LONG, d_ord, ASM_C);
fprintf (f, "\t%s The next field is documented as being the number of functions\n", ASM_C);
fprintf (f, "\t%s yet it doesn't look like that in real PE dlls\n", ASM_C);
fprintf (f, "\t%s But it shouldn't be a problem, causes there's\n", ASM_C);
fprintf (f, "\t%s always the number of names field\n", ASM_C);
fprintf (f, "\t%s %d %s Number of functions\n", ASM_LONG, d_nfuncs, ASM_C);
fprintf (f, "\t%s %d %s Number of names\n", ASM_LONG, d_nfuncs, ASM_C);
fprintf (f, "\t%s afuncs %s Address of functions\n", ASM_LONG, ASM_C);
fprintf (f, "\t%s anames %s Address of names\n", ASM_LONG, ASM_C);
fprintf (f, "\t%s anords %s Address of ordinals\n", ASM_LONG, ASM_C);
fprintf (f, "name: %s \"%s.%s\"\n", ASM_TEXT, outfile_prefix, d_suffix);
fprintf (f, "afuncs:\n");
i = d_ord;
for (exp = d_exports; exp; exp = exp->next)
{
if (exp->ordinal != i)
{
fprintf (f, "\t%s\t%d\t@ %d..%d missing\n", ASM_SPACE,
(exp->ordinal - i) * 4,
i, exp->ordinal - 1);
i = exp->ordinal;
}
fprintf (f, "\t%s %s\t%s %d\n", ASM_LONG, exp->name, ASM_C, exp->ordinal);
i++;
}
fprintf (f, "anames:\n");
for (i = 0, exp = d_exports; exp; i++, exp = exp->next)
if (exp->noname)
fprintf (f, "\t%s 0\t%sNoname\n", ASM_LONG, ASM_C);
else
fprintf (f, "\t%s n%d\n", ASM_LONG, i);
fprintf (f, "anords:\n");
for (exp = d_exports; exp; exp = exp->next)
fprintf (f, "\t%s %d\n", ASM_SHORT, exp->ordinal - d_ord);
for (i = 0, exp = d_exports; exp; i++, exp = exp->next)
if (exp->noname)
fprintf (f, "@n%d: %s \"%s\"\n", i, ASM_TEXT, exp->name);
else
fprintf (f, "n%d: %s \"%s\"\n", i, ASM_TEXT, exp->name);
if (a_list)
{
fprintf(f,"\t.section .drectve\n");
for (dl = a_list; dl; dl = dl->next)
{
fprintf (f,"\t%s\t\"%s\"\n", ASM_TEXT, dl->text);
}
}
if (d_list)
{
fprintf(f,"\t.section .rdata\n");
for (dl = d_list; dl; dl = dl->next)
{
char *p;
int l;
/* We dont output as ascii 'cause there can
be quote characters in the string */
l = 0;
for (p = dl->text; *p; p++) {
if (l == 0)
fprintf(f,"\t%s\t", ASM_BYTE);
else
fprintf(f,",");
fprintf(f,"%d", *p);
if (p[1] == 0) {
fprintf(f,",0\n");
break;
}
if (++l == 10) {
fprintf(f,"\n");
l = 0;
}
}
}
}
fclose (f);
}
/**********************************************************************/
gen_lib_file ()
{
char outfile[PATHMAX];
int i;
FILE *f;
export_type *exp;
sprintf (outfile, "%s.lib.s", outfile_prefix);
f = fopen (outfile, "w");
if (!f)
{
fprintf (stderr, "Unable to open output file %s\n", outfile);
exit (1);
}
dump_def_info (f);
fprintf (f, "\t.text\n");
fprintf (f, "%s Thunk table\n", ASM_C);
for (i = 0, exp = d_exports; exp; i++, exp = exp->next)
{
fprintf (f, "\t%s\t%s\n", ASM_GLOBAL, exp->name);
fprintf (f, "\t%s\t__imp__%s\n", ASM_GLOBAL, exp->name);
}
for (i = 0, exp = d_exports; exp; i++, exp = exp->next)
{
fprintf (f, "%s:\t%s\t__imp__%s\n", exp->name, ASM_JUMP, exp->name);
}
fprintf (f, "%s IMAGE_IMPORT_DESCRIPTOR\n", ASM_C);
fprintf (f, "\t.section .idata$2\n");
fprintf (f, "\t%s\thname\t%s Ptr to image import by name list\n", ASM_LONG, ASM_C);
fprintf (f, "\t%s\t%d\t%s time\n", ASM_LONG, time (0), ASM_C);
fprintf (f, "\t%s\t0\t%s Forwarder chain\n", ASM_LONG, ASM_C);
fprintf (f, "\t%s\tiname\t%s imported dll's name\n", ASM_LONG, ASM_C);
fprintf (f, "\t%s\tfthunk\t%s pointer to firstthunk\n", ASM_LONG, ASM_C);
fprintf (f, "\n%s Loader modifies this\n", ASM_C);
fprintf (f, "\t.section .idata$5\n");
fprintf (f, "fthunk:\n");
for (i = 0, exp = d_exports; exp; i++, exp = exp->next)
{
fprintf (f, "__imp__%s:\n", exp->name);
fprintf (f, "\t%s\tID%d\n", ASM_LONG, i);
}
fprintf (f, "\n%s Hint name array\n", ASM_C);
fprintf (f, "\t.section .idata$4\n");
fprintf (f, "hname:\n");
for (i = 0, exp = d_exports; exp; i++, exp = exp->next)
{
fprintf (f, "\t%s\tID%d\n", ASM_LONG, i);
}
fprintf (f, "%s Hint/name array storage and import dll name\n", ASM_C);
fprintf (f, "\t.section .idata$6\n");
for (i = 0, exp = d_exports; exp; i++, exp = exp->next)
{
fprintf (f, "ID%d:\t%s\t%d\n", i, ASM_SHORT, exp->ordinal);
fprintf (f, "\t%s\t\"%s\"\n", ASM_TEXT, exp->name);
}
fprintf (f, "iname:\t%s\t\"%s.%s\"\n", ASM_TEXT, outfile_prefix, d_suffix);
fclose (f);
}
/**********************************************************************/
/* Run through the information gathered from the .o files and the
.def file and work out the best stuff */
int
pfunc (a, b)
void *a;
void *b;
{
export_type *ap = *(export_type **) a;
export_type *bp = *(export_type **) b;
if (ap->ordinal == bp->ordinal)
return 0;
/* unset ordinals go to the bottom */
if (ap->ordinal == -1)
return 1;
if (bp->ordinal == -1)
return -1;
return (ap->ordinal - bp->ordinal);
}
int
nfunc (a, b)
void *a;
void *b;
{
export_type *ap = *(export_type **) a;
export_type *bp = *(export_type **) b;
return (strcmp (ap->name, bp->name));
}
static
void
remove_null_names (ptr)
export_type **ptr;
{
int src;
int dst;
for (dst = src = 0; src < d_nfuncs; src++)
{
if (ptr[src])
{
ptr[dst] = ptr[src];
dst++;
}
}
d_nfuncs = dst;
}
static void
dtab (ptr)
export_type **ptr;
{
#ifdef SACDEBUG
int i;
for (i = 0; i < d_nfuncs; i++)
{
if (ptr[i])
{
printf ("%d %s @ %d %s%s\n",
i, ptr[i]->name, ptr[i]->ordinal,
ptr[i]->noname ? "NONAME " : "",
ptr[i]->constant ? "CONSTANT" : "");
}
else
printf ("empty\n");
}
#endif
}
static void
process_duplicates (d_export_vec)
export_type **d_export_vec;
{
int more = 1;
while (more)
{
more = 0;
/* Remove duplicates */
qsort (d_export_vec, d_nfuncs, sizeof (export_type *), nfunc);
dtab (d_export_vec);
for (i = 0; i < d_nfuncs - 1; i++)
{
if (strcmp (d_export_vec[i]->name,
d_export_vec[i + 1]->name) == 0)
{
export_type *a = d_export_vec[i];
export_type *b = d_export_vec[i + 1];
more = 1;
fprintf (stderr, "warning, ignoring duplicate EXPORT %s\n",
a->name);
if (a->ordinal != -1
&& b->ordinal != -1)
{
fprintf (stderr, "Error, duplicate EXPORT with oridinals %s\n",
a->name);
exit (1);
}
/* Merge attributes */
b->ordinal = a->ordinal > 0 ? a->ordinal : b->ordinal;
b->constant |= a->constant;
b->noname |= a->noname;
d_export_vec[i] = 0;
}
dtab (d_export_vec);
remove_null_names (d_export_vec);
dtab (d_export_vec);
}
}
}
static void
fill_ordinals (d_export_vec)
export_type **d_export_vec;
{
int lowest = 0;
qsort (d_export_vec, d_nfuncs, sizeof (export_type *), pfunc);
/* fill in the unset ordinals with ones from the minimum */
for (i = 0; i < d_nfuncs; i++)
{
if (d_export_vec[i]->ordinal == -1)
{
d_export_vec[i]->ordinal = lowest++;
}
else
{
if (lowest == d_export_vec[i]->ordinal)
{
fprintf (stderr, "Warning, Duplicate ordinal %s @ %d\n",
d_export_vec[i]->name,
d_export_vec[i]->ordinal);
}
lowest = d_export_vec[i]->ordinal + 1;
}
}
/* Work out the lowest ordinal number */
if (d_export_vec[0])
d_ord = d_export_vec[0]->ordinal;
}
void
mangle_defs ()
{
/* First work out the minimum ordinal chosen */
export_type *exp;
int lowest = 0;
int i;
export_type **d_export_vec
= (export_type **) xmalloc (sizeof (export_type *) * d_nfuncs);
for (i = 0, exp = d_exports; exp; i++, exp = exp->next)
{
d_export_vec[i] = exp;
}
process_duplicates (d_export_vec);
fill_ordinals (d_export_vec);
/* Put back the list in the new order */
d_exports = 0;
for (i = d_nfuncs - 1; i >= 0; i--)
{
d_export_vec[i]->next = d_exports;
d_exports = d_export_vec[i];
}
}
/**********************************************************************/
void
usage (file, status)
FILE *file;
int status;
{
fprintf (file, "Usage %s [-m|--machine machine] [-o outprefix] [-d|--def deffile] [--def deffile]\n", program_name);
exit (status);
}
static struct option long_options[] =
{
{"def", required_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{"machine", required_argument, NULL, 'm'},
0
};
int
main (ac, av)
int ac;
char **av;
{
int c;
char *firstarg = 0;
program_name = av[0];
oav = av;
while ((c = getopt_long (ac, av, "h?m:o:Dd:", long_options, 0)) != EOF)
{
switch (c)
{
case 'h':
case '?':
usage(stderr,0);
break;
case 'm':
mname = optarg;
break;
case 'o':
outfile_prefix = optarg;
break;
case 'D':
yydebug = 1;
break;
case 'd':
def_file = optarg;
break;
default:
usage (stderr, 1);
}
}
for (i = 0; mtable[i].type; i++)
{
if (strcmp (mtable[i].type, mname) == 0)
break;
}
if (!mtable[i].type)
{
fprintf(stderr,"Machine not supported\n");
exit(1);
}
machine = i;
if (def_file)
{
process_def_file (def_file);
}
while (optind < ac)
{
if (!firstarg)
firstarg = av[optind];
scan_obj_file (av[optind]);
optind++;
}
if (!outfile_prefix)
{
if (d_name)
outfile_prefix = d_name;
else if (def_file)
outfile_prefix = def_file;
else if (firstarg)
outfile_prefix = firstarg;
else
{
fprintf (stderr, "No way to create an output filename\n");
exit (1);
}
}
outfile_prefix = prefix (outfile_prefix);
mangle_defs ();
gen_exp_file ();
gen_lib_file ();
return 0;
}