From d3ca9a538c9305de81734d296c5ddcca8e5fa3ed Mon Sep 17 00:00:00 2001 From: DJ Delorie Date: Tue, 17 Nov 1998 03:18:06 +0000 Subject: [PATCH] * emultempl/pe.em (gld_i386_finish): generate import library * deffile.h: add hint member. * pe-dll.c (pe_dll_generate_implib): New function with helpers; generates the import library directly from the export table. (fill_edata): remember the actual hint for the import library. --- ld/ChangeLog | 8 + ld/emultempl/pe.em | 27 ++- ld/pe-dll.c | 469 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 497 insertions(+), 7 deletions(-) diff --git a/ld/ChangeLog b/ld/ChangeLog index d1ce60a4573..52e18666484 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,11 @@ +Mon Nov 16 22:14:07 1998 DJ Delorie + + * emultempl/pe.em (gld_i386_finish): generate import library + * deffile.h: add hint member. + * pe-dll.c (pe_dll_generate_implib): New function with helpers; + generates the import library directly from the export table. + (fill_edata): remember the actual hint for the import library. + Sat Nov 14 14:36:24 1998 Ian Lance Taylor * ld.1: Some cleanups from NOKUBI Hirotaka . diff --git a/ld/emultempl/pe.em b/ld/emultempl/pe.em index 34f6f7ae1f1..d241837ebd2 100644 --- a/ld/emultempl/pe.em +++ b/ld/emultempl/pe.em @@ -70,6 +70,7 @@ int pe_dll_do_default_excludes = 1; int pe_dll_kill_ats = 0; int pe_dll_stdcall_aliases = 0; int pe_enable_stdcall_fixup = -1; /* 0=disable 1=enable */ +static char *pe_implib_filename = 0; extern const char *output_filename; @@ -108,6 +109,7 @@ gld_${EMULATION_NAME}_before_parse() #define OPTION_STDCALL_ALIASES (OPTION_KILL_ATS + 1) #define OPTION_ENABLE_STDCALL_FIXUP (OPTION_STDCALL_ALIASES + 1) #define OPTION_DISABLE_STDCALL_FIXUP (OPTION_ENABLE_STDCALL_FIXUP + 1) +#define OPTION_IMPLIB_FILENAME (OPTION_DISABLE_STDCALL_FIXUP + 1) static struct option longopts[] = { @@ -127,6 +129,7 @@ static struct option longopts[] = {"stack", required_argument, NULL, OPTION_STACK}, {"subsystem", required_argument, NULL, OPTION_SUBSYSTEM}, {"support-old-code", no_argument, NULL, OPTION_SUPPORT_OLD_CODE}, +#ifdef TARGET_IS_i386pe /* getopt allows abbreviations, so we do this to stop it from treating -o as an abbreviation for this option */ {"output-def", required_argument, NULL, OPTION_OUT_DEF}, @@ -137,6 +140,8 @@ static struct option longopts[] = {"add-stdcall-alias", no_argument, NULL, OPTION_STDCALL_ALIASES}, {"enable-stdcall-fixup", no_argument, NULL, OPTION_ENABLE_STDCALL_FIXUP}, {"disable-stdcall-fixup", no_argument, NULL, OPTION_DISABLE_STDCALL_FIXUP}, + {"out-implib", required_argument, NULL, OPTION_IMPLIB_FILENAME}, +#endif {NULL, no_argument, NULL, 0} }; @@ -198,13 +203,16 @@ gld_${EMULATION_NAME}_list_options (file) fprintf (file, _(" --stack Set size of the initial stack\n")); fprintf (file, _(" --subsystem [:] Set required OS subsystem [& version]\n")); fprintf (file, _(" --support-old-code Support interworking with old code\n")); - fprintf (file, _(" --output-def Generate a .DEF file for the built DLL\n")); - fprintf (file, _(" --export-all-symbols Automatically export all globals to DLL\n")); - fprintf (file, _(" --exclude-symbols sym,sym,... Exclude symbols from automatic export\n")); - fprintf (file, _(" --kill-at Remove @nn from exported symbols\n")); +#ifdef TARGET_IS_i386pe fprintf (file, _(" --add-stdcall-alias Export symbols with and without @nn\n")); - fprintf (file, _(" --enable-stdcall-fixup Link _sym to _sym@nn without warnings\n")); fprintf (file, _(" --disable-stdcall-fixup Don't link _sym to _sym@nn\n")); + fprintf (file, _(" --enable-stdcall-fixup Link _sym to _sym@nn without warnings\n")); + fprintf (file, _(" --exclude-symbols sym,sym,... Exclude symbols from automatic export\n")); + fprintf (file, _(" --export-all-symbols Automatically export all globals to DLL\n")); + fprintf (file, _(" --kill-at Remove @nn from exported symbols\n")); + fprintf (file, _(" --out-implib Generate import library\n")); + fprintf (file, _(" --output-def Generate a .DEF file for the built DLL\n")); +#endif } static void @@ -424,6 +432,9 @@ gld_${EMULATION_NAME}_parse_args(argc, argv) case OPTION_DISABLE_STDCALL_FIXUP: pe_enable_stdcall_fixup = 0; break; + case OPTION_IMPLIB_FILENAME: + pe_implib_filename = xstrdup (optarg); + break; } return 1; } @@ -773,7 +784,11 @@ gld_${EMULATION_NAME}_finish () { #ifdef TARGET_IS_i386pe if (link_info.shared) - pe_dll_fill_sections (output_bfd, &link_info); + { + pe_dll_fill_sections (output_bfd, &link_info); + if (pe_implib_filename) + pe_dll_generate_implib (pe_def_file, pe_implib_filename); + } if (pe_out_def_filename) pe_dll_generate_def_file (pe_out_def_filename); #endif diff --git a/ld/pe-dll.c b/ld/pe-dll.c index 51520c49a13..fddf42fed68 100644 --- a/ld/pe-dll.c +++ b/ld/pe-dll.c @@ -515,7 +515,7 @@ fill_edata (abfd, info) bfd *abfd; struct bfd_link_info *info; { - int i; + int i, hint; unsigned char *edirectory; unsigned long *eaddresses; unsigned long *enameptrs; @@ -550,6 +550,7 @@ fill_edata (abfd, info) bfd_put_32 (abfd, ERVA (eordinals), edata_d + 36); /* Ok, now for the filling in part */ + hint = 0; for (i = 0; i < export_table_size; i++) { int s = exported_symbols[i]; @@ -569,6 +570,7 @@ fill_edata (abfd, info) enamestr += strlen (enamestr) + 1; bfd_put_16 (abfd, i, (void *) eordinals); enameptrs++; + pe_def_file->exports[s].hint = hint++; } eordinals++; } @@ -894,6 +896,471 @@ pe_dll_generate_def_file (pe_out_def_filename) } } +/************************************************************************ + + Generate the import library + + ************************************************************************/ + +static asymbol **symtab; +static int symptr; +static int tmp_seq; +static const char *dll_filename; +static char *dll_symname; + +static asection * +quick_section(abfd, name, flags, align) + bfd *abfd; + const char *name; + int flags; + int align; +{ + asection *sec; + asymbol *sym; + + sec = bfd_make_section_old_way(abfd, name); + sec->output_section = sec; + bfd_set_section_flags (abfd, sec, flags); + bfd_set_section_alignment (abfd, sec, align); + + sym = bfd_make_empty_symbol(abfd); + symtab[symptr++] = sym; + sym->name = sec->name; + sym->section = sec; + sym->flags = BSF_LOCAL; + sym->value = 0; + + return sec; +} + +static void +quick_symbol (abfd, n1, n2, n3, sec, flags, addr) + bfd *abfd; + char *n1; + char *n2; + char *n3; + asection *sec; + int flags; + int addr; +{ + asymbol *sym = bfd_make_empty_symbol(abfd); + char *name = (char *) xmalloc (strlen(n1) + strlen(n2) + strlen(n3) + 1); + strcpy (name, n1); + strcat (name, n2); + strcat (name, n3); + sym->name = name; + sym->section = sec; + sym->flags = flags; + sym->value = addr; + symtab[symptr++] = sym; +} + +static arelent **reltab = 0; +static int relcount = 0, relsize = 0; + +static void +quick_reloc (abfd, address, which_howto, symidx) + bfd *abfd; + int address; + int which_howto; + int symidx; +{ + if (relcount >= (relsize-1)) + { + relsize += 10; + if (reltab) + reltab = (arelent **) xrealloc (reltab, relsize * sizeof (arelent *)); + else + reltab = (arelent **) xmalloc (relsize * sizeof (arelent *)); + } + reltab[relcount] = (arelent *) xmalloc (sizeof (arelent)); + reltab[relcount]->address = address; + reltab[relcount]->addend = 0; + reltab[relcount]->howto = bfd_reloc_type_lookup (abfd, which_howto); + reltab[relcount]->sym_ptr_ptr = symtab + symidx; + relcount++; +} + +static void +save_relocs (asection *sec) +{ + reltab[relcount] = 0; + sec->orelocation = reltab; + sec->reloc_count = relcount; + reltab = 0; + relcount = relsize = 0; +} + +#define UNDSEC (asection *) &bfd_und_section + +/* + * .section .idata$2 + * .global __head_my_dll + * __head_my_dll: + * .rva hname + * .long 0 + * .long 0 + * .rva __my_dll_iname + * .rva fthunk + * + * .section .idata$5 + * .long 0 + * fthunk: + * + * .section .idata$4 + * .long 0 + * hname: + */ + +#define BFD_OPEN_OLDWAY 0 + +bfd * +make_head (parent) + bfd *parent; +{ + asection *id2, *id5, *id4; + unsigned char *d2, *d5, *d4; + +#if BFD_OPEN_OLDWAY + bfd *abfd = bfd_openw ("dh.o", 0); +#else + bfd *abfd = bfd_create ("dh.o", parent); + bfd_make_writable (abfd); +#endif + bfd_set_format (abfd, bfd_object); + bfd_set_arch_mach (abfd, bfd_arch_i386, 0); + + symptr = 0; + symtab = (asymbol **) xmalloc (6 * sizeof (asymbol *)); + id2 = quick_section(abfd, ".idata$2", SEC_HAS_CONTENTS, 2); + id5 = quick_section(abfd, ".idata$5", SEC_HAS_CONTENTS, 2); + id4 = quick_section(abfd, ".idata$4", SEC_HAS_CONTENTS, 2); + quick_symbol (abfd, "__head_", dll_symname, "", id2, BSF_GLOBAL, 0); + quick_symbol (abfd, "_", dll_symname, "_iname", UNDSEC, BSF_GLOBAL, 0); + + bfd_set_section_size(abfd, id2, 20); + d2 = (unsigned char *) xmalloc (20); + memset (d2, 0, 20); + d2[0] = d2[16] = 4; /* reloc addend */ + quick_reloc(abfd, 0, BFD_RELOC_RVA, 2); + quick_reloc(abfd, 12, BFD_RELOC_RVA, 4); + quick_reloc(abfd, 16, BFD_RELOC_RVA, 1); + save_relocs(id2); + + bfd_set_section_size (abfd, id5, 4); + d5 = (unsigned char *) xmalloc (4); + memset (d5, 0, 4); + + bfd_set_section_size (abfd, id4, 4); + d4 = (unsigned char *) xmalloc (4); + memset (d4, 0, 4); + + bfd_set_symtab(abfd, symtab, symptr); + + bfd_set_section_contents(abfd, id2, d2, 0, 20); + bfd_set_section_contents(abfd, id5, d5, 0, 4); + bfd_set_section_contents(abfd, id4, d4, 0, 4); + +#if BFD_OPEN_OLDWAY + bfd_close (abfd); + return bfd_openr("dh.o", 0); +#else + bfd_make_readable (abfd); + return abfd; +#endif +} + +/* + * .section .idata$4 + * .long 0 + * .section .idata$5 + * .long 0 + * .section idata$7 + * .global __my_dll_iname + *__my_dll_iname: + * .asciz "my.dll" + */ + +bfd * +make_tail (parent) + bfd *parent; +{ + asection *id4, *id5, *id7; + unsigned char *d4, *d5, *d7; + int len; + +#if BFD_OPEN_OLDWAY + bfd *abfd = bfd_openw ("dt.o", 0); +#else + bfd *abfd = bfd_create ("dt.o", parent); + bfd_make_writable (abfd); +#endif + bfd_set_format (abfd, bfd_object); + bfd_set_arch_mach (abfd, bfd_arch_i386, 0); + + symptr = 0; + symtab = (asymbol **) xmalloc (5 * sizeof (asymbol *)); + id4 = quick_section(abfd, ".idata$4", SEC_HAS_CONTENTS, 2); + id5 = quick_section(abfd, ".idata$5", SEC_HAS_CONTENTS, 2); + id7 = quick_section(abfd, ".idata$7", SEC_HAS_CONTENTS, 2); + quick_symbol (abfd, "_", dll_symname, "_iname", id7, BSF_GLOBAL, 0); + + bfd_set_section_size(abfd, id4, 4); + d4 = (unsigned char *) xmalloc (4); + memset (d4, 0, 4); + + bfd_set_section_size (abfd, id5, 4); + d5 = (unsigned char *) xmalloc (4); + memset (d5, 0, 4); + + len = strlen(dll_filename)+1; + if (len & 1) + len ++; + bfd_set_section_size (abfd, id7, len); + d7 = (unsigned char *) xmalloc (len); + strcpy (d7, dll_filename); + + bfd_set_symtab(abfd, symtab, symptr); + + bfd_set_section_contents(abfd, id4, d4, 0, 4); + bfd_set_section_contents(abfd, id5, d5, 0, 4); + bfd_set_section_contents(abfd, id7, d7, 0, len); + +#if BFD_OPEN_OLDWAY + bfd_close (abfd); + return bfd_openr ("dt.o", 0); +#else + bfd_make_readable (abfd); + return abfd; +#endif +} + +/* + * .text + * .global _function + * .global ___imp_function + * .global __imp__function + *_function: + * jmp *__imp__function: + * + * .section idata$7 + * .long __head_my_dll + * + * .section .idata$5 + *___imp_function: + *__imp__function: + *iat? + * .section .idata$4 + *iat? + * .section .idata$6 + *ID: + * .short + * .asciz "function" xlate? (add underscore, kill at) + */ + +unsigned char jmp_ix86_bytes[] = { + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, 0x90, 0x90 +}; + + +bfd * +make_one (exp, parent) + def_file_export *exp; + bfd *parent; +{ + asection *tx, *id7, *id5, *id4, *id6; + unsigned char *td, *d7, *d5, *d4, *d6; + int len; + char *oname; + bfd *abfd; + + oname = (char *) xmalloc (20); + sprintf(oname, "ds%d.o", tmp_seq); + tmp_seq++; +#if BFD_OPEN_OLDWAY + abfd = bfd_openw (oname, 0); +#else + abfd = bfd_create (oname, parent); + bfd_make_writable (abfd); +#endif + bfd_set_format (abfd, bfd_object); + bfd_set_arch_mach (abfd, bfd_arch_i386, 0); + + symptr = 0; + symtab = (asymbol **) xmalloc (10 * sizeof (asymbol *)); + tx = quick_section(abfd, ".text", SEC_CODE|SEC_HAS_CONTENTS, 2); + id7 = quick_section(abfd, ".idata$7", SEC_HAS_CONTENTS, 2); + id5 = quick_section(abfd, ".idata$5", SEC_HAS_CONTENTS, 2); + id4 = quick_section(abfd, ".idata$4", SEC_HAS_CONTENTS, 2); + id6 = quick_section(abfd, ".idata$6", SEC_HAS_CONTENTS, 2); + quick_symbol (abfd, "_", exp->name, "", tx, BSF_GLOBAL, 0); + quick_symbol (abfd, "__head_", dll_symname, "", UNDSEC, BSF_GLOBAL, 0); + quick_symbol (abfd, "___imp_", exp->name, "", id5, BSF_GLOBAL, 0); + quick_symbol (abfd, "__imp__", exp->name, "", id5, BSF_GLOBAL, 0); + + bfd_set_section_size (abfd, tx, 8); + td = (unsigned char *) xmalloc (8); + memcpy (td, jmp_ix86_bytes, 8); + quick_reloc (abfd, 2, BFD_RELOC_32, 2); + save_relocs (tx); + + bfd_set_section_size(abfd, id7, 4); + d7 = (unsigned char *) xmalloc (4); + memset (d7, 0, 4); + quick_reloc (abfd, 0, BFD_RELOC_RVA, 6); + save_relocs (id7); + + bfd_set_section_size (abfd, id5, 4); + d5 = (unsigned char *) xmalloc (4); + memset (d5, 0, 4); + if (exp->flag_noname) + { + d5[0] = exp->ordinal; + d5[1] = exp->ordinal >> 8; + d5[3] = 0x80; + } + else + { + quick_reloc (abfd, 0, BFD_RELOC_RVA, 4); + save_relocs (id5); + } + + bfd_set_section_size (abfd, id4, 4); + d4 = (unsigned char *) xmalloc (4); + memset (d4, 0, 4); + if (exp->flag_noname) + { + d5[0] = exp->ordinal; + d5[1] = exp->ordinal >> 8; + d5[3] = 0x80; + } + else + { + quick_reloc (abfd, 0, BFD_RELOC_RVA, 4); + save_relocs (id4); + } + + if (exp->flag_noname) + { + len = 0; + bfd_set_section_size(abfd, id6, 0); + } + else + { + len = strlen(exp->name) + 3; + if (len & 1) + len++; + bfd_set_section_size(abfd, id6, len); + d6 = (unsigned char *) xmalloc (len); + memset (d6, 0, len); + d6[0] = exp->hint & 0xff; + d6[1] = exp->hint >> 8; + strcpy(d6+2, exp->name); + } + + bfd_set_symtab(abfd, symtab, symptr); + + bfd_set_section_contents(abfd, tx, td, 0, 8); + bfd_set_section_contents(abfd, id7, d7, 0, 4); + bfd_set_section_contents(abfd, id5, d5, 0, 4); + bfd_set_section_contents(abfd, id4, d4, 0, 4); + bfd_set_section_contents(abfd, id6, d6, 0, len); + +#if BFD_OPEN_OLDWAY + bfd_close(abfd); + return bfd_openr(oname, 0); +#else + bfd_make_readable (abfd); + return abfd; +#endif +} + +void +pe_dll_generate_implib (def, impfilename) + def_file *def; + char *impfilename; +{ + int i; + /*export_type *exp;*/ + bfd *ar_head; + bfd *ar_tail; + bfd *outarch; + bfd * head = 0; + + dll_filename = def->name; + if (dll_filename == 0) + { + dll_filename = dll_name; + for (i=0; impfilename[i]; i++) + if (impfilename[i] == '/' || impfilename[i] == '\\') + dll_filename = impfilename+1; + } + dll_symname = xstrdup (dll_filename); + for (i=0; dll_symname[i]; i++) + if (!isalnum (dll_symname[i])) + dll_symname[i] = '_'; + + unlink (impfilename); + + outarch = bfd_openw (impfilename, 0); + + if (!outarch) + { + /* xgettext:c-format */ + einfo (_("%XCan't open .lib file: %s\n"), impfilename); + return; + } + + /* xgettext:c-format */ + einfo (_("Creating library file: %s\n"), impfilename); + + 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 (outarch); + ar_tail = make_tail (outarch); + + if (ar_head == NULL || ar_tail == NULL) + return; + + for (i = 0; inum_exports; i++) + { + bfd *n = make_one (def->exports+i, outarch); + 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)) + einfo ("%Xbfd_set_archive_head: %s\n", bfd_errmsg (bfd_get_error ())); + + if (! bfd_close (outarch)) + einfo ("%Xbfd_close %s: %s\n", impfilename, bfd_errmsg (bfd_get_error ())); + + while (head != NULL) + { + bfd *n = head->next; + bfd_close (head); + head = n; + } + + unlink ("dh.o"); + unlink ("dt.o"); + for (i=0; i