1130803Smarcel/* Read the export table symbols from a portable executable and
2130803Smarcel   convert to internal format, for GDB. Used as a last resort if no
3130803Smarcel   debugging symbols recognized.
4130803Smarcel
5130803Smarcel   Copyright 2003 Free Software Foundation, Inc.
6130803Smarcel
7130803Smarcel   This file is part of GDB.
8130803Smarcel
9130803Smarcel   This program is free software; you can redistribute it and/or modify
10130803Smarcel   it under the terms of the GNU General Public License as published by
11130803Smarcel   the Free Software Foundation; either version 2 of the License, or
12130803Smarcel   (at your option) any later version.
13130803Smarcel
14130803Smarcel   This program is distributed in the hope that it will be useful,
15130803Smarcel   but WITHOUT ANY WARRANTY; without even the implied warranty of
16130803Smarcel   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17130803Smarcel   GNU General Public License for more details.
18130803Smarcel
19130803Smarcel   You should have received a copy of the GNU General Public License
20130803Smarcel   along with this program; if not, write to the Free Software
21130803Smarcel   Foundation, Inc., 59 Temple Place - Suite 330,
22130803Smarcel   Boston, MA 02111-1307, USA.
23130803Smarcel
24130803Smarcel   Contributed by Raoul M. Gough (RaoulGough@yahoo.co.uk). */
25130803Smarcel
26130803Smarcel#include "coff-pe-read.h"
27130803Smarcel
28130803Smarcel#include "bfd.h"
29130803Smarcel
30130803Smarcel#include "defs.h"
31130803Smarcel#include "gdbtypes.h"
32130803Smarcel
33130803Smarcel#include "symtab.h"
34130803Smarcel#include "symfile.h"
35130803Smarcel#include "objfiles.h"
36130803Smarcel
37130803Smarcel/* Internal section information */
38130803Smarcel
39130803Smarcelstruct read_pe_section_data
40130803Smarcel{
41130803Smarcel  CORE_ADDR vma_offset;		/* Offset to loaded address of section. */
42130803Smarcel  unsigned long rva_start;	/* Start offset within the pe. */
43130803Smarcel  unsigned long rva_end;	/* End offset within the pe. */
44130803Smarcel  enum minimal_symbol_type ms_type;	/* Type to assign symbols in section. */
45130803Smarcel};
46130803Smarcel
47130803Smarcel#define PE_SECTION_INDEX_TEXT     0
48130803Smarcel#define PE_SECTION_INDEX_DATA     1
49130803Smarcel#define PE_SECTION_INDEX_BSS      2
50130803Smarcel#define PE_SECTION_TABLE_SIZE     3
51130803Smarcel#define PE_SECTION_INDEX_INVALID -1
52130803Smarcel
53130803Smarcel/* Get the index of the named section in our own array, which contains
54130803Smarcel   text, data and bss in that order. Return PE_SECTION_INDEX_INVALID
55130803Smarcel   if passed an unrecognised section name. */
56130803Smarcel
57130803Smarcelstatic int
58130803Smarcelread_pe_section_index (const char *section_name)
59130803Smarcel{
60130803Smarcel  if (strcmp (section_name, ".text") == 0)
61130803Smarcel    {
62130803Smarcel      return PE_SECTION_INDEX_TEXT;
63130803Smarcel    }
64130803Smarcel
65130803Smarcel  else if (strcmp (section_name, ".data") == 0)
66130803Smarcel    {
67130803Smarcel      return PE_SECTION_INDEX_DATA;
68130803Smarcel    }
69130803Smarcel
70130803Smarcel  else if (strcmp (section_name, ".bss") == 0)
71130803Smarcel    {
72130803Smarcel      return PE_SECTION_INDEX_BSS;
73130803Smarcel    }
74130803Smarcel
75130803Smarcel  else
76130803Smarcel    {
77130803Smarcel      return PE_SECTION_INDEX_INVALID;
78130803Smarcel    }
79130803Smarcel}
80130803Smarcel
81130803Smarcel/* Record the virtual memory address of a section. */
82130803Smarcel
83130803Smarcelstatic void
84130803Smarcelget_section_vmas (bfd *abfd, asection *sectp, void *context)
85130803Smarcel{
86130803Smarcel  struct read_pe_section_data *sections = context;
87130803Smarcel  int sectix = read_pe_section_index (sectp->name);
88130803Smarcel
89130803Smarcel  if (sectix != PE_SECTION_INDEX_INVALID)
90130803Smarcel    {
91130803Smarcel      /* Data within the section start at rva_start in the pe and at
92130803Smarcel         bfd_get_section_vma() within memory. Store the offset. */
93130803Smarcel
94130803Smarcel      sections[sectix].vma_offset
95130803Smarcel	= bfd_get_section_vma (abfd, sectp) - sections[sectix].rva_start;
96130803Smarcel    }
97130803Smarcel}
98130803Smarcel
99130803Smarcel/* Create a minimal symbol entry for an exported symbol. */
100130803Smarcel
101130803Smarcelstatic void
102130803Smarceladd_pe_exported_sym (char *sym_name,
103130803Smarcel		     unsigned long func_rva,
104130803Smarcel		     const struct read_pe_section_data *section_data,
105130803Smarcel		     const char *dll_name, struct objfile *objfile)
106130803Smarcel{
107130803Smarcel  /* Add the stored offset to get the loaded address of the symbol. */
108130803Smarcel
109130803Smarcel  CORE_ADDR vma = func_rva + section_data->vma_offset;
110130803Smarcel
111130803Smarcel  char *qualified_name = 0;
112130803Smarcel  int dll_name_len = strlen (dll_name);
113130803Smarcel  int count;
114130803Smarcel
115130803Smarcel  /* Generate a (hopefully unique) qualified name using the first part
116130803Smarcel     of the dll name, e.g. KERNEL32!AddAtomA. This matches the style
117130803Smarcel     used by windbg from the "Microsoft Debugging Tools for Windows". */
118130803Smarcel
119130803Smarcel  qualified_name = xmalloc (dll_name_len + strlen (sym_name) + 2);
120130803Smarcel
121130803Smarcel  strncpy (qualified_name, dll_name, dll_name_len);
122130803Smarcel  qualified_name[dll_name_len] = '!';
123130803Smarcel  strcpy (qualified_name + dll_name_len + 1, sym_name);
124130803Smarcel
125130803Smarcel  prim_record_minimal_symbol (qualified_name,
126130803Smarcel			      vma, section_data->ms_type, objfile);
127130803Smarcel
128130803Smarcel  xfree (qualified_name);
129130803Smarcel
130130803Smarcel  /* Enter the plain name as well, which might not be unique. */
131130803Smarcel  prim_record_minimal_symbol (sym_name, vma, section_data->ms_type, objfile);
132130803Smarcel}
133130803Smarcel
134130803Smarcel/* Truncate a dll_name at the first dot character. */
135130803Smarcel
136130803Smarcelstatic void
137130803Smarcelread_pe_truncate_name (char *dll_name)
138130803Smarcel{
139130803Smarcel  while (*dll_name)
140130803Smarcel    {
141130803Smarcel      if ((*dll_name) == '.')
142130803Smarcel	{
143130803Smarcel	  *dll_name = '\0';	/* truncates and causes loop exit. */
144130803Smarcel	}
145130803Smarcel
146130803Smarcel      else
147130803Smarcel	{
148130803Smarcel	  ++dll_name;
149130803Smarcel	}
150130803Smarcel    }
151130803Smarcel}
152130803Smarcel
153130803Smarcel/* Low-level support functions, direct from the ld module pe-dll.c. */
154130803Smarcelstatic unsigned int
155130803Smarcelpe_get16 (bfd *abfd, int where)
156130803Smarcel{
157130803Smarcel  unsigned char b[2];
158130803Smarcel
159130803Smarcel  bfd_seek (abfd, (file_ptr) where, SEEK_SET);
160130803Smarcel  bfd_bread (b, (bfd_size_type) 2, abfd);
161130803Smarcel  return b[0] + (b[1] << 8);
162130803Smarcel}
163130803Smarcel
164130803Smarcelstatic unsigned int
165130803Smarcelpe_get32 (bfd *abfd, int where)
166130803Smarcel{
167130803Smarcel  unsigned char b[4];
168130803Smarcel
169130803Smarcel  bfd_seek (abfd, (file_ptr) where, SEEK_SET);
170130803Smarcel  bfd_bread (b, (bfd_size_type) 4, abfd);
171130803Smarcel  return b[0] + (b[1] << 8) + (b[2] << 16) + (b[3] << 24);
172130803Smarcel}
173130803Smarcel
174130803Smarcelstatic unsigned int
175130803Smarcelpe_as32 (void *ptr)
176130803Smarcel{
177130803Smarcel  unsigned char *b = ptr;
178130803Smarcel
179130803Smarcel  return b[0] + (b[1] << 8) + (b[2] << 16) + (b[3] << 24);
180130803Smarcel}
181130803Smarcel
182130803Smarcel/* Read the (non-debug) export symbol table from a portable
183130803Smarcel   executable. Code originally lifted from the ld function
184130803Smarcel   pe_implied_import_dll in pe-dll.c. */
185130803Smarcel
186130803Smarcelvoid
187130803Smarcelread_pe_exported_syms (struct objfile *objfile)
188130803Smarcel{
189130803Smarcel  bfd *dll = objfile->obfd;
190130803Smarcel  unsigned long pe_header_offset, opthdr_ofs, num_entries, i;
191130803Smarcel  unsigned long export_rva, export_size, nsections, secptr, expptr;
192130803Smarcel  unsigned long exp_funcbase;
193130803Smarcel  unsigned char *expdata, *erva;
194130803Smarcel  unsigned long name_rvas, ordinals, nexp, ordbase;
195130803Smarcel  char *dll_name;
196130803Smarcel
197130803Smarcel  /* Array elements are for text, data and bss in that order
198130803Smarcel     Initialization with start_rva > end_rva guarantees that
199130803Smarcel     unused sections won't be matched. */
200130803Smarcel  struct read_pe_section_data section_data[PE_SECTION_TABLE_SIZE]
201130803Smarcel    = { {0, 1, 0, mst_text},
202130803Smarcel  {0, 1, 0, mst_data},
203130803Smarcel  {0, 1, 0, mst_bss}
204130803Smarcel  };
205130803Smarcel
206130803Smarcel  struct cleanup *back_to = 0;
207130803Smarcel
208130803Smarcel  char const *target = bfd_get_target (objfile->obfd);
209130803Smarcel
210130803Smarcel  if ((strcmp (target, "pe-i386") != 0) && (strcmp (target, "pei-i386") != 0))
211130803Smarcel    {
212130803Smarcel      /* This is not an i386 format file. Abort now, because the code
213130803Smarcel         is untested on anything else. *FIXME* test on further
214130803Smarcel         architectures and loosen or remove this test. */
215130803Smarcel      return;
216130803Smarcel    }
217130803Smarcel
218130803Smarcel  /* Get pe_header, optional header and numbers of export entries.  */
219130803Smarcel  pe_header_offset = pe_get32 (dll, 0x3c);
220130803Smarcel  opthdr_ofs = pe_header_offset + 4 + 20;
221130803Smarcel  num_entries = pe_get32 (dll, opthdr_ofs + 92);
222130803Smarcel
223130803Smarcel  if (num_entries < 1)		/* No exports.  */
224130803Smarcel    {
225130803Smarcel      return;
226130803Smarcel    }
227130803Smarcel
228130803Smarcel  export_rva = pe_get32 (dll, opthdr_ofs + 96);
229130803Smarcel  export_size = pe_get32 (dll, opthdr_ofs + 100);
230130803Smarcel  nsections = pe_get16 (dll, pe_header_offset + 4 + 2);
231130803Smarcel  secptr = (pe_header_offset + 4 + 20 +
232130803Smarcel	    pe_get16 (dll, pe_header_offset + 4 + 16));
233130803Smarcel  expptr = 0;
234130803Smarcel
235130803Smarcel  /* Get the rva and size of the export section.  */
236130803Smarcel  for (i = 0; i < nsections; i++)
237130803Smarcel    {
238130803Smarcel      char sname[8];
239130803Smarcel      unsigned long secptr1 = secptr + 40 * i;
240130803Smarcel      unsigned long vaddr = pe_get32 (dll, secptr1 + 12);
241130803Smarcel      unsigned long vsize = pe_get32 (dll, secptr1 + 16);
242130803Smarcel      unsigned long fptr = pe_get32 (dll, secptr1 + 20);
243130803Smarcel
244130803Smarcel      bfd_seek (dll, (file_ptr) secptr1, SEEK_SET);
245130803Smarcel      bfd_bread (sname, (bfd_size_type) 8, dll);
246130803Smarcel
247130803Smarcel      if (vaddr <= export_rva && vaddr + vsize > export_rva)
248130803Smarcel	{
249130803Smarcel	  expptr = fptr + (export_rva - vaddr);
250130803Smarcel	  if (export_rva + export_size > vaddr + vsize)
251130803Smarcel	    export_size = vsize - (export_rva - vaddr);
252130803Smarcel	  break;
253130803Smarcel	}
254130803Smarcel    }
255130803Smarcel
256130803Smarcel  if (export_size == 0)
257130803Smarcel    {
258130803Smarcel      /* Empty export table. */
259130803Smarcel      return;
260130803Smarcel    }
261130803Smarcel
262130803Smarcel  /* Scan sections and store the base and size of the relevant sections. */
263130803Smarcel  for (i = 0; i < nsections; i++)
264130803Smarcel    {
265130803Smarcel      unsigned long secptr1 = secptr + 40 * i;
266130803Smarcel      unsigned long vsize = pe_get32 (dll, secptr1 + 8);
267130803Smarcel      unsigned long vaddr = pe_get32 (dll, secptr1 + 12);
268130803Smarcel      unsigned long flags = pe_get32 (dll, secptr1 + 36);
269130803Smarcel      char sec_name[9];
270130803Smarcel      int sectix;
271130803Smarcel
272130803Smarcel      sec_name[8] = '\0';
273130803Smarcel      bfd_seek (dll, (file_ptr) secptr1 + 0, SEEK_SET);
274130803Smarcel      bfd_bread (sec_name, (bfd_size_type) 8, dll);
275130803Smarcel
276130803Smarcel      sectix = read_pe_section_index (sec_name);
277130803Smarcel
278130803Smarcel      if (sectix != PE_SECTION_INDEX_INVALID)
279130803Smarcel	{
280130803Smarcel	  section_data[sectix].rva_start = vaddr;
281130803Smarcel	  section_data[sectix].rva_end = vaddr + vsize;
282130803Smarcel	}
283130803Smarcel    }
284130803Smarcel
285130803Smarcel  expdata = (unsigned char *) xmalloc (export_size);
286130803Smarcel  back_to = make_cleanup (xfree, expdata);
287130803Smarcel
288130803Smarcel  bfd_seek (dll, (file_ptr) expptr, SEEK_SET);
289130803Smarcel  bfd_bread (expdata, (bfd_size_type) export_size, dll);
290130803Smarcel  erva = expdata - export_rva;
291130803Smarcel
292130803Smarcel  nexp = pe_as32 (expdata + 24);
293130803Smarcel  name_rvas = pe_as32 (expdata + 32);
294130803Smarcel  ordinals = pe_as32 (expdata + 36);
295130803Smarcel  ordbase = pe_as32 (expdata + 16);
296130803Smarcel  exp_funcbase = pe_as32 (expdata + 28);
297130803Smarcel
298130803Smarcel  /* Use internal dll name instead of full pathname. */
299130803Smarcel  dll_name = pe_as32 (expdata + 12) + erva;
300130803Smarcel
301130803Smarcel  bfd_map_over_sections (dll, get_section_vmas, section_data);
302130803Smarcel
303130803Smarcel  /* Adjust the vma_offsets in case this PE got relocated. This
304130803Smarcel     assumes that *all* sections share the same relocation offset
305130803Smarcel     as the text section. */
306130803Smarcel  for (i = 0; i < PE_SECTION_TABLE_SIZE; i++)
307130803Smarcel    {
308130803Smarcel      section_data[i].vma_offset
309130803Smarcel	+= ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));
310130803Smarcel    }
311130803Smarcel
312130803Smarcel  printf_filtered ("Minimal symbols from %s...", dll_name);
313130803Smarcel  wrap_here ("");
314130803Smarcel
315130803Smarcel  /* Truncate name at first dot. Should maybe also convert to all
316130803Smarcel     lower case for convenience on Windows. */
317130803Smarcel  read_pe_truncate_name (dll_name);
318130803Smarcel
319130803Smarcel  /* Iterate through the list of symbols.  */
320130803Smarcel  for (i = 0; i < nexp; i++)
321130803Smarcel    {
322130803Smarcel      /* Pointer to the names vector.  */
323130803Smarcel      unsigned long name_rva = pe_as32 (erva + name_rvas + i * 4);
324130803Smarcel
325130803Smarcel      /* Pointer to the function address vector.  */
326130803Smarcel      unsigned long func_rva = pe_as32 (erva + exp_funcbase + i * 4);
327130803Smarcel
328130803Smarcel      /* Find this symbol's section in our own array. */
329130803Smarcel      int sectix = 0;
330130803Smarcel
331130803Smarcel      for (sectix = 0; sectix < PE_SECTION_TABLE_SIZE; ++sectix)
332130803Smarcel	{
333130803Smarcel	  if ((func_rva >= section_data[sectix].rva_start)
334130803Smarcel	      && (func_rva < section_data[sectix].rva_end))
335130803Smarcel	    {
336130803Smarcel	      add_pe_exported_sym (erva + name_rva,
337130803Smarcel				   func_rva,
338130803Smarcel				   section_data + sectix, dll_name, objfile);
339130803Smarcel	      break;
340130803Smarcel	    }
341130803Smarcel	}
342130803Smarcel    }
343130803Smarcel
344130803Smarcel  /* discard expdata. */
345130803Smarcel  do_cleanups (back_to);
346130803Smarcel}
347