Commit | Line | Data |
---|---|---|
1b6bc7e0 CF |
1 | /* Read the export table symbols from a portable executable and |
2 | convert to internal format, for GDB. Used as a last resort if no | |
3 | debugging symbols recognized. | |
4 | ||
4c38e0a4 | 5 | Copyright (C) 2003, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. |
1b6bc7e0 CF |
6 | |
7 | This file is part of GDB. | |
8 | ||
9 | This program is free software; you can redistribute it and/or modify | |
10 | it under the terms of the GNU General Public License as published by | |
a9762ec7 | 11 | the Free Software Foundation; either version 3 of the License, or |
1b6bc7e0 CF |
12 | (at your option) any later version. |
13 | ||
14 | This program is distributed in the hope that it will be useful, | |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | GNU General Public License for more details. | |
18 | ||
19 | You should have received a copy of the GNU General Public License | |
a9762ec7 | 20 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
1b6bc7e0 CF |
21 | |
22 | Contributed by Raoul M. Gough (RaoulGough@yahoo.co.uk). */ | |
23 | ||
24 | #include "coff-pe-read.h" | |
25 | ||
26 | #include "bfd.h" | |
27 | ||
28 | #include "defs.h" | |
29 | #include "gdbtypes.h" | |
30 | ||
31 | #include "symtab.h" | |
32 | #include "symfile.h" | |
33 | #include "objfiles.h" | |
34 | ||
35 | /* Internal section information */ | |
36 | ||
37 | struct read_pe_section_data | |
38 | { | |
39 | CORE_ADDR vma_offset; /* Offset to loaded address of section. */ | |
40 | unsigned long rva_start; /* Start offset within the pe. */ | |
41 | unsigned long rva_end; /* End offset within the pe. */ | |
42 | enum minimal_symbol_type ms_type; /* Type to assign symbols in section. */ | |
43 | }; | |
44 | ||
45 | #define PE_SECTION_INDEX_TEXT 0 | |
46 | #define PE_SECTION_INDEX_DATA 1 | |
47 | #define PE_SECTION_INDEX_BSS 2 | |
48 | #define PE_SECTION_TABLE_SIZE 3 | |
49 | #define PE_SECTION_INDEX_INVALID -1 | |
50 | \f | |
51 | /* Get the index of the named section in our own array, which contains | |
52 | text, data and bss in that order. Return PE_SECTION_INDEX_INVALID | |
53 | if passed an unrecognised section name. */ | |
54 | ||
55 | static int | |
56 | read_pe_section_index (const char *section_name) | |
57 | { | |
58 | if (strcmp (section_name, ".text") == 0) | |
59 | { | |
60 | return PE_SECTION_INDEX_TEXT; | |
61 | } | |
62 | ||
63 | else if (strcmp (section_name, ".data") == 0) | |
64 | { | |
65 | return PE_SECTION_INDEX_DATA; | |
66 | } | |
67 | ||
68 | else if (strcmp (section_name, ".bss") == 0) | |
69 | { | |
70 | return PE_SECTION_INDEX_BSS; | |
71 | } | |
72 | ||
73 | else | |
74 | { | |
75 | return PE_SECTION_INDEX_INVALID; | |
76 | } | |
77 | } | |
78 | ||
79 | /* Record the virtual memory address of a section. */ | |
80 | ||
81 | static void | |
82 | get_section_vmas (bfd *abfd, asection *sectp, void *context) | |
83 | { | |
84 | struct read_pe_section_data *sections = context; | |
85 | int sectix = read_pe_section_index (sectp->name); | |
86 | ||
87 | if (sectix != PE_SECTION_INDEX_INVALID) | |
88 | { | |
89 | /* Data within the section start at rva_start in the pe and at | |
90 | bfd_get_section_vma() within memory. Store the offset. */ | |
91 | ||
92 | sections[sectix].vma_offset | |
93 | = bfd_get_section_vma (abfd, sectp) - sections[sectix].rva_start; | |
94 | } | |
95 | } | |
96 | \f | |
97 | /* Create a minimal symbol entry for an exported symbol. */ | |
98 | ||
99 | static void | |
100 | add_pe_exported_sym (char *sym_name, | |
101 | unsigned long func_rva, | |
102 | const struct read_pe_section_data *section_data, | |
103 | const char *dll_name, struct objfile *objfile) | |
104 | { | |
105 | /* Add the stored offset to get the loaded address of the symbol. */ | |
106 | ||
107 | CORE_ADDR vma = func_rva + section_data->vma_offset; | |
108 | ||
109 | char *qualified_name = 0; | |
110 | int dll_name_len = strlen (dll_name); | |
1b6bc7e0 CF |
111 | |
112 | /* Generate a (hopefully unique) qualified name using the first part | |
113 | of the dll name, e.g. KERNEL32!AddAtomA. This matches the style | |
114 | used by windbg from the "Microsoft Debugging Tools for Windows". */ | |
115 | ||
116 | qualified_name = xmalloc (dll_name_len + strlen (sym_name) + 2); | |
117 | ||
118 | strncpy (qualified_name, dll_name, dll_name_len); | |
119 | qualified_name[dll_name_len] = '!'; | |
120 | strcpy (qualified_name + dll_name_len + 1, sym_name); | |
121 | ||
122 | prim_record_minimal_symbol (qualified_name, | |
123 | vma, section_data->ms_type, objfile); | |
124 | ||
125 | xfree (qualified_name); | |
126 | ||
127 | /* Enter the plain name as well, which might not be unique. */ | |
128 | prim_record_minimal_symbol (sym_name, vma, section_data->ms_type, objfile); | |
129 | } | |
130 | ||
131 | /* Truncate a dll_name at the first dot character. */ | |
132 | ||
133 | static void | |
134 | read_pe_truncate_name (char *dll_name) | |
135 | { | |
136 | while (*dll_name) | |
137 | { | |
138 | if ((*dll_name) == '.') | |
139 | { | |
140 | *dll_name = '\0'; /* truncates and causes loop exit. */ | |
141 | } | |
142 | ||
143 | else | |
144 | { | |
145 | ++dll_name; | |
146 | } | |
147 | } | |
148 | } | |
149 | \f | |
150 | /* Low-level support functions, direct from the ld module pe-dll.c. */ | |
151 | static unsigned int | |
152 | pe_get16 (bfd *abfd, int where) | |
153 | { | |
154 | unsigned char b[2]; | |
155 | ||
156 | bfd_seek (abfd, (file_ptr) where, SEEK_SET); | |
157 | bfd_bread (b, (bfd_size_type) 2, abfd); | |
158 | return b[0] + (b[1] << 8); | |
159 | } | |
160 | ||
161 | static unsigned int | |
162 | pe_get32 (bfd *abfd, int where) | |
163 | { | |
164 | unsigned char b[4]; | |
165 | ||
166 | bfd_seek (abfd, (file_ptr) where, SEEK_SET); | |
167 | bfd_bread (b, (bfd_size_type) 4, abfd); | |
168 | return b[0] + (b[1] << 8) + (b[2] << 16) + (b[3] << 24); | |
169 | } | |
170 | ||
171 | static unsigned int | |
172 | pe_as32 (void *ptr) | |
173 | { | |
174 | unsigned char *b = ptr; | |
175 | ||
176 | return b[0] + (b[1] << 8) + (b[2] << 16) + (b[3] << 24); | |
177 | } | |
178 | \f | |
179 | /* Read the (non-debug) export symbol table from a portable | |
180 | executable. Code originally lifted from the ld function | |
181 | pe_implied_import_dll in pe-dll.c. */ | |
182 | ||
183 | void | |
184 | read_pe_exported_syms (struct objfile *objfile) | |
185 | { | |
186 | bfd *dll = objfile->obfd; | |
187 | unsigned long pe_header_offset, opthdr_ofs, num_entries, i; | |
188 | unsigned long export_rva, export_size, nsections, secptr, expptr; | |
189 | unsigned long exp_funcbase; | |
190 | unsigned char *expdata, *erva; | |
191 | unsigned long name_rvas, ordinals, nexp, ordbase; | |
192 | char *dll_name; | |
a68ddad5 KT |
193 | int is_pe64 = 0; |
194 | int is_pe32 = 0; | |
1b6bc7e0 CF |
195 | |
196 | /* Array elements are for text, data and bss in that order | |
197 | Initialization with start_rva > end_rva guarantees that | |
198 | unused sections won't be matched. */ | |
199 | struct read_pe_section_data section_data[PE_SECTION_TABLE_SIZE] | |
200 | = { {0, 1, 0, mst_text}, | |
201 | {0, 1, 0, mst_data}, | |
202 | {0, 1, 0, mst_bss} | |
203 | }; | |
204 | ||
205 | struct cleanup *back_to = 0; | |
206 | ||
207 | char const *target = bfd_get_target (objfile->obfd); | |
208 | ||
5e13bd89 PA |
209 | is_pe64 = (strcmp (target, "pe-x86-64") == 0 |
210 | || strcmp (target, "pei-x86-64") == 0); | |
211 | is_pe32 = (strcmp (target, "pe-i386") == 0 | |
212 | || strcmp (target, "pei-i386") == 0 | |
213 | || strcmp (target, "pe-arm-wince-little") == 0 | |
214 | || strcmp (target, "pei-arm-wince-little") == 0); | |
215 | ||
a68ddad5 | 216 | if (!is_pe32 && !is_pe64) |
1b6bc7e0 | 217 | { |
5e13bd89 PA |
218 | /* This is not a recognized PE format file. Abort now, because |
219 | the code is untested on anything else. *FIXME* test on | |
220 | further architectures and loosen or remove this test. */ | |
1b6bc7e0 CF |
221 | return; |
222 | } | |
223 | ||
224 | /* Get pe_header, optional header and numbers of export entries. */ | |
225 | pe_header_offset = pe_get32 (dll, 0x3c); | |
226 | opthdr_ofs = pe_header_offset + 4 + 20; | |
a68ddad5 | 227 | if (is_pe64) |
1dac1b47 | 228 | num_entries = pe_get32 (dll, opthdr_ofs + 108); |
a68ddad5 KT |
229 | else |
230 | num_entries = pe_get32 (dll, opthdr_ofs + 92); | |
1b6bc7e0 CF |
231 | |
232 | if (num_entries < 1) /* No exports. */ | |
233 | { | |
234 | return; | |
235 | } | |
236 | ||
a68ddad5 KT |
237 | if (is_pe64) |
238 | { | |
239 | export_rva = pe_get32 (dll, opthdr_ofs + 112); | |
240 | export_size = pe_get32 (dll, opthdr_ofs + 116); | |
241 | } | |
242 | else | |
243 | { | |
244 | export_rva = pe_get32 (dll, opthdr_ofs + 96); | |
245 | export_size = pe_get32 (dll, opthdr_ofs + 100); | |
246 | } | |
1b6bc7e0 CF |
247 | nsections = pe_get16 (dll, pe_header_offset + 4 + 2); |
248 | secptr = (pe_header_offset + 4 + 20 + | |
249 | pe_get16 (dll, pe_header_offset + 4 + 16)); | |
250 | expptr = 0; | |
251 | ||
252 | /* Get the rva and size of the export section. */ | |
253 | for (i = 0; i < nsections; i++) | |
254 | { | |
255 | char sname[8]; | |
256 | unsigned long secptr1 = secptr + 40 * i; | |
257 | unsigned long vaddr = pe_get32 (dll, secptr1 + 12); | |
258 | unsigned long vsize = pe_get32 (dll, secptr1 + 16); | |
259 | unsigned long fptr = pe_get32 (dll, secptr1 + 20); | |
260 | ||
261 | bfd_seek (dll, (file_ptr) secptr1, SEEK_SET); | |
262 | bfd_bread (sname, (bfd_size_type) 8, dll); | |
263 | ||
264 | if (vaddr <= export_rva && vaddr + vsize > export_rva) | |
265 | { | |
266 | expptr = fptr + (export_rva - vaddr); | |
267 | if (export_rva + export_size > vaddr + vsize) | |
268 | export_size = vsize - (export_rva - vaddr); | |
269 | break; | |
270 | } | |
271 | } | |
272 | ||
273 | if (export_size == 0) | |
274 | { | |
275 | /* Empty export table. */ | |
276 | return; | |
277 | } | |
278 | ||
279 | /* Scan sections and store the base and size of the relevant sections. */ | |
280 | for (i = 0; i < nsections; i++) | |
281 | { | |
282 | unsigned long secptr1 = secptr + 40 * i; | |
283 | unsigned long vsize = pe_get32 (dll, secptr1 + 8); | |
284 | unsigned long vaddr = pe_get32 (dll, secptr1 + 12); | |
1b6bc7e0 CF |
285 | char sec_name[9]; |
286 | int sectix; | |
287 | ||
288 | sec_name[8] = '\0'; | |
289 | bfd_seek (dll, (file_ptr) secptr1 + 0, SEEK_SET); | |
290 | bfd_bread (sec_name, (bfd_size_type) 8, dll); | |
291 | ||
292 | sectix = read_pe_section_index (sec_name); | |
293 | ||
294 | if (sectix != PE_SECTION_INDEX_INVALID) | |
295 | { | |
296 | section_data[sectix].rva_start = vaddr; | |
297 | section_data[sectix].rva_end = vaddr + vsize; | |
298 | } | |
299 | } | |
300 | ||
301 | expdata = (unsigned char *) xmalloc (export_size); | |
302 | back_to = make_cleanup (xfree, expdata); | |
303 | ||
304 | bfd_seek (dll, (file_ptr) expptr, SEEK_SET); | |
305 | bfd_bread (expdata, (bfd_size_type) export_size, dll); | |
306 | erva = expdata - export_rva; | |
307 | ||
308 | nexp = pe_as32 (expdata + 24); | |
309 | name_rvas = pe_as32 (expdata + 32); | |
310 | ordinals = pe_as32 (expdata + 36); | |
311 | ordbase = pe_as32 (expdata + 16); | |
312 | exp_funcbase = pe_as32 (expdata + 28); | |
313 | ||
314 | /* Use internal dll name instead of full pathname. */ | |
315 | dll_name = pe_as32 (expdata + 12) + erva; | |
316 | ||
317 | bfd_map_over_sections (dll, get_section_vmas, section_data); | |
318 | ||
319 | /* Adjust the vma_offsets in case this PE got relocated. This | |
320 | assumes that *all* sections share the same relocation offset | |
321 | as the text section. */ | |
322 | for (i = 0; i < PE_SECTION_TABLE_SIZE; i++) | |
323 | { | |
324 | section_data[i].vma_offset | |
325 | += ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile)); | |
326 | } | |
327 | ||
1b6bc7e0 CF |
328 | /* Truncate name at first dot. Should maybe also convert to all |
329 | lower case for convenience on Windows. */ | |
330 | read_pe_truncate_name (dll_name); | |
331 | ||
332 | /* Iterate through the list of symbols. */ | |
333 | for (i = 0; i < nexp; i++) | |
334 | { | |
335 | /* Pointer to the names vector. */ | |
336 | unsigned long name_rva = pe_as32 (erva + name_rvas + i * 4); | |
337 | ||
338 | /* Pointer to the function address vector. */ | |
339 | unsigned long func_rva = pe_as32 (erva + exp_funcbase + i * 4); | |
340 | ||
341 | /* Find this symbol's section in our own array. */ | |
342 | int sectix = 0; | |
343 | ||
344 | for (sectix = 0; sectix < PE_SECTION_TABLE_SIZE; ++sectix) | |
345 | { | |
346 | if ((func_rva >= section_data[sectix].rva_start) | |
347 | && (func_rva < section_data[sectix].rva_end)) | |
348 | { | |
349 | add_pe_exported_sym (erva + name_rva, | |
350 | func_rva, | |
351 | section_data + sectix, dll_name, objfile); | |
352 | break; | |
353 | } | |
354 | } | |
355 | } | |
356 | ||
357 | /* discard expdata. */ | |
358 | do_cleanups (back_to); | |
359 | } |