diff options
Diffstat (limited to 'binutils/dlltool.c')
-rw-r--r-- | binutils/dlltool.c | 3199 |
1 files changed, 3199 insertions, 0 deletions
diff --git a/binutils/dlltool.c b/binutils/dlltool.c new file mode 100644 index 00000000000..ed5cf5e3fcc --- /dev/null +++ b/binutils/dlltool.c @@ -0,0 +1,3199 @@ +/* dlltool.c -- tool to generate stuff for PE style DLLs + Copyright (C) 1995, 96, 97, 98, 1999 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., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, 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) + + See "Peering Inside the PE: A Tour of the Win32 Portable Executable + File Format", MSJ 1994, Volume 9 for more information. + Also see "Microsoft Portable Executable and Common Object File Format, + Specification 4.1" for more information. + + 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 ".drectve" 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] [DATA] ) * + Declares name1 as an exported symbol from the + DLL, with optional ordinal number <integer> + + IMPORTS ( ( <internal-name> = <module-name> . <integer> ) + | ( [ <internal-name> = ] <module-name> . <external-name> )) * + Declares that <external-name> or the exported function whoes ordinal number + is <integer> is to be imported from the file <module-name>. If + <internal-name> is specified then this is the name that the imported + function will be refered to in the body of the DLL. + + 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 .drectve 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 + .drectve section. The linker will see this and act + upon it. + + + A -export:<name> in a .drectve 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 .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\""); + + void adef (char * s) + { + printf ("hello from the dll %s\n", s); + } + + void 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\""); + + void cdef (char * s) + { + printf ("hello from the dll %s\n", s); + } + + void ddef (char * s) + { + printf ("hello from the dll and the other entry point %s\n", s); + } + + printf() + { + return 9; + } + + main.c + + void 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 --def thedll.def --output-exp thedll.o --output-lib thedll.a + + # build the dll with the library with file1.o, file2.o and the export table + ld -o thedll.dll thedll.o thedll.in + + # 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.a + + */ + +/* .idata section description + + The .idata section is the import table. It is a collection of several + subsections used to keep the pieces for each dll together: .idata$[234567]. + IE: Each dll's .idata$2's are catenated together, each .idata$3's, etc. + + .idata$2 = Import Directory Table + = array of IMAGE_IMPORT_DESCRIPTOR's. + + DWORD Import Lookup Table; - pointer to .idata$4 + DWORD TimeDateStamp; - currently always 0 + DWORD ForwarderChain; - currently always 0 + DWORD Name; - pointer to dll's name + PIMAGE_THUNK_DATA FirstThunk; - pointer to .idata$5 + + .idata$3 = null terminating entry for .idata$2. + + .idata$4 = Import Lookup Table + = array of array of pointers to hint name table. + There is one for each dll being imported from, and each dll's set is + terminated by a trailing NULL. + + .idata$5 = Import Address Table + = array of array of pointers to hint name table. + There is one for each dll being imported from, and each dll's set is + terminated by a trailing NULL. + Initially, this table is identical to the Import Lookup Table. However, + at load time, the loader overwrites the entries with the address of the + function. + + .idata$6 = Hint Name Table + = Array of { short, asciz } entries, one for each imported function. + The `short' is the function's ordinal number. + + .idata$7 = dll name (eg: "kernel32.dll"). (.idata$6 for ppc) +*/ + +/* AIX requires this to be the first thing in the file. */ +#ifndef __GNUC__ +# ifdef _AIX + #pragma alloca +#endif +#endif + +#define show_allnames 0 + +#define PAGE_SIZE 4096 +#define PAGE_MASK (-PAGE_SIZE) +#include "bfd.h" +#include "libiberty.h" +#include "bucomm.h" +#include "getopt.h" +#include "demangle.h" +#include "dlltool.h" + +#include <ctype.h> +#include <time.h> +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#ifdef DLLTOOL_ARM +#include "coff/arm.h" +#include "coff/internal.h" +#endif + +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#else /* ! HAVE_SYS_WAIT_H */ +#if ! defined (_WIN32) || defined (__CYGWIN32__) +#ifndef WIFEXITED +#define WIFEXITED(w) (((w)&0377) == 0) +#endif +#ifndef WIFSIGNALED +#define WIFSIGNALED(w) (((w)&0377) != 0177 && ((w)&~0377) == 0) +#endif +#ifndef WTERMSIG +#define WTERMSIG(w) ((w) & 0177) +#endif +#ifndef WEXITSTATUS +#define WEXITSTATUS(w) (((w) >> 8) & 0377) +#endif +#else /* defined (_WIN32) && ! defined (__CYGWIN32__) */ +#ifndef WIFEXITED +#define WIFEXITED(w) (((w) & 0xff) == 0) +#endif +#ifndef WIFSIGNALED +#define WIFSIGNALED(w) (((w) & 0xff) != 0 && ((w) & 0xff) != 0x7f) +#endif +#ifndef WTERMSIG +#define WTERMSIG(w) ((w) & 0x7f) +#endif +#ifndef WEXITSTATUS +#define WEXITSTATUS(w) (((w) & 0xff00) >> 8) +#endif +#endif /* defined (_WIN32) && ! defined (__CYGWIN32__) */ +#endif /* ! HAVE_SYS_WAIT_H */ + +/* ifunc and ihead data structures: ttk@cygnus.com 1997 + + When IMPORT declarations are encountered in a .def file the + function import information is stored in a structure referenced by + the global variable IMPORT_LIST. The structure is a linked list + containing the names of the dll files each function is imported + from and a linked list of functions being imported from that dll + file. This roughly parallels the structure of the .idata section + in the PE object file. + + The contents of .def file are interpreted from within the + process_def_file function. Every time an IMPORT declaration is + encountered, it is broken up into its component parts and passed to + def_import. IMPORT_LIST is initialized to NULL in function main. */ + +typedef struct ifunct +{ + char *name; /* name of function being imported */ + int ord; /* two-byte ordinal value associated with function */ + struct ifunct *next; +} ifunctype; + +typedef struct iheadt +{ + char *dllname; /* name of dll file imported from */ + long nfuncs; /* number of functions in list */ + struct ifunct *funchead; /* first function in list */ + struct ifunct *functail; /* last function in list */ + struct iheadt *next; /* next dll file in list */ +} iheadtype; + +/* Structure containing all import information as defined in .def file + (qv "ihead structure"). */ + +static iheadtype *import_list = NULL; + +static char *as_name = "as"; +static char * as_flags = ""; + +static int no_idata4; +static int no_idata5; +static char *exp_name; +static char *imp_name; +static char *head_label; +static char *imp_name_lab; +static char *dll_name; + +static int add_indirect = 0; +static int add_underscore = 0; +static int dontdeltemps = 0; + +#ifdef DLLTOOL_ARM +static int interwork = 0; +#endif + +/* True if we should export all symbols. Otherwise, we only export + symbols listed in .drectve sections or in the def file. */ +static boolean export_all_symbols; + +/* True if we should exclude the symbols in DEFAULT_EXCLUDES when + exporting all symbols. */ +static boolean do_default_excludes; + +/* Default symbols to exclude when exporting all the symbols. */ +static const char *default_excludes = "DllMain@12,DllEntryPoint@0,impure_ptr"; + +static char *def_file; + +extern char * program_name; + +static int machine; +static int killat; +static int add_stdcall_alias; +static int verbose; +static FILE *output_def; +static FILE *base_file; + +#ifdef DLLTOOL_BEOS +static const char *mname = "beos"; +#endif + +#ifdef DLLTOOL_ARM +static const char *mname = "arm"; +#endif + +#ifdef DLLTOOL_I386 +static const char *mname = "i386"; +#endif + +#ifdef DLLTOOL_PPC +static const char *mname = "ppc"; +#endif + +#define PATHMAX 250 /* What's the right name for this ? */ + +#define TMP_ASM "dc.s" +#define TMP_HEAD_S "dh.s" +#define TMP_HEAD_O "dh.o" +#define TMP_TAIL_S "dt.s" +#define TMP_TAIL_O "dt.o" +#define TMP_STUB "ds" + +/* This bit of assemly does jmp * .... +s set how_jtab_roff to mark where the 32bit abs branch should go */ +static const unsigned char i386_jtab[] = +{ + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, 0x90, 0x90 +}; + +static const unsigned char arm_jtab[] = +{ + 0x00, 0xc0, 0x9f, 0xe5, + 0x00, 0xf0, 0x9c, 0xe5, + 0, 0, 0, 0 +}; + +static const unsigned char thumb_jtab[] = +{ + 0xc0, 0xb4, + 0x02, 0x4e, + 0x36, 0x68, + 0x01, 0x96, + 0x40, 0xbd, + 0xc0, 0x46, + 0, 0, 0, 0 +}; + +/* This is the glue sequence for PowerPC PE. There is a */ +/* tocrel16-tocdefn reloc against the first instruction. */ +/* We also need a IMGLUE reloc against the glue function */ +/* to restore the toc saved by the third instruction in */ +/* the glue. */ +static const unsigned char ppc_jtab[] = +{ + 0x00, 0x00, 0x62, 0x81, /* lwz r11,0(r2) */ + /* Reloc TOCREL16 __imp_xxx */ + 0x00, 0x00, 0x8B, 0x81, /* lwz r12,0(r11) */ + 0x04, 0x00, 0x41, 0x90, /* stw r2,4(r1) */ + 0xA6, 0x03, 0x89, 0x7D, /* mtctr r12 */ + 0x04, 0x00, 0x4B, 0x80, /* lwz r2,4(r11) */ + 0x20, 0x04, 0x80, 0x4E /* bctr */ +}; + +#ifdef DLLTOOL_PPC +/* the glue instruction, picks up the toc from the stw in */ +/* the above code: "lwz r2,4(r1)" */ +static bfd_vma ppc_glue_insn = 0x80410004; +#endif + +/* The outfile array must be big enough to contain a fully + qualified path name, plus an arbitary series of command + line switches. We hope that PATH_MAX times two will be + enough. */ +static char outfile [PATHMAX * 2]; + +struct mac + { + const char *type; + const char *how_byte; + const char *how_short; + const char *how_long; + const char *how_asciz; + const char *how_comment; + const char *how_jump; + const char *how_global; + const char *how_space; + const char *how_align_short; + const char *how_align_long; + const char *how_bfd_target; + enum bfd_architecture how_bfd_arch; + const unsigned char *how_jtab; + int how_jtab_size; /* size of the jtab entry */ + int how_jtab_roff; /* offset into it for the ind 32 reloc into idata 5 */ + }; + +static const struct mac +mtable[] = +{ + { +#define MARM 0 + "arm", ".byte", ".short", ".long", ".asciz", "@", + "ldr\tip,[pc]\n\tldr\tpc,[ip]\n\t.long", + ".global", ".space", ".align\t2",".align\t4","pe-arm-little", bfd_arch_arm, + arm_jtab, sizeof (arm_jtab), 8 + } + , + { +#define M386 1 + "i386", ".byte", ".short", ".long", ".asciz", "#", "jmp *", ".global", ".space", ".align\t2",".align\t4","pe-i386",bfd_arch_i386, + i386_jtab, sizeof (i386_jtab), 2 + } + , + { +#define MPPC 2 + "ppc", ".byte", ".short", ".long", ".asciz", "#", "jmp *", ".global", ".space", ".align\t2",".align\t4","pe-powerpcle",bfd_arch_powerpc, + ppc_jtab, sizeof (ppc_jtab), 0 + } + , + { +#define MTHUMB 3 + "thumb", ".byte", ".short", ".long", ".asciz", "@", + "push\t{r6, r7}\n\tldr\tr6, [pc, #8]\n\tldr\tr6, [r6]\n\tstr\tr6, [sp, #4]\n\tpop\t{r6, pc}\n\tnop", + ".global", ".space", ".align\t2",".align\t4","pe-arm-little", bfd_arch_arm, + thumb_jtab, sizeof (thumb_jtab), 12 + } + , +{ 0} +}; + +typedef struct dlist +{ + char *text; + struct dlist *next; +} +dlist_type; + +typedef struct export + { + const char *name; + const char *internal_name; + int ordinal; + int constant; + int noname; + int data; + int hint; + struct export *next; + } +export_type; + +/* A list of symbols which we should not export. */ + +struct string_list +{ + struct string_list *next; + char *string; +}; + +static struct string_list *excludes; + +static const char *rvaafter PARAMS ((int)); +static const char *rvabefore PARAMS ((int)); +static const char *asm_prefix PARAMS ((int)); +static void append_import PARAMS ((const char *, const char *, int)); +static void run PARAMS ((const char *, char *)); +static void scan_drectve_symbols PARAMS ((bfd *)); +static void scan_filtered_symbols PARAMS ((bfd *, PTR, long, unsigned int)); +static void add_excludes PARAMS ((const char *)); +static boolean match_exclude PARAMS ((const char *)); +static void set_default_excludes PARAMS ((void)); +static long filter_symbols PARAMS ((bfd *, PTR, long, unsigned int)); +static void scan_all_symbols PARAMS ((bfd *)); +static void scan_open_obj_file PARAMS ((bfd *)); +static void scan_obj_file PARAMS ((const char *)); +static void dump_def_info PARAMS ((FILE *)); +static int sfunc PARAMS ((const void *, const void *)); +static void flush_page PARAMS ((FILE *, long *, int, int)); +static void gen_def_file PARAMS ((void)); +static void generate_idata_ofile PARAMS ((FILE *)); +static void gen_exp_file PARAMS ((void)); +static const char *xlate PARAMS ((const char *)); +#if 0 +static void dump_iat PARAMS ((FILE *, export_type *)); +#endif +static char *make_label PARAMS ((const char *, const char *)); +static bfd *make_one_lib_file PARAMS ((export_type *, int)); +static bfd *make_head PARAMS ((void)); +static bfd *make_tail PARAMS ((void)); +static void gen_lib_file PARAMS ((void)); +static int pfunc PARAMS ((const void *, const void *)); +static int nfunc PARAMS ((const void *, const void *)); +static void remove_null_names PARAMS ((export_type **)); +static void dtab PARAMS ((export_type **)); +static void process_duplicates PARAMS ((export_type **)); +static void fill_ordinals PARAMS ((export_type **)); +static int alphafunc PARAMS ((const void *, const void *)); +static void mangle_defs PARAMS ((void)); +static void usage PARAMS ((FILE *, int)); +static void display PARAMS ((const char *, va_list)); +static void inform PARAMS ((const char *, ...)); +static void warn PARAMS ((const char *, ...)); + +static void +display (message, args) + const char * message; + va_list args; +{ + if (program_name != NULL) + fprintf (stderr, "%s: ", program_name); + + vfprintf (stderr, message, args); + + if (message [strlen (message) - 1] != '\n') + fputc ('\n', stderr); +} + + +static void +#ifdef __STDC__ +inform (const char * message, ...) +#else +inform (message, va_alist) + const char * message; + va_dcl +#endif +{ + va_list args; + + if (!verbose) + return; + +#ifdef __STDC__ + va_start (args, message); +#else + va_start (args); +#endif + + display (message, args); + + va_end (args); +} + +static void +#ifdef __STDC__ +warn (const char * message, ...) +#else +warn (message, va_alist) + const char * message; + va_dcl +#endif +{ + va_list args; + +#ifdef __STDC__ + va_start (args, message); +#else + va_start (args); +#endif + + display (message, args); + + va_end (args); +} + +static const char * +rvaafter (machine) + int machine; +{ + switch (machine) + { + case MARM: + case M386: + case MPPC: + case MTHUMB: + break; + default: + /* xgettext:c-format */ + fatal (_("Internal error: Unknown machine type: %d\n"), machine); + break; + } + return ""; +} + +static const char * +rvabefore (machine) + int machine; +{ + switch (machine) + { + case MARM: + case M386: + case MPPC: + case MTHUMB: + return ".rva\t"; + default: + /* xgettext:c-format */ + fatal (_("Internal error: Unknown machine type: %d\n"), machine); + break; + } + return ""; +} + +static const char * +asm_prefix (machine) + int machine; +{ + switch (machine) + { + case MARM: + case MPPC: + case MTHUMB: + break; + case M386: + return "_"; + default: + /* xgettext:c-format */ + fatal (_("Internal error: Unknown machine type: %d\n"), machine); + break; + } + return ""; +} + +#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 ASM_ALIGN_SHORT mtable[machine].how_align_short +#define ASM_RVA_BEFORE rvabefore(machine) +#define ASM_RVA_AFTER rvaafter(machine) +#define ASM_PREFIX asm_prefix(machine) +#define ASM_ALIGN_LONG mtable[machine].how_align_long +#define HOW_BFD_TARGET 0 /* always default*/ +#define HOW_BFD_ARCH mtable[machine].how_bfd_arch +#define HOW_JTAB mtable[machine].how_jtab +#define HOW_JTAB_SIZE mtable[machine].how_jtab_size +#define HOW_JTAB_ROFF mtable[machine].how_jtab_roff +static char **oav; + +void +process_def_file (name) + const char *name; +{ + FILE *f = fopen (name, FOPEN_RT); + + if (!f) + /* xgettext:c-format */ + fatal (_("Can't open def file: %s"), name); + + yyin = f; + + /* xgettext:c-format */ + inform (_("Processing def file: %s"), name); + + yyparse (); + + inform (_("Processed def file")); +} + +/**********************************************************************/ + +/* Communications with the parser */ + +static const char *d_name; /* Arg to NAME or LIBRARY */ +static int d_nfuncs; /* Number of functions exported */ +static int d_named_nfuncs; /* Number of named functions exported */ +static int d_low_ord; /* Lowest ordinal index */ +static int d_high_ord; /* Highest ordinal index */ +static export_type *d_exports; /*list of exported functions */ +static export_type **d_exports_lexically; /* vector of exported functions in alpha order */ +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; + +int +yyerror (err) + const char *err; +{ + /* xgettext:c-format */ + warn (_("Syntax error in def file %s:%d\n"), def_file, linenumber); + + return 0; +} + +void +def_exports (name, internal_name, ordinal, noname, constant, data) + const char *name; + const char *internal_name; + int ordinal; + int noname; + int constant; + int data; +{ + struct export *p = (struct export *) xmalloc (sizeof (*p)); + + p->name = name; + p->internal_name = internal_name ? internal_name : name; + p->ordinal = ordinal; + p->constant = constant; + p->noname = noname; + p->data = data; + p->next = d_exports; + d_exports = p; + d_nfuncs++; +} + +void +def_name (name, base) + const char *name; + int base; +{ + /* xgettext:c-format */ + inform (_("NAME: %s base: %x"), name, base); + + if (d_is_dll) + warn (_("Can't have LIBRARY and NAME\n")); + + d_name = name; + /* if --dllname not provided, use the one in the DEF file. + FIXME: Is this appropriate for executables? */ + if (! dll_name) + dll_name = xstrdup (name); + d_is_exe = 1; +} + +void +def_library (name, base) + const char *name; + int base; +{ + /* xgettext:c-format */ + inform (_("LIBRARY: %s base: %x"), name, base); + + if (d_is_exe) + warn (_("%s: Can't have LIBRARY and NAME\n"), program_name); + + d_name = name; + /* if --dllname not provided, use the one in the DEF file. */ + if (! dll_name) + dll_name = xstrdup (name); + d_is_dll = 1; +} + +void +def_description (desc) + const char *desc; +{ + dlist_type *d = (dlist_type *) xmalloc (sizeof (dlist_type)); + d->text = xstrdup (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 = xstrdup (dir); + d->next = a_list; + a_list = d; +} + +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 (xstrdup (b)); +} + +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 (xstrdup (b)); +} + +/* append_import simply adds the given import definition to the global + import_list. It is used by def_import. */ + +static void +append_import (symbol_name, dll_name, func_ordinal) + const char *symbol_name; + const char *dll_name; + int func_ordinal; +{ + iheadtype **pq; + iheadtype *q; + + for (pq = &import_list; *pq != NULL; pq = &(*pq)->next) + { + if (strcmp ((*pq)->dllname, dll_name) == 0) + { + q = *pq; + q->functail->next = xmalloc (sizeof (ifunctype)); + q->functail = q->functail->next; + q->functail->ord = func_ordinal; + q->functail->name = xstrdup (symbol_name); + q->functail->next = NULL; + q->nfuncs++; + return; + } + } + + q = xmalloc (sizeof (iheadtype)); + q->dllname = xstrdup (dll_name); + q->nfuncs = 1; + q->funchead = xmalloc (sizeof (ifunctype)); + q->functail = q->funchead; + q->next = NULL; + q->functail->name = xstrdup (symbol_name); + q->functail->ord = func_ordinal; + q->functail->next = NULL; + + *pq = q; +} + +/* def_import is called from within defparse.y when an IMPORT + declaration is encountered. Depending on the form of the + declaration, the module name may or may not need ".dll" to be + appended to it, the name of the function may be stored in internal + or entry, and there may or may not be an ordinal value associated + with it. */ + +/* A note regarding the parse modes: + In defparse.y we have to accept import declarations which follow + any one of the following forms: + <func_name_in_app> = <dll_name>.<func_name_in_dll> + <func_name_in_app> = <dll_name>.<number> + <dll_name>.<func_name_in_dll> + <dll_name>.<number> + Furthermore, the dll's name may or may not end with ".dll", which + complicates the parsing a little. Normally the dll's name is + passed to def_import() in the "module" parameter, but when it ends + with ".dll" it gets passed in "module" sans ".dll" and that needs + to be reappended. + + def_import gets five parameters: + APP_NAME - the name of the function in the application, if + present, or NULL if not present. + MODULE - the name of the dll, possibly sans extension (ie, '.dll'). + DLLEXT - the extension of the dll, if present, NULL if not present. + ENTRY - the name of the function in the dll, if present, or NULL. + ORD_VAL - the numerical tag of the function in the dll, if present, + or NULL. Exactly one of <entry> or <ord_val> must be + present (i.e., not NULL). */ + +void +def_import (app_name, module, dllext, entry, ord_val) + const char *app_name; + const char *module; + const char *dllext; + const char *entry; + int ord_val; +{ + const char *application_name; + char *buf; + + if (entry != NULL) + application_name = entry; + else + { + if (app_name != NULL) + application_name = app_name; + else + application_name = ""; + } + + if (dllext != NULL) + { + buf = (char *) alloca (strlen (module) + strlen (dllext) + 2); + sprintf (buf, "%s.%s", module, dllext); + module = buf; + } + + append_import (application_name, module, ord_val); +} + +void +def_version (major, minor) + int major; + int minor; +{ + printf ("VERSION %d.%d\n", major, minor); +} + +void +def_section (name, attr) + const 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 (xstrdup (buf)); +} + +void +def_code (attr) + int attr; +{ + + def_section ("CODE", attr); +} + +void +def_data (attr) + int attr; +{ + def_section ("DATA", attr); +} + +/**********************************************************************/ + +static void +run (what, args) + const char *what; + char *args; +{ + char *s; + int pid, wait_status; + int i; + const char **argv; + char *errmsg_fmt, *errmsg_arg; + char *temp_base = choose_temp_base (); + + inform ("run: %s %s\n", what, args); + + /* Count the args */ + i = 0; + for (s = args; *s; s++) + if (*s == ' ') + i++; + i++; + argv = alloca (sizeof (char *) * (i + 3)); + i = 0; + argv[i++] = what; + s = args; + while (1) + { + while (*s == ' ') + ++s; + argv[i++] = s; + while (*s != ' ' && *s != 0) + s++; + if (*s == 0) + break; + *s++ = 0; + } + argv[i++] = NULL; + + pid = pexecute (argv[0], (char * const *) argv, program_name, temp_base, + &errmsg_fmt, &errmsg_arg, PEXECUTE_ONE | PEXECUTE_SEARCH); + + if (pid == -1) + { + inform (strerror (errno)); + + fatal (errmsg_fmt, errmsg_arg); + } + + pid = pwait (pid, & wait_status, 0); + + if (pid == -1) + { + /* xgettext:c-format */ + fatal (_("wait: %s"), strerror (errno)); + } + else if (WIFSIGNALED (wait_status)) + { + /* xgettext:c-format */ + fatal (_("subprocess got fatal signal %d"), WTERMSIG (wait_status)); + } + else if (WIFEXITED (wait_status)) + { + if (WEXITSTATUS (wait_status) != 0) + /* xgettext:c-format */ + warn (_("%s exited with status %d\n"), + what, WEXITSTATUS (wait_status)); + } + else + abort (); +} + +/* Look for a list of symbols to export in the .drectve section of + ABFD. Pass each one to def_exports. */ + +static void +scan_drectve_symbols (abfd) + bfd *abfd; +{ + asection * s; + int size; + char * buf; + char * p; + char * e; + + /* Look for .drectve's */ + s = bfd_get_section_by_name (abfd, ".drectve"); + + if (s == NULL) + return; + + size = bfd_get_section_size_before_reloc (s); + buf = xmalloc (size); + + bfd_get_section_contents (abfd, s, buf, 0, size); + + /* xgettext:c-format */ + inform (_("Sucking in info from .drective section in %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 < e && *p != ' ' && *p != '-') + p++; + c = xmalloc (p - name + 1); + memcpy (c, name, p - name); + c[p - name] = 0; + + /* FIXME: The 5th arg is for the `constant' field. + What should it be? Not that it matters since it's not + currently useful. */ + def_exports (c, 0, -1, 0, 0, 0); + + if (add_stdcall_alias && strchr (c, '@')) + { + char *exported_name = xstrdup (c); + char *atsym = strchr (exported_name, '@'); + *atsym = '\0'; + def_exports (exported_name, xstrdup (c), -1, 0, 0, 0); + } + } + else + p++; + } + free (buf); +} + +/* Look through the symbols in MINISYMS, and add each one to list of + symbols to export. */ + +static void +scan_filtered_symbols (abfd, minisyms, symcount, size) + bfd *abfd; + PTR minisyms; + long symcount; + unsigned int size; +{ + asymbol *store; + bfd_byte *from, *fromend; + + store = bfd_make_empty_symbol (abfd); + if (store == NULL) + bfd_fatal (bfd_get_filename (abfd)); + + from = (bfd_byte *) minisyms; + fromend = from + symcount * size; + for (; from < fromend; from += size) + { + asymbol *sym; + const char *symbol_name; + + sym = bfd_minisymbol_to_symbol (abfd, false, from, store); + if (sym == NULL) + bfd_fatal (bfd_get_filename (abfd)); + + symbol_name = bfd_asymbol_name (sym); + if (bfd_get_symbol_leading_char (abfd) == symbol_name[0]) + ++symbol_name; + + def_exports (xstrdup (symbol_name) , 0, -1, 0, 0, 0); + + if (add_stdcall_alias && strchr (symbol_name, '@')) + { + char *exported_name = xstrdup (symbol_name); + char *atsym = strchr (exported_name, '@'); + *atsym = '\0'; + def_exports (exported_name, xstrdup (symbol_name), -1, 0, 0, 0); + } + } +} + +/* Add a list of symbols to exclude. */ + +static void +add_excludes (new_excludes) + const char *new_excludes; +{ + char *local_copy; + char *exclude_string; + + local_copy = xstrdup (new_excludes); + + exclude_string = strtok (local_copy, ",:"); + for (; exclude_string; exclude_string = strtok (NULL, ",:")) + { + struct string_list *new_exclude; + + new_exclude = ((struct string_list *) + xmalloc (sizeof (struct string_list))); + new_exclude->string = (char *) xmalloc (strlen (exclude_string) + 2); + /* FIXME: Is it always right to add a leading underscore? */ + sprintf (new_exclude->string, "_%s", exclude_string); + new_exclude->next = excludes; + excludes = new_exclude; + + /* xgettext:c-format */ + inform (_("Excluding symbol: %s\n"), exclude_string); + } + + free (local_copy); +} + +/* See if STRING is on the list of symbols to exclude. */ + +static boolean +match_exclude (string) + const char *string; +{ + struct string_list *excl_item; + + for (excl_item = excludes; excl_item; excl_item = excl_item->next) + if (strcmp (string, excl_item->string) == 0) + return true; + return false; +} + +/* Add the default list of symbols to exclude. */ + +static void +set_default_excludes (void) +{ + add_excludes (default_excludes); +} + +/* Choose which symbols to export. */ + +static long +filter_symbols (abfd, minisyms, symcount, size) + bfd *abfd; + PTR minisyms; + long symcount; + unsigned int size; +{ + bfd_byte *from, *fromend, *to; + asymbol *store; + + store = bfd_make_empty_symbol (abfd); + if (store == NULL) + bfd_fatal (bfd_get_filename (abfd)); + + from = (bfd_byte *) minisyms; + fromend = from + symcount * size; + to = (bfd_byte *) minisyms; + + for (; from < fromend; from += size) + { + int keep = 0; + asymbol *sym; + + sym = bfd_minisymbol_to_symbol (abfd, false, (const PTR) from, store); + if (sym == NULL) + bfd_fatal (bfd_get_filename (abfd)); + + /* Check for external and defined only symbols. */ + keep = (((sym->flags & BSF_GLOBAL) != 0 + || (sym->flags & BSF_WEAK) != 0 + || bfd_is_com_section (sym->section)) + && ! bfd_is_und_section (sym->section)); + + keep = keep && ! match_exclude (sym->name); + + if (keep) + { + memcpy (to, from, size); + to += size; + } + } + + return (to - (bfd_byte *) minisyms) / size; +} + +/* Export all symbols in ABFD, except for ones we were told not to + export. */ + +static void +scan_all_symbols (abfd) + bfd *abfd; +{ + long symcount; + PTR minisyms; + unsigned int size; + + /* Ignore bfds with an import descriptor table. We assume that any + such BFD contains symbols which are exported from another DLL, + and we don't want to reexport them from here. */ + if (bfd_get_section_by_name (abfd, ".idata$4")) + return; + + if (! (bfd_get_file_flags (abfd) & HAS_SYMS)) + { + /* xgettext:c-format */ + warn (_("%s: no symbols\n"), bfd_get_filename (abfd)); + return; + } + + symcount = bfd_read_minisymbols (abfd, false, &minisyms, &size); + if (symcount < 0) + bfd_fatal (bfd_get_filename (abfd)); + + if (symcount == 0) + { + /* xgettext:c-format */ + warn (_("%s: no symbols\n"), bfd_get_filename (abfd)); + return; + } + + /* Discard the symbols we don't want to export. It's OK to do this + in place; we'll free the storage anyway. */ + + symcount = filter_symbols (abfd, minisyms, symcount, size); + scan_filtered_symbols (abfd, minisyms, symcount, size); + + free (minisyms); +} + +/* Look at the object file to decide which symbols to export. */ + +static void +scan_open_obj_file (abfd) + bfd *abfd; +{ + if (export_all_symbols) + scan_all_symbols (abfd); + else + scan_drectve_symbols (abfd); + + /* FIXME: we ought to read in and block out the base relocations */ + + /* xgettext:c-format */ + inform (_("%s: Done reading %s\n"), bfd_get_filename (abfd)); +} + +static void +scan_obj_file (filename) + const char *filename; +{ + bfd * f = bfd_openr (filename, 0); + + if (!f) + /* xgettext:c-format */ + fatal (_("Unable to open object file: %s"), filename); + + /* xgettext:c-format */ + inform (_("Scanning object file %s"), filename); + + 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); + } + } + else if (bfd_check_format (f, bfd_object)) + { + scan_open_obj_file (f); + } + + bfd_close (f); +} + +/**********************************************************************/ + +static 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 %s @ %d %s%s%s\n", + ASM_C, + i, + exp->name, + exp->internal_name, + exp->ordinal, + exp->noname ? "NONAME " : "", + exp->constant ? "CONSTANT" : "", + exp->data ? "DATA" : ""); + } +} + +/* Generate the .exp file */ + +static int +sfunc (a, b) + const void *a; + const void *b; +{ + return *(const long *) a - *(const long *) b; +} + +static void +flush_page (f, need, page_addr, on_page) + FILE *f; + long *need; + int page_addr; + int on_page; +{ + int i; + + /* Flush this page */ + fprintf (f, "\t%s\t0x%08x\t%s Starting RVA for chunk\n", + ASM_LONG, + page_addr, + ASM_C); + fprintf (f, "\t%s\t0x%x\t%s Size of block\n", + ASM_LONG, + (on_page * 2) + (on_page & 1) * 2 + 8, + ASM_C); + for (i = 0; i < on_page; i++) + { + fprintf (f, "\t%s\t0x%lx\n", ASM_SHORT, (need[i] - page_addr) | 0x3000); + } + /* And padding */ + if (on_page & 1) + fprintf (f, "\t%s\t0x%x\n", ASM_SHORT, 0 | 0x0000); +} + +static void +gen_def_file () +{ + int i; + export_type *exp; + + inform (_("Adding exports to output file")); + + fprintf (output_def, ";"); + for (i = 0; oav[i]; i++) + fprintf (output_def, " %s", oav[i]); + + fprintf (output_def, "\nEXPORTS\n"); + + for (i = 0, exp = d_exports; exp; i++, exp = exp->next) + { + char *quote = strchr (exp->name, '.') ? "\"" : ""; + char *res = cplus_demangle (exp->internal_name, DMGL_ANSI | DMGL_PARAMS); + + if (strcmp (exp->name, exp->internal_name) == 0) + { + + fprintf (output_def, "\t%s%s%s @ %d%s%s ; %s\n", + quote, + exp->name, + quote, + exp->ordinal, + exp->noname ? " NONAME" : "", + exp->data ? " DATA" : "", + res ? res : ""); + } + else + { + char *quote1 = strchr (exp->internal_name, '.') ? "\"" : ""; + /* char *alias = */ + fprintf (output_def, "\t%s%s%s = %s%s%s @ %d%s%s ; %s\n", + quote, + exp->name, + quote, + quote1, + exp->internal_name, + quote1, + exp->ordinal, + exp->noname ? " NONAME" : "", + exp->data ? " DATA" : "", + res ? res : ""); + } + if (res) + free (res); + } + + inform (_("Added exports to output file")); +} + +/* generate_idata_ofile generates the portable assembly source code + for the idata sections. It appends the source code to the end of + the file. */ + +static void +generate_idata_ofile (filvar) + FILE *filvar; +{ + iheadtype *headptr; + ifunctype *funcptr; + int headindex; + int funcindex; + int nheads; + + if (import_list == NULL) + return; + + fprintf (filvar, "%s Import data sections\n", ASM_C); + fprintf (filvar, "\n\t.section\t.idata$2\n"); + fprintf (filvar, "\t%s\tdoi_idata\n", ASM_GLOBAL); + fprintf (filvar, "doi_idata:\n"); + + nheads = 0; + for (headptr = import_list; headptr != NULL; headptr = headptr->next) + { + fprintf (filvar, "\t%slistone%d%s\t%s %s\n", + ASM_RVA_BEFORE, nheads, ASM_RVA_AFTER, + ASM_C, headptr->dllname); + fprintf (filvar, "\t%s\t0\n", ASM_LONG); + fprintf (filvar, "\t%s\t0\n", ASM_LONG); + fprintf (filvar, "\t%sdllname%d%s\n", + ASM_RVA_BEFORE, nheads, ASM_RVA_AFTER); + fprintf (filvar, "\t%slisttwo%d%s\n\n", + ASM_RVA_BEFORE, nheads, ASM_RVA_AFTER); + nheads++; + } + + fprintf (filvar, "\t%s\t0\n", ASM_LONG); /* NULL record at */ + fprintf (filvar, "\t%s\t0\n", ASM_LONG); /* end of idata$2 */ + fprintf (filvar, "\t%s\t0\n", ASM_LONG); /* section */ + fprintf (filvar, "\t%s\t0\n", ASM_LONG); + fprintf (filvar, "\t%s\t0\n", ASM_LONG); + + fprintf (filvar, "\n\t.section\t.idata$4\n"); + headindex = 0; + for (headptr = import_list; headptr != NULL; headptr = headptr->next) + { + fprintf (filvar, "listone%d:\n", headindex); + for ( funcindex = 0; funcindex < headptr->nfuncs; funcindex++ ) + fprintf (filvar, "\t%sfuncptr%d_%d%s\n", + ASM_RVA_BEFORE, headindex, funcindex, ASM_RVA_AFTER); + fprintf (filvar,"\t%s\t0\n", ASM_LONG); /* NULL terminating list */ + headindex++; + } + + fprintf (filvar, "\n\t.section\t.idata$5\n"); + headindex = 0; + for (headptr = import_list; headptr != NULL; headptr = headptr->next) + { + fprintf (filvar, "listtwo%d:\n", headindex); + for ( funcindex = 0; funcindex < headptr->nfuncs; funcindex++ ) + fprintf (filvar, "\t%sfuncptr%d_%d%s\n", + ASM_RVA_BEFORE, headindex, funcindex, ASM_RVA_AFTER); + fprintf (filvar, "\t%s\t0\n", ASM_LONG); /* NULL terminating list */ + headindex++; + } + + fprintf (filvar, "\n\t.section\t.idata$6\n"); + headindex = 0; + for (headptr = import_list; headptr != NULL; headptr = headptr->next) + { + funcindex = 0; + for (funcptr = headptr->funchead; funcptr != NULL; + funcptr = funcptr->next) + { + fprintf (filvar,"funcptr%d_%d:\n", headindex, funcindex); + fprintf (filvar,"\t%s\t%d\n", ASM_SHORT, + ((funcptr->ord) & 0xFFFF)); + fprintf (filvar,"\t%s\t\"%s\"\n", ASM_TEXT, funcptr->name); + fprintf (filvar,"\t%s\t0\n", ASM_BYTE); + funcindex++; + } + headindex++; + } + + fprintf (filvar, "\n\t.section\t.idata$7\n"); + headindex = 0; + for (headptr = import_list; headptr != NULL; headptr = headptr->next) + { + fprintf (filvar,"dllname%d:\n", headindex); + fprintf (filvar,"\t%s\t\"%s\"\n", ASM_TEXT, headptr->dllname); + fprintf (filvar,"\t%s\t0\n", ASM_BYTE); + headindex++; + } +} + +static void +gen_exp_file () +{ + FILE *f; + int i; + export_type *exp; + dlist_type *dl; + + /* xgettext:c-format */ + inform (_("Generating export file: %s\n"), exp_name); + + f = fopen (TMP_ASM, FOPEN_WT); + if (!f) + /* xgettext:c-format */ + fatal (_("Unable to open temporary assembler file: %s"), TMP_ASM); + + /* xgettext:c-format */ + inform (_("Opened temporary file: %s"), TMP_ASM); + + dump_def_info (f); + + if (d_exports) + { + fprintf (f, "\t.section .edata\n\n"); + fprintf (f, "\t%s 0 %s Allways 0\n", ASM_LONG, ASM_C); + fprintf (f, "\t%s 0x%lx %s Time and date\n", ASM_LONG, (long) time(0), + ASM_C); + fprintf (f, "\t%s 0 %s Major and Minor version\n", ASM_LONG, ASM_C); + fprintf (f, "\t%sname%s %s Ptr to name of dll\n", ASM_RVA_BEFORE, ASM_RVA_AFTER, ASM_C); + fprintf (f, "\t%s %d %s Starting ordinal of exports\n", ASM_LONG, d_low_ord, ASM_C); + + + fprintf (f, "\t%s %d %s Number of functions\n", ASM_LONG, d_high_ord - d_low_ord + 1, ASM_C); + fprintf(f,"\t%s named funcs %d, low ord %d, high ord %d\n", + ASM_C, + d_named_nfuncs, d_low_ord, d_high_ord); + fprintf (f, "\t%s %d %s Number of names\n", ASM_LONG, + show_allnames ? d_high_ord - d_low_ord + 1 : d_named_nfuncs, ASM_C); + fprintf (f, "\t%safuncs%s %s Address of functions\n", ASM_RVA_BEFORE, ASM_RVA_AFTER, ASM_C); + + fprintf (f, "\t%sanames%s %s Address of Name Pointer Table\n", + ASM_RVA_BEFORE, ASM_RVA_AFTER, ASM_C); + + fprintf (f, "\t%sanords%s %s Address of ordinals\n", ASM_RVA_BEFORE, ASM_RVA_AFTER, ASM_C); + + fprintf (f, "name: %s \"%s\"\n", ASM_TEXT, dll_name); + + + fprintf(f,"%s Export address Table\n", ASM_C); + fprintf(f,"\t%s\n", ASM_ALIGN_LONG); + fprintf (f, "afuncs:\n"); + i = d_low_ord; + + for (exp = d_exports; exp; exp = exp->next) + { + if (exp->ordinal != i) + { +#if 0 + fprintf (f, "\t%s\t%d\t%s %d..%d missing\n", + ASM_SPACE, + (exp->ordinal - i) * 4, + ASM_C, + i, exp->ordinal - 1); + i = exp->ordinal; +#endif + while (i < exp->ordinal) + { + fprintf(f,"\t%s\t0\n", ASM_LONG); + i++; + } + } + fprintf (f, "\t%s%s%s%s\t%s %d\n", ASM_RVA_BEFORE, + ASM_PREFIX, + exp->internal_name, ASM_RVA_AFTER, ASM_C, exp->ordinal); + i++; + } + + fprintf (f,"%s Export Name Pointer Table\n", ASM_C); + fprintf (f, "anames:\n"); + + for (i = 0; (exp = d_exports_lexically[i]); i++) + { + if (!exp->noname || show_allnames) + fprintf (f, "\t%sn%d%s\n", + ASM_RVA_BEFORE, exp->ordinal, ASM_RVA_AFTER); + } + + fprintf (f,"%s Export Oridinal Table\n", ASM_C); + fprintf (f, "anords:\n"); + for (i = 0; (exp = d_exports_lexically[i]); i++) + { + if (!exp->noname || show_allnames) + fprintf (f, "\t%s %d\n", ASM_SHORT, exp->ordinal - d_low_ord); + } + + fprintf(f,"%s Export Name Table\n", ASM_C); + for (i = 0; (exp = d_exports_lexically[i]); i++) + if (!exp->noname || show_allnames) + fprintf (f, "n%d: %s \"%s\"\n", + exp->ordinal, 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; + } + } + } + } + } + + + /* Add to the output file a way of getting to the exported names + without using the import library. */ + if (add_indirect) + { + fprintf (f, "\t.section\t.rdata\n"); + for (i = 0, exp = d_exports; exp; i++, exp = exp->next) + if (!exp->noname || show_allnames) + { + /* We use a single underscore for MS compatibility, and a + double underscore for backward compatibility with old + cygwin releases. */ + fprintf (f, "\t%s\t__imp_%s\n", ASM_GLOBAL, exp->name); + fprintf (f, "\t%s\t_imp__%s\n", ASM_GLOBAL, exp->name); + fprintf (f, "__imp_%s:\n", exp->name); + fprintf (f, "_imp__%s:\n", exp->name); + fprintf (f, "\t%s\t%s\n", ASM_LONG, exp->name); + } + } + + /* Dump the reloc section if a base file is provided */ + if (base_file) + { + int addr; + long need[PAGE_SIZE]; + long page_addr; + int numbytes; + int num_entries; + long *copy; + int j; + int on_page; + fprintf (f, "\t.section\t.init\n"); + fprintf (f, "lab:\n"); + + fseek (base_file, 0, SEEK_END); + numbytes = ftell (base_file); + fseek (base_file, 0, SEEK_SET); + copy = xmalloc (numbytes); + fread (copy, 1, numbytes, base_file); + num_entries = numbytes / sizeof (long); + + + fprintf (f, "\t.section\t.reloc\n"); + if (num_entries) + { + int src; + int dst = 0; + int last = -1; + int totsize = 0; + + qsort (copy, num_entries, sizeof (long), sfunc); + /* Delete duplcates */ + for (src = 0; src < num_entries; src++) + { + if (last != copy[src]) + last = copy[dst++] = copy[src]; + } + num_entries = dst; + addr = copy[0]; + page_addr = addr & PAGE_MASK; /* work out the page addr */ + on_page = 0; + for (j = 0; j < num_entries; j++) + { + totsize += 2; + addr = copy[j]; + if ((addr & PAGE_MASK) != page_addr) + { + totsize += 8 + (on_page & 1)*2; + flush_page (f, need, page_addr, on_page); + on_page = 0; + page_addr = addr & PAGE_MASK; + } + need[on_page++] = addr; + } + + /* Pad the section to an even 32-byte boundary. This will make + the BeOS loader much happier, and shouldn't matter for other + OSes. */ + while ((totsize + 8 + (on_page & 1)*2) % 32 != 0) + { + /* 0x0000 is an absolute relocation that should be ignored. */ + need[on_page++] = 0x0000; + totsize += 2; + } + + flush_page (f, need, page_addr, on_page); + + /* fprintf (f, "\t%s\t0,0\t%s End\n", ASM_LONG, ASM_C);*/ + } + } + + generate_idata_ofile (f); + + fclose (f); + + /* assemble the file */ + sprintf (outfile, "%s -o %s %s", as_flags, exp_name, TMP_ASM); + +#ifdef DLLTOOL_ARM + if (interwork) + strcat (outfile, " -mthumb-interwork"); +#endif + + run (as_name, outfile); + + if (dontdeltemps == 0) + unlink (TMP_ASM); + + inform (_("Generated exports file")); +} + +static const char * +xlate (name) + const char *name; +{ + if (add_underscore) + { + char *copy = xmalloc (strlen (name) + 2); + copy[0] = '_'; + strcpy (copy + 1, name); + name = copy; + } + + if (killat) + { + char *p; + p = strchr (name, '@'); + if (p) + *p = 0; + } + return name; +} + +/**********************************************************************/ + +#if 0 + +static void +dump_iat (f, exp) + FILE *f; + export_type *exp; +{ + if (exp->noname && !show_allnames ) + { + fprintf (f, "\t%s\t0x%08x\n", + ASM_LONG, + exp->ordinal | 0x80000000); /* hint or orindal ?? */ + } + else + { + fprintf (f, "\t%sID%d%s\n", ASM_RVA_BEFORE, + exp->ordinal, + ASM_RVA_AFTER); + } +} + +#endif + +typedef struct +{ + int id; + const char *name; + int flags; + int align; + asection *sec; + asymbol *sym; + asymbol **sympp; + int size; + unsigned char *data; +} sinfo; + +#ifndef DLLTOOL_PPC + +#define TEXT 0 +#define DATA 1 +#define BSS 2 +#define IDATA7 3 +#define IDATA5 4 +#define IDATA4 5 +#define IDATA6 6 + +#define NSECS 7 + +static sinfo secdata[NSECS] = +{ + { TEXT, ".text", SEC_CODE | SEC_HAS_CONTENTS, 2}, + { DATA, ".data", SEC_DATA, 2}, + { BSS, ".bss", 0, 2}, + { IDATA7, ".idata$7", SEC_HAS_CONTENTS, 2}, + { IDATA5, ".idata$5", SEC_HAS_CONTENTS, 2}, + { IDATA4, ".idata$4", SEC_HAS_CONTENTS, 2}, + { IDATA6, ".idata$6", SEC_HAS_CONTENTS, 1} +}; + +#else + +/* Sections numbered to make the order the same as other PowerPC NT */ +/* compilers. This also keeps funny alignment thingies from happening. */ +#define TEXT 0 +#define PDATA 1 +#define RDATA 2 +#define IDATA5 3 +#define IDATA4 4 +#define IDATA6 5 +#define IDATA7 6 +#define DATA 7 +#define BSS 8 + +#define NSECS 9 + +static sinfo secdata[NSECS] = +{ + { TEXT, ".text", SEC_CODE | SEC_HAS_CONTENTS, 3}, + { PDATA, ".pdata", SEC_HAS_CONTENTS, 2}, + { RDATA, ".reldata", SEC_HAS_CONTENTS, 2}, + { IDATA5, ".idata$5", SEC_HAS_CONTENTS, 2}, + { IDATA4, ".idata$4", SEC_HAS_CONTENTS, 2}, + { IDATA6, ".idata$6", SEC_HAS_CONTENTS, 1}, + { IDATA7, ".idata$7", SEC_HAS_CONTENTS, 2}, + { DATA, ".data", SEC_DATA, 2}, + { BSS, ".bss", 0, 2} +}; + +#endif + +/* +This is what we're trying to make. We generate the imp symbols with +both single and double underscores, for compatibility. + + .text + .global _GetFileVersionInfoSizeW@8 + .global __imp_GetFileVersionInfoSizeW@8 +_GetFileVersionInfoSizeW@8: + jmp * __imp_GetFileVersionInfoSizeW@8 + .section .idata$7 # To force loading of head + .long __version_a_head +# Import Address Table + .section .idata$5 +__imp_GetFileVersionInfoSizeW@8: + .rva ID2 + +# Import Lookup Table + .section .idata$4 + .rva ID2 +# Hint/Name table + .section .idata$6 +ID2: .short 2 + .asciz "GetFileVersionInfoSizeW" + + +For the PowerPC, here's the variation on the above scheme: + +# Rather than a simple "jmp *", the code to get to the dll function +# looks like: + .text + lwz r11,[tocv]__imp_function_name(r2) +# RELOC: 00000000 TOCREL16,TOCDEFN __imp_function_name + lwz r12,0(r11) + stw r2,4(r1) + mtctr r12 + lwz r2,4(r11) + bctr +*/ + +static char * +make_label (prefix, name) + const char *prefix; + const char *name; +{ + int len = strlen (ASM_PREFIX) + strlen (prefix) + strlen (name); + char *copy = xmalloc (len +1 ); + strcpy (copy, ASM_PREFIX); + strcat (copy, prefix); + strcat (copy, name); + return copy; +} + +static bfd * +make_one_lib_file (exp, i) + export_type *exp; + int i; +{ +#if 0 + { + FILE *f; + char *prefix="d"; + sprintf (outfile, "%ss%05d.s", prefix, i); + f = fopen (outfile, FOPEN_WT); + fprintf (f, "\t.text\n"); + fprintf (f, "\t%s\t%s%s\n", ASM_GLOBAL, ASM_PREFIX, exp->name); + fprintf (f, "\t%s\t__imp_%s\n", ASM_GLOBAL, exp->name); + fprintf (f, "\t%s\t_imp__%s\n", ASM_GLOBAL, exp->name); + fprintf (f, "%s%s:\n\t%s\t__imp_%s\n", ASM_PREFIX, + exp->name, ASM_JUMP, exp->name); + + fprintf (f, "\t.section\t.idata$7\t%s To force loading of head\n", ASM_C); + fprintf (f, "\t%s\t%s\n", ASM_LONG, head_label); + + + fprintf (f,"%s Import Address Table\n", ASM_C); + + fprintf (f, "\t.section .idata$5\n"); + fprintf (f, "__imp_%s:\n", exp->name); + fprintf (f, "_imp__%s:\n", exp->name); + + dump_iat (f, exp); + + fprintf (f, "\n%s Import Lookup Table\n", ASM_C); + fprintf (f, "\t.section .idata$4\n"); + + dump_iat (f, exp); + + if(!exp->noname || show_allnames) + { + fprintf (f, "%s Hint/Name table\n", ASM_C); + fprintf (f, "\t.section .idata$6\n"); + fprintf (f, "ID%d:\t%s\t%d\n", exp->ordinal, ASM_SHORT, exp->hint); + fprintf (f, "\t%s\t\"%s\"\n", ASM_TEXT, xlate (exp->name)); + } + + fclose (f); + + sprintf (outfile, "%s -o %ss%05d.o %ss%d.s", + as_flags, prefix, i, prefix, i); + +#ifdef DLLTOOL_ARM + if (interwork) + strcat (outfile, " -mthumb-interwork"); +#endif + + run (as_name, outfile); + } +#else /* if 0 */ + { + bfd * abfd; + asymbol * exp_label; + asymbol * iname; + asymbol * iname2; + asymbol * iname_lab; + asymbol ** iname_lab_pp; + asymbol ** iname_pp; +#ifdef DLLTOOL_PPC + asymbol ** fn_pp; + asymbol ** toc_pp; +#define EXTRA 2 +#endif +#ifndef EXTRA +#define EXTRA 0 +#endif + asymbol * ptrs[NSECS + 4 + EXTRA + 1]; + + char * outname = xmalloc (10); + int oidx = 0; + + + sprintf (outname, "%s%05d.o", TMP_STUB, i); + + abfd = bfd_openw (outname, HOW_BFD_TARGET); + + if (!abfd) + /* xgettext:c-format */ + fatal (_("bfd_open failed open stub file: %s"), outname); + + /* xgettext:c-format */ + inform (_("Creating stub file: %s"), outname); + + bfd_set_format (abfd, bfd_object); + bfd_set_arch_mach (abfd, HOW_BFD_ARCH, 0); + +#ifdef DLLTOOL_ARM + if (interwork) + bfd_set_private_flags (abfd, F_INTERWORK); +#endif + + /* First make symbols for the sections */ + for (i = 0; i < NSECS; i++) + { + sinfo *si = secdata + i; + if (si->id != i) + abort(); + si->sec = bfd_make_section_old_way (abfd, si->name); + bfd_set_section_flags (abfd, + si->sec, + si->flags); + + bfd_set_section_alignment(abfd, si->sec, si->align); + si->sec->output_section = si->sec; + si->sym = bfd_make_empty_symbol(abfd); + si->sym->name = si->sec->name; + si->sym->section = si->sec; + si->sym->flags = BSF_LOCAL; + si->sym->value = 0; + ptrs[oidx] = si->sym; + si->sympp = ptrs + oidx; + si->size = 0; + si->data = NULL; + + oidx++; + } + + if (! exp->data) + { + exp_label = bfd_make_empty_symbol (abfd); + exp_label->name = make_label ("", exp->name); + + /* On PowerPC, the function name points to a descriptor in + the rdata section, the first element of which is a + pointer to the code (..function_name), and the second + points to the .toc */ +#ifdef DLLTOOL_PPC + if (machine == MPPC) + exp_label->section = secdata[RDATA].sec; + else +#endif + exp_label->section = secdata[TEXT].sec; + + exp_label->flags = BSF_GLOBAL; + exp_label->value = 0; + +#ifdef DLLTOOL_ARM + if (machine == MTHUMB) + bfd_coff_set_symbol_class (abfd, exp_label, C_THUMBEXTFUNC); +#endif + ptrs[oidx++] = exp_label; + } + + /* Generate imp symbols with one underscore for Microsoft + compatibility, and with two underscores for backward + compatibility with old versions of cygwin. */ + iname = bfd_make_empty_symbol(abfd); + iname->name = make_label ("__imp_", exp->name); + iname->section = secdata[IDATA5].sec; + iname->flags = BSF_GLOBAL; + iname->value = 0; + + iname2 = bfd_make_empty_symbol(abfd); + iname2->name = make_label ("_imp__", exp->name); + iname2->section = secdata[IDATA5].sec; + iname2->flags = BSF_GLOBAL; + iname2->value = 0; + + iname_lab = bfd_make_empty_symbol(abfd); + + iname_lab->name = head_label; + iname_lab->section = (asection *)&bfd_und_section; + iname_lab->flags = 0; + iname_lab->value = 0; + + + iname_pp = ptrs + oidx; + ptrs[oidx++] = iname; + ptrs[oidx++] = iname2; + + iname_lab_pp = ptrs + oidx; + ptrs[oidx++] = iname_lab; + +#ifdef DLLTOOL_PPC + /* The symbol refering to the code (.text) */ + { + asymbol *function_name; + + function_name = bfd_make_empty_symbol(abfd); + function_name->name = make_label ("..", exp->name); + function_name->section = secdata[TEXT].sec; + function_name->flags = BSF_GLOBAL; + function_name->value = 0; + + fn_pp = ptrs + oidx; + ptrs[oidx++] = function_name; + } + + /* The .toc symbol */ + { + asymbol *toc_symbol; /* The .toc symbol */ + + toc_symbol = bfd_make_empty_symbol (abfd); + toc_symbol->name = make_label (".", "toc"); + toc_symbol->section = (asection *)&bfd_und_section; + toc_symbol->flags = BSF_GLOBAL; + toc_symbol->value = 0; + + toc_pp = ptrs + oidx; + ptrs[oidx++] = toc_symbol; + } +#endif + + ptrs[oidx] = 0; + + for (i = 0; i < NSECS; i++) + { + sinfo *si = secdata + i; + asection *sec = si->sec; + arelent *rel; + arelent **rpp; + + switch (i) + { + case TEXT: + if (! exp->data) + { + si->size = HOW_JTAB_SIZE; + si->data = xmalloc (HOW_JTAB_SIZE); + memcpy (si->data, HOW_JTAB, HOW_JTAB_SIZE); + + /* add the reloc into idata$5 */ + rel = xmalloc (sizeof (arelent)); + + rpp = xmalloc (sizeof (arelent *) * 2); + rpp[0] = rel; + rpp[1] = 0; + + rel->address = HOW_JTAB_ROFF; + rel->addend = 0; + + if (machine == MPPC) + { + rel->howto = bfd_reloc_type_lookup (abfd, + BFD_RELOC_16_GOTOFF); + rel->sym_ptr_ptr = iname_pp; + } + else + { + rel->howto = bfd_reloc_type_lookup (abfd, BFD_RELOC_32); + rel->sym_ptr_ptr = secdata[IDATA5].sympp; + } + sec->orelocation = rpp; + sec->reloc_count = 1; + } + break; + case IDATA4: + case IDATA5: + /* An idata$4 or idata$5 is one word long, and has an + rva to idata$6 */ + + si->data = xmalloc (4); + si->size = 4; + + if (exp->noname) + { + si->data[0] = exp->ordinal ; + si->data[1] = exp->ordinal >> 8; + si->data[2] = exp->ordinal >> 16; + si->data[3] = 0x80; + } + else + { + sec->reloc_count = 1; + memset (si->data, 0, si->size); + rel = xmalloc (sizeof (arelent)); + rpp = xmalloc (sizeof (arelent *) * 2); + rpp[0] = rel; + rpp[1] = 0; + rel->address = 0; + rel->addend = 0; + rel->howto = bfd_reloc_type_lookup (abfd, BFD_RELOC_RVA); + rel->sym_ptr_ptr = secdata[IDATA6].sympp; + sec->orelocation = rpp; + } + + break; + + case IDATA6: + if (!exp->noname) + { + /* This used to add 1 to exp->hint. I don't know + why it did that, and it does not match what I see + in programs compiled with the MS tools. */ + int idx = exp->hint; + si->size = strlen (xlate (exp->name)) + 3; + si->data = xmalloc (si->size); + si->data[0] = idx & 0xff; + si->data[1] = idx >> 8; + strcpy (si->data + 2, xlate (exp->name)); + } + break; + case IDATA7: + si->size = 4; + si->data =xmalloc(4); + memset (si->data, 0, si->size); + rel = xmalloc (sizeof (arelent)); + rpp = xmalloc (sizeof (arelent *) * 2); + rpp[0] = rel; + rel->address = 0; + rel->addend = 0; + rel->howto = bfd_reloc_type_lookup (abfd, BFD_RELOC_RVA); + rel->sym_ptr_ptr = iname_lab_pp; + sec->orelocation = rpp; + sec->reloc_count = 1; + break; + +#ifdef DLLTOOL_PPC + case PDATA: + { + /* The .pdata section is 5 words long. */ + /* Think of it as: */ + /* struct */ + /* { */ + /* bfd_vma BeginAddress, [0x00] */ + /* EndAddress, [0x04] */ + /* ExceptionHandler, [0x08] */ + /* HandlerData, [0x0c] */ + /* PrologEndAddress; [0x10] */ + /* }; */ + + /* So this pdata section setups up this as a glue linkage to + a dll routine. There are a number of house keeping things + we need to do: + + 1. In the name of glue trickery, the ADDR32 relocs for 0, + 4, and 0x10 are set to point to the same place: + "..function_name". + 2. There is one more reloc needed in the pdata section. + The actual glue instruction to restore the toc on + return is saved as the offset in an IMGLUE reloc. + So we need a total of four relocs for this section. + + 3. Lastly, the HandlerData field is set to 0x03, to indicate + that this is a glue routine. + */ + arelent *imglue, *ba_rel, *ea_rel, *pea_rel; + + /* alignment must be set to 2**2 or you get extra stuff */ + bfd_set_section_alignment(abfd, sec, 2); + + si->size = 4 * 5; + si->data =xmalloc(4 * 5); + memset (si->data, 0, si->size); + rpp = xmalloc (sizeof (arelent *) * 5); + rpp[0] = imglue = xmalloc (sizeof (arelent)); + rpp[1] = ba_rel = xmalloc (sizeof (arelent)); + rpp[2] = ea_rel = xmalloc (sizeof (arelent)); + rpp[3] = pea_rel = xmalloc (sizeof (arelent)); + rpp[4] = 0; + + /* stick the toc reload instruction in the glue reloc */ + bfd_put_32(abfd, ppc_glue_insn, (char *) &imglue->address); + + imglue->addend = 0; + imglue->howto = bfd_reloc_type_lookup (abfd, + BFD_RELOC_32_GOTOFF); + imglue->sym_ptr_ptr = fn_pp; + + ba_rel->address = 0; + ba_rel->addend = 0; + ba_rel->howto = bfd_reloc_type_lookup (abfd, BFD_RELOC_32); + ba_rel->sym_ptr_ptr = fn_pp; + + bfd_put_32(abfd, 0x18, si->data + 0x04); + ea_rel->address = 4; + ea_rel->addend = 0; + ea_rel->howto = bfd_reloc_type_lookup (abfd, BFD_RELOC_32); + ea_rel->sym_ptr_ptr = fn_pp; + + /* mark it as glue */ + bfd_put_32(abfd, 0x03, si->data + 0x0c); + + /* mark the prolog end address */ + bfd_put_32(abfd, 0x0D, si->data + 0x10); + pea_rel->address = 0x10; + pea_rel->addend = 0; + pea_rel->howto = bfd_reloc_type_lookup (abfd, BFD_RELOC_32); + pea_rel->sym_ptr_ptr = fn_pp; + + sec->orelocation = rpp; + sec->reloc_count = 4; + break; + } + case RDATA: + /* Each external function in a PowerPC PE file has a two word + descriptor consisting of: + 1. The address of the code. + 2. The address of the appropriate .toc + We use relocs to build this. + */ + + si->size = 8; + si->data = xmalloc (8); + memset (si->data, 0, si->size); + + rpp = xmalloc (sizeof (arelent *) * 3); + rpp[0] = rel = xmalloc (sizeof (arelent)); + rpp[1] = xmalloc (sizeof (arelent)); + rpp[2] = 0; + + rel->address = 0; + rel->addend = 0; + rel->howto = bfd_reloc_type_lookup (abfd, BFD_RELOC_32); + rel->sym_ptr_ptr = fn_pp; + + rel = rpp[1]; + + rel->address = 4; + rel->addend = 0; + rel->howto = bfd_reloc_type_lookup (abfd, BFD_RELOC_32); + rel->sym_ptr_ptr = toc_pp; + + sec->orelocation = rpp; + sec->reloc_count = 2; + break; +#endif /* DLLTOOL_PPC */ + } + } + + { + bfd_vma vma = 0; + /* Size up all the sections */ + for (i = 0; i < NSECS; i++) + { + sinfo *si = secdata + i; + + bfd_set_section_size (abfd, si->sec, si->size); + bfd_set_section_vma (abfd, si->sec, vma); + +/* vma += si->size;*/ + } + } + /* Write them out */ + for (i = 0; i < NSECS; i++) + { + sinfo *si = secdata + i; + + if (i == IDATA5 && no_idata5) + continue; + + if (i == IDATA4 && no_idata4) + continue; + + bfd_set_section_contents (abfd, si->sec, + si->data, 0, + si->size); + } + + bfd_set_symtab (abfd, ptrs, oidx); + bfd_close (abfd); + abfd = bfd_openr (outname, HOW_BFD_TARGET); + return abfd; + } +#endif +} + +static bfd * +make_head () +{ + FILE * f = fopen (TMP_HEAD_S, FOPEN_WT); + + fprintf (f, "%s IMAGE_IMPORT_DESCRIPTOR\n", ASM_C); + fprintf (f, "\t.section .idata$2\n"); + + fprintf(f,"\t%s\t%s\n", ASM_GLOBAL,head_label); + + fprintf (f, "%s:\n", head_label); + + fprintf (f, "\t%shname%s\t%sPtr to image import by name list\n", + ASM_RVA_BEFORE, ASM_RVA_AFTER, ASM_C); + + fprintf (f, "\t%sthis should be the timestamp, but NT sometimes\n", ASM_C); + fprintf (f, "\t%sdoesn't load DLLs when this is set.\n", ASM_C); + fprintf (f, "\t%s\t0\t%s loaded time\n", ASM_LONG, ASM_C); + fprintf (f, "\t%s\t0\t%s Forwarder chain\n", ASM_LONG, ASM_C); + fprintf (f, "\t%s__%s_iname%s\t%s imported dll's name\n", + ASM_RVA_BEFORE, + imp_name_lab, + ASM_RVA_AFTER, + ASM_C); + fprintf (f, "\t%sfthunk%s\t%s pointer to firstthunk\n", + ASM_RVA_BEFORE, + ASM_RVA_AFTER, ASM_C); + + fprintf (f, "%sStuff for compatibility\n", ASM_C); + + if (!no_idata5) + { + fprintf (f, "\t.section\t.idata$5\n"); + fprintf (f, "\t%s\t0\n", ASM_LONG); + fprintf (f, "fthunk:\n"); + } + if (!no_idata4) + { + fprintf (f, "\t.section\t.idata$4\n"); + + fprintf (f, "\t%s\t0\n", ASM_LONG); + fprintf (f, "\t.section .idata$4\n"); + fprintf (f, "hname:\n"); + } + fclose (f); + + sprintf (outfile, "%s -o %s %s", as_flags, TMP_HEAD_O, TMP_HEAD_S); + +#ifdef DLLTOOL_ARM + if (interwork) + strcat (outfile, " -mthumb-interwork"); +#endif + + run (as_name, outfile); + + return bfd_openr (TMP_HEAD_O, HOW_BFD_TARGET); +} + +static bfd * +make_tail () +{ + FILE * f = fopen (TMP_TAIL_S, FOPEN_WT); + + if (!no_idata4) + { + fprintf (f, "\t.section .idata$4\n"); + fprintf (f, "\t%s\t0\n", ASM_LONG); + } + if (!no_idata5) + { + fprintf (f, "\t.section .idata$5\n"); + fprintf (f, "\t%s\t0\n", ASM_LONG); + } + +#ifdef DLLTOOL_PPC + /* Normally, we need to see a null descriptor built in idata$3 to + act as the terminator for the list. The ideal way, I suppose, + would be to mark this section as a comdat type 2 section, so + only one would appear in the final .exe (if our linker supported + comdat, that is) or cause it to be inserted by something else (say + crt0) + */ + + fprintf (f, "\t.section .idata$3\n"); + fprintf (f, "\t%s\t0\n", ASM_LONG); + fprintf (f, "\t%s\t0\n", ASM_LONG); + fprintf (f, "\t%s\t0\n", ASM_LONG); + fprintf (f, "\t%s\t0\n", ASM_LONG); + fprintf (f, "\t%s\t0\n", ASM_LONG); +#endif + +#ifdef DLLTOOL_PPC + /* Other PowerPC NT compilers use idata$6 for the dllname, so I + do too. Original, huh? */ + fprintf (f, "\t.section .idata$6\n"); +#else + fprintf (f, "\t.section .idata$7\n"); +#endif + + fprintf (f, "\t%s\t__%s_iname\n", ASM_GLOBAL, imp_name_lab); + fprintf (f, "__%s_iname:\t%s\t\"%s\"\n", + imp_name_lab, ASM_TEXT, dll_name); + + fclose (f); + + sprintf (outfile, "%s -o %s %s", as_flags, TMP_TAIL_O, TMP_TAIL_S); + +#ifdef DLLTOOL_ARM + if (interwork) + strcat (outfile, " -mthumb-interwork"); +#endif + + run (as_name, outfile); + + return bfd_openr (TMP_TAIL_O, HOW_BFD_TARGET); +} + +static void +gen_lib_file () +{ + int i; + export_type *exp; + bfd *ar_head; + bfd *ar_tail; + bfd *outarch; + bfd * head = 0; + + unlink (imp_name); + + outarch = bfd_openw (imp_name, HOW_BFD_TARGET); + + if (!outarch) + /* xgettext:c-format */ + fatal (_("Can't open .lib file: %s"), imp_name); + + /* xgettext:c-format */ + inform (_("Creating library file: %s\n"), imp_name); + + bfd_set_format (outarch, bfd_archive); + outarch->has_armap = 1; + + /* Work out a reasonable size of things to put onto one line. */ + + ar_head = make_head (); + ar_tail = make_tail(); + + if (ar_head == NULL || ar_tail == NULL) + return; + + for (i = 0; (exp = d_exports_lexically[i]); i++) + { + bfd *n = make_one_lib_file (exp, i); + n->next = head; + head = n; + } + + /* Now stick them all into the archive */ + + ar_head->next = head; + ar_tail->next = ar_head; + head = ar_tail; + + if (! bfd_set_archive_head (outarch, head)) + bfd_fatal ("bfd_set_archive_head"); + + if (! bfd_close (outarch)) + bfd_fatal (imp_name); + + while (head != NULL) + { + bfd *n = head->next; + bfd_close (head); + head = n; + } + + /* Delete all the temp files */ + + if (dontdeltemps == 0) + { + unlink (TMP_HEAD_O); + unlink (TMP_HEAD_S); + unlink (TMP_TAIL_O); + unlink (TMP_TAIL_S); + } + + if (dontdeltemps < 2) + { + for (i = 0, exp = d_exports; exp; i++, exp = exp->next) + { + sprintf (outfile, "%s%05d.o", TMP_STUB, i); + if (unlink (outfile) < 0) + /* xgettext:c-format */ + warn (_("cannot delete %s: %s\n"), outfile, strerror (errno)); + } + } + + inform (_("Created lib file")); +} + +/**********************************************************************/ + +/* Run through the information gathered from the .o files and the + .def file and work out the best stuff */ +static int +pfunc (a, b) + const void *a; + const 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); +} + +static int +nfunc (a, b) + const void *a; + const 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%s\n", + i, ptr[i]->name, ptr[i]->ordinal, + ptr[i]->noname ? "NONAME " : "", + ptr[i]->constant ? "CONSTANT" : "", + ptr[i]->data ? "DATA" : ""); + } + else + printf ("empty\n"); + } +#endif +} + +static void +process_duplicates (d_export_vec) + export_type **d_export_vec; +{ + int more = 1; + int i; + 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; + + /* xgettext:c-format */ + inform (_("Warning, ignoring duplicate EXPORT %s %d,%d\n"), + a->name, a->ordinal, b->ordinal); + + if (a->ordinal != -1 + && b->ordinal != -1) + /* xgettext:c-format */ + fatal (_("Error, duplicate EXPORT with oridinals: %s"), + a->name); + + /* Merge attributes */ + b->ordinal = a->ordinal > 0 ? a->ordinal : b->ordinal; + b->constant |= a->constant; + b->noname |= a->noname; + b->data |= a->data; + d_export_vec[i] = 0; + } + + dtab (d_export_vec); + remove_null_names (d_export_vec); + dtab (d_export_vec); + } + } + + + /* Count the names */ + for (i = 0; i < d_nfuncs; i++) + { + if (!d_export_vec[i]->noname) + d_named_nfuncs++; + } +} + +static void +fill_ordinals (d_export_vec) + export_type **d_export_vec; +{ + int lowest = -1; + int i; + char *ptr; + int size = 65536; + + qsort (d_export_vec, d_nfuncs, sizeof (export_type *), pfunc); + + /* fill in the unset ordinals with ones from our range */ + + ptr = (char *) xmalloc (size); + + memset (ptr, 0, size); + + /* Mark in our large vector all the numbers that are taken */ + for (i = 0; i < d_nfuncs; i++) + { + if (d_export_vec[i]->ordinal != -1) + { + ptr[d_export_vec[i]->ordinal] = 1; + if (lowest == -1 || d_export_vec[i]->ordinal < lowest) + { + lowest = d_export_vec[i]->ordinal; + } + } + } + + /* Start at 1 for compatibility with MS toolchain. */ + if (lowest == -1) + lowest = 1; + + /* Now fill in ordinals where the user wants us to choose. */ + for (i = 0; i < d_nfuncs; i++) + { + if (d_export_vec[i]->ordinal == -1) + { + register int j; + + /* First try within or after any user supplied range. */ + for (j = lowest; j < size; j++) + if (ptr[j] == 0) + { + ptr[j] = 1; + d_export_vec[i]->ordinal = j; + goto done; + } + + /* Then try before the range. */ + for (j = lowest; j >0; j--) + if (ptr[j] == 0) + { + ptr[j] = 1; + d_export_vec[i]->ordinal = j; + goto done; + } + done:; + } + } + + free (ptr); + + /* And resort */ + + qsort (d_export_vec, d_nfuncs, sizeof (export_type *), pfunc); + + /* Work out the lowest and highest ordinal numbers. */ + if (d_nfuncs) + { + if (d_export_vec[0]) + d_low_ord = d_export_vec[0]->ordinal; + if (d_export_vec[d_nfuncs-1]) + d_high_ord = d_export_vec[d_nfuncs-1]->ordinal; + } +} + +static int +alphafunc (av,bv) + const void *av; + const void *bv; +{ + const export_type **a = (const export_type **) av; + const export_type **b = (const export_type **) bv; + + return strcmp ((*a)->name, (*b)->name); +} + +static void +mangle_defs () +{ + /* First work out the minimum ordinal chosen */ + + export_type *exp; + + int i; + int hint = 0; + export_type **d_export_vec + = (export_type **) xmalloc (sizeof (export_type *) * d_nfuncs); + + inform (_("Processing definitions")); + + 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]; + } + + /* Build list in alpha order */ + d_exports_lexically = (export_type **) + xmalloc (sizeof (export_type *) * (d_nfuncs + 1)); + + for (i = 0, exp = d_exports; exp; i++, exp = exp->next) + { + d_exports_lexically[i] = exp; + } + d_exports_lexically[i] = 0; + + qsort (d_exports_lexically, i, sizeof (export_type *), alphafunc); + + /* Fill exp entries with their hint values */ + + for (i = 0; i < d_nfuncs; i++) + { + if (!d_exports_lexically[i]->noname || show_allnames) + d_exports_lexically[i]->hint = hint++; + } + + inform (_("Processed definitions")); +} + +/**********************************************************************/ + +static void +usage (file, status) + FILE *file; + int status; +{ + /* xgetext:c-format */ + fprintf (file, _("Usage %s <options> <object-files>\n"), program_name); + /* xgetext:c-format */ + fprintf (file, _(" -m --machine <machine> Create {arm, i386, ppc, thumb} DLL. [default: %s]\n"), mname); + fprintf (file, _(" -e --output-exp <outname> Generate an export file.\n")); + fprintf (file, _(" -l --output-lib <outname> Generate an interface library.\n")); + fprintf (file, _(" -a --add-indirect Add dll indirects to export file.\n")); + fprintf (file, _(" -D --dllname <name> Name of input dll to put into interface lib.\n")); + fprintf (file, _(" -d --input-def <deffile> Name of .def file to be read in.\n")); + fprintf (file, _(" -z --output-def <deffile> Name of .def file to be created.\n")); + fprintf (file, _(" --export-all-symbols Export all symbols to .def\n")); + fprintf (file, _(" --no-export-all-symbols Only export listed symbols\n")); + fprintf (file, _(" --exclude-symbols <list> Don't export <list>\n")); + fprintf (file, _(" --no-default-excludes Clear default exclude symbols\n")); + fprintf (file, _(" -b --base-file <basefile> Read linker generated base file.\n")); + fprintf (file, _(" -x --no-idata4 Don't generate idata$4 section.\n")); + fprintf (file, _(" -c --no-idata5 Don't generate idata$5 section.\n")); + fprintf (file, _(" -U --add-underscore Add underscores to symbols in interface library.\n")); + fprintf (file, _(" -k --kill-at Kill @<n> from exported names.\n")); + fprintf (file, _(" -A --add-stdcall-alias Add aliases without @<n>.\n")); + fprintf (file, _(" -S --as <name> Use <name> for assembler.\n")); + fprintf (file, _(" -f --as-flags <flags> Pass <flags> to the assembler.\n")); +#ifdef DLLTOOL_ARM + fprintf (file, _(" -i --interwork Support ARM/Thumb interworking.\n")); +#endif + fprintf (file, _(" -n --no-delete Keep temp files (repeat for extra preservation).\n")); + fprintf (file, _(" -v --verbose Be verbose.\n")); + fprintf (file, _(" -V --version Display the program version.\n")); + fprintf (file, _(" -h --help Display this information.\n")); + + exit (status); +} + +#define OPTION_EXPORT_ALL_SYMS 150 +#define OPTION_NO_EXPORT_ALL_SYMS (OPTION_EXPORT_ALL_SYMS + 1) +#define OPTION_EXCLUDE_SYMS (OPTION_NO_EXPORT_ALL_SYMS + 1) +#define OPTION_NO_DEFAULT_EXCLUDES (OPTION_EXCLUDE_SYMS + 1) +#define OPTION_NO_IDATA4 'x' +#define OPTION_NO_IDATA5 'c' + +static const struct option long_options[] = +{ + {"no-delete", no_argument, NULL, 'n'}, + {"dllname", required_argument, NULL, 'D'}, + {"no-idata4", no_argument, NULL, OPTION_NO_IDATA4}, + {"no-idata5", no_argument, NULL, OPTION_NO_IDATA5}, + {"output-exp", required_argument, NULL, 'e'}, + {"output-def", required_argument, NULL, 'z'}, + {"export-all-symbols", no_argument, NULL, OPTION_EXPORT_ALL_SYMS}, + {"no-export-all-symbols", no_argument, NULL, OPTION_NO_EXPORT_ALL_SYMS}, + {"exclude-symbols", required_argument, NULL, OPTION_EXCLUDE_SYMS}, + {"no-default-excludes", no_argument, NULL, OPTION_NO_DEFAULT_EXCLUDES}, + {"output-lib", required_argument, NULL, 'l'}, + {"def", required_argument, NULL, 'd'}, /* for compatiblity with older versions */ + {"input-def", required_argument, NULL, 'd'}, + {"add-underscore", no_argument, NULL, 'U'}, + {"kill-at", no_argument, NULL, 'k'}, + {"add-stdcall-alias", no_argument, NULL, 'A'}, + {"verbose", no_argument, NULL, 'v'}, + {"version", no_argument, NULL, 'V'}, + {"help", no_argument, NULL, 'h'}, + {"machine", required_argument, NULL, 'm'}, + {"add-indirect", no_argument, NULL, 'a'}, + {"base-file", required_argument, NULL, 'b'}, + {"as", required_argument, NULL, 'S'}, + {"as-flags", required_argument, NULL, 'f'}, +#ifdef DLLTOOL_ARM + {"interwork", no_argument, NULL, 'i'}, +#endif + {0} +}; + +int +main (ac, av) + int ac; + char **av; +{ + int c; + int i; + char *firstarg = 0; + program_name = av[0]; + oav = av; + +#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES) + setlocale (LC_MESSAGES, ""); +#endif + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + while ((c = getopt_long (ac, av, "xcz:S:aD:l:e:nkAvVb:Uh?m:d:f:i", + long_options, 0)) + != EOF) + { + switch (c) + { + case OPTION_NO_IDATA4: + no_idata4 = 1; + break; + case OPTION_NO_IDATA5: + no_idata5 = 1; + break; + case OPTION_EXPORT_ALL_SYMS: + export_all_symbols = true; + break; + case OPTION_NO_EXPORT_ALL_SYMS: + export_all_symbols = false; + break; + case OPTION_EXCLUDE_SYMS: + add_excludes (optarg); + break; + case OPTION_NO_DEFAULT_EXCLUDES: + do_default_excludes = false; + break; + case 'S': + as_name = optarg; + break; + case 'f': + as_flags = optarg; + break; + + /* ignored for compatibility */ + case 'u': + break; + case 'a': + add_indirect = 1; + break; + case 'z': + output_def = fopen (optarg, FOPEN_WT); + break; + case 'D': + dll_name = optarg; + break; + case 'l': + imp_name = optarg; + break; + case 'e': + exp_name = optarg; + break; + case 'h': + usage (stdout, 0); + break; + case 'm': + mname = optarg; + break; + case 'v': + verbose = 1; + break; + case 'V': + print_version (program_name); + break; +#ifdef DLLTOOL_ARM + case 'i': + interwork = 1; + break; +#endif + case 'y': +#if 0 + /* We don't currently define YYDEBUG when building + defparse.y. */ + yydebug = 1; +#endif + break; + case 'U': + add_underscore = 1; + break; + case 'k': + killat = 1; + break; + case 'A': + add_stdcall_alias = 1; + break; + case 'd': + def_file = optarg; + break; + case 'n': + dontdeltemps++; + break; + case 'b': + base_file = fopen (optarg, FOPEN_RB); + + if (!base_file) + /* xgettext:c-format */ + fatal (_("Unable to open base-file: %s"), optarg); + + break; + default: + usage (stderr, 1); + break; + } + } + + for (i = 0; mtable[i].type; i++) + { + if (strcmp (mtable[i].type, mname) == 0) + break; + } + + if (!mtable[i].type) + /* xgettext:c-format */ + fatal (_("Machine '%s' not supported"), mname); + + machine = i; + +#ifdef DLLTOOL_ARM + /* Always enable interworking for Thumb targets. */ + if (machine == MTHUMB && (! interwork)) + interwork = 1; +#endif + + if (!dll_name && exp_name) + { + int len = strlen (exp_name) + 5; + dll_name = xmalloc (len); + strcpy (dll_name, exp_name); + strcat (dll_name, ".dll"); + } + + /* Don't use the default exclude list if we're reading only the + symbols in the .drectve section. The default excludes are meant + to avoid exporting DLL entry point and Cygwin32 impure_ptr. */ + if (! export_all_symbols) + do_default_excludes = false; + + if (do_default_excludes) + set_default_excludes (); + + if (def_file) + process_def_file (def_file); + + while (optind < ac) + { + if (!firstarg) + firstarg = av[optind]; + scan_obj_file (av[optind]); + optind++; + } + + mangle_defs (); + + if (exp_name) + gen_exp_file (); + + if (imp_name) + { + /* Make imp_name safe for use as a label. */ + char *p; + + imp_name_lab = xstrdup (imp_name); + for (p = imp_name_lab; *p; p++) + { + if (!isalpha ((unsigned char) *p) && !isdigit ((unsigned char) *p)) + *p = '_'; + } + head_label = make_label("_head_", imp_name_lab); + gen_lib_file (); + } + + if (output_def) + gen_def_file (); + + return 0; +} |