X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=ld%2Fpe-dll.c;h=7617337e2d61cec487a91f9a470a6dba83ea6546;hb=396a24671935a40f9b9681416e8837488fefcea2;hp=fd70c781b55e4ced87fc996568995e565eec400a;hpb=658957dba80424f79b0efb3860e3e4e1d7798fc8;p=deliverable%2Fbinutils-gdb.git diff --git a/ld/pe-dll.c b/ld/pe-dll.c index fd70c781b5..7617337e2d 100644 --- a/ld/pe-dll.c +++ b/ld/pe-dll.c @@ -32,7 +32,7 @@ #include "ldlang.h" #include "ldwrite.h" #include "ldmisc.h" -#include "ldgram.h" +#include #include "ldmain.h" #include "ldfile.h" #include "ldemul.h" @@ -59,7 +59,7 @@ code modifications). 2. This is done completely in bounds of the PE specification (to be fair, - there's a place where it pokes nose out of, but in practise it works). + there's a place where it pokes nose out of, but in practice it works). So, resulting module can be used with any other PE compiler/linker. 3. Auto-import is fully compatible with standard import method and they @@ -74,7 +74,7 @@ The obvious and only way to get rid of dllimport insanity is to make client access variable directly in the DLL, bypassing extra dereference. I.e., - whenever client contains someting like + whenever client contains something like mov dll_var,%eax, @@ -82,7 +82,7 @@ DLL. The aim is to make OS loader do so, and than make ld help with that. Import section of PE made following way: there's a vector of structures each describing imports from particular DLL. Each such structure points - to two other parellel vectors: one holding imported names, and one which + to two other parallel vectors: one holding imported names, and one which will hold address of corresponding imported name. So, the solution is de-vectorize these structures, making import locations be sparse and pointing directly into code. Before continuing, it is worth a note that, @@ -104,7 +104,7 @@ above: PE specification rambles that name vector (OriginalFirstThunk) should run in parallel with addresses vector (FirstThunk), i.e. that they should have same number of elements and terminated with zero. We violate - this, since FirstThunk points directly into machine code. But in practise, + this, since FirstThunk points directly into machine code. But in practice, OS loader implemented the sane way: it goes thru OriginalFirstThunk and puts addresses to FirstThunk, not something else. It once again should be noted that dll and symbol name structures are reused across fixup entries @@ -115,7 +115,7 @@ in windows9x kernel32.dll, so if you use it, you have two IMAGE_IMPORT_DESCRIPTORS for kernel32.dll). Yet other question is whether referencing the same PE structures several times is valid. The answer is why - not, prohibitting that (detecting violation) would require more work on + not, prohibiting that (detecting violation) would require more work on behalf of loader than not doing it. See also: ld/emultempl/pe.em. */ @@ -141,6 +141,7 @@ static bfd *filler_bfd; static struct sec *edata_s, *reloc_s; static unsigned char *edata_d, *reloc_d; static size_t edata_sz, reloc_sz; +static int runtime_pseudo_relocs_created = 0; typedef struct { @@ -228,12 +229,14 @@ static autofilter_entry_type autofilter_symbollist[] = /* Do not specify library suffix explicitly, to allow for dllized versions. */ static autofilter_entry_type autofilter_liblist[] = { - { "libgcc.", 7 }, - { "libstdc++.", 10 }, - { "libmingw32.", 11 }, - { "libg2c.", 7 }, - { "libsupc++.", 10 }, - { "libobjc.", 8 }, + { "libgcc", 6 }, + { "libstdc++", 9 }, + { "libmingw32", 10 }, + { "libmingwex", 10 }, + { "libg2c", 6 }, + { "libsupc++", 9 }, + { "libobjc", 7 }, + { "libgcj", 6 }, { NULL, 0 } }; @@ -257,6 +260,8 @@ static autofilter_entry_type autofilter_symbolprefixlist[] = /* { "__imp_", 6 }, */ /* Do __imp_ explicitly to save time. */ { "__rtti_", 7 }, + /* Don't re-export auto-imported symbols. */ + { "_nm_", 4 }, { "__builtin_", 10 }, /* Don't export symbols specifying internal DLL layout. */ { "_head_", 6 }, @@ -301,6 +306,10 @@ static bfd *make_singleton_name_thunk PARAMS ((const char *, bfd *)); static char *make_import_fixup_mark PARAMS ((arelent *)); static bfd *make_import_fixup_entry PARAMS ((const char *, const char *, const char *, bfd *)); +static bfd *make_runtime_pseudo_reloc + PARAMS ((const char *, const char *, int, bfd *)); +static bfd *pe_create_runtime_relocator_reference + PARAMS ((bfd *)); static unsigned int pe_get16 PARAMS ((bfd *, int)); static unsigned int pe_get32 PARAMS ((bfd *, int)); static unsigned int pe_as32 PARAMS ((void *)); @@ -581,7 +590,7 @@ process_def_file (abfd, info) sprintf (name, "%s%s", U("_imp_"), sn); blhe = bfd_link_hash_lookup (info->hash, name, - false, false, false); + FALSE, FALSE, FALSE); free (name); if (blhe && blhe->type == bfd_link_hash_defined) @@ -616,7 +625,8 @@ process_def_file (abfd, info) /* This will preserve internal_name, which may have been pointing to the same memory as name, or might not have. */ - char *tmp = xstrdup (pe_def_file->exports[i].name); + int lead_at = (*pe_def_file->exports[i].name =='@'); + char *tmp = xstrdup (pe_def_file->exports[i].name + lead_at); *(strchr (tmp, '@')) = 0; pe_def_file->exports[i].name = tmp; @@ -630,7 +640,8 @@ process_def_file (abfd, info) { if (strchr (pe_def_file->exports[i].name, '@')) { - char *tmp = xstrdup (pe_def_file->exports[i].name); + int lead_at = (*pe_def_file->exports[i].name == '@' ) ; + char *tmp = xstrdup (pe_def_file->exports[i].name + lead_at); *(strchr (tmp, '@')) = 0; if (auto_export (NULL, pe_def_file, tmp)) @@ -699,7 +710,8 @@ process_def_file (abfd, info) { char *name = (char *) xmalloc (strlen (pe_def_file->exports[i].internal_name) + 2); - if (pe_details->underscored) + if (pe_details->underscored + && (*pe_def_file->exports[i].internal_name != '@')) { *name = '_'; strcpy (name + 1, pe_def_file->exports[i].internal_name); @@ -709,7 +721,7 @@ process_def_file (abfd, info) blhe = bfd_link_hash_lookup (info->hash, name, - false, false, true); + FALSE, FALSE, TRUE); if (blhe && (blhe->type == bfd_link_hash_defined @@ -913,7 +925,8 @@ fill_exported_offsets (abfd, info) { char *name = (char *) xmalloc (strlen (pe_def_file->exports[i].internal_name) + 2); - if (pe_details->underscored) + if (pe_details->underscored + && (*pe_def_file->exports[i].internal_name != '@')) { *name = '_'; strcpy (name + 1, pe_def_file->exports[i].internal_name); @@ -923,7 +936,7 @@ fill_exported_offsets (abfd, info) blhe = bfd_link_hash_lookup (info->hash, name, - false, false, true); + FALSE, FALSE, TRUE); if (blhe && (blhe->type == bfd_link_hash_defined)) exported_symbol_offsets[i] = blhe->u.def.value; @@ -1809,13 +1822,27 @@ make_one (exp, parent) 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); - if (! exp->flag_data) - quick_symbol (abfd, U (""), exp->internal_name, "", tx, BSF_GLOBAL, 0); - quick_symbol (abfd, U ("_head_"), dll_symname, "", UNDSEC, BSF_GLOBAL, 0); - quick_symbol (abfd, U ("_imp__"), exp->internal_name, "", id5, BSF_GLOBAL, 0); - /* Symbol to reference ord/name of imported - symbol, used to implement auto-import. */ - quick_symbol (abfd, U("_nm__"), exp->internal_name, "", id6, BSF_GLOBAL, 0); + + if (*exp->internal_name == '@') + { + if (! exp->flag_data) + quick_symbol (abfd, "", exp->internal_name, "", tx, BSF_GLOBAL, 0); + quick_symbol (abfd, U ("_head_"), dll_symname, "", UNDSEC, BSF_GLOBAL, 0); + quick_symbol (abfd, U ("_imp_"), exp->internal_name, "", id5, BSF_GLOBAL, 0); + /* Fastcall applies only to functions, + so no need for auto-import symbol. */ + } + else + { + if (! exp->flag_data) + quick_symbol (abfd, U (""), exp->internal_name, "", tx, BSF_GLOBAL, 0); + quick_symbol (abfd, U ("_head_"), dll_symname, "", UNDSEC, BSF_GLOBAL, 0); + quick_symbol (abfd, U ("_imp__"), exp->internal_name, "", id5, BSF_GLOBAL, 0); + /* Symbol to reference ord/name of imported + data symbol, used to implement auto-import. */ + if (exp->flag_data) + quick_symbol (abfd, U("_nm__"), exp->internal_name, "", id6, BSF_GLOBAL,0); + } if (pe_dll_compat_implib) quick_symbol (abfd, U ("__imp_"), exp->internal_name, "", id5, BSF_GLOBAL, 0); @@ -1974,7 +2001,7 @@ make_import_fixup_mark (rel) struct symbol_cache_entry *sym = *rel->sym_ptr_ptr; bfd *abfd = bfd_asymbol_bfd (sym); - struct coff_link_hash_entry *myh = NULL; + struct bfd_link_hash_entry *bh; if (!fixup_name) { @@ -1996,15 +2023,20 @@ make_import_fixup_mark (rel) sprintf (fixup_name, "__fu%d_%s", counter++, sym->name); + bh = NULL; bfd_coff_link_add_one_symbol (&link_info, abfd, fixup_name, BSF_GLOBAL, current_sec, /* sym->section, */ - rel->address, NULL, true, false, - (struct bfd_link_hash_entry **) &myh); + rel->address, NULL, TRUE, FALSE, &bh); + + if (0) + { + struct coff_link_hash_entry *myh; + + myh = (struct coff_link_hash_entry *) bh; + printf ("type:%d\n", myh->type); + printf ("%s\n", myh->root.u.def.section->name); + } -#if 0 - printf ("type:%d\n", myh->type); - printf ("%s\n", myh->root.u.def.section->name); -#endif return fixup_name; } @@ -2067,15 +2099,112 @@ make_import_fixup_entry (name, fixup_name, dll_symname, parent) return abfd; } +/* .section .rdata_runtime_pseudo_reloc + .long addend + .rva __fuNN_SYM (pointer to reference (address) in text) */ + +static bfd * +make_runtime_pseudo_reloc (name, fixup_name, addend, parent) + const char *name ATTRIBUTE_UNUSED; + const char *fixup_name; + int addend; + bfd *parent; +{ + asection *rt_rel; + unsigned char *rt_rel_d; + char *oname; + bfd *abfd; + + oname = (char *) xmalloc (20); + sprintf (oname, "rtr%06d.o", tmp_seq); + tmp_seq++; + + abfd = bfd_create (oname, parent); + bfd_find_target (pe_details->object_target, abfd); + bfd_make_writable (abfd); + + bfd_set_format (abfd, bfd_object); + bfd_set_arch_mach (abfd, pe_details->bfd_arch, 0); + + symptr = 0; + symtab = (asymbol **) xmalloc (2 * sizeof (asymbol *)); + rt_rel = quick_section (abfd, ".rdata_runtime_pseudo_reloc", SEC_HAS_CONTENTS, 2); + + quick_symbol (abfd, "", fixup_name, "", UNDSEC, BSF_GLOBAL, 0); + + bfd_set_section_size (abfd, rt_rel, 8); + rt_rel_d = (unsigned char *) xmalloc (8); + rt_rel->contents = rt_rel_d; + memset (rt_rel_d, 0, 8); + bfd_put_32 (abfd, addend, rt_rel_d); + + quick_reloc (abfd, 4, BFD_RELOC_RVA, 1); + save_relocs (rt_rel); + + bfd_set_symtab (abfd, symtab, symptr); + + bfd_set_section_contents (abfd, rt_rel, rt_rel_d, 0, 8); + + bfd_make_readable (abfd); + return abfd; +} + +/* .section .rdata + .rva __pei386_runtime_relocator */ + +static bfd * +pe_create_runtime_relocator_reference (parent) + bfd *parent; +{ + asection *extern_rt_rel; + unsigned char *extern_rt_rel_d; + char *oname; + bfd *abfd; + + oname = (char *) xmalloc (20); + sprintf (oname, "ertr%06d.o", tmp_seq); + tmp_seq++; + + abfd = bfd_create (oname, parent); + bfd_find_target (pe_details->object_target, abfd); + bfd_make_writable (abfd); + + bfd_set_format (abfd, bfd_object); + bfd_set_arch_mach (abfd, pe_details->bfd_arch, 0); + + symptr = 0; + symtab = (asymbol **) xmalloc (2 * sizeof (asymbol *)); + extern_rt_rel = quick_section (abfd, ".rdata", SEC_HAS_CONTENTS, 2); + + quick_symbol (abfd, "", "__pei386_runtime_relocator", "", UNDSEC, BSF_NO_FLAGS, 0); + + bfd_set_section_size (abfd, extern_rt_rel, 4); + extern_rt_rel_d = (unsigned char *) xmalloc (4); + extern_rt_rel->contents = extern_rt_rel_d; + + quick_reloc (abfd, 0, BFD_RELOC_RVA, 1); + save_relocs (extern_rt_rel); + + bfd_set_symtab (abfd, symtab, symptr); + + bfd_set_section_contents (abfd, extern_rt_rel, extern_rt_rel_d, 0, 4); + + bfd_make_readable (abfd); + return abfd; +} + void -pe_create_import_fixup (rel) +pe_create_import_fixup (rel, s, addend) arelent *rel; + asection *s; + int addend; { char buf[300]; struct symbol_cache_entry *sym = *rel->sym_ptr_ptr; struct bfd_link_hash_entry *name_thunk_sym; const char *name = sym->name; char *fixup_name = make_import_fixup_mark (rel); + bfd *b; sprintf (buf, U ("_nm_thnk_%s"), name); @@ -2087,17 +2216,42 @@ pe_create_import_fixup (rel) add_bfd_to_link (b, b->filename, &link_info); /* If we ever use autoimport, we have to cast text section writable. */ - config.text_read_only = false; + config.text_read_only = FALSE; } - { - extern char * pe_data_import_dll; - char * dll_symname = pe_data_import_dll ? pe_data_import_dll : "unknown"; + if (addend == 0 || link_info.pei386_runtime_pseudo_reloc) + { + extern char * pe_data_import_dll; + char * dll_symname = pe_data_import_dll ? pe_data_import_dll : "unknown"; - bfd *b = make_import_fixup_entry (name, fixup_name, dll_symname, - output_bfd); - add_bfd_to_link (b, b->filename, &link_info); - } + b = make_import_fixup_entry (name, fixup_name, dll_symname, output_bfd); + add_bfd_to_link (b, b->filename, &link_info); + } + + if (addend != 0) + { + if (link_info.pei386_runtime_pseudo_reloc) + { + if (pe_dll_extra_pe_debug) + printf ("creating runtime pseudo-reloc entry for %s (addend=%d)\n", + fixup_name, addend); + b = make_runtime_pseudo_reloc (name, fixup_name, addend, output_bfd); + add_bfd_to_link (b, b->filename, &link_info); + + if (runtime_pseudo_relocs_created == 0) + { + b = pe_create_runtime_relocator_reference (output_bfd); + add_bfd_to_link (b, b->filename, &link_info); + } + runtime_pseudo_relocs_created++; + } + else + { + einfo (_("%C: variable '%T' can't be auto-imported. Please read the documentation for ld's --enable-auto-import for details.\n"), + s->owner, s, rel->address, sym->name); + einfo ("%X"); + } + } } @@ -2222,20 +2376,32 @@ pe_process_import_defs (output_bfd, link_info) { def_file_export exp; struct bfd_link_hash_entry *blhe; - + int lead_at = (*pe_def_file->imports[i].internal_name == '@'); /* See if we need this import. */ char *name = (char *) xmalloc (strlen (pe_def_file->imports[i].internal_name) + 2 + 6); - sprintf (name, "%s%s", U (""), pe_def_file->imports[i].internal_name); + + if (lead_at) + sprintf (name, "%s%s", "", pe_def_file->imports[i].internal_name); + else + sprintf (name, "%s%s",U (""), pe_def_file->imports[i].internal_name); + blhe = bfd_link_hash_lookup (link_info->hash, name, - false, false, false); + FALSE, FALSE, FALSE); + if (!blhe || (blhe && blhe->type != bfd_link_hash_undefined)) { - sprintf (name, "%s%s", U ("_imp__"), - pe_def_file->imports[i].internal_name); + if (lead_at) + sprintf (name, "%s%s", U ("_imp_"), + pe_def_file->imports[i].internal_name); + else + sprintf (name, "%s%s", U ("_imp__"), + pe_def_file->imports[i].internal_name); + blhe = bfd_link_hash_lookup (link_info->hash, name, - false, false, false); + FALSE, FALSE, FALSE); } free (name); + if (blhe && blhe->type == bfd_link_hash_undefined) { bfd *one; @@ -2252,7 +2418,7 @@ pe_process_import_defs (output_bfd, link_info) exp.hint = exp.ordinal >= 0 ? exp.ordinal : 0; exp.flag_private = 0; exp.flag_constant = 0; - exp.flag_data = 0; + exp.flag_data = pe_def_file->imports[i].data; exp.flag_noname = exp.name ? 0 : 1; one = make_one (&exp, output_bfd); add_bfd_to_link (one, one->filename, link_info); @@ -2269,8 +2435,8 @@ pe_process_import_defs (output_bfd, link_info) } /* We were handed a *.DLL file. Parse it and turn it into a set of - IMPORTS directives in the def file. Return true if the file was - handled, false if not. */ + IMPORTS directives in the def file. Return TRUE if the file was + handled, FALSE if not. */ static unsigned int pe_get16 (abfd, where) @@ -2318,16 +2484,23 @@ pe_as32 (ptr) return b[0] + (b[1] << 8) + (b[2] << 16) + (b[3] << 24); } -boolean +bfd_boolean pe_implied_import_dll (filename) const char *filename; { bfd *dll; unsigned long pe_header_offset, opthdr_ofs, num_entries, i; unsigned long export_rva, export_size, nsections, secptr, expptr; + unsigned long exp_funcbase; unsigned char *expdata, *erva; unsigned long name_rvas, ordinals, nexp, ordbase; const char *dll_name; + /* Initialization with start > end guarantees that is_data + will not be set by mistake, and avoids compiler warning. */ + unsigned long data_start = 1; + unsigned long data_end = 0; + unsigned long bss_start = 1; + unsigned long bss_end = 0; /* No, I can't use bfd here. kernel32.dll puts its export table in the middle of the .rdata section. */ @@ -2335,27 +2508,23 @@ pe_implied_import_dll (filename) if (!dll) { einfo ("%Xopen %s: %s\n", filename, bfd_errmsg (bfd_get_error ())); - return false; + return FALSE; } /* PEI dlls seem to be bfd_objects. */ if (!bfd_check_format (dll, bfd_object)) { einfo ("%X%s: this doesn't appear to be a DLL\n", filename); - return false; + return FALSE; } - dll_name = filename; - for (i = 0; filename[i]; i++) - if (filename[i] == '/' || filename[i] == '\\' || filename[i] == ':') - dll_name = filename + i + 1; - + /* Get pe_header, optional header and numbers of export entries. */ pe_header_offset = pe_get32 (dll, 0x3c); opthdr_ofs = pe_header_offset + 4 + 20; num_entries = pe_get32 (dll, opthdr_ofs + 92); if (num_entries < 1) /* No exports. */ - return false; + return FALSE; export_rva = pe_get32 (dll, opthdr_ofs + 96); export_size = pe_get32 (dll, opthdr_ofs + 100); @@ -2364,6 +2533,7 @@ pe_implied_import_dll (filename) pe_get16 (dll, pe_header_offset + 4 + 16)); expptr = 0; + /* Get the rva and size of the export section. */ for (i = 0; i < nsections; i++) { char sname[8]; @@ -2384,6 +2554,40 @@ pe_implied_import_dll (filename) } } + /* Scan sections and store the base and size of the + data and bss segments in data/base_start/end. */ + for (i = 0; i < nsections; i++) + { + unsigned long secptr1 = secptr + 40 * i; + unsigned long vsize = pe_get32 (dll, secptr1 + 8); + unsigned long vaddr = pe_get32 (dll, secptr1 + 12); + unsigned long flags = pe_get32 (dll, secptr1 + 36); + char sec_name[9]; + + sec_name[8] = '\0'; + bfd_seek (dll, (file_ptr) secptr1 + 0, SEEK_SET); + bfd_bread (sec_name, (bfd_size_type) 8, dll); + + if (strcmp(sec_name,".data") == 0) + { + data_start = vaddr; + data_end = vaddr + vsize; + + if (pe_dll_extra_pe_debug) + printf ("%s %s: 0x%08lx-0x%08lx (0x%08lx)\n", + __FUNCTION__, sec_name, vaddr, vaddr + vsize, flags); + } + else if (strcmp (sec_name,".bss") == 0) + { + bss_start = vaddr; + bss_end = vaddr + vsize; + + if (pe_dll_extra_pe_debug) + printf ("%s %s: 0x%08lx-0x%08lx (0x%08lx)\n", + __FUNCTION__, sec_name, vaddr, vaddr + vsize, flags); + } + } + expdata = (unsigned char *) xmalloc (export_size); bfd_seek (dll, (file_ptr) expptr, SEEK_SET); bfd_bread (expdata, (bfd_size_type) export_size, dll); @@ -2396,17 +2600,44 @@ pe_implied_import_dll (filename) name_rvas = pe_as32 (expdata + 32); ordinals = pe_as32 (expdata + 36); ordbase = pe_as32 (expdata + 16); + exp_funcbase = pe_as32 (expdata + 28); + + /* Use internal dll name instead of filename + to enable symbolic dll linking. */ + dll_name = pe_as32 (expdata + 12) + erva; + /* Iterate through the list of symbols. */ for (i = 0; i < nexp; i++) { + /* Pointer to the names vector. */ unsigned long name_rva = pe_as32 (erva + name_rvas + i * 4); def_file_import *imp; - - imp = def_file_add_import (pe_def_file, erva + name_rva, dll_name, - i, 0); + /* Pointer to the function address vector. */ + unsigned long func_rva = pe_as32 (erva + exp_funcbase + i * 4); + int is_data = 0; + + /* Skip unwanted symbols, which are + exported in buggy auto-import releases. */ + if (strncmp (erva + name_rva, "_nm_", 4) != 0) + { + /* is_data is true if the address is in the data or bss segment. */ + is_data = + (func_rva >= data_start && func_rva < data_end) + || (func_rva >= bss_start && func_rva < bss_end); + + imp = def_file_add_import (pe_def_file, erva + name_rva, + dll_name, i, 0); + /* Mark symbol type. */ + imp->data = is_data; + + if (pe_dll_extra_pe_debug) + printf ("%s dll-name: %s sym: %s addr: 0x%lx %s\n", + __FUNCTION__, dll_name, erva + name_rva, + func_rva, is_data ? "(data)" : ""); + } } - return true; + return TRUE; } /* These are the main functions, called from the emulation. The first