Commit | Line | Data |
---|---|---|
85e747d2 UW |
1 | /* Cell SPU GNU/Linux support -- shared library handling. |
2 | Copyright (C) 2009 Free Software Foundation, Inc. | |
3 | ||
4 | Contributed by Ulrich Weigand <uweigand@de.ibm.com>. | |
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 | |
10 | the Free Software Foundation; either version 2 of the License, or | |
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 | |
19 | along with this program; if not, write to the Free Software | |
20 | Foundation, Inc., 59 Temple Place - Suite 330, | |
21 | Boston, MA 02111-1307, USA. */ | |
22 | ||
23 | #include "defs.h" | |
24 | #include "gdbcore.h" | |
25 | #include "gdb_string.h" | |
26 | #include "gdb_assert.h" | |
27 | #include "gdb_stat.h" | |
28 | #include "arch-utils.h" | |
29 | #include "bfd.h" | |
30 | #include "symtab.h" | |
31 | #include "solib.h" | |
32 | #include "solib-svr4.h" | |
33 | #include "solist.h" | |
34 | #include "inferior.h" | |
35 | #include "objfiles.h" | |
36 | #include "observer.h" | |
37 | #include "breakpoint.h" | |
38 | #include "gdbthread.h" | |
39 | ||
40 | #include "spu-tdep.h" | |
41 | ||
42 | /* Highest SPE id (file handle) the inferior may have. */ | |
43 | #define MAX_SPE_FD 1024 | |
44 | ||
45 | /* Stand-alone SPE executable? */ | |
46 | #define spu_standalone_p() \ | |
47 | (symfile_objfile && symfile_objfile->obfd \ | |
48 | && bfd_get_arch (symfile_objfile->obfd) == bfd_arch_spu) | |
49 | ||
50 | ||
51 | /* Relocate main SPE executable. */ | |
52 | static void | |
53 | spu_relocate_main_executable (int spufs_fd) | |
54 | { | |
55 | struct objfile *objfile; | |
56 | struct cleanup *old_chain; | |
57 | struct section_offsets *new_offsets; | |
58 | int i; | |
59 | ||
60 | for (objfile = symfile_objfile; | |
61 | objfile; | |
62 | objfile = objfile->separate_debug_objfile) | |
63 | { | |
64 | new_offsets = xcalloc (objfile->num_sections, | |
65 | sizeof (struct section_offsets)); | |
66 | old_chain = make_cleanup (xfree, new_offsets); | |
67 | ||
68 | for (i = 0; i < objfile->num_sections; i++) | |
69 | new_offsets->offsets[i] = SPUADDR (spufs_fd, 0); | |
70 | ||
71 | objfile_relocate (objfile, new_offsets); | |
72 | do_cleanups (old_chain); | |
73 | } | |
74 | } | |
75 | ||
76 | /* When running a stand-alone SPE executable, we may need to skip one more | |
77 | exec event on startup, to get past the binfmt_misc loader. */ | |
78 | static void | |
79 | spu_skip_standalone_loader (void) | |
80 | { | |
81 | if (target_has_execution && !current_inferior ()->attach_flag) | |
82 | { | |
83 | struct target_waitstatus ws; | |
84 | ||
85 | /* Only some kernels report an extra SIGTRAP with the binfmt_misc | |
86 | loader; others do not. In addition, if we have attached to an | |
87 | already running inferior instead of starting a new one, we will | |
88 | not see the extra SIGTRAP -- and we cannot readily distinguish | |
89 | the two cases, in particular with the extended-remote target. | |
90 | ||
91 | Thus we issue a single-step here. If no extra SIGTRAP was pending, | |
92 | this will step past the first instruction of the stand-alone SPE | |
93 | executable loader, but we don't care about that. */ | |
94 | ||
95 | inferior_thread ()->in_infcall = 1; /* Suppress MI messages. */ | |
96 | ||
97 | target_resume (inferior_ptid, 1, TARGET_SIGNAL_0); | |
98 | target_wait (minus_one_ptid, &ws, 0); | |
99 | set_executing (minus_one_ptid, 0); | |
100 | ||
101 | inferior_thread ()->in_infcall = 0; | |
102 | } | |
103 | } | |
104 | ||
105 | /* Build a list of `struct so_list' objects describing the shared | |
106 | objects currently loaded in the inferior. */ | |
107 | static struct so_list * | |
108 | spu_current_sos (void) | |
109 | { | |
110 | enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch); | |
111 | struct so_list *head; | |
112 | struct so_list **link_ptr; | |
113 | ||
114 | char buf[MAX_SPE_FD * 4]; | |
115 | int i, size; | |
116 | ||
117 | /* First, retrieve the SVR4 shared library list. */ | |
118 | head = svr4_so_ops.current_sos (); | |
119 | ||
120 | /* Append our libraries to the end of the list. */ | |
121 | for (link_ptr = &head; *link_ptr; link_ptr = &(*link_ptr)->next) | |
122 | ; | |
123 | ||
124 | /* Determine list of SPU ids. */ | |
125 | size = target_read (¤t_target, TARGET_OBJECT_SPU, NULL, | |
126 | buf, 0, sizeof buf); | |
127 | ||
128 | /* Do not add stand-alone SPE executable context as shared library, | |
129 | but relocate main SPE executable objfile. */ | |
130 | if (spu_standalone_p ()) | |
131 | { | |
132 | if (size == 4) | |
133 | { | |
134 | int fd = extract_unsigned_integer (buf, 4, byte_order); | |
135 | spu_relocate_main_executable (fd); | |
136 | ||
137 | /* Re-enable breakpoints after main SPU context was established; | |
138 | see also comments in spu_solib_create_inferior_hook. */ | |
139 | enable_breakpoints_after_startup (); | |
140 | } | |
141 | ||
142 | return head; | |
143 | } | |
144 | ||
145 | /* Create an so_list entry for each SPU id. */ | |
146 | for (i = 0; i < size; i += 4) | |
147 | { | |
148 | int fd = extract_unsigned_integer (buf + i, 4, byte_order); | |
149 | struct so_list *new; | |
150 | ||
151 | unsigned long long addr; | |
152 | char annex[32], id[100]; | |
153 | int len; | |
154 | ||
155 | /* Read object ID. There's a race window where the inferior may have | |
156 | already created the SPE context, but not installed the object-id | |
157 | yet. Skip such entries; we'll be back for them later. */ | |
158 | xsnprintf (annex, sizeof annex, "%d/object-id", fd); | |
159 | len = target_read (¤t_target, TARGET_OBJECT_SPU, annex, | |
160 | id, 0, sizeof id); | |
161 | if (len <= 0 || len >= sizeof id) | |
162 | continue; | |
163 | id[len] = 0; | |
164 | if (sscanf (id, "0x%llx", &addr) != 1 || !addr) | |
165 | continue; | |
166 | ||
167 | /* Allocate so_list structure. */ | |
168 | new = XZALLOC (struct so_list); | |
169 | ||
170 | /* Encode FD and object ID in path name. Choose the name so as not | |
171 | to conflict with any (normal) SVR4 library path name. */ | |
172 | xsnprintf (new->so_name, sizeof new->so_name, "@0x%llx <%d>", addr, fd); | |
173 | strcpy (new->so_original_name, new->so_name); | |
174 | ||
175 | *link_ptr = new; | |
176 | link_ptr = &new->next; | |
177 | } | |
178 | ||
179 | return head; | |
180 | } | |
181 | ||
182 | /* Free so_list information. */ | |
183 | static void | |
184 | spu_free_so (struct so_list *so) | |
185 | { | |
186 | if (so->so_original_name[0] != '@') | |
187 | svr4_so_ops.free_so (so); | |
188 | } | |
189 | ||
190 | /* Relocate section addresses. */ | |
191 | static void | |
192 | spu_relocate_section_addresses (struct so_list *so, | |
193 | struct target_section *sec) | |
194 | { | |
195 | if (so->so_original_name[0] != '@') | |
196 | svr4_so_ops.relocate_section_addresses (so, sec); | |
197 | else | |
198 | { | |
199 | unsigned long long addr; | |
200 | int fd; | |
201 | ||
202 | /* Set addr_low/high to just LS offset for display. */ | |
203 | if (so->addr_low == 0 && so->addr_high == 0 | |
204 | && strcmp (sec->the_bfd_section->name, ".text") == 0) | |
205 | { | |
206 | so->addr_low = sec->addr; | |
207 | so->addr_high = sec->endaddr; | |
208 | } | |
209 | ||
210 | /* Decode object ID. */ | |
211 | if (sscanf (so->so_original_name, "@0x%llx <%d>", &addr, &fd) != 2) | |
212 | internal_error (__FILE__, __LINE__, "bad object ID"); | |
213 | ||
214 | sec->addr = SPUADDR (fd, sec->addr); | |
215 | sec->endaddr = SPUADDR (fd, sec->endaddr); | |
216 | } | |
217 | } | |
218 | ||
219 | ||
220 | /* Inferior memory should contain an SPE executable image at location ADDR. | |
221 | Allocate a BFD representing that executable. Return NULL on error. */ | |
222 | ||
223 | static void * | |
224 | spu_bfd_iovec_open (bfd *nbfd, void *open_closure) | |
225 | { | |
226 | return open_closure; | |
227 | } | |
228 | ||
229 | static int | |
230 | spu_bfd_iovec_close (bfd *nbfd, void *stream) | |
231 | { | |
232 | xfree (stream); | |
233 | return 1; | |
234 | } | |
235 | ||
236 | static file_ptr | |
237 | spu_bfd_iovec_pread (bfd *abfd, void *stream, void *buf, | |
238 | file_ptr nbytes, file_ptr offset) | |
239 | { | |
240 | CORE_ADDR addr = *(CORE_ADDR *)stream; | |
241 | int ret; | |
242 | ||
243 | ret = target_read_memory (addr + offset, buf, nbytes); | |
244 | if (ret != 0) | |
245 | { | |
246 | bfd_set_error (bfd_error_invalid_operation); | |
247 | return -1; | |
248 | } | |
249 | ||
250 | return nbytes; | |
251 | } | |
252 | ||
253 | static int | |
254 | spu_bfd_iovec_stat (bfd *abfd, void *stream, struct stat *sb) | |
255 | { | |
256 | /* We don't have an easy way of finding the size of embedded spu | |
257 | images. We could parse the in-memory ELF header and section | |
258 | table to find the extent of the last section but that seems | |
259 | pointless when the size is needed only for checks of other | |
260 | parsed values in dbxread.c. */ | |
261 | sb->st_size = INT_MAX; | |
262 | return 0; | |
263 | } | |
264 | ||
265 | static bfd * | |
266 | spu_bfd_fopen (char *name, CORE_ADDR addr) | |
267 | { | |
268 | bfd *nbfd; | |
269 | ||
270 | CORE_ADDR *open_closure = xmalloc (sizeof (CORE_ADDR)); | |
271 | *open_closure = addr; | |
272 | ||
273 | nbfd = bfd_openr_iovec (xstrdup (name), "elf32-spu", | |
274 | spu_bfd_iovec_open, open_closure, | |
275 | spu_bfd_iovec_pread, spu_bfd_iovec_close, | |
276 | spu_bfd_iovec_stat); | |
277 | if (!nbfd) | |
278 | return NULL; | |
279 | ||
280 | if (!bfd_check_format (nbfd, bfd_object)) | |
281 | { | |
282 | bfd_close (nbfd); | |
283 | return NULL; | |
284 | } | |
285 | ||
286 | return nbfd; | |
287 | } | |
288 | ||
289 | /* Open shared library BFD. */ | |
290 | static bfd * | |
291 | spu_bfd_open (char *pathname) | |
292 | { | |
293 | char *original_name = strrchr (pathname, '@'); | |
294 | bfd *abfd; | |
295 | asection *spu_name; | |
296 | unsigned long long addr; | |
297 | int fd; | |
298 | ||
299 | /* Handle regular SVR4 libraries. */ | |
300 | if (!original_name) | |
301 | return svr4_so_ops.bfd_open (pathname); | |
302 | ||
303 | /* Decode object ID. */ | |
304 | if (sscanf (original_name, "@0x%llx <%d>", &addr, &fd) != 2) | |
305 | internal_error (__FILE__, __LINE__, "bad object ID"); | |
306 | ||
307 | /* Open BFD representing SPE executable. */ | |
308 | abfd = spu_bfd_fopen (original_name, (CORE_ADDR) addr); | |
309 | if (!abfd) | |
310 | error (_("Cannot read SPE executable at %s"), original_name); | |
311 | ||
312 | /* Retrieve SPU name note. */ | |
313 | spu_name = bfd_get_section_by_name (abfd, ".note.spu_name"); | |
314 | if (spu_name) | |
315 | { | |
316 | int sect_size = bfd_section_size (abfd, spu_name); | |
317 | if (sect_size > 20) | |
318 | { | |
319 | char *buf = alloca (sect_size - 20 + strlen (original_name) + 1); | |
320 | bfd_get_section_contents (abfd, spu_name, buf, 20, sect_size - 20); | |
321 | buf[sect_size - 20] = '\0'; | |
322 | ||
323 | strcat (buf, original_name); | |
324 | ||
325 | xfree ((char *)abfd->filename); | |
326 | abfd->filename = xstrdup (buf); | |
327 | } | |
328 | } | |
329 | ||
330 | return abfd; | |
331 | } | |
332 | ||
333 | /* Lookup global symbol in a SPE executable. */ | |
334 | static struct symbol * | |
335 | spu_lookup_lib_symbol (const struct objfile *objfile, | |
336 | const char *name, | |
337 | const char *linkage_name, | |
338 | const domain_enum domain) | |
339 | { | |
340 | if (bfd_get_arch (objfile->obfd) == bfd_arch_spu) | |
341 | return lookup_global_symbol_from_objfile (objfile, name, linkage_name, | |
342 | domain); | |
343 | ||
344 | if (svr4_so_ops.lookup_lib_global_symbol != NULL) | |
345 | return svr4_so_ops.lookup_lib_global_symbol (objfile, name, linkage_name, | |
346 | domain); | |
347 | return NULL; | |
348 | } | |
349 | ||
350 | /* Enable shared library breakpoint. */ | |
351 | static int | |
352 | spu_enable_break (struct objfile *objfile) | |
353 | { | |
354 | struct minimal_symbol *spe_event_sym = NULL; | |
355 | ||
356 | /* The libspe library will call __spe_context_update_event whenever any | |
357 | SPE context is allocated or destroyed. */ | |
358 | spe_event_sym = lookup_minimal_symbol ("__spe_context_update_event", | |
359 | NULL, objfile); | |
360 | ||
361 | /* Place a solib_event breakpoint on the symbol. */ | |
362 | if (spe_event_sym) | |
363 | { | |
364 | CORE_ADDR addr = SYMBOL_VALUE_ADDRESS (spe_event_sym); | |
365 | addr = gdbarch_convert_from_func_ptr_addr (target_gdbarch, addr, | |
366 | ¤t_target); | |
367 | create_solib_event_breakpoint (target_gdbarch, addr); | |
368 | return 1; | |
369 | } | |
370 | ||
371 | return 0; | |
372 | } | |
373 | ||
374 | /* Create inferior hook. */ | |
375 | static void | |
376 | spu_solib_create_inferior_hook (void) | |
377 | { | |
378 | /* Remove all previously installed solib breakpoints. Both the SVR4 | |
379 | code and us will re-install all required breakpoints. */ | |
380 | remove_solib_event_breakpoints (); | |
381 | ||
382 | /* Handle SPE stand-alone executables. */ | |
383 | if (spu_standalone_p ()) | |
384 | { | |
385 | /* After an SPE stand-alone executable was loaded, we'll receive | |
386 | an additional trap due to the binfmt_misc handler. Make sure | |
387 | to skip that trap. */ | |
388 | spu_skip_standalone_loader (); | |
389 | ||
390 | /* If the user established breakpoints before starting the inferior, GDB | |
391 | would attempt to insert those now. This would fail because the SPU | |
392 | context has not yet been created and the SPU executable has not yet | |
393 | been loaded. To prevent such failures, we disable all user-created | |
394 | breakpoints now; they will be re-enabled in spu_current_sos once the | |
395 | main SPU context has been detected. */ | |
396 | disable_breakpoints_before_startup (); | |
397 | ||
398 | /* A special case arises when re-starting an executable, because at | |
399 | this point it still resides at the relocated address range that was | |
400 | determined during its last execution. We need to undo the relocation | |
401 | so that that multi-architecture target recognizes the stand-alone | |
402 | initialization special case. */ | |
403 | spu_relocate_main_executable (-1); | |
404 | } | |
405 | ||
406 | /* Call SVR4 hook -- this will re-insert the SVR4 solib breakpoints. */ | |
407 | svr4_so_ops.solib_create_inferior_hook (); | |
408 | ||
409 | /* If the inferior is statically linked against libspe, we need to install | |
410 | our own solib breakpoint right now. Otherwise, it will be installed by | |
411 | the solib_loaded observer below as soon as libspe is loaded. */ | |
412 | spu_enable_break (NULL); | |
413 | } | |
414 | ||
415 | /* Install SPE "shared library" handling. This is called by -tdep code | |
416 | that wants to support SPU as a secondary architecture. */ | |
417 | void | |
418 | set_spu_solib_ops (struct gdbarch *gdbarch) | |
419 | { | |
420 | static struct target_so_ops spu_so_ops; | |
421 | ||
422 | /* Initialize this lazily, to avoid an initialization order | |
423 | dependency on solib-svr4.c's _initialize routine. */ | |
424 | if (spu_so_ops.current_sos == NULL) | |
425 | { | |
426 | spu_so_ops = svr4_so_ops; | |
427 | spu_so_ops.solib_create_inferior_hook = spu_solib_create_inferior_hook; | |
428 | spu_so_ops.relocate_section_addresses = spu_relocate_section_addresses; | |
429 | spu_so_ops.free_so = spu_free_so; | |
430 | spu_so_ops.current_sos = spu_current_sos; | |
431 | spu_so_ops.bfd_open = spu_bfd_open; | |
432 | spu_so_ops.lookup_lib_global_symbol = spu_lookup_lib_symbol; | |
433 | } | |
434 | ||
435 | set_solib_ops (gdbarch, &spu_so_ops); | |
436 | } | |
437 | ||
438 | /* Observer for the solib_loaded event. Used to install our breakpoint | |
439 | if libspe is a shared library. */ | |
440 | static void | |
441 | spu_solib_loaded (struct so_list *so) | |
442 | { | |
443 | if (strstr (so->so_original_name, "/libspe") != NULL) | |
444 | { | |
445 | solib_read_symbols (so, so->from_tty ? SYMFILE_VERBOSE : 0); | |
446 | spu_enable_break (so->objfile); | |
447 | } | |
448 | } | |
449 | ||
450 | void | |
451 | _initialize_spu_solib (void) | |
452 | { | |
453 | observer_attach_solib_loaded (spu_solib_loaded); | |
454 | } | |
455 |