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.
This commit is contained in:
Nick Clifton 2009-01-13 09:23:51 +00:00
parent 840b96a769
commit 71c57c1668
4 changed files with 322 additions and 96 deletions

View File

@ -1,3 +1,34 @@
2009-01-13 Charles Wilson <cygwin@cwilson.fastmail.fm>
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 <amodra@bigpond.net.au>
PR 7034

View File

@ -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 <implib> to
report an error when the import library is associated with
multiple DLLs.
* Added --identify <implib> option to dlltool, which determines the
name of the DLL associated with the specified <implib>.
* 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.

View File

@ -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. */
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.
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.
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 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. */
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.
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 <prefix> Use <prefix> to construct temp file names.\n"));
fprintf (file, _(" -I --identify <implib> Report the name of the DLL associated with <implib>.\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;

View File

@ -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