From 71c57c1668a60d27a82086f766e636a8b017e410 Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Tue, 13 Jan 2009 09:23:51 +0000 Subject: [PATCH] Add --identify-strict option. Handle ms-style implibs: * dlltool.c (file scope): Added new globals identify_ms and identify_strict. New typedef dll_name_list_type, and globals identify_dll_name_list_head and identify_dll_name_list_tail. Added new global identify_member_contains_symname_result. (identify_append_dll_name_to_list): New function. (identify_count_dll_name_list): New function. (identify_print_dll_name_list): New function. (identify_free_dll_name_list): New function. (identify_search_archive): Changed signature to take function pointer to operation to apply to each member, and data to pass on to that function. (identify_search_member): Changed signature to accept user data from caller. (identify_member_contains_symname): New function. (identify_dll_for_implib): Rewrite. Now determines whether implib is ms- or binutils- style, before searching sections for dllname. Allows multiple dllnames. (identify_process_section_p): Search alternate section for dllname when implib is ms-style. (identify_search_section): Add additional conditions to excludes candidate sections from consideration. (usage): Added --identify-strict. (long_options): Added --identify-strict. (main): Handle --identify-strict option. * doc/binutils.texi: Document --identify-strict option. * NEWS: Document --identify and --identify-strict options. --- binutils/ChangeLog | 31 ++++ binutils/NEWS | 7 + binutils/dlltool.c | 359 ++++++++++++++++++++++++++++++++++----------- binutils/doc/binutils.texi | 21 ++- 4 files changed, 322 insertions(+), 96 deletions(-) diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 3012152..e5944e8 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,34 @@ +2009-01-13 Charles Wilson + + Add --identify-strict option. Handle ms-style implibs: + + * dlltool.c (file scope): Added new globals identify_ms and + identify_strict. New typedef dll_name_list_type, and globals + identify_dll_name_list_head and identify_dll_name_list_tail. Added + new global identify_member_contains_symname_result. + (identify_append_dll_name_to_list): New function. + (identify_count_dll_name_list): New function. + (identify_print_dll_name_list): New function. + (identify_free_dll_name_list): New function. + (identify_search_archive): Changed signature to take function + pointer to operation to apply to each member, and data to pass on + to that function. + (identify_search_member): Changed signature to accept user data + from caller. + (identify_member_contains_symname): New function. + (identify_dll_for_implib): Rewrite. Now determines whether implib + is ms- or binutils- style, before searching sections for + dllname. Allows multiple dllnames. + (identify_process_section_p): Search alternate section for dllname + when implib is ms-style. + (identify_search_section): Add additional conditions to excludes + candidate sections from consideration. + (usage): Added --identify-strict. + (long_options): Added --identify-strict. + (main): Handle --identify-strict option. + * doc/binutils.texi: Document --identify-strict option. + * NEWS: Document --identify and --identify-strict options. + 2009-01-13 Alan Modra PR 7034 diff --git a/binutils/NEWS b/binutils/NEWS index 10a1370..4cc89e1 100644 --- a/binutils/NEWS +++ b/binutils/NEWS @@ -3,6 +3,13 @@ * Add new option --use-nul-prefixed-import-tables to dlltool to allow fall- back to old import table generation with null element prefix. +* Added --identify-strict switch to cause --identify to + report an error when the import library is associated with + multiple DLLs. + +* Added --identify option to dlltool, which determines the + name of the DLL associated with the specified . + * Support for PowerPC booke64 instructions has been removed. The assembler no longer accepts -mbooke32 or -mbooke64 and the disassembler no longer accepts -Mbooke32 or -Mbooke64. Instead, -mbooke and -Mbooke should be used. diff --git a/binutils/dlltool.c b/binutils/dlltool.c index 7a11022..8c8ac48 100644 --- a/binutils/dlltool.c +++ b/binutils/dlltool.c @@ -1,6 +1,6 @@ /* dlltool.c -- tool to generate stuff for PE style DLLs Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, - 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. This file is part of GNU Binutils. @@ -353,7 +353,28 @@ static int no_idata5; static char *exp_name; static char *imp_name; static char *identify_imp_name; -static char *identify_dll_name; +static bfd_boolean identify_ms; +static bfd_boolean identify_strict; + +/* Holds a linked list of dllnames associated with the + specified import lib. Used by the identify_* code. + The _head entry is always empty (_head->dllname is + NULL). */ +typedef struct dll_name_list_t +{ + char * dllname; + struct dll_name_list_t * next; +} dll_name_list_type; + +static dll_name_list_type * identify_dll_name_list_head; +static dll_name_list_type * identify_dll_name_list_tail; +/* dll_name_list management functions. */ +static void identify_append_dll_name_to_list (bfd_byte *); +static int identify_count_dll_name_list (void); +static void identify_print_dll_name_list (void); +static void identify_free_dll_name_list (dll_name_list_type *); +static bfd_boolean identify_member_contains_symname_result = FALSE; + static char *head_label; static char *imp_name_lab; static char *dll_name; @@ -732,10 +753,13 @@ static bfd *make_head (void); static bfd *make_tail (void); static void gen_lib_file (void); static void identify_dll_for_implib (void); -static void identify_search_archive (bfd*); -static void identify_search_member (bfd*, bfd*); +static void identify_search_archive + (bfd *, void (*) (bfd *, bfd *, void *), void *); +static void identify_search_member (bfd *, bfd *, void *); static bfd_boolean identify_process_section_p (asection *); static void identify_search_section (bfd *, asection *, void *); +static void identify_member_contains_symname (bfd *, bfd *, void *); + static int pfunc (const void *, const void *); static int nfunc (const void *, const void *); static void remove_null_names (export_type **); @@ -2943,69 +2967,220 @@ gen_lib_file (void) inform (_("Created lib file")); } -/* identify_dll_for_implib +/* Management of the identify_dll_name_list. */ + +static void +identify_append_dll_name_to_list (bfd_byte * data) +{ + /* Allocate new node. */ + dll_name_list_type * entry = + (dll_name_list_type *) xmalloc (sizeof (dll_name_list_type)); + + /* Initialize its values. */ + entry->dllname = xstrdup ((char *) data); + entry->next = NULL; + + /* Add to tail, and move tail. */ + identify_dll_name_list_tail->next = entry; + identify_dll_name_list_tail = entry; +} + +static int +identify_count_dll_name_list (void) +{ + int count = 0; + dll_name_list_type * p = identify_dll_name_list_head; + + while (p && p->next) + { + count++; + p = p->next; + } + return count; +} + +static void +identify_print_dll_name_list (void) +{ + dll_name_list_type * p = identify_dll_name_list_head; + + while (p && p->next && p->next->dllname && *(p->next->dllname)) + { + printf ("%s\n", p->next->dllname); + p = p->next; + } +} + +static void +identify_free_dll_name_list (dll_name_list_type * entry) +{ + if (entry) + { + if (entry->next) + { + identify_free_dll_name_list (entry->next); + entry->next = NULL; + } + if (entry->dllname) + { + free (entry->dllname); + entry->dllname = NULL; + } + free (entry); + } +} + +/* Search the symbol table of the suppled BFD for a symbol whose name matches + OBJ (where obj is cast to const char *). If found, set global variable + identify_member_contains_symname_result TRUE. It is the caller's + responsibility to set the result variable FALSE before iterating with + this function. */ + +static void +identify_member_contains_symname (bfd * abfd, + bfd * archive_bfd ATTRIBUTE_UNUSED, + void * obj) +{ + long storage_needed; + asymbol ** symbol_table; + long number_of_symbols; + long i; + const char * name = (const char *) obj; + + /* If we already found the symbol in a different member, + short circuit. */ + if (identify_member_contains_symname_result) + return; + + storage_needed = bfd_get_symtab_upper_bound (abfd); + if (storage_needed <= 0) + return; + + symbol_table = xmalloc (storage_needed); + number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table); + if (number_of_symbols < 0) + { + free (symbol_table); + return; + } + + for (i = 0; i < number_of_symbols; i++) + { + if (strncmp (symbol_table[i]->name, name, strlen (name)) == 0) + { + identify_member_contains_symname_result = TRUE; + break; + } + } + free (symbol_table); +} + +/* This is the main implementation for the --identify option. + Given the name of an import library in identify_imp_name, first determine + if the import library is a GNU binutils-style one (where the DLL name is + stored in an .idata$7 (.idata$6 on PPC) section, or if it is a MS-style + one (where the DLL name, along with much other data, is stored in the + .idata$6 section). We determine the style of import library by searching + for the DLL-structure symbol inserted by MS tools: + __NULL_IMPORT_DESCRIPTOR. - This is the main implementation for the --identify option. - Given the name of an import library in identify_imp_name, - search all archive members for an .idata$7 section - (.idata$6 on PPC). This section will consist of a single - char* constant, indicating the name of the DLL represented - by the import library. + Once we know which section to search, evaluate each section for the + appropriate properties that indicate it may contain the name of the + associated DLL (this differs depending on the style). Add the contents + of all sections which meet the criteria to a linked list of dll names. + + Finally, print them all to stdout. (If --identify-strict, an error is + reported if more than one match was found). */ - It is possible to construct an import library that has - two members with a non-empty .idata$7 section, but these - are not often seen in normal operation. In this case, - an error is flagged. -*/ static void identify_dll_for_implib (void) { - bfd* abfd = NULL; + bfd * abfd = NULL; + int count = 0; + + /* Initialize identify_dll_name_list. */ + identify_dll_name_list_head = xmalloc (sizeof (dll_name_list_type)); + identify_dll_name_list_head->dllname = NULL; + identify_dll_name_list_head->next = NULL; + identify_dll_name_list_tail = identify_dll_name_list_head; bfd_init (); abfd = bfd_openr (identify_imp_name, 0); if (abfd == NULL) + bfd_fatal (identify_imp_name); + + if (! bfd_check_format (abfd, bfd_archive)) { - bfd_fatal (identify_imp_name); + if (! bfd_close (abfd)) + bfd_fatal (identify_imp_name); + + fatal (_("%s is not a library"), identify_imp_name); } + + /* Detect if this a Microsoft import library. */ + identify_member_contains_symname_result = FALSE; + identify_search_archive (abfd, identify_member_contains_symname, + "__NULL_IMPORT_DESCRIPTOR"); + if (identify_member_contains_symname_result) + identify_ms = TRUE; + + /* Rewind the bfd. */ + if (! bfd_close (abfd)) + bfd_fatal (identify_imp_name); + abfd = bfd_openr (identify_imp_name, 0); + if (abfd == NULL) + bfd_fatal (identify_imp_name); + if (!bfd_check_format (abfd, bfd_archive)) { if (!bfd_close (abfd)) bfd_fatal (identify_imp_name); - fatal ("%s is not a library", identify_imp_name); + fatal (_("%s is not a library"), identify_imp_name); } + + /* Now search for the dll name. */ + identify_search_archive (abfd, identify_search_member, NULL); - identify_search_archive (abfd); - - if (!bfd_close (abfd)) + if (! bfd_close (abfd)) bfd_fatal (identify_imp_name); - if (identify_dll_name && *identify_dll_name) + count = identify_count_dll_name_list(); + if (count > 0) { - printf ("%s\n",identify_dll_name); - free (identify_dll_name); - identify_dll_name = NULL; + if (identify_strict && count > 1) + { + identify_free_dll_name_list (identify_dll_name_list_head); + identify_dll_name_list_head = NULL; + fatal (_("Import library `%s' specifies two or more dlls"), + identify_imp_name); + } + identify_print_dll_name_list(); + identify_free_dll_name_list (identify_dll_name_list_head); + identify_dll_name_list_head = NULL; } else { - fatal ("Unable to determine dll name for %s (not an import library?)", identify_imp_name); + identify_free_dll_name_list (identify_dll_name_list_head); + identify_dll_name_list_head = NULL; + fatal (_("Unable to determine dll name for `%s' (not an import library?)"), + identify_imp_name); } } -/* identify_search_archive +/* Loop over all members of the archive, applying the supplied function to + each member that is a bfd_object. The function will be called as if: + func (member_bfd, abfd, user_storage) */ - Loop over all members of the archive, inspecting - each for the presence of an .idata$7 (.idata$6 on PPC) - section with non-empty contents. -*/ static void -identify_search_archive (bfd* abfd) +identify_search_archive (bfd * abfd, + void (* operation) (bfd *, bfd *, void *), + void * user_storage) { - bfd *arfile = NULL; - bfd *last_arfile = NULL; - char **matching; + bfd * arfile = NULL; + bfd * last_arfile = NULL; + char ** matching; while (1) { @@ -3017,19 +3192,18 @@ identify_search_archive (bfd* abfd) bfd_fatal (bfd_get_filename (abfd)); break; } + if (bfd_check_format_matches (arfile, bfd_object, &matching)) - { - identify_search_member (arfile, abfd); - } + (*operation) (arfile, abfd, user_storage); else { bfd_nonfatal (bfd_get_filename (arfile)); free (matching); } + if (last_arfile != NULL) - { - bfd_close (last_arfile); - } + bfd_close (last_arfile); + last_arfile = arfile; } @@ -3039,23 +3213,20 @@ identify_search_archive (bfd* abfd) } } -/* identify_search_member +/* Call the identify_search_section() function for each section of this + archive member. */ - Search all sections of an archive member for the - one with section name of .idata$7 (.idata$6 on PPC) - and non-empty contents. -*/ static void -identify_search_member (bfd* abfd, bfd* archive_bfd ATTRIBUTE_UNUSED) +identify_search_member (bfd *abfd, + bfd *archive_bfd ATTRIBUTE_UNUSED, + void *obj) { - bfd_map_over_sections (abfd, identify_search_section, NULL); + bfd_map_over_sections (abfd, identify_search_section, obj); } -/* identify_process_section_p +/* This predicate returns true if section->name matches the desired value. + By default, this is .idata$7 (.idata$6 on PPC, or when --identify-ms). */ - This predicate returns true if section->name - is .idata$7 (.idata$6 on PPC). -*/ static bfd_boolean identify_process_section_p (asection * section) { @@ -3066,27 +3237,23 @@ identify_process_section_p (asection * section) #else ".idata$7"; #endif + static const char * MS_SECTION_NAME = ".idata$6"; - if (strcmp (SECTION_NAME, section->name) == 0) + const char * section_name = + (identify_ms ? MS_SECTION_NAME : SECTION_NAME); + + if (strcmp (section_name, section->name) == 0) return TRUE; return FALSE; } -/* identify_search_section - - If *section has contents and its name is .idata$7 - (.data$6 on PPC) then store the contents in - identify_dll_name as an xmalloc'ed array. +/* If *section has contents and its name is .idata$7 (.data$6 on PPC or if + import lib ms-generated) -- and it satisfies several other constraints + -- then store the contents in the list pointed to by + identify_dll_name_list_head. */ - However, if identify_dll_name already has - a value, flag an error. We don't know how to handle - import libraries that directly reference more than - one DLL. (This is different than forwarded symbols. - Such import libraries are not seen in normal operation, - and must be specifically constructed.) -*/ static void -identify_search_section (bfd *abfd, asection *section, void *dummy ATTRIBUTE_UNUSED) +identify_search_section (bfd * abfd, asection * section, void * dummy ATTRIBUTE_UNUSED) { bfd_byte *data = 0; bfd_size_type datasize; @@ -3097,35 +3264,44 @@ identify_search_section (bfd *abfd, asection *section, void *dummy ATTRIBUTE_UNU if (! identify_process_section_p (section)) return; + /* Binutils import libs seem distinguish the .idata$7 section that contains + the DLL name from other .idata$7 sections by the absence of the + SEC_RELOC flag. */ + if (!identify_ms && ((section->flags & SEC_RELOC) == SEC_RELOC)) + return; + + /* MS import libs seem to distinguish the .idata$6 section + that contains the DLL name from other .idata$6 sections + by the presence of the SEC_DATA flag. */ + if (identify_ms && ((section->flags & SEC_DATA) == 0)) + return; + if ((datasize = bfd_section_size (abfd, section)) == 0) return; - data = (bfd_byte*) xmalloc (datasize + 1); + data = (bfd_byte *) xmalloc (datasize + 1); data[0] = '\0'; bfd_get_section_contents (abfd, section, data, 0, datasize); data[datasize] = '\0'; - if (data[0] != '\0') - { - if (identify_dll_name != NULL) - { - if (*identify_dll_name != '\0') - { - /* The import library specifies two different DLLs. - Treat this as an error. */ - fatal ("Import library `%s' specifies two or more dlls: `%s' and `%s'", - identify_imp_name, identify_dll_name, data); - } - else - { - /* For some reason memory was allocated, but the - contents were empty. Free the memory and continue. */ - free (identify_dll_name); - } - } - identify_dll_name = xstrdup ((char*) data); - } + /* Use a heuristic to determine if data is a dll name. + Possible to defeat this if (a) the library has MANY + (more than 0x302f) imports, (b) it is an ms-style + import library, but (c) it is buggy, in that the SEC_DATA + flag is set on the "wrong" sections. This heuristic might + also fail to record a valid dll name if the dllname is + uses a multibyte or unicode character set (is that valid?). + + This heuristic is based on the fact that symbols names in + the chosen section -- as opposed to the dll name -- begin + at offset 2 in the data. The first two bytes are a 16bit + little-endian count, and start at 0x0000. However, the dll + name begins at offset 0 in the data. We assume that the + dll name does not contain unprintable characters. */ + if (data[0] != '\0' && ISPRINT (data[0]) + && ((datasize < 2) || ISPRINT (data[1]))) + identify_append_dll_name_to_list (data); free (data); } @@ -3138,6 +3314,7 @@ pfunc (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; @@ -3385,6 +3562,7 @@ usage (FILE *file, int status) fprintf (file, _(" -n --no-delete Keep temp files (repeat for extra preservation).\n")); fprintf (file, _(" -t --temp-prefix Use to construct temp file names.\n")); fprintf (file, _(" -I --identify Report the name of the DLL associated with .\n")); + fprintf (file, _(" --identify-strict Causes --identify to report error when multiple DLLs.\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")); @@ -3406,6 +3584,7 @@ usage (FILE *file, int status) #define OPTION_ADD_STDCALL_UNDERSCORE (OPTION_NO_DEFAULT_EXCLUDES + 1) #define OPTION_USE_NUL_PREFIXED_IMPORT_TABLES \ (OPTION_ADD_STDCALL_UNDERSCORE + 1) +#define OPTION_IDENTIFY_STRICT (OPTION_USE_NUL_PREFIXED_IMPORT_TABLES + 1) static const struct option long_options[] = { @@ -3430,6 +3609,7 @@ static const struct option long_options[] = {"add-stdcall-alias", no_argument, NULL, 'A'}, {"ext-prefix-alias", required_argument, NULL, 'p'}, {"identify", required_argument, NULL, 'I'}, + {"identify-strict", no_argument, NULL, OPTION_IDENTIFY_STRICT}, {"verbose", no_argument, NULL, 'v'}, {"version", no_argument, NULL, 'V'}, {"help", no_argument, NULL, 'h'}, @@ -3495,6 +3675,9 @@ main (int ac, char **av) case OPTION_ADD_STDCALL_UNDERSCORE: add_stdcall_underscore = 1; break; + case OPTION_IDENTIFY_STRICT: + identify_strict = 1; + break; case 'x': no_idata4 = 1; break; diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi index beb8aa8..2afe4c2 100644 --- a/binutils/doc/binutils.texi +++ b/binutils/doc/binutils.texi @@ -11,7 +11,7 @@ @copying @c man begin COPYRIGHT Copyright @copyright{} 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 @@ -3373,7 +3373,8 @@ dlltool [@option{-d}|@option{--input-def} @var{def-file-name}] [@option{-p}|@option{--ext-prefix-alias} @var{prefix}] [@option{-x}|@option{--no-idata4}] [@option{-c}|@option{--no-idata5}] [@option{--use-nul-prefixed-import-tables}] - [@option{-I}|@option{--identify} @var{library-file-name}] [@option{-i}|@option{--interwork}] + [@option{-I}|@option{--identify} @var{library-file-name}] [@option{--identify-strict}] + [@option{-i}|@option{--interwork}] [@option{-n}|@option{--nodelete}] [@option{-t}|@option{--temp-prefix} @var{prefix}] [@option{-v}|@option{--verbose}] [@option{-h}|@option{--help}] [@option{-V}|@option{--version}] @@ -3601,12 +3602,16 @@ with certain operating systems. @item -I @var{filename} @itemx --identify @var{filename} Specifies that @command{dlltool} should inspect the import library -indicated by @var{filename} and report, on @code{stdout}, the name of -the associated DLL. This can be performed in addition to any other -operations indicated by the other options and arguments. @command{dlltool} -@option{--identify} fails if the import library does not exist, is not -actually an import library, or (rarely) if the import library somehow -specifies more than one associated DLL. +indicated by @var{filename} and report, on @code{stdout}, the name(s) +of the associated DLL(s). This can be performed in addition to any +other operations indicated by the other options and arguments. +@command{dlltool} fails if the import library does not exist or is not +actually an import library. See also @option{--identify-strict}. + +@item --identify-strict +Modifies the behavior of the @option{--identify} option, such +that an error is reported if @var{filename} is associated with +more than one DLL. @item -i @itemx --interwork -- 2.7.4