Commit | Line | Data |
---|---|---|
cfa9d6d9 DJ |
1 | /* Definitions for targets which report shared library events. |
2 | ||
3 | Copyright (C) 2007 | |
4 | Free Software Foundation, Inc. | |
5 | ||
6 | This file is part of GDB. | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
a9762ec7 | 10 | the Free Software Foundation; either version 3 of the License, or |
cfa9d6d9 DJ |
11 | (at your option) any later version. |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
a9762ec7 | 19 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
cfa9d6d9 DJ |
20 | |
21 | #include "defs.h" | |
22 | #include "objfiles.h" | |
23 | #include "solist.h" | |
24 | #include "symtab.h" | |
25 | #include "symfile.h" | |
26 | #include "target.h" | |
27 | #include "vec.h" | |
28 | ||
29 | #include "gdb_string.h" | |
30 | ||
31 | DEF_VEC_O(CORE_ADDR); | |
32 | ||
33 | /* Private data for each loaded library. */ | |
34 | struct lm_info | |
35 | { | |
36 | /* The library's name. The name is normally kept in the struct | |
37 | so_list; it is only here during XML parsing. */ | |
38 | char *name; | |
39 | ||
40 | /* The base addresses for each independently relocatable segment of | |
41 | this shared library. */ | |
42 | VEC(CORE_ADDR) *segment_bases; | |
43 | ||
44 | /* The cached offsets for each section of this shared library, | |
45 | determined from SEGMENT_BASES. */ | |
46 | struct section_offsets *offsets; | |
47 | }; | |
48 | ||
49 | typedef struct lm_info *lm_info_p; | |
50 | DEF_VEC_P(lm_info_p); | |
51 | ||
52 | #if !defined(HAVE_LIBEXPAT) | |
53 | ||
54 | static VEC(lm_info_p) | |
55 | solib_target_parse_libraries (const char *library) | |
56 | { | |
57 | static int have_warned; | |
58 | ||
59 | if (!have_warned) | |
60 | { | |
61 | have_warned = 1; | |
62 | warning (_("Can not parse XML library list; XML support was disabled " | |
63 | "at compile time")); | |
64 | } | |
65 | ||
66 | return NULL; | |
67 | } | |
68 | ||
69 | #else /* HAVE_LIBEXPAT */ | |
70 | ||
71 | #include "xml-support.h" | |
72 | ||
73 | /* Handle the start of a <segment> element. */ | |
74 | ||
75 | static void | |
76 | library_list_start_segment (struct gdb_xml_parser *parser, | |
77 | const struct gdb_xml_element *element, | |
78 | void *user_data, VEC(gdb_xml_value_s) *attributes) | |
79 | { | |
80 | VEC(lm_info_p) **list = user_data; | |
81 | struct lm_info *last = VEC_last (lm_info_p, *list); | |
82 | ULONGEST *address_p = VEC_index (gdb_xml_value_s, attributes, 0)->value; | |
358eb95e | 83 | CORE_ADDR address = (CORE_ADDR) *address_p; |
cfa9d6d9 | 84 | |
358eb95e | 85 | VEC_safe_push (CORE_ADDR, last->segment_bases, &address); |
cfa9d6d9 DJ |
86 | } |
87 | ||
88 | /* Handle the start of a <library> element. */ | |
89 | ||
90 | static void | |
91 | library_list_start_library (struct gdb_xml_parser *parser, | |
92 | const struct gdb_xml_element *element, | |
93 | void *user_data, VEC(gdb_xml_value_s) *attributes) | |
94 | { | |
95 | VEC(lm_info_p) **list = user_data; | |
96 | struct lm_info *item = XZALLOC (struct lm_info); | |
97 | const char *name = VEC_index (gdb_xml_value_s, attributes, 0)->value; | |
98 | ||
99 | item->name = xstrdup (name); | |
100 | VEC_safe_push (lm_info_p, *list, item); | |
101 | } | |
102 | ||
103 | /* Handle the start of a <library-list> element. */ | |
104 | ||
105 | static void | |
106 | library_list_start_list (struct gdb_xml_parser *parser, | |
107 | const struct gdb_xml_element *element, | |
108 | void *user_data, VEC(gdb_xml_value_s) *attributes) | |
109 | { | |
110 | char *version = VEC_index (gdb_xml_value_s, attributes, 0)->value; | |
111 | ||
112 | if (strcmp (version, "1.0") != 0) | |
113 | gdb_xml_error (parser, | |
114 | _("Library list has unsupported version \"%s\""), | |
115 | version); | |
116 | } | |
117 | ||
118 | /* Discard the constructed library list. */ | |
119 | ||
120 | static void | |
121 | solib_target_free_library_list (void *p) | |
122 | { | |
123 | VEC(lm_info_p) **result = p; | |
124 | struct lm_info *info; | |
125 | int ix; | |
126 | ||
127 | for (ix = 0; VEC_iterate (lm_info_p, *result, ix, info); ix++) | |
128 | { | |
129 | xfree (info->name); | |
130 | VEC_free (CORE_ADDR, info->segment_bases); | |
131 | xfree (info); | |
132 | } | |
133 | VEC_free (lm_info_p, *result); | |
134 | *result = NULL; | |
135 | } | |
136 | ||
137 | /* The allowed elements and attributes for an XML library list. | |
138 | The root element is a <library-list>. */ | |
139 | ||
140 | const struct gdb_xml_attribute segment_attributes[] = { | |
141 | { "address", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, | |
142 | { NULL, GDB_XML_AF_NONE, NULL, NULL } | |
143 | }; | |
144 | ||
145 | const struct gdb_xml_element library_children[] = { | |
146 | { "segment", segment_attributes, NULL, GDB_XML_EF_REPEATABLE, | |
147 | library_list_start_segment, NULL }, | |
148 | { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } | |
149 | }; | |
150 | ||
151 | const struct gdb_xml_attribute library_attributes[] = { | |
152 | { "name", GDB_XML_AF_NONE, NULL, NULL }, | |
153 | { NULL, GDB_XML_AF_NONE, NULL, NULL } | |
154 | }; | |
155 | ||
156 | const struct gdb_xml_element library_list_children[] = { | |
157 | { "library", library_attributes, library_children, | |
158 | GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL, | |
159 | library_list_start_library, NULL }, | |
160 | { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } | |
161 | }; | |
162 | ||
163 | const struct gdb_xml_attribute library_list_attributes[] = { | |
164 | { "version", GDB_XML_AF_NONE, NULL, NULL }, | |
165 | { NULL, GDB_XML_AF_NONE, NULL, NULL } | |
166 | }; | |
167 | ||
168 | const struct gdb_xml_element library_list_elements[] = { | |
169 | { "library-list", library_list_attributes, library_list_children, | |
170 | GDB_XML_EF_NONE, library_list_start_list, NULL }, | |
171 | { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } | |
172 | }; | |
173 | ||
174 | static VEC(lm_info_p) * | |
175 | solib_target_parse_libraries (const char *library) | |
176 | { | |
177 | struct gdb_xml_parser *parser; | |
178 | VEC(lm_info_p) *result = NULL; | |
179 | struct cleanup *before_deleting_result, *back_to; | |
180 | ||
181 | back_to = make_cleanup (null_cleanup, NULL); | |
182 | parser = gdb_xml_create_parser_and_cleanup (_("target library list"), | |
183 | library_list_elements, &result); | |
184 | gdb_xml_use_dtd (parser, "library-list.dtd"); | |
185 | ||
186 | before_deleting_result = make_cleanup (solib_target_free_library_list, | |
187 | &result); | |
188 | ||
189 | if (gdb_xml_parse (parser, library) == 0) | |
190 | /* Parsed successfully, don't need to delete the result. */ | |
191 | discard_cleanups (before_deleting_result); | |
192 | ||
193 | do_cleanups (back_to); | |
194 | return result; | |
195 | } | |
196 | #endif | |
197 | ||
198 | static struct so_list * | |
199 | solib_target_current_sos (void) | |
200 | { | |
201 | struct so_list *new_solib, *start = NULL, *last = NULL; | |
202 | const char *library_document; | |
203 | VEC(lm_info_p) *library_list; | |
204 | struct lm_info *info; | |
205 | int ix; | |
206 | ||
207 | /* Fetch the list of shared libraries. */ | |
208 | library_document = target_read_stralloc (¤t_target, | |
209 | TARGET_OBJECT_LIBRARIES, | |
210 | NULL); | |
211 | if (library_document == NULL) | |
212 | return NULL; | |
213 | ||
214 | /* Parse the list. */ | |
215 | library_list = solib_target_parse_libraries (library_document); | |
216 | if (library_list == NULL) | |
217 | return NULL; | |
218 | ||
219 | /* Build a struct so_list for each entry on the list. */ | |
220 | for (ix = 0; VEC_iterate (lm_info_p, library_list, ix, info); ix++) | |
221 | { | |
222 | new_solib = XZALLOC (struct so_list); | |
223 | strncpy (new_solib->so_name, info->name, SO_NAME_MAX_PATH_SIZE - 1); | |
224 | new_solib->so_name[SO_NAME_MAX_PATH_SIZE - 1] = '\0'; | |
225 | strncpy (new_solib->so_original_name, info->name, | |
226 | SO_NAME_MAX_PATH_SIZE - 1); | |
227 | new_solib->so_original_name[SO_NAME_MAX_PATH_SIZE - 1] = '\0'; | |
228 | new_solib->lm_info = info; | |
229 | ||
230 | /* We no longer need this copy of the name. */ | |
231 | xfree (info->name); | |
232 | info->name = NULL; | |
233 | ||
234 | /* Add it to the list. */ | |
235 | if (!start) | |
236 | last = start = new_solib; | |
237 | else | |
238 | { | |
239 | last->next = new_solib; | |
240 | last = new_solib; | |
241 | } | |
242 | } | |
243 | ||
244 | /* Free the library list, but not its members. */ | |
245 | VEC_free (lm_info_p, library_list); | |
246 | ||
247 | return start; | |
248 | } | |
249 | ||
250 | static void | |
251 | solib_target_special_symbol_handling (void) | |
252 | { | |
253 | /* Nothing needed. */ | |
254 | } | |
255 | ||
256 | static void | |
257 | solib_target_solib_create_inferior_hook (void) | |
258 | { | |
259 | /* Nothing needed. */ | |
260 | } | |
261 | ||
262 | static void | |
263 | solib_target_clear_solib (void) | |
264 | { | |
265 | /* Nothing needed. */ | |
266 | } | |
267 | ||
268 | static void | |
269 | solib_target_free_so (struct so_list *so) | |
270 | { | |
271 | gdb_assert (so->lm_info->name == NULL); | |
272 | xfree (so->lm_info->offsets); | |
273 | VEC_free (CORE_ADDR, so->lm_info->segment_bases); | |
274 | xfree (so->lm_info); | |
275 | } | |
276 | ||
277 | static void | |
278 | solib_target_relocate_section_addresses (struct so_list *so, | |
279 | struct section_table *sec) | |
280 | { | |
281 | int flags = bfd_get_section_flags (sec->bfd, sec->the_bfd_section); | |
282 | CORE_ADDR offset; | |
283 | ||
284 | /* Build the offset table only once per object file. We can not do | |
285 | it any earlier, since we need to open the file first. */ | |
286 | if (so->lm_info->offsets == NULL) | |
287 | { | |
288 | struct symfile_segment_data *data; | |
289 | int num_sections = bfd_count_sections (so->abfd); | |
290 | ||
291 | so->lm_info->offsets = xzalloc (SIZEOF_N_SECTION_OFFSETS (num_sections)); | |
292 | ||
293 | data = get_symfile_segment_data (so->abfd); | |
294 | if (data == NULL) | |
295 | warning (_("Could not relocate shared library \"%s\": no segments"), | |
296 | so->so_name); | |
297 | else | |
298 | { | |
299 | ULONGEST orig_delta; | |
300 | int i; | |
301 | int num_bases = VEC_length (CORE_ADDR, so->lm_info->segment_bases); | |
302 | CORE_ADDR *segment_bases = VEC_address (CORE_ADDR, | |
303 | so->lm_info->segment_bases); | |
304 | ||
305 | if (!symfile_map_offsets_to_segments (so->abfd, data, | |
306 | so->lm_info->offsets, | |
307 | num_bases, segment_bases)) | |
308 | warning (_("Could not relocate shared library \"%s\": bad offsets"), | |
309 | so->so_name); | |
310 | ||
311 | /* Find the range of addresses to report for this library in | |
312 | "info sharedlibrary". Report any consecutive segments | |
313 | which were relocated as a single unit. */ | |
314 | gdb_assert (num_bases > 0); | |
315 | orig_delta = segment_bases[0] - data->segment_bases[0]; | |
316 | ||
317 | for (i = 1; i < data->num_segments; i++) | |
318 | { | |
319 | /* If we have run out of offsets, assume all remaining segments | |
320 | have the same offset. */ | |
321 | if (i >= num_bases) | |
322 | continue; | |
323 | ||
324 | /* If this segment does not have the same offset, do not include | |
325 | it in the library's range. */ | |
326 | if (segment_bases[i] - data->segment_bases[i] != orig_delta) | |
327 | break; | |
328 | } | |
329 | ||
330 | so->addr_low = segment_bases[0]; | |
331 | so->addr_high = (data->segment_bases[i - 1] | |
332 | + data->segment_sizes[i - 1] | |
5d9643ba PA |
333 | + orig_delta); |
334 | gdb_assert (so->addr_low <= so->addr_high); | |
cfa9d6d9 DJ |
335 | |
336 | free_symfile_segment_data (data); | |
337 | } | |
338 | } | |
339 | ||
340 | offset = so->lm_info->offsets->offsets[sec->the_bfd_section->index]; | |
341 | sec->addr += offset; | |
342 | sec->endaddr += offset; | |
343 | } | |
344 | ||
345 | static int | |
346 | solib_target_open_symbol_file_object (void *from_ttyp) | |
347 | { | |
348 | /* We can't locate the main symbol file based on the target's | |
349 | knowledge; the user has to specify it. */ | |
350 | return 0; | |
351 | } | |
352 | ||
353 | static int | |
354 | solib_target_in_dynsym_resolve_code (CORE_ADDR pc) | |
355 | { | |
356 | /* We don't have a range of addresses for the dynamic linker; there | |
357 | may not be one in the program's address space. So only report | |
358 | PLT entries (which may be import stubs). */ | |
359 | return in_plt_section (pc, NULL); | |
360 | } | |
361 | ||
362 | static struct target_so_ops solib_target_so_ops; | |
363 | ||
364 | extern initialize_file_ftype _initialize_solib_target; /* -Wmissing-prototypes */ | |
365 | ||
366 | void | |
367 | _initialize_solib_target (void) | |
368 | { | |
369 | solib_target_so_ops.relocate_section_addresses | |
370 | = solib_target_relocate_section_addresses; | |
371 | solib_target_so_ops.free_so = solib_target_free_so; | |
372 | solib_target_so_ops.clear_solib = solib_target_clear_solib; | |
373 | solib_target_so_ops.solib_create_inferior_hook | |
374 | = solib_target_solib_create_inferior_hook; | |
375 | solib_target_so_ops.special_symbol_handling | |
376 | = solib_target_special_symbol_handling; | |
377 | solib_target_so_ops.current_sos = solib_target_current_sos; | |
378 | solib_target_so_ops.open_symbol_file_object | |
379 | = solib_target_open_symbol_file_object; | |
380 | solib_target_so_ops.in_dynsym_resolve_code | |
381 | = solib_target_in_dynsym_resolve_code; | |
382 | ||
383 | current_target_so_ops = &solib_target_so_ops; | |
384 | } |