Commit | Line | Data |
---|---|---|
e2882c85 | 1 | /* Copyright (C) 2013-2018 Free Software Foundation, Inc. |
4d1eb6b4 JB |
2 | |
3 | This file is part of GDB. | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 3 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
17 | ||
18 | #include "defs.h" | |
19 | #include "solib-aix.h" | |
20 | #include "solist.h" | |
21 | #include "inferior.h" | |
22 | #include "gdb_bfd.h" | |
23 | #include "gdbcore.h" | |
24 | #include "objfiles.h" | |
25 | #include "symtab.h" | |
26 | #include "xcoffread.h" | |
76727919 | 27 | #include "observable.h" |
4d1eb6b4 JB |
28 | #include "gdbcmd.h" |
29 | ||
30 | /* Variable controlling the output of the debugging traces for | |
31 | this module. */ | |
32 | static int solib_aix_debug; | |
33 | ||
34 | /* Our private data in struct so_list. */ | |
35 | ||
d0e449a1 | 36 | struct lm_info_aix : public lm_info_base |
4d1eb6b4 JB |
37 | { |
38 | /* The name of the file mapped by the loader. Apart from the entry | |
39 | for the main executable, this is usually a shared library (which, | |
40 | on AIX, is an archive library file, created using the "ar" | |
41 | command). */ | |
6c401f72 | 42 | std::string filename; |
4d1eb6b4 JB |
43 | |
44 | /* The name of the shared object file with the actual dynamic | |
6c401f72 SM |
45 | loading dependency. This may be empty (Eg. main executable). */ |
46 | std::string member_name; | |
4d1eb6b4 JB |
47 | |
48 | /* The address in inferior memory where the text section got mapped. */ | |
6c401f72 | 49 | CORE_ADDR text_addr = 0; |
4d1eb6b4 JB |
50 | |
51 | /* The size of the text section, obtained via the loader data. */ | |
6c401f72 | 52 | ULONGEST text_size = 0; |
4d1eb6b4 JB |
53 | |
54 | /* The address in inferior memory where the data section got mapped. */ | |
6c401f72 | 55 | CORE_ADDR data_addr = 0; |
4d1eb6b4 JB |
56 | |
57 | /* The size of the data section, obtained via the loader data. */ | |
6c401f72 | 58 | ULONGEST data_size = 0; |
4d1eb6b4 JB |
59 | }; |
60 | ||
d0e449a1 SM |
61 | typedef lm_info_aix *lm_info_aix_p; |
62 | DEF_VEC_P(lm_info_aix_p); | |
4d1eb6b4 | 63 | |
4d1eb6b4 JB |
64 | /* This module's per-inferior data. */ |
65 | ||
66 | struct solib_aix_inferior_data | |
67 | { | |
68 | /* The list of shared libraries. NULL if not computed yet. | |
69 | ||
70 | Note that the first element of this list is always the main | |
71 | executable, which is not technically a shared library. But | |
72 | we need that information to perform its relocation, and | |
73 | the same principles applied to shared libraries also apply | |
74 | to the main executable. So it's simpler to keep it as part | |
75 | of this list. */ | |
d0e449a1 | 76 | VEC (lm_info_aix_p) *library_list; |
4d1eb6b4 JB |
77 | }; |
78 | ||
79 | /* Key to our per-inferior data. */ | |
80 | static const struct inferior_data *solib_aix_inferior_data_handle; | |
81 | ||
82 | /* Return this module's data for the given inferior. | |
83 | If none is found, add a zero'ed one now. */ | |
84 | ||
85 | static struct solib_aix_inferior_data * | |
86 | get_solib_aix_inferior_data (struct inferior *inf) | |
87 | { | |
88 | struct solib_aix_inferior_data *data; | |
89 | ||
19ba03f4 SM |
90 | data = ((struct solib_aix_inferior_data *) |
91 | inferior_data (inf, solib_aix_inferior_data_handle)); | |
4d1eb6b4 JB |
92 | if (data == NULL) |
93 | { | |
41bf6aca | 94 | data = XCNEW (struct solib_aix_inferior_data); |
4d1eb6b4 JB |
95 | set_inferior_data (inf, solib_aix_inferior_data_handle, data); |
96 | } | |
97 | ||
98 | return data; | |
99 | } | |
100 | ||
101 | #if !defined(HAVE_LIBEXPAT) | |
102 | ||
103 | /* Dummy implementation if XML support is not compiled in. */ | |
104 | ||
d0e449a1 | 105 | static VEC (lm_info_aix_p) * |
4d1eb6b4 JB |
106 | solib_aix_parse_libraries (const char *library) |
107 | { | |
108 | static int have_warned; | |
109 | ||
110 | if (!have_warned) | |
111 | { | |
112 | have_warned = 1; | |
113 | warning (_("Can not parse XML library list; XML support was disabled " | |
114 | "at compile time")); | |
115 | } | |
116 | ||
117 | return NULL; | |
118 | } | |
119 | ||
814a3ff7 JB |
120 | /* Dummy implementation if XML support is not compiled in. */ |
121 | ||
122 | static void | |
123 | solib_aix_free_library_list (void *p) | |
124 | { | |
125 | } | |
126 | ||
4d1eb6b4 JB |
127 | #else /* HAVE_LIBEXPAT */ |
128 | ||
129 | #include "xml-support.h" | |
130 | ||
131 | /* Handle the start of a <library> element. */ | |
132 | ||
133 | static void | |
134 | library_list_start_library (struct gdb_xml_parser *parser, | |
135 | const struct gdb_xml_element *element, | |
136 | void *user_data, | |
4d0fdd9b | 137 | std::vector<gdb_xml_value> &attributes) |
4d1eb6b4 | 138 | { |
d0e449a1 | 139 | VEC (lm_info_aix_p) **list = (VEC (lm_info_aix_p) **) user_data; |
6c401f72 | 140 | lm_info_aix *item = new lm_info_aix; |
4d1eb6b4 JB |
141 | struct gdb_xml_value *attr; |
142 | ||
143 | attr = xml_find_attribute (attributes, "name"); | |
4d0fdd9b | 144 | item->filename = xstrdup ((const char *) attr->value.get ()); |
4d1eb6b4 JB |
145 | |
146 | attr = xml_find_attribute (attributes, "member"); | |
147 | if (attr != NULL) | |
4d0fdd9b | 148 | item->member_name = xstrdup ((const char *) attr->value.get ()); |
4d1eb6b4 JB |
149 | |
150 | attr = xml_find_attribute (attributes, "text_addr"); | |
4d0fdd9b | 151 | item->text_addr = * (ULONGEST *) attr->value.get (); |
4d1eb6b4 JB |
152 | |
153 | attr = xml_find_attribute (attributes, "text_size"); | |
4d0fdd9b | 154 | item->text_size = * (ULONGEST *) attr->value.get (); |
4d1eb6b4 JB |
155 | |
156 | attr = xml_find_attribute (attributes, "data_addr"); | |
4d0fdd9b | 157 | item->data_addr = * (ULONGEST *) attr->value.get (); |
4d1eb6b4 JB |
158 | |
159 | attr = xml_find_attribute (attributes, "data_size"); | |
4d0fdd9b | 160 | item->data_size = * (ULONGEST *) attr->value.get (); |
4d1eb6b4 | 161 | |
d0e449a1 | 162 | VEC_safe_push (lm_info_aix_p, *list, item); |
4d1eb6b4 JB |
163 | } |
164 | ||
8c56e112 | 165 | /* Handle the start of a <library-list-aix> element. */ |
4d1eb6b4 JB |
166 | |
167 | static void | |
168 | library_list_start_list (struct gdb_xml_parser *parser, | |
169 | const struct gdb_xml_element *element, | |
4d0fdd9b SM |
170 | void *user_data, |
171 | std::vector<gdb_xml_value> &attributes) | |
4d1eb6b4 | 172 | { |
4d0fdd9b SM |
173 | char *version |
174 | = (char *) xml_find_attribute (attributes, "version")->value.get (); | |
4d1eb6b4 JB |
175 | |
176 | if (strcmp (version, "1.0") != 0) | |
177 | gdb_xml_error (parser, | |
178 | _("Library list has unsupported version \"%s\""), | |
179 | version); | |
180 | } | |
181 | ||
182 | /* Discard the constructed library list. */ | |
183 | ||
184 | static void | |
185 | solib_aix_free_library_list (void *p) | |
186 | { | |
d0e449a1 SM |
187 | VEC (lm_info_aix_p) **result = (VEC (lm_info_aix_p) **) p; |
188 | lm_info_aix *info; | |
4d1eb6b4 JB |
189 | int ix; |
190 | ||
191 | if (solib_aix_debug) | |
192 | fprintf_unfiltered (gdb_stdlog, "DEBUG: solib_aix_free_library_list\n"); | |
193 | ||
d0e449a1 | 194 | for (ix = 0; VEC_iterate (lm_info_aix_p, *result, ix, info); ix++) |
6c401f72 SM |
195 | delete info; |
196 | ||
d0e449a1 | 197 | VEC_free (lm_info_aix_p, *result); |
4d1eb6b4 JB |
198 | *result = NULL; |
199 | } | |
200 | ||
201 | /* The allowed elements and attributes for an AIX library list | |
8c56e112 | 202 | described in XML format. The root element is a <library-list-aix>. */ |
4d1eb6b4 JB |
203 | |
204 | static const struct gdb_xml_attribute library_attributes[] = | |
205 | { | |
206 | { "name", GDB_XML_AF_NONE, NULL, NULL }, | |
207 | { "member", GDB_XML_AF_OPTIONAL, NULL, NULL }, | |
208 | { "text_addr", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, | |
209 | { "text_size", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, | |
210 | { "data_addr", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, | |
211 | { "data_size", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, | |
212 | { NULL, GDB_XML_AF_NONE, NULL, NULL } | |
213 | }; | |
214 | ||
215 | static const struct gdb_xml_element library_list_children[] = | |
216 | { | |
217 | { "library", library_attributes, NULL, | |
218 | GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL, | |
219 | library_list_start_library, NULL}, | |
220 | { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } | |
221 | }; | |
222 | ||
223 | static const struct gdb_xml_attribute library_list_attributes[] = | |
224 | { | |
225 | { "version", GDB_XML_AF_NONE, NULL, NULL }, | |
226 | { NULL, GDB_XML_AF_NONE, NULL, NULL } | |
227 | }; | |
228 | ||
229 | static const struct gdb_xml_element library_list_elements[] = | |
230 | { | |
8c56e112 | 231 | { "library-list-aix", library_list_attributes, library_list_children, |
4d1eb6b4 JB |
232 | GDB_XML_EF_NONE, library_list_start_list, NULL }, |
233 | { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } | |
234 | }; | |
235 | ||
236 | /* Parse LIBRARY, a string containing the loader info in XML format, | |
d0e449a1 | 237 | and return an lm_info_aix_p vector. |
4d1eb6b4 JB |
238 | |
239 | Return NULL if the parsing failed. */ | |
240 | ||
d0e449a1 | 241 | static VEC (lm_info_aix_p) * |
4d1eb6b4 JB |
242 | solib_aix_parse_libraries (const char *library) |
243 | { | |
d0e449a1 | 244 | VEC (lm_info_aix_p) *result = NULL; |
4d1eb6b4 JB |
245 | struct cleanup *back_to = make_cleanup (solib_aix_free_library_list, |
246 | &result); | |
247 | ||
248 | if (gdb_xml_parse_quick (_("aix library list"), "library-list-aix.dtd", | |
249 | library_list_elements, library, &result) == 0) | |
250 | { | |
251 | /* Parsed successfully, keep the result. */ | |
252 | discard_cleanups (back_to); | |
253 | return result; | |
254 | } | |
255 | ||
256 | do_cleanups (back_to); | |
257 | return NULL; | |
258 | } | |
259 | ||
260 | #endif /* HAVE_LIBEXPAT */ | |
261 | ||
262 | /* Return the loader info for the given inferior (INF), or NULL if | |
263 | the list could not be computed. | |
264 | ||
265 | Cache the result in per-inferior data, so as to avoid recomputing it | |
266 | each time this function is called. | |
267 | ||
268 | If an error occurs while computing this list, and WARNING_MSG | |
269 | is not NULL, then print a warning including WARNING_MSG and | |
270 | a description of the error. */ | |
271 | ||
d0e449a1 | 272 | static VEC (lm_info_aix_p) * |
4d1eb6b4 JB |
273 | solib_aix_get_library_list (struct inferior *inf, const char *warning_msg) |
274 | { | |
275 | struct solib_aix_inferior_data *data; | |
4d1eb6b4 JB |
276 | |
277 | /* If already computed, return the cached value. */ | |
278 | data = get_solib_aix_inferior_data (inf); | |
279 | if (data->library_list != NULL) | |
280 | return data->library_list; | |
281 | ||
9018be22 | 282 | gdb::optional<gdb::char_vector> library_document |
8b88a78e | 283 | = target_read_stralloc (current_top_target (), TARGET_OBJECT_LIBRARIES_AIX, |
b7b030ad | 284 | NULL); |
9018be22 | 285 | if (!library_document && warning_msg != NULL) |
4d1eb6b4 | 286 | { |
ff99b71b | 287 | warning (_("%s (failed to read TARGET_OBJECT_LIBRARIES_AIX)"), |
4d1eb6b4 JB |
288 | warning_msg); |
289 | return NULL; | |
290 | } | |
4d1eb6b4 JB |
291 | |
292 | if (solib_aix_debug) | |
293 | fprintf_unfiltered (gdb_stdlog, | |
ff99b71b | 294 | "DEBUG: TARGET_OBJECT_LIBRARIES_AIX = \n%s\n", |
9018be22 | 295 | library_document->data ()); |
4d1eb6b4 | 296 | |
9018be22 | 297 | data->library_list = solib_aix_parse_libraries (library_document->data ()); |
4d1eb6b4 JB |
298 | if (data->library_list == NULL && warning_msg != NULL) |
299 | { | |
300 | warning (_("%s (missing XML support?)"), warning_msg); | |
4d1eb6b4 JB |
301 | return NULL; |
302 | } | |
303 | ||
4d1eb6b4 JB |
304 | return data->library_list; |
305 | } | |
306 | ||
307 | /* If the .bss section's VMA is set to an address located before | |
308 | the end of the .data section, causing the two sections to overlap, | |
309 | return the overlap in bytes. Otherwise, return zero. | |
310 | ||
311 | Motivation: | |
312 | ||
313 | The GNU linker sometimes sets the start address of the .bss session | |
314 | before the end of the .data section, making the 2 sections overlap. | |
315 | The loader appears to handle this situation gracefully, by simply | |
316 | loading the bss section right after the end of the .data section. | |
317 | ||
318 | This means that the .data and the .bss sections are sometimes | |
319 | no longer relocated by the same amount. The problem is that | |
320 | the ldinfo data does not contain any information regarding | |
321 | the relocation of the .bss section, assuming that it would be | |
322 | identical to the information provided for the .data section | |
323 | (this is what would normally happen if the program was linked | |
324 | correctly). | |
325 | ||
326 | GDB therefore needs to detect those cases, and make the corresponding | |
327 | adjustment to the .bss section offset computed from the ldinfo data | |
328 | when necessary. This function returns the adjustment amount (or | |
329 | zero when no adjustment is needed). */ | |
330 | ||
331 | static CORE_ADDR | |
332 | solib_aix_bss_data_overlap (bfd *abfd) | |
333 | { | |
334 | struct bfd_section *data_sect, *bss_sect; | |
335 | ||
336 | data_sect = bfd_get_section_by_name (abfd, ".data"); | |
337 | if (data_sect == NULL) | |
338 | return 0; /* No overlap possible. */ | |
339 | ||
340 | bss_sect = bfd_get_section_by_name (abfd, ".bss"); | |
341 | if (bss_sect == NULL) | |
342 | return 0; /* No overlap possible. */ | |
343 | ||
344 | /* Assume the problem only occurs with linkers that place the .bss | |
345 | section after the .data section (the problem has only been | |
346 | observed when using the GNU linker, and the default linker | |
347 | script always places the .data and .bss sections in that order). */ | |
348 | if (bfd_section_vma (abfd, bss_sect) | |
349 | < bfd_section_vma (abfd, data_sect)) | |
350 | return 0; | |
351 | ||
352 | if (bfd_section_vma (abfd, bss_sect) | |
353 | < bfd_section_vma (abfd, data_sect) + bfd_get_section_size (data_sect)) | |
354 | return ((bfd_section_vma (abfd, data_sect) | |
355 | + bfd_get_section_size (data_sect)) | |
356 | - bfd_section_vma (abfd, bss_sect)); | |
357 | ||
358 | return 0; | |
359 | } | |
360 | ||
361 | /* Implement the "relocate_section_addresses" target_so_ops method. */ | |
362 | ||
363 | static void | |
364 | solib_aix_relocate_section_addresses (struct so_list *so, | |
365 | struct target_section *sec) | |
366 | { | |
4d1eb6b4 | 367 | struct bfd_section *bfd_sect = sec->the_bfd_section; |
57e6060e | 368 | bfd *abfd = bfd_sect->owner; |
4d1eb6b4 | 369 | const char *section_name = bfd_section_name (abfd, bfd_sect); |
d0e449a1 | 370 | lm_info_aix *info = (lm_info_aix *) so->lm_info; |
4d1eb6b4 JB |
371 | |
372 | if (strcmp (section_name, ".text") == 0) | |
373 | { | |
374 | sec->addr = info->text_addr; | |
375 | sec->endaddr = sec->addr + info->text_size; | |
376 | ||
377 | /* The text address given to us by the loader contains | |
378 | XCOFF headers, so we need to adjust by this much. */ | |
379 | sec->addr += bfd_sect->filepos; | |
380 | } | |
381 | else if (strcmp (section_name, ".data") == 0) | |
382 | { | |
383 | sec->addr = info->data_addr; | |
384 | sec->endaddr = sec->addr + info->data_size; | |
385 | } | |
386 | else if (strcmp (section_name, ".bss") == 0) | |
387 | { | |
060cfbef JB |
388 | /* The information provided by the loader does not include |
389 | the address of the .bss section, but we know that it gets | |
390 | relocated by the same offset as the .data section. So, | |
391 | compute the relocation offset for the .data section, and | |
392 | apply it to the .bss section as well. If the .data section | |
393 | is not defined (which seems highly unlikely), do our best | |
394 | by assuming no relocation. */ | |
395 | struct bfd_section *data_sect | |
396 | = bfd_get_section_by_name (abfd, ".data"); | |
397 | CORE_ADDR data_offset = 0; | |
398 | ||
399 | if (data_sect != NULL) | |
400 | data_offset = info->data_addr - bfd_section_vma (abfd, data_sect); | |
401 | ||
402 | sec->addr = bfd_section_vma (abfd, bfd_sect) + data_offset; | |
4d1eb6b4 JB |
403 | sec->addr += solib_aix_bss_data_overlap (abfd); |
404 | sec->endaddr = sec->addr + bfd_section_size (abfd, bfd_sect); | |
405 | } | |
406 | else | |
407 | { | |
408 | /* All other sections should not be relocated. */ | |
4d1eb6b4 JB |
409 | sec->addr = bfd_section_vma (abfd, bfd_sect); |
410 | sec->endaddr = sec->addr + bfd_section_size (abfd, bfd_sect); | |
411 | } | |
412 | } | |
413 | ||
414 | /* Implement the "free_so" target_so_ops method. */ | |
415 | ||
416 | static void | |
417 | solib_aix_free_so (struct so_list *so) | |
418 | { | |
6c401f72 SM |
419 | lm_info_aix *li = (lm_info_aix *) so->lm_info; |
420 | ||
4d1eb6b4 JB |
421 | if (solib_aix_debug) |
422 | fprintf_unfiltered (gdb_stdlog, "DEBUG: solib_aix_free_so (%s)\n", | |
423 | so->so_name); | |
6c401f72 SM |
424 | |
425 | delete li; | |
4d1eb6b4 JB |
426 | } |
427 | ||
428 | /* Implement the "clear_solib" target_so_ops method. */ | |
429 | ||
430 | static void | |
431 | solib_aix_clear_solib (void) | |
432 | { | |
433 | /* Nothing needed. */ | |
434 | } | |
435 | ||
436 | /* Compute and return the OBJFILE's section_offset array, using | |
437 | the associated loader info (INFO). | |
438 | ||
439 | The resulting array is computed on the heap and must be | |
440 | deallocated after use. */ | |
441 | ||
f709fabb | 442 | static gdb::unique_xmalloc_ptr<struct section_offsets> |
4d1eb6b4 | 443 | solib_aix_get_section_offsets (struct objfile *objfile, |
d0e449a1 | 444 | lm_info_aix *info) |
4d1eb6b4 | 445 | { |
4d1eb6b4 | 446 | bfd *abfd = objfile->obfd; |
4d1eb6b4 | 447 | |
f709fabb TT |
448 | gdb::unique_xmalloc_ptr<struct section_offsets> offsets |
449 | (XCNEWVEC (struct section_offsets, objfile->num_sections)); | |
4d1eb6b4 JB |
450 | |
451 | /* .text */ | |
452 | ||
453 | if (objfile->sect_index_text != -1) | |
454 | { | |
455 | struct bfd_section *sect | |
456 | = objfile->sections[objfile->sect_index_text].the_bfd_section; | |
457 | ||
458 | offsets->offsets[objfile->sect_index_text] | |
459 | = info->text_addr + sect->filepos - bfd_section_vma (abfd, sect); | |
460 | } | |
461 | ||
462 | /* .data */ | |
463 | ||
464 | if (objfile->sect_index_data != -1) | |
465 | { | |
466 | struct bfd_section *sect | |
467 | = objfile->sections[objfile->sect_index_data].the_bfd_section; | |
468 | ||
469 | offsets->offsets[objfile->sect_index_data] | |
470 | = info->data_addr - bfd_section_vma (abfd, sect); | |
471 | } | |
472 | ||
473 | /* .bss | |
474 | ||
475 | The offset of the .bss section should be identical to the offset | |
476 | of the .data section. If no .data section (which seems hard to | |
477 | believe it is possible), assume it is zero. */ | |
478 | ||
479 | if (objfile->sect_index_bss != -1 | |
480 | && objfile->sect_index_data != -1) | |
481 | { | |
482 | offsets->offsets[objfile->sect_index_bss] | |
483 | = (offsets->offsets[objfile->sect_index_data] | |
484 | + solib_aix_bss_data_overlap (abfd)); | |
485 | } | |
486 | ||
487 | /* All other sections should not need relocation. */ | |
488 | ||
489 | return offsets; | |
490 | } | |
491 | ||
492 | /* Implement the "solib_create_inferior_hook" target_so_ops method. */ | |
493 | ||
494 | static void | |
495 | solib_aix_solib_create_inferior_hook (int from_tty) | |
496 | { | |
497 | const char *warning_msg = "unable to relocate main executable"; | |
d0e449a1 SM |
498 | VEC (lm_info_aix_p) *library_list; |
499 | lm_info_aix *exec_info; | |
4d1eb6b4 JB |
500 | |
501 | /* We need to relocate the main executable... */ | |
502 | ||
503 | library_list = solib_aix_get_library_list (current_inferior (), | |
504 | warning_msg); | |
505 | if (library_list == NULL) | |
506 | return; /* Warning already printed. */ | |
507 | ||
d0e449a1 | 508 | if (VEC_length (lm_info_aix_p, library_list) < 1) |
4d1eb6b4 JB |
509 | { |
510 | warning (_("unable to relocate main executable (no info from loader)")); | |
511 | return; | |
512 | } | |
513 | ||
d0e449a1 | 514 | exec_info = VEC_index (lm_info_aix_p, library_list, 0); |
4d1eb6b4 JB |
515 | |
516 | if (symfile_objfile != NULL) | |
517 | { | |
f709fabb | 518 | gdb::unique_xmalloc_ptr<struct section_offsets> offsets |
4d1eb6b4 | 519 | = solib_aix_get_section_offsets (symfile_objfile, exec_info); |
4d1eb6b4 | 520 | |
f709fabb | 521 | objfile_relocate (symfile_objfile, offsets.get ()); |
4d1eb6b4 JB |
522 | } |
523 | } | |
524 | ||
4d1eb6b4 JB |
525 | /* Implement the "current_sos" target_so_ops method. */ |
526 | ||
527 | static struct so_list * | |
528 | solib_aix_current_sos (void) | |
529 | { | |
530 | struct so_list *start = NULL, *last = NULL; | |
d0e449a1 SM |
531 | VEC (lm_info_aix_p) *library_list; |
532 | lm_info_aix *info; | |
4d1eb6b4 JB |
533 | int ix; |
534 | ||
535 | library_list = solib_aix_get_library_list (current_inferior (), NULL); | |
536 | if (library_list == NULL) | |
537 | return NULL; | |
538 | ||
539 | /* Build a struct so_list for each entry on the list. | |
540 | We skip the first entry, since this is the entry corresponding | |
541 | to the main executable, not a shared library. */ | |
d0e449a1 | 542 | for (ix = 1; VEC_iterate (lm_info_aix_p, library_list, ix, info); ix++) |
4d1eb6b4 | 543 | { |
41bf6aca | 544 | struct so_list *new_solib = XCNEW (struct so_list); |
6c401f72 | 545 | std::string so_name; |
4d1eb6b4 | 546 | |
6c401f72 | 547 | if (info->member_name.empty ()) |
4d1eb6b4 JB |
548 | { |
549 | /* INFO->FILENAME is probably not an archive, but rather | |
550 | a shared object. Unusual, but it should be possible | |
551 | to link a program against a shared object directory, | |
552 | without having to put it in an archive first. */ | |
6c401f72 | 553 | so_name = info->filename; |
4d1eb6b4 JB |
554 | } |
555 | else | |
556 | { | |
557 | /* This is the usual case on AIX, where the shared object | |
558 | is a member of an archive. Create a synthetic so_name | |
559 | that follows the same convention as AIX's ldd tool | |
560 | (Eg: "/lib/libc.a(shr.o)"). */ | |
6c401f72 SM |
561 | so_name = string_printf ("%s(%s)", info->filename.c_str (), |
562 | info->member_name.c_str ()); | |
4d1eb6b4 | 563 | } |
6c401f72 | 564 | strncpy (new_solib->so_original_name, so_name.c_str (), |
4d1eb6b4 JB |
565 | SO_NAME_MAX_PATH_SIZE - 1); |
566 | new_solib->so_name[SO_NAME_MAX_PATH_SIZE - 1] = '\0'; | |
567 | memcpy (new_solib->so_name, new_solib->so_original_name, | |
568 | SO_NAME_MAX_PATH_SIZE); | |
6c401f72 | 569 | new_solib->lm_info = new lm_info_aix (*info); |
4d1eb6b4 JB |
570 | |
571 | /* Add it to the list. */ | |
572 | if (!start) | |
573 | last = start = new_solib; | |
574 | else | |
575 | { | |
576 | last->next = new_solib; | |
577 | last = new_solib; | |
578 | } | |
579 | } | |
580 | ||
581 | return start; | |
582 | } | |
583 | ||
584 | /* Implement the "open_symbol_file_object" target_so_ops method. */ | |
585 | ||
586 | static int | |
bf469271 | 587 | solib_aix_open_symbol_file_object (int from_tty) |
4d1eb6b4 JB |
588 | { |
589 | return 0; | |
590 | } | |
591 | ||
592 | /* Implement the "in_dynsym_resolve_code" target_so_ops method. */ | |
593 | ||
594 | static int | |
595 | solib_aix_in_dynsym_resolve_code (CORE_ADDR pc) | |
596 | { | |
597 | return 0; | |
598 | } | |
599 | ||
600 | /* Implement the "bfd_open" target_so_ops method. */ | |
601 | ||
192b62ce | 602 | static gdb_bfd_ref_ptr |
692d6f97 | 603 | solib_aix_bfd_open (const char *pathname) |
4d1eb6b4 JB |
604 | { |
605 | /* The pathname is actually a synthetic filename with the following | |
606 | form: "/path/to/sharedlib(member.o)" (double-quotes excluded). | |
607 | split this into archive name and member name. | |
608 | ||
609 | FIXME: This is a little hacky. Perhaps we should provide access | |
610 | to the solib's lm_info here? */ | |
611 | const int path_len = strlen (pathname); | |
692d6f97 | 612 | const char *sep; |
4d1eb6b4 | 613 | int filename_len; |
754c39c2 | 614 | int found_file; |
4d1eb6b4 JB |
615 | |
616 | if (pathname[path_len - 1] != ')') | |
617 | return solib_bfd_open (pathname); | |
618 | ||
619 | /* Search for the associated parens. */ | |
620 | sep = strrchr (pathname, '('); | |
621 | if (sep == NULL) | |
622 | { | |
623 | /* Should never happen, but recover as best as we can (trying | |
624 | to open pathname without decoding, possibly leading to | |
625 | a failure), rather than triggering an assert failure). */ | |
626 | warning (_("missing '(' in shared object pathname: %s"), pathname); | |
627 | return solib_bfd_open (pathname); | |
628 | } | |
629 | filename_len = sep - pathname; | |
630 | ||
192b62ce TT |
631 | std::string filename (string_printf ("%.*s", filename_len, pathname)); |
632 | std::string member_name (string_printf ("%.*s", path_len - filename_len - 2, | |
633 | sep + 1)); | |
4d1eb6b4 | 634 | |
754c39c2 UW |
635 | /* Calling solib_find makes certain that sysroot path is set properly |
636 | if program has a dependency on .a archive and sysroot is set via | |
637 | set sysroot command. */ | |
797bc1cb TT |
638 | gdb::unique_xmalloc_ptr<char> found_pathname |
639 | = solib_find (filename.c_str (), &found_file); | |
754c39c2 UW |
640 | if (found_pathname == NULL) |
641 | perror_with_name (pathname); | |
797bc1cb TT |
642 | gdb_bfd_ref_ptr archive_bfd (solib_bfd_fopen (found_pathname.get (), |
643 | found_file)); | |
4d1eb6b4 JB |
644 | if (archive_bfd == NULL) |
645 | { | |
646 | warning (_("Could not open `%s' as an executable file: %s"), | |
192b62ce | 647 | filename.c_str (), bfd_errmsg (bfd_get_error ())); |
4d1eb6b4 JB |
648 | return NULL; |
649 | } | |
650 | ||
192b62ce TT |
651 | if (bfd_check_format (archive_bfd.get (), bfd_object)) |
652 | return archive_bfd; | |
4d1eb6b4 | 653 | |
192b62ce | 654 | if (! bfd_check_format (archive_bfd.get (), bfd_archive)) |
4d1eb6b4 JB |
655 | { |
656 | warning (_("\"%s\": not in executable format: %s."), | |
192b62ce | 657 | filename.c_str (), bfd_errmsg (bfd_get_error ())); |
4d1eb6b4 JB |
658 | return NULL; |
659 | } | |
660 | ||
192b62ce TT |
661 | gdb_bfd_ref_ptr object_bfd |
662 | (gdb_bfd_openr_next_archived_file (archive_bfd.get (), NULL)); | |
4d1eb6b4 JB |
663 | while (object_bfd != NULL) |
664 | { | |
192b62ce | 665 | if (member_name == object_bfd->filename) |
4d1eb6b4 JB |
666 | break; |
667 | ||
192b62ce TT |
668 | object_bfd = gdb_bfd_openr_next_archived_file (archive_bfd.get (), |
669 | object_bfd.get ()); | |
4d1eb6b4 JB |
670 | } |
671 | ||
672 | if (object_bfd == NULL) | |
673 | { | |
192b62ce TT |
674 | warning (_("\"%s\": member \"%s\" missing."), filename.c_str (), |
675 | member_name.c_str ()); | |
4d1eb6b4 JB |
676 | return NULL; |
677 | } | |
678 | ||
192b62ce | 679 | if (! bfd_check_format (object_bfd.get (), bfd_object)) |
4d1eb6b4 JB |
680 | { |
681 | warning (_("%s(%s): not in object format: %s."), | |
192b62ce TT |
682 | filename.c_str (), member_name.c_str (), |
683 | bfd_errmsg (bfd_get_error ())); | |
4d1eb6b4 JB |
684 | return NULL; |
685 | } | |
686 | ||
754c39c2 UW |
687 | /* Override the returned bfd's name with the name returned from solib_find |
688 | along with appended parenthesized member name in order to allow commands | |
689 | listing all shared libraries to display. Otherwise, we would only be | |
690 | displaying the name of the archive member object. */ | |
192b62ce | 691 | xfree (bfd_get_filename (object_bfd.get ())); |
754c39c2 | 692 | object_bfd->filename = xstrprintf ("%s%s", |
192b62ce TT |
693 | bfd_get_filename (archive_bfd.get ()), |
694 | sep); | |
b030cf11 | 695 | |
4d1eb6b4 JB |
696 | return object_bfd; |
697 | } | |
698 | ||
699 | /* Return the obj_section corresponding to OBJFILE's data section, | |
700 | or NULL if not found. */ | |
701 | /* FIXME: Define in a more general location? */ | |
702 | ||
703 | static struct obj_section * | |
704 | data_obj_section_from_objfile (struct objfile *objfile) | |
705 | { | |
706 | struct obj_section *osect; | |
707 | ||
708 | ALL_OBJFILE_OSECTIONS (objfile, osect) | |
709 | if (strcmp (bfd_section_name (objfile->obfd, osect->the_bfd_section), | |
710 | ".data") == 0) | |
711 | return osect; | |
712 | ||
713 | return NULL; | |
714 | } | |
715 | ||
716 | /* Return the TOC value corresponding to the given PC address, | |
717 | or raise an error if the value could not be determined. */ | |
718 | ||
719 | CORE_ADDR | |
720 | solib_aix_get_toc_value (CORE_ADDR pc) | |
721 | { | |
722 | struct obj_section *pc_osect = find_pc_section (pc); | |
723 | struct obj_section *data_osect; | |
724 | CORE_ADDR result; | |
725 | ||
726 | if (pc_osect == NULL) | |
727 | error (_("unable to find TOC entry for pc %s " | |
728 | "(no section contains this PC)"), | |
729 | core_addr_to_string (pc)); | |
730 | ||
731 | data_osect = data_obj_section_from_objfile (pc_osect->objfile); | |
732 | if (data_osect == NULL) | |
733 | error (_("unable to find TOC entry for pc %s " | |
734 | "(%s has no data section)"), | |
4262abfb | 735 | core_addr_to_string (pc), objfile_name (pc_osect->objfile)); |
4d1eb6b4 JB |
736 | |
737 | result = (obj_section_addr (data_osect) | |
738 | + xcoff_get_toc_offset (pc_osect->objfile)); | |
739 | if (solib_aix_debug) | |
740 | fprintf_unfiltered (gdb_stdlog, | |
741 | "DEBUG: solib_aix_get_toc_value (pc=%s) -> %s\n", | |
742 | core_addr_to_string (pc), | |
743 | core_addr_to_string (result)); | |
744 | ||
745 | return result; | |
746 | } | |
747 | ||
748 | /* This module's normal_stop observer. */ | |
749 | ||
750 | static void | |
751 | solib_aix_normal_stop_observer (struct bpstats *unused_1, int unused_2) | |
752 | { | |
753 | struct solib_aix_inferior_data *data | |
754 | = get_solib_aix_inferior_data (current_inferior ()); | |
755 | ||
756 | /* The inferior execution has been resumed, and it just stopped | |
757 | again. This means that the list of shared libraries may have | |
758 | evolved. Reset our cached value. */ | |
759 | solib_aix_free_library_list (&data->library_list); | |
760 | } | |
761 | ||
762 | /* Implements the "show debug aix-solib" command. */ | |
763 | ||
764 | static void | |
765 | show_solib_aix_debug (struct ui_file *file, int from_tty, | |
766 | struct cmd_list_element *c, const char *value) | |
767 | { | |
768 | fprintf_filtered (file, _("solib-aix debugging is %s.\n"), value); | |
769 | } | |
770 | ||
771 | /* The target_so_ops for AIX targets. */ | |
772 | struct target_so_ops solib_aix_so_ops; | |
773 | ||
4d1eb6b4 JB |
774 | void |
775 | _initialize_solib_aix (void) | |
776 | { | |
777 | solib_aix_so_ops.relocate_section_addresses | |
778 | = solib_aix_relocate_section_addresses; | |
779 | solib_aix_so_ops.free_so = solib_aix_free_so; | |
780 | solib_aix_so_ops.clear_solib = solib_aix_clear_solib; | |
781 | solib_aix_so_ops.solib_create_inferior_hook | |
782 | = solib_aix_solib_create_inferior_hook; | |
4d1eb6b4 JB |
783 | solib_aix_so_ops.current_sos = solib_aix_current_sos; |
784 | solib_aix_so_ops.open_symbol_file_object | |
785 | = solib_aix_open_symbol_file_object; | |
786 | solib_aix_so_ops.in_dynsym_resolve_code | |
787 | = solib_aix_in_dynsym_resolve_code; | |
788 | solib_aix_so_ops.bfd_open = solib_aix_bfd_open; | |
789 | ||
790 | solib_aix_inferior_data_handle = register_inferior_data (); | |
791 | ||
76727919 | 792 | gdb::observers::normal_stop.attach (solib_aix_normal_stop_observer); |
4d1eb6b4 JB |
793 | |
794 | /* Debug this file's internals. */ | |
795 | add_setshow_boolean_cmd ("aix-solib", class_maintenance, | |
796 | &solib_aix_debug, _("\ | |
797 | Control the debugging traces for the solib-aix module."), _("\ | |
798 | Show whether solib-aix debugging traces are enabled."), _("\ | |
799 | When on, solib-aix debugging traces are enabled."), | |
800 | NULL, | |
801 | show_solib_aix_debug, | |
802 | &setdebuglist, &showdebuglist); | |
803 | } |