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 | ||
0fb0cc75 | 5 | Copyright (C) 2003, 2007, 2008, 2009 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); | |
111 | int count; | |
112 | ||
113 | /* Generate a (hopefully unique) qualified name using the first part | |
114 | of the dll name, e.g. KERNEL32!AddAtomA. This matches the style | |
115 | used by windbg from the "Microsoft Debugging Tools for Windows". */ | |
116 | ||
117 | qualified_name = xmalloc (dll_name_len + strlen (sym_name) + 2); | |
118 | ||
119 | strncpy (qualified_name, dll_name, dll_name_len); | |
120 | qualified_name[dll_name_len] = '!'; | |
121 | strcpy (qualified_name + dll_name_len + 1, sym_name); | |
122 | ||
123 | prim_record_minimal_symbol (qualified_name, | |
124 | vma, section_data->ms_type, objfile); | |
125 | ||
126 | xfree (qualified_name); | |
127 | ||
128 | /* Enter the plain name as well, which might not be unique. */ | |
129 | prim_record_minimal_symbol (sym_name, vma, section_data->ms_type, objfile); | |
130 | } | |
131 | ||
132 | /* Truncate a dll_name at the first dot character. */ | |
133 | ||
134 | static void | |
135 | read_pe_truncate_name (char *dll_name) | |
136 | { | |
137 | while (*dll_name) | |
138 | { | |
139 | if ((*dll_name) == '.') | |
140 | { | |
141 | *dll_name = '\0'; /* truncates and causes loop exit. */ | |
142 | } | |
143 | ||
144 | else | |
145 | { | |
146 | ++dll_name; | |
147 | } | |
148 | } | |
149 | } | |
150 | \f | |
151 | /* Low-level support functions, direct from the ld module pe-dll.c. */ | |
152 | static unsigned int | |
153 | pe_get16 (bfd *abfd, int where) | |
154 | { | |
155 | unsigned char b[2]; | |
156 | ||
157 | bfd_seek (abfd, (file_ptr) where, SEEK_SET); | |
158 | bfd_bread (b, (bfd_size_type) 2, abfd); | |
159 | return b[0] + (b[1] << 8); | |
160 | } | |
161 | ||
162 | static unsigned int | |
163 | pe_get32 (bfd *abfd, int where) | |
164 | { | |
165 | unsigned char b[4]; | |
166 | ||
167 | bfd_seek (abfd, (file_ptr) where, SEEK_SET); | |
168 | bfd_bread (b, (bfd_size_type) 4, abfd); | |
169 | return b[0] + (b[1] << 8) + (b[2] << 16) + (b[3] << 24); | |
170 | } | |
171 | ||
172 | static unsigned int | |
173 | pe_as32 (void *ptr) | |
174 | { | |
175 | unsigned char *b = ptr; | |
176 | ||
177 | return b[0] + (b[1] << 8) + (b[2] << 16) + (b[3] << 24); | |
178 | } | |
179 | \f | |
180 | /* Read the (non-debug) export symbol table from a portable | |
181 | executable. Code originally lifted from the ld function | |
182 | pe_implied_import_dll in pe-dll.c. */ | |
183 | ||
184 | void | |
185 | read_pe_exported_syms (struct objfile *objfile) | |
186 | { | |
187 | bfd *dll = objfile->obfd; | |
188 | unsigned long pe_header_offset, opthdr_ofs, num_entries, i; | |
189 | unsigned long export_rva, export_size, nsections, secptr, expptr; | |
190 | unsigned long exp_funcbase; | |
191 | unsigned char *expdata, *erva; | |
192 | unsigned long name_rvas, ordinals, nexp, ordbase; | |
193 | char *dll_name; | |
a68ddad5 KT |
194 | int is_pe64 = 0; |
195 | int is_pe32 = 0; | |
1b6bc7e0 CF |
196 | |
197 | /* Array elements are for text, data and bss in that order | |
198 | Initialization with start_rva > end_rva guarantees that | |
199 | unused sections won't be matched. */ | |
200 | struct read_pe_section_data section_data[PE_SECTION_TABLE_SIZE] | |
201 | = { {0, 1, 0, mst_text}, | |
202 | {0, 1, 0, mst_data}, | |
203 | {0, 1, 0, mst_bss} | |
204 | }; | |
205 | ||
206 | struct cleanup *back_to = 0; | |
207 | ||
208 | char const *target = bfd_get_target (objfile->obfd); | |
209 | ||
5e13bd89 PA |
210 | is_pe64 = (strcmp (target, "pe-x86-64") == 0 |
211 | || strcmp (target, "pei-x86-64") == 0); | |
212 | is_pe32 = (strcmp (target, "pe-i386") == 0 | |
213 | || strcmp (target, "pei-i386") == 0 | |
214 | || strcmp (target, "pe-arm-wince-little") == 0 | |
215 | || strcmp (target, "pei-arm-wince-little") == 0); | |
216 | ||
a68ddad5 | 217 | if (!is_pe32 && !is_pe64) |
1b6bc7e0 | 218 | { |
5e13bd89 PA |
219 | /* This is not a recognized PE format file. Abort now, because |
220 | the code is untested on anything else. *FIXME* test on | |
221 | further architectures and loosen or remove this test. */ | |
1b6bc7e0 CF |
222 | return; |
223 | } | |
224 | ||
225 | /* Get pe_header, optional header and numbers of export entries. */ | |
226 | pe_header_offset = pe_get32 (dll, 0x3c); | |
227 | opthdr_ofs = pe_header_offset + 4 + 20; | |
a68ddad5 | 228 | if (is_pe64) |
1dac1b47 | 229 | num_entries = pe_get32 (dll, opthdr_ofs + 108); |
a68ddad5 KT |
230 | else |
231 | num_entries = pe_get32 (dll, opthdr_ofs + 92); | |
1b6bc7e0 CF |
232 | |
233 | if (num_entries < 1) /* No exports. */ | |
234 | { | |
235 | return; | |
236 | } | |
237 | ||
a68ddad5 KT |
238 | if (is_pe64) |
239 | { | |
240 | export_rva = pe_get32 (dll, opthdr_ofs + 112); | |
241 | export_size = pe_get32 (dll, opthdr_ofs + 116); | |
242 | } | |
243 | else | |
244 | { | |
245 | export_rva = pe_get32 (dll, opthdr_ofs + 96); | |
246 | export_size = pe_get32 (dll, opthdr_ofs + 100); | |
247 | } | |
1b6bc7e0 CF |
248 | nsections = pe_get16 (dll, pe_header_offset + 4 + 2); |
249 | secptr = (pe_header_offset + 4 + 20 + | |
250 | pe_get16 (dll, pe_header_offset + 4 + 16)); | |
251 | expptr = 0; | |
252 | ||
253 | /* Get the rva and size of the export section. */ | |
254 | for (i = 0; i < nsections; i++) | |
255 | { | |
256 | char sname[8]; | |
257 | unsigned long secptr1 = secptr + 40 * i; | |
258 | unsigned long vaddr = pe_get32 (dll, secptr1 + 12); | |
259 | unsigned long vsize = pe_get32 (dll, secptr1 + 16); | |
260 | unsigned long fptr = pe_get32 (dll, secptr1 + 20); | |
261 | ||
262 | bfd_seek (dll, (file_ptr) secptr1, SEEK_SET); | |
263 | bfd_bread (sname, (bfd_size_type) 8, dll); | |
264 | ||
265 | if (vaddr <= export_rva && vaddr + vsize > export_rva) | |
266 | { | |
267 | expptr = fptr + (export_rva - vaddr); | |
268 | if (export_rva + export_size > vaddr + vsize) | |
269 | export_size = vsize - (export_rva - vaddr); | |
270 | break; | |
271 | } | |
272 | } | |
273 | ||
274 | if (export_size == 0) | |
275 | { | |
276 | /* Empty export table. */ | |
277 | return; | |
278 | } | |
279 | ||
280 | /* Scan sections and store the base and size of the relevant sections. */ | |
281 | for (i = 0; i < nsections; i++) | |
282 | { | |
283 | unsigned long secptr1 = secptr + 40 * i; | |
284 | unsigned long vsize = pe_get32 (dll, secptr1 + 8); | |
285 | unsigned long vaddr = pe_get32 (dll, secptr1 + 12); | |
286 | unsigned long flags = pe_get32 (dll, secptr1 + 36); | |
287 | char sec_name[9]; | |
288 | int sectix; | |
289 | ||
290 | sec_name[8] = '\0'; | |
291 | bfd_seek (dll, (file_ptr) secptr1 + 0, SEEK_SET); | |
292 | bfd_bread (sec_name, (bfd_size_type) 8, dll); | |
293 | ||
294 | sectix = read_pe_section_index (sec_name); | |
295 | ||
296 | if (sectix != PE_SECTION_INDEX_INVALID) | |
297 | { | |
298 | section_data[sectix].rva_start = vaddr; | |
299 | section_data[sectix].rva_end = vaddr + vsize; | |
300 | } | |
301 | } | |
302 | ||
303 | expdata = (unsigned char *) xmalloc (export_size); | |
304 | back_to = make_cleanup (xfree, expdata); | |
305 | ||
306 | bfd_seek (dll, (file_ptr) expptr, SEEK_SET); | |
307 | bfd_bread (expdata, (bfd_size_type) export_size, dll); | |
308 | erva = expdata - export_rva; | |
309 | ||
310 | nexp = pe_as32 (expdata + 24); | |
311 | name_rvas = pe_as32 (expdata + 32); | |
312 | ordinals = pe_as32 (expdata + 36); | |
313 | ordbase = pe_as32 (expdata + 16); | |
314 | exp_funcbase = pe_as32 (expdata + 28); | |
315 | ||
316 | /* Use internal dll name instead of full pathname. */ | |
317 | dll_name = pe_as32 (expdata + 12) + erva; | |
318 | ||
319 | bfd_map_over_sections (dll, get_section_vmas, section_data); | |
320 | ||
321 | /* Adjust the vma_offsets in case this PE got relocated. This | |
322 | assumes that *all* sections share the same relocation offset | |
323 | as the text section. */ | |
324 | for (i = 0; i < PE_SECTION_TABLE_SIZE; i++) | |
325 | { | |
326 | section_data[i].vma_offset | |
327 | += ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile)); | |
328 | } | |
329 | ||
1b6bc7e0 CF |
330 | /* Truncate name at first dot. Should maybe also convert to all |
331 | lower case for convenience on Windows. */ | |
332 | read_pe_truncate_name (dll_name); | |
333 | ||
334 | /* Iterate through the list of symbols. */ | |
335 | for (i = 0; i < nexp; i++) | |
336 | { | |
337 | /* Pointer to the names vector. */ | |
338 | unsigned long name_rva = pe_as32 (erva + name_rvas + i * 4); | |
339 | ||
340 | /* Pointer to the function address vector. */ | |
341 | unsigned long func_rva = pe_as32 (erva + exp_funcbase + i * 4); | |
342 | ||
343 | /* Find this symbol's section in our own array. */ | |
344 | int sectix = 0; | |
345 | ||
346 | for (sectix = 0; sectix < PE_SECTION_TABLE_SIZE; ++sectix) | |
347 | { | |
348 | if ((func_rva >= section_data[sectix].rva_start) | |
349 | && (func_rva < section_data[sectix].rva_end)) | |
350 | { | |
351 | add_pe_exported_sym (erva + name_rva, | |
352 | func_rva, | |
353 | section_data + sectix, dll_name, objfile); | |
354 | break; | |
355 | } | |
356 | } | |
357 | } | |
358 | ||
359 | /* discard expdata. */ | |
360 | do_cleanups (back_to); | |
361 | } |