[PATCH] Fix up msymbol type of dll trampoline to mst_solib_trampoline

Yao Qi
2013-06-24T03:11:58Z

Hi,
I find the following fail when testing native mingw32 gdb,

(gdb) br foo
Breakpoint 3 at 0x401418 (2 locations)
FAIL: gdb.base/solib-symbol.exp: foo in libmd

two breakpoint locations are set on "<foo>" (the dll trampoline) and
"foo" (the function in dll). It is wrong because dll has been loaded,
and it is expected to set one breakpoint on "foo" in dll only. Native
Linux GDB behaves correctly in this case.

When breakpoint is set in CLI, like "b foo", GDB will search the
minimal symbols of "foo" when parsing the linespec "foo". During the
search, GDB will sort minimal symbols in classification order and only
record the minimal symbols with the lowest classification. See how
classify_mtype is called in search_minsyms_for_name. On Linux, on
this point, there are two minimal symbols "foo" with different types,
one is the function in shared lib (mst_text) and the other is the plt
stub (mst_solib_trampoline). According to the rule in classify_mtype
and algorithm in search_minsyms_for_name, only one minimal symbol,
whose type is mst_text, is record, and only one breakpoint location is
set on it finally.

However, on windows, there are two minimal symbols "foo" too, one is
the function in dll, and the other is the dll trampoline (which is
similar to plt stub, IMO). The type of both of these minimal symbols
is "mst_text", so the have the same priority in classify_mtype, and
two of them are recorded. That is the root cause of this problem.

I think the right fix could be setting the type of minimal symbol to
"mst_solib_trampoline" if it is the dll trampoline. Read the comments
to mst_solib_trampoline get this idea confirmed,

/* GDB uses mst_solib_trampoline for the start address of a shared
library trampoline entry. Breakpoints for shared library functions
are put there if the shared library is not yet loaded.
After the shared library is loaded, lookup_minimal_symbol will
prefer the minimal symbol from the shared library (usually
a mst_text symbol) over the mst_solib_trampoline symbol, and the
breakpoints will be moved to their true address in the shared
library via breakpoint_re_set. */
mst_solib_trampoline, /* Shared library trampoline code */

The rationale of this patch is to fix up the type of minimal symbol to
"mst_solib_trampoline" when reading the file if the minimal symbol is
a dll trampoline. We find that there is always a symbol "_impl_foo"
coexists with dll trampoline "foo" in the current minimal symbols red
in. Our approach is to collect all minimal symbols which name has
prefix "_imp_", remove the prefix, and find minimal symbols. If
found, it is a dll trampoline. For example, there are several
symbols,

foo, bar, _imp_foo, _imp_baz,

we record symbols with "_imp_" prefix in a set {_imp_foo, _imp_baz},
remove prefix {foo, baz}, and look for them in these symbols. Symbol
foo is found, and it is a dll trampoline.

Regression test mingw32 native gdb with a remote host board file. The
fail I mentioned above is fixed.

gdb:

2013-06-24 Yao Qi <yao@codesourcery.com>

* coffread.c: Use DEF_VEC_P to define vector type.
(coff_symtab_read): Define local variable 'dll_trampoline'.
Record minimal symbols into 'dll_trampoline' if their names
have prefix "_imp_ or "__imp_". Set the type of minimal
symbol to 'mst_solib_trampoline' if it is a dll trampoline.
* minsyms.c (prim_find_minimal_symbol): New function.
* minsyms.h (prim_find_minimal_symbol): Declare.
---
gdb/coffread.c | 43 +++++++++++++++++++++++++++++++++++++++++++
gdb/minsyms.c | 27 +++++++++++++++++++++++++++
gdb/minsyms.h | 2 ++
3 files changed, 72 insertions(+), 0 deletions(-)

diff --git a/gdb/coffread.c b/gdb/coffread.c
index bf39085..c0f08b8 100644
--- a/gdb/coffread.c
+++ b/gdb/coffread.c
@@ -724,6 +724,9 @@ coff_symfile_finish (struct objfile *objfile)
}
\f

+typedef struct minimal_symbol *msymbolp;
+DEF_VEC_P (msymbolp);
+
/* Given pointers to a symbol table in coff style exec file,
analyze them and create struct symtab's describing the symbols.
NSYMS is the number of symbols in the symbol table.
@@ -757,6 +760,8 @@ coff_symtab_read (long symtab_offset, unsigned int nsyms,
int val;
CORE_ADDR tmpaddr;
struct minimal_symbol *msym;
+ /* A set of minimal_symbol which has prefix "__imp_" or "_imp_". */
+ VEC (msymbolp) *name_prefix_imp = NULL;

/* Work around a stdio bug in SunOS4.1.1 (this makes me nervous....
it's hard to know I've really worked around it. The fix should
@@ -1006,6 +1011,16 @@ coff_symtab_read (long symtab_offset, unsigned int nsyms,
SYMBOL_VALUE (sym) = tmpaddr;
SYMBOL_SECTION (sym) = sec;
}
+
+ /* Record minimal symbols which name are prefixed by "__imp_"
+ or "_imp_" in set NAME_PREFIX_IMP if their type is
+ mst_data. Note that 'maintenance print msymbols' shows
+ that type of these "_imp_XXXX" symbols is mst_data. */
+ if (msym != NULL && ms_type == mst_data
+ && (strncmp (cs->c_name, "__imp_", 6) == 0
+ || strncmp (cs->c_name, "_imp_", 5) == 0))
+ VEC_safe_push (msymbolp, name_prefix_imp, msym);
+
}
break;

@@ -1151,6 +1166,34 @@ coff_symtab_read (long symtab_offset, unsigned int nsyms,
}
}

+ if (pe_file)
+ {
+ int ix;
+ struct minimal_symbol *msym_dll;
+
+ /* Set NAME_PREFIX_IMP contains minimal symbols which name is
+ prefixed by "__imp_" or "_imp_". In each iteration, look for the
+ minimal symbols just red in by matching the minimal symbol name
+ without the prefix. */
+ for (ix = 0;
+ VEC_iterate (msymbolp, name_prefix_imp, ix, msym_dll);
+ ix++)
+ {
+ char *buffer = xstrdup (SYMBOL_LINKAGE_NAME (msym_dll));
+ const char *name = (buffer[1] == '_' ? &buffer[7] : &buffer[6]);
+ struct minimal_symbol *found
+ = prim_find_minimal_symbol (name);
+
+ /* If found, there are symbols named "_imp_foo" and "foo"
+ respectively red in from the current objfile. Set the type
+ of symbol "foo" as 'mst_solib_trampoline'. */
+ if (found != NULL && MSYMBOL_TYPE (found) == mst_text)
+ MSYMBOL_TYPE (found) = mst_solib_trampoline;
+
+ xfree (buffer);
+ }
+ }
+
if ((nsyms == 0) && (pe_file))
{
/* We've got no debugging symbols, but it's a portable
diff --git a/gdb/minsyms.c b/gdb/minsyms.c
index 89e538a..f5c96db 100644
--- a/gdb/minsyms.c
+++ b/gdb/minsyms.c
@@ -1257,6 +1257,33 @@ install_minimal_symbols (struct objfile *objfile)
}
}

+/* Look for the minimal symbol which name is NAME. Return NULL if not
+ found. */
+
+struct minimal_symbol *
+prim_find_minimal_symbol (const char *name)
+{
+ struct msym_bunch *bunch;
+ int max;
+
+ max = msym_bunch_index;
+ for (bunch = msym_bunch; bunch != NULL; bunch = bunch->next)
+ {
+ int bindex;
+
+ for (bindex = 0; bindex < max; bindex++)
+ {
+ struct minimal_symbol *msym = &bunch->contents[bindex];
+
+ if (strcmp (name, SYMBOL_LINKAGE_NAME (msym)) == 0)
+ return msym;
+ }
+ max = BUNCH_SIZE;
+ }
+
+ return NULL;
+}
+
/* See minsyms.h. */

void
diff --git a/gdb/minsyms.h b/gdb/minsyms.h
index 4d48477..137ee00 100644
--- a/gdb/minsyms.h
+++ b/gdb/minsyms.h
@@ -252,4 +252,6 @@ void iterate_over_minimal_symbols (struct objfile *objf,
void *),
void *user_data);

+struct minimal_symbol* prim_find_minimal_symbol (const char *name);
+
#endif /* MINSYMS_H */
--
1.7.7.6