[RFC] Fix .text section offset for windows DLL (was Calling __stdcall functions in the inferior)

Pierre Muller
2012-10-15T13:36:07Z

[-- Attachment #1: Type: text/plain, Size: 2815 bytes --]

Eli reported a problem with
GDB trying to call a function in kernel32.dll.

The idea that the calling convention was responsible for
the signal when trying to call a function declared in kernel32 dll
did not make muich sense for a function like GetLastError,
which has no parameters.
All i386 calling convention agree that simple return values
should be in register EAX.
Anyhow an error on location of return value
should only have the effect of losing the correct return value but never
generate a signal...

After some investigation, I noticed that
the minimal symbol GetLastError was offset by 0xf0000
respective to the '_imp__GetLastError@0'.

The patch attached allowed me to call
(gdb) call GetLastError ()
And get a simple
$2 = 126
(for instance, depending on when you do the call).

Could someone please test the patch and
confirm that it does fix the problem?

The main fix consists in a new function
pe_text_section_offset that I added to coff-pe-read.c source,
which allows to fetch the real offset of the .text section relative
to the image base, instead of assuming 0x1000.
I tried to leave the code in
windows_xfer_shared_library "backward compatible",
in the sense that if the DLL is not available (as might happen
using gdbserver) we still default to 0x1000 value.

In fact, the patch contains a second fix
about the parsing of exported symbols,
which currently can mix symbol names and associated RVA addresses
if some function had a imposed ordinal value (which can lead to
empty entries in the RVA addresses array).

I did not separate the two parts as I was unsure if
they are really independent.

Comments most welcome,

Pierre Muller
GDB pascal language maintainer

> -----Message d'origine-----
> De : gdb-owner@sourceware.org [mailto:gdb-owner@sourceware.org] De la part
> de Eli Zaretskii
> Envoyé : vendredi 12 octobre 2012 15:26
> À : Pedro Alves
> Cc : mark.kettenis@xs4all.nl; gdb@sourceware.org
> Objet : Re: Calling __stdcall functions in the inferior
>
> > Date: Fri, 12 Oct 2012 12:27:53 +0100
> > From: Pedro Alves <palves@redhat.com>
> > CC: Mark Kettenis <mark.kettenis@xs4all.nl>, gdb@sourceware.org
> >
> > In gcc/config/i386/winnt.c:
> >
> > /* Return string which is the function name, identified by ID,
modified
> > with a suffix consisting of an atsign (@) followed by the number of
> > bytes of arguments. If ID is NULL use the DECL_NAME as base. If
> > FASTCALL is true, also add the FASTCALL_PREFIX.
> > Return NULL if no change required. */
> >
> > static tree
> > gen_stdcall_or_fastcall_suffix (tree decl, tree id, bool fastcall)
> > {
> >
> > As you see above, fastcall also has identifiable decoration.
>
> Thanks.

[-- Attachment #2: fix-dll-offset.patch --]
[-- Type: application/octet-stream, Size: 7399 bytes --]

projecttype:gdb
revision:HEAD
email:muller@ics.u-strasbg.fr

2012-10-15 Pierre Muller <muller@ics.u-strasbg.fr>

* coff-pe-read.h (pe_text_section_offset): Declare new function.
* coff-pe-read.c (pe_as16): New function.
(read_pe_exported_syms): Use ordinal of function to
retrieve correct RVA address of function.
(pe_text_section_offset): New function.

* windows-tdep.c (windows_xfer_shared_library): Use
pe_text_section_offset function instead of possibly wrong
0x1000 constant for .text sextion offset.

Index: src/gdb/coff-pe-read.h
===================================================================
RCS file: /cvs/src/src/gdb/coff-pe-read.h,v
retrieving revision 1.11
diff -u -p -r1.11 coff-pe-read.h
--- src/gdb/coff-pe-read.h 4 Jan 2012 08:17:00 -0000 1.11
+++ src/gdb/coff-pe-read.h 15 Oct 2012 12:36:49 -0000
@@ -21,11 +21,15 @@

#if !defined (COFF_PE_READ_H)
#define COFF_PE_READ_H
+#include "defs.h"

struct objfile;
+struct bfd;

/* Read the export table and convert it to minimal symbol table
entries */
extern void read_pe_exported_syms (struct objfile *objfile);

+extern CORE_ADDR pe_text_section_offset (struct bfd *abfd);
+
#endif /* !defined (COFF_PE_READ_H) */
Index: src/gdb/coff-pe-read.c
===================================================================
RCS file: /cvs/src/src/gdb/coff-pe-read.c,v
retrieving revision 1.17
diff -u -p -r1.17 coff-pe-read.c
--- src/gdb/coff-pe-read.c 4 Jan 2012 08:17:00 -0000 1.17
+++ src/gdb/coff-pe-read.c 15 Oct 2012 12:36:49 -0000
@@ -170,6 +170,14 @@ pe_get32 (bfd *abfd, int where)
}

static unsigned int
+pe_as16 (void *ptr)
+{
+ unsigned char *b = ptr;
+
+ return b[0] + (b[1] << 8);
+}
+
+static unsigned int
pe_as32 (void *ptr)
{
unsigned char *b = ptr;
@@ -336,26 +344,119 @@ read_pe_exported_syms (struct objfile *o
{
/* Pointer to the names vector. */
unsigned long name_rva = pe_as32 (erva + name_rvas + i * 4);
+ /* Retrieve ordinal value */
+
+ unsigned long ordinal = pe_as16 (erva + ordinals + i * 2);
+

/* Pointer to the function address vector. */
- unsigned long func_rva = pe_as32 (erva + exp_funcbase + i * 4);
+ /* This is relatived to ordinal value. */
+ unsigned long func_rva = pe_as32 (erva + exp_funcbase +
+ ordinal * 4);

/* Find this symbol's section in our own array. */
int sectix = 0;
+ int section_found = 0;

for (sectix = 0; sectix < PE_SECTION_TABLE_SIZE; ++sectix)
{
if ((func_rva >= section_data[sectix].rva_start)
&& (func_rva < section_data[sectix].rva_end))
{
+ section_found = 1;
add_pe_exported_sym (erva + name_rva,
func_rva,
section_data + sectix, dll_name, objfile);
break;
}
}
+ if (!section_found)
+ {
+ char * forward_name = (char *) (erva + func_rva);
+ char * funcname = (char *) (erva + name_rva);
+ if ((func_rva >= export_rva)
+ && (func_rva < export_rva + export_size))
+ printf ("%s is a forward to %s\n", funcname, forward_name);
+ }
}

/* Discard expdata. */
do_cleanups (back_to);
}
+
+CORE_ADDR
+pe_text_section_offset (struct bfd *abfd)
+
+{
+ bfd *dll = abfd;
+ 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;
+ char *dll_name;
+ int is_pe64 = 0;
+ int is_pe32 = 0;
+
+ char const *target = bfd_get_target (dll);
+
+ is_pe64 = (strcmp (target, "pe-x86-64") == 0
+ || strcmp (target, "pei-x86-64") == 0);
+ is_pe32 = (strcmp (target, "pe-i386") == 0
+ || strcmp (target, "pei-i386") == 0
+ || strcmp (target, "pe-arm-wince-little") == 0
+ || strcmp (target, "pei-arm-wince-little") == 0);
+
+ if (!is_pe32 && !is_pe64)
+ {
+ /* This is not a recognized PE format file. Abort now, because
+ the code is untested on anything else. *FIXME* test on
+ further architectures and loosen or remove this test. */
+ return 0;
+ }
+
+ /* Get pe_header, optional header and numbers of export entries. */
+ pe_header_offset = pe_get32 (dll, 0x3c);
+ opthdr_ofs = pe_header_offset + 4 + 20;
+ if (is_pe64)
+ num_entries = pe_get32 (dll, opthdr_ofs + 108);
+ else
+ num_entries = pe_get32 (dll, opthdr_ofs + 92);
+
+ if (num_entries < 1) /* No exports. */
+ {
+ return 0;
+ }
+
+ if (is_pe64)
+ {
+ export_rva = pe_get32 (dll, opthdr_ofs + 112);
+ export_size = pe_get32 (dll, opthdr_ofs + 116);
+ }
+ else
+ {
+ export_rva = pe_get32 (dll, opthdr_ofs + 96);
+ export_size = pe_get32 (dll, opthdr_ofs + 100);
+ }
+ nsections = pe_get16 (dll, pe_header_offset + 4 + 2);
+ secptr = (pe_header_offset + 4 + 20 +
+ 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];
+ unsigned long secptr1 = secptr + 40 * i;
+ unsigned long vaddr = pe_get32 (dll, secptr1 + 12);
+ unsigned long vsize = pe_get32 (dll, secptr1 + 16);
+ unsigned long fptr = pe_get32 (dll, secptr1 + 20);
+
+ bfd_seek (dll, (file_ptr) secptr1, SEEK_SET);
+ bfd_bread (sname, (bfd_size_type) 8, dll);
+ if (strcmp (sname, ".text") == 0)
+ return vaddr;
+ }
+
+ return 0;
+}
Index: src/gdb/windows-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/windows-tdep.c,v
retrieving revision 1.16
diff -u -p -r1.16 windows-tdep.c
--- src/gdb/windows-tdep.c 5 Jun 2012 13:50:57 -0000 1.16
+++ src/gdb/windows-tdep.c 15 Oct 2012 12:36:49 -0000
@@ -27,6 +27,9 @@
#include "gdbcmd.h"
#include "gdbthread.h"
#include "objfiles.h"
+#include "symfile.h"
+#include "coff-pe-read.h"
+#include "gdb_bfd.h"

struct cmd_list_element *info_w32_cmdlist;

@@ -387,6 +390,9 @@ windows_xfer_shared_library (const char*
struct gdbarch *gdbarch, struct obstack *obstack)
{
char *p;
+ struct bfd * dll;
+ CORE_ADDR text_offset;
+ CORE_ADDR default_text_offset = 0x1000;
obstack_grow_str (obstack, "<library name=\"");
p = xml_escape_text (so_name);
obstack_grow_str (obstack, p);
@@ -395,7 +401,19 @@ windows_xfer_shared_library (const char*
/* The symbols in a dll are offset by 0x1000, which is the
offset from 0 of the first byte in an image - because of the file
header and the section alignment. */
- obstack_grow_str (obstack, paddress (gdbarch, load_addr + 0x1000));
+ dll = gdb_bfd_open_maybe_remote (so_name);
+ if (dll != NULL)
+ {
+ text_offset = pe_text_section_offset (dll);
+ if (text_offset != default_text_offset)
+ warning (_("DLL %s has .text section at offset %s\n"),so_name,
+ core_addr_to_string (text_offset));
+ gdb_bfd_unref (dll);
+ }
+ else
+ text_offset = default_text_offset;
+
+ obstack_grow_str (obstack, paddress (gdbarch, load_addr + text_offset));
obstack_grow_str (obstack, "\"/></library>");
}