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