#define show_allnames 0 /* 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., 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) 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 [ , ] The result is going to be .EXE LIBRARY [ , ] The result is going to be .DLL EXPORTS ( [ = ] [ @ ] [ NONAME ] [CONSTANT] ) * Declares name1 as an exported symbol from the DLL, with optional ordinal number IMPORTS ( [ = ] . ) * Ignored for compatibility DESCRIPTION Puts into output .exp file in the .rdata section [STACKSIZE|HEAPSIZE] [ , ] Generates --stack|--heap , in the output .drectve section. The linker will see this and act upon it. [CODE|DATA] + SECTIONS ( + )* = READ | WRITE | EXECUTE | SHARED Generates --attr in the output .drectve section. The linker will see this and act upon it. A -export: in a .drectve section in an input .o or .a file to this program is equivalent to a EXPORTS 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\""); 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 --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 */ #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 #ifdef HAVE_SYS_WAIT_H #include #else #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 #endif char *as_name = "as"; 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; int yyparse(); int yydebug; static char *def_file; static char *program_name; char *strrchr (); char *strdup (); static int machine; int killat; static int verbose; FILE *output_def; FILE *base_file; #ifdef DLLTOOL_ARM static char *mname = "arm"; #endif #ifdef DLLTOOL_I386 static char *mname = "i386"; #endif #ifdef DLLTOOL_PPC static char *mname = "ppc"; #endif #define PATHMAX 250 /* What's the right name for this ? */ /* This bit of assemly does jmp * .... s set how_jtab_roff to mark where the 32bit abs branch should go */ unsigned char i386_jtab[] = { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, 0x90, 0x90}; unsigned char arm_jtab[] = { 0x00, 0xc0, 0x9f, 0xe5, 0x00, 0xf0, 0x9c, 0xe5, 0, 0, 0, 0}; /* If I understand what is going on here, this will need more for ppc support, but this lets the program start. Kim Knuttila (krk@cygnus.com) */ unsigned char ppc_jtab[] = { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, 0x90, 0x90}; char outfile[PATHMAX]; 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; char *how_align_short; char *how_align_long; char *how_bfd_target; enum bfd_architecture how_bfd_arch; 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 */ } 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),2, } , { 0} }; char * rvaafter (machine) int machine; { switch (machine) { case MARM: return ""; case M386: return ""; case MPPC: return ""; } return ""; } char * rvabefore (machine) int machine; { switch (machine) { case MARM: return ".rva\t"; case M386: return ".rva\t"; case MPPC: return ".rva\t"; } return ""; } char * asm_prefix (machine) int machine; { switch (machine) { case MARM: return ""; case M386: return "_"; case MPPC: return ""; } 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; FILE *yyin; /* communications with flex */ extern int linenumber; void process_def_file (name) char *name; { FILE *f = fopen (name, FOPEN_RT); if (!f) { fprintf (stderr, "%s: Can't open def file %s\n", program_name, 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 *internal_name; int ordinal; int constant; int noname; int hint; 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_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 () { fprintf (stderr, "%s: Syntax error in def file %s:%d\n", program_name, def_file, linenumber); return 0; } void def_exports (name, internal_name, ordinal, noname, constant) char *name; char *internal_name; int ordinal; int noname; int constant; { 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->next = d_exports; d_exports = p; d_nfuncs++; } void def_name (name, base) char *name; int base; { if (verbose) fprintf (stderr, "%s NAME %s base %x\n", program_name, name, base); if (d_is_dll) { fprintf (stderr, "Can't have LIBRARY and NAME\n"); } d_name = name; d_is_exe = 1; } void def_library (name, base) char *name; int base; { if (verbose) printf ("%s: LIBRARY %s base %x\n", program_name, name, base); if (d_is_exe) { fprintf (stderr, "%s: Can't have LIBRARY and NAME\n", program_name); } d_name = name; 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; { if (verbose) fprintf (stderr, "%s: IMPORTS are ignored", program_name); } void def_version (major, minor) int major; int 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 run (what, args) char *what; char *args; { char *s; int pid; int i; char **argv; extern char **environ; if (verbose) fprintf (stderr, "%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) { argv[i++] = s; while (*s != ' ' && *s != 0) s++; if (*s == 0) break; *s++ = 0; } argv[i++] = 0; pid = vfork (); if (pid == 0) { execvp (what, argv); fprintf (stderr, "%s: can't exec %s\n", program_name, what); exit (1); } else if (pid == -1) { extern int errno; fprintf (stderr, "%s: vfork failed, %d\n", program_name, errno); exit (1); } else { int status; waitpid (pid, &status, 0); if (status) { if (WIFSIGNALED (status)) { fprintf (stderr, "%s: %s %s terminated with signal %d\n", program_name, what, args, WTERMSIG (status)); exit (1); } if (WIFEXITED (status)) { fprintf (stderr, "%s: %s %s terminated with exit status %d\n", program_name, what, args, WEXITSTATUS (status)); exit (1); } } } } /* read in and block out the base relocations */ static void basenames (abfd) bfd *abfd; { } void scan_open_obj_file (abfd) bfd *abfd; { /* Look for .drectve's */ 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); if (verbose) fprintf (stderr, "%s: Sucking in info from %s\n", program_name, 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); } basenames (abfd); if (verbose) fprintf (stderr, "%s: Done readin\n", program_name); } void scan_obj_file (filename) char *filename; { bfd *f = bfd_openr (filename, 0); if (!f) { fprintf (stderr, "%s: Unable to open object file %s\n", program_name, 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); } } else if (bfd_check_format (f, bfd_object)) { scan_open_obj_file (f); } bfd_close (f); } /**********************************************************************/ 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\n", ASM_C, i, exp->name, exp->internal_name, exp->ordinal, exp->noname ? "NONAME " : "", exp->constant ? "CONSTANT" : ""); } } /* Generate the .exp file */ int sfunc (a, b) long *a; long *b; { return *a - *b; } static void flush_page (f, need, page_addr, on_page) FILE *f; int *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%x\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); } void gen_def_file () { int i; export_type *exp; 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, '.') ? "\"" : ""; fprintf (output_def, "\t%s%s%s @ %d%s ; %s\n", quote, exp->name, quote, exp->ordinal, exp->noname ? " NONAME" : "", cplus_demangle (exp->internal_name, DMGL_ANSI | DMGL_PARAMS)); } } void gen_exp_file () { FILE *f; int i; export_type *exp; dlist_type *dl; sprintf (outfile, "t%s", exp_name); if (verbose) fprintf (stderr, "%s: Generate exp file %s\n", program_name, exp_name); f = fopen (outfile, FOPEN_WT); if (!f) { fprintf (stderr, "%s: Unable to open output file %s\n", program_name, outfile); exit (1); } if (verbose) { fprintf (stderr, "%s: Opened file %s\n", program_name, outfile); } 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%x %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%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) { fprintf (f, "\t%s\t__imp_%s\n", ASM_GLOBAL, 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 = malloc (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; 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++) { addr = copy[j]; if ((addr & PAGE_MASK) != page_addr) { flush_page (f, need, page_addr, on_page); on_page = 0; page_addr = addr & PAGE_MASK; } need[on_page++] = addr; } flush_page (f, need, page_addr, on_page); /* fprintf (f, "\t%s\t0,0\t%s End\n", ASM_LONG, ASM_C);*/ } } fclose (f); /* assemble the file */ sprintf (outfile, "-o %s t%s", exp_name, exp_name); run (as_name, outfile); if (dontdeltemps == 0) { sprintf (outfile, "t%s", exp_name); unlink (outfile); } } static char * xlate (char *name) { if (add_underscore) { char *copy = malloc (strlen (name) + 2); copy[0] = '_'; strcpy (copy + 1, name); name = copy; } if (killat) { char *p; p = strchr (name, '@'); if (p) *p = 0; } return name; } /**********************************************************************/ 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); } } typedef struct { int id; const char *name; int flags; asection *sec; asymbol *sym; asymbol **sympp; int size; unsigned char *data; } sinfo; #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}, { DATA, ".data", SEC_DATA}, { BSS,".bss" }, { IDATA7, ".idata$7",SEC_HAS_CONTENTS}, { IDATA5, ".idata$5", SEC_HAS_CONTENTS}, { IDATA4, ".idata$4", SEC_HAS_CONTENTS}, { IDATA6,".idata$6", SEC_HAS_CONTENTS} }; /* This is what we're trying to make .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" */ 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%d.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, "%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); 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, "-o %ss%d.o %ss%d.s", prefix, i, prefix, i); run (as_name, outfile); } else { bfd *abfd; asymbol *exp_label; asymbol *iname; asymbol *iname_lab; asymbol **iname_lab_pp; asymbol *ptrs[NSECS+3+1]; /* one symbol for each section, 2 extra + a null */ char *outname = xmalloc (10); int oidx = 0; sprintf (outname, "ds%d.o", i); abfd = bfd_openw (outname, HOW_BFD_TARGET); if (!abfd) { fprintf (stderr, "%s: bfd_open failed open output file %s\n", program_name, outname); exit (1); } bfd_set_format (abfd, bfd_object); bfd_set_arch_mach (abfd, HOW_BFD_ARCH, 0); 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); 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; oidx++; } exp_label = bfd_make_empty_symbol(abfd); exp_label->name = make_label ("",exp->name); exp_label->section = secdata[TEXT].sec; exp_label->flags = BSF_GLOBAL; exp_label->value = 0; ptrs[oidx++] = exp_label; 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; 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; ptrs[oidx++] = iname; iname_lab_pp = ptrs + oidx; ptrs[oidx++] = iname_lab; 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: 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; 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) { int idx = exp->hint + 1; 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_32); rel->sym_ptr_ptr = iname_lab_pp; sec->orelocation = rpp; sec->reloc_count = 1; break; } } { 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; } } static bfd * make_head() { FILE * f = fopen ("dh.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, "-o dh.o dh.s"); run (as_name, outfile); return bfd_openr ("dh.o", HOW_BFD_TARGET); } static bfd * make_tail() { FILE * f = fopen ("dt.s", FOPEN_WT); fprintf (f, "\t.section .idata$7\n"); 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); 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); } fclose (f); sprintf (outfile, "-o dt.o dt.s"); run (as_name, outfile); return bfd_openr ("dt.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) { fprintf (stderr, "%s: Can't open .lib file %s\n", program_name, imp_name); exit (1); } 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(); 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; bfd_set_archive_head (outarch, head); bfd_close (outarch); /* Delete all the temp files */ if (dontdeltemps == 0) { sprintf (outfile, "dh.o"); unlink (outfile); sprintf (outfile, "dh.s"); unlink (outfile); sprintf (outfile, "dt.o"); unlink (outfile); sprintf (outfile, "dt.s"); unlink (outfile); } if (dontdeltemps < 2) for (i = 0, exp = d_exports; exp; i++, exp = exp->next) { sprintf (outfile, "ds%d.o",i); unlink (outfile); } } /**********************************************************************/ /* 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; 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; if (verbose) fprintf (stderr, "Warning, ignoring duplicate EXPORT %s %d,%d\n", a->name, a->ordinal, b->ordinal); 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); } } /* 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 = 0; int i; char *ptr; qsort (d_export_vec, d_nfuncs, sizeof (export_type *), pfunc); /* fill in the unset ordinals with ones from our range */ ptr = (char *) malloc (65536); memset (ptr, 0, 65536); /* 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 == 0) lowest = d_export_vec[i]->ordinal; } } /* Start at 1 for compatibility with MS toolchain. */ if (lowest == 0) lowest = 1; for (i = 0; i < d_nfuncs; i++) { if (d_export_vec[i]->ordinal == -1) { int j; for (j = lowest; j < 65536; j++) if (ptr[j] == 0) { ptr[j] = 1; d_export_vec[i]->ordinal = j; goto done; } for (j = 1; j < lowest; 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; } } int alphafunc(av,bv) void *av; void *bv; { export_type **a = av; export_type **b = bv; return strcmp ((*a)->name, (*b)->name); } 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); 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++; } } /**********************************************************************/ void usage (file, status) FILE *file; int status; { fprintf (file, "Usage %s \n", program_name); fprintf (file, " --machine \n"); fprintf (file, " --output-exp Generate export file.\n"); fprintf (file, " --output-lib Generate input library.\n"); fprintf (file, " --add-indirect Add dll indirects to export file.\n"); fprintf (file, " --dllname Name of input dll to put into output lib.\n"); fprintf (file, " --def Name input .def file\n"); fprintf (file, " --output-def Name output .def file\n"); fprintf (file, " --base-file Read linker generated base file\n"); fprintf (file, " --no-idata4 Don't generate idata$4 section\n"); fprintf (file, " --no-idata5 Don't generate idata$5 section\n"); fprintf (file, " -v Verbose\n"); fprintf (file, " -U Add underscores to .lib\n"); fprintf (file, " -k Kill @ from exported names\n"); fprintf (file, " --as Use for assembler\n"); fprintf (file, " --nodelete Keep temp files.\n"); exit (status); } #define OPTION_NO_IDATA4 'x' #define OPTION_NO_IDATA5 'c' static struct option long_options[] = { {"nodelete", 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'}, {"output-lib", required_argument, NULL, 'l'}, {"def", required_argument, NULL, 'd'}, {"add-underscore", no_argument, NULL, 'U'}, {"killat", no_argument, NULL, 'k'}, {"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'}, {0} }; int main (ac, av) int ac; char **av; { int c; int i; char *firstarg = 0; program_name = av[0]; oav = av; while ((c = getopt_long (ac, av, "xcz:S:R:A:puaD:l:e:nkvbUh?m:yd:", long_options, 0)) != EOF) { switch (c) { case OPTION_NO_IDATA4: no_idata4 = 1; break; case OPTION_NO_IDATA5: no_idata5 = 1; break; case 'S': as_name = 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': case '?': usage (stderr, 0); break; case 'm': mname = optarg; break; case 'v': verbose = 1; break; case 'y': yydebug = 1; break; case 'U': add_underscore = 1; break; case 'k': killat = 1; break; case 'd': def_file = optarg; break; case 'n': dontdeltemps++; break; case 'b': base_file = fopen (optarg, FOPEN_RB); if (!base_file) { fprintf (stderr, "%s: Unable to open base-file %s\n", av[0], optarg); exit (1); } 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 (!dll_name && exp_name) { char len = strlen (exp_name) + 5; dll_name = xmalloc (len); strcpy (dll_name, exp_name); strcat (dll_name, ".dll"); } 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 = strdup (imp_name); for (p = imp_name_lab; *p; *p++) { if (!isalpha (*p) && !isdigit (*p)) *p = '_'; } head_label = make_label("_head_", imp_name_lab); gen_lib_file (); } if (output_def) gen_def_file (); return 0; }