Commit | Line | Data |
---|---|---|
abeeff98 LM |
1 | /* Handle ROCm Code Objects for GDB, the GNU Debugger. |
2 | ||
ca9af5a1 LM |
3 | Copyright (C) 2019-2020 Free Software Foundation, Inc. |
4 | Copyright (C) 2019-2020 Advanced Micro Devices, Inc. All rights reserved. | |
abeeff98 LM |
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 3 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, see <http://www.gnu.org/licenses/>. */ | |
20 | ||
21 | #include "defs.h" | |
22 | ||
23 | #include "arch-utils.h" | |
24 | #include "elf-bfd.h" | |
84281c0e | 25 | #include "gdb/fileio.h" |
abeeff98 LM |
26 | #include "gdbcore.h" |
27 | #include "inferior.h" | |
28 | #include "objfiles.h" | |
29 | #include "observable.h" | |
30 | #include "rocm-tdep.h" | |
31 | #include "solib-svr4.h" | |
32 | #include "solib.h" | |
33 | #include "solist.h" | |
34 | #include "symfile.h" | |
35 | ||
36 | #include <functional> | |
37 | #include <string> | |
84281c0e | 38 | #include <unordered_map> |
abeeff98 LM |
39 | |
40 | /* ROCm-specific inferior data. */ | |
41 | ||
42 | struct solib_info | |
43 | { | |
44 | /* List of code objects loaded into the inferior. */ | |
45 | struct so_list *solib_list; | |
46 | }; | |
47 | ||
48 | /* Per-inferior data key. */ | |
49 | static const struct inferior_key<solib_info> rocm_solib_data; | |
50 | ||
51 | struct target_so_ops rocm_solib_ops; | |
52 | ||
53 | /* Free the solib linked list. */ | |
54 | ||
55 | static void | |
56 | rocm_free_solib_list (struct solib_info *info) | |
57 | { | |
58 | while (info->solib_list != NULL) | |
59 | { | |
60 | struct so_list *next = info->solib_list->next; | |
61 | ||
62 | free_so (info->solib_list); | |
63 | info->solib_list = next; | |
64 | } | |
65 | ||
66 | info->solib_list = NULL; | |
67 | } | |
68 | ||
69 | /* Fetch the solib_info data for the current inferior. */ | |
70 | ||
71 | static struct solib_info * | |
72 | get_solib_info (void) | |
73 | { | |
74 | struct inferior *inf = current_inferior (); | |
75 | ||
76 | struct solib_info *info = rocm_solib_data.get (inf); | |
77 | if (info == NULL) | |
78 | info = rocm_solib_data.emplace (inf); | |
79 | ||
80 | return info; | |
81 | } | |
82 | ||
83 | /* Relocate section addresses. */ | |
84 | ||
85 | static void | |
86 | rocm_solib_relocate_section_addresses (struct so_list *so, | |
87 | struct target_section *sec) | |
88 | { | |
89 | if (!rocm_is_amdgcn_gdbarch (gdbarch_from_bfd (so->abfd))) | |
90 | { | |
91 | svr4_so_ops.relocate_section_addresses (so, sec); | |
92 | return; | |
93 | } | |
94 | ||
95 | lm_info_svr4 *li = (lm_info_svr4 *)so->lm_info; | |
96 | sec->addr = sec->addr + li->l_addr; | |
97 | sec->endaddr = sec->endaddr + li->l_addr; | |
98 | } | |
99 | ||
100 | /* Make a deep copy of the solib linked list. */ | |
101 | ||
102 | static struct so_list * | |
103 | rocm_solib_copy_list (const struct so_list *src) | |
104 | { | |
105 | struct so_list *dst = NULL; | |
106 | struct so_list **link = &dst; | |
107 | ||
108 | while (src != NULL) | |
109 | { | |
110 | struct so_list *newobj; | |
111 | ||
112 | newobj = XNEW (struct so_list); | |
113 | memcpy (newobj, src, sizeof (struct so_list)); | |
114 | ||
115 | lm_info_svr4 *src_li = (lm_info_svr4 *)src->lm_info; | |
116 | newobj->lm_info = new lm_info_svr4 (*src_li); | |
117 | ||
118 | newobj->next = NULL; | |
119 | *link = newobj; | |
120 | link = &newobj->next; | |
121 | ||
122 | src = src->next; | |
123 | } | |
124 | ||
125 | return dst; | |
126 | } | |
127 | ||
128 | /* Build a list of `struct so_list' objects describing the shared | |
129 | objects currently loaded in the inferior. */ | |
130 | ||
131 | static struct so_list * | |
132 | rocm_solib_current_sos (void) | |
133 | { | |
134 | /* First, retrieve the host-side shared library list. */ | |
135 | struct so_list *head = svr4_so_ops.current_sos (); | |
136 | ||
137 | /* Then, the device-side shared library list. */ | |
138 | struct so_list *list = get_solib_info ()->solib_list; | |
139 | ||
140 | if (!list) | |
141 | return head; | |
142 | ||
143 | list = rocm_solib_copy_list (list); | |
144 | ||
145 | if (!head) | |
146 | return list; | |
147 | ||
148 | /* Append our libraries to the end of the list. */ | |
149 | struct so_list *tail; | |
150 | for (tail = head; tail->next; tail = tail->next) | |
151 | /* Nothing. */; | |
152 | tail->next = list; | |
153 | ||
154 | return head; | |
155 | } | |
156 | ||
84281c0e | 157 | struct rocm_code_object_stream |
abeeff98 | 158 | { |
84281c0e LM |
159 | /* The target file descriptor for this stream */ |
160 | int fd; | |
161 | ||
162 | /* The offset of the ELF file image in the target file. */ | |
163 | ULONGEST offset; | |
abeeff98 LM |
164 | |
165 | /* The size of the ELF file image. */ | |
166 | ULONGEST size; | |
167 | }; | |
168 | ||
169 | static void * | |
84281c0e | 170 | rocm_bfd_iovec_open (bfd *abfd, void *inferior) |
abeeff98 | 171 | { |
84281c0e LM |
172 | std::string uri (bfd_get_filename (abfd)); |
173 | ||
174 | std::string protocol_delim ("://"); | |
175 | size_t protocol_end = uri.find (protocol_delim); | |
176 | std::string protocol = uri.substr (0, protocol_end); | |
177 | protocol_end += protocol_delim.length (); | |
178 | ||
179 | std::transform (protocol.begin (), protocol.end (), protocol.begin (), | |
180 | [] (unsigned char c) { return std::tolower (c); }); | |
181 | if (protocol != "file") | |
182 | { | |
183 | warning (_ ("`%s': protocol not supported: %s"), uri.c_str (), | |
184 | protocol.c_str ()); | |
185 | bfd_set_error (bfd_error_bad_value); | |
186 | return nullptr; | |
187 | } | |
188 | ||
189 | std::string path; | |
190 | size_t path_end = uri.find_first_of ("#?", protocol_end); | |
191 | if (path_end != std::string::npos) | |
192 | path = uri.substr (protocol_end, path_end++ - protocol_end); | |
193 | else | |
194 | path = uri.substr (protocol_end); | |
195 | ||
196 | /* %-decode the string. */ | |
197 | std::string decoded_path; | |
198 | decoded_path.reserve (path.length ()); | |
199 | for (size_t i = 0; i < path.length (); ++i) | |
200 | if (path[i] == '%' && std::isxdigit (path[i + 1]) | |
201 | && std::isxdigit (path[i + 2])) | |
202 | { | |
203 | decoded_path += std::stoi (path.substr (i + 1, 2), 0, 16); | |
204 | i += 2; | |
205 | } | |
206 | else | |
207 | decoded_path += path[i]; | |
208 | ||
209 | /* Tokenize the query/fragment. */ | |
210 | std::vector<std::string> tokens; | |
211 | size_t pos, last = path_end; | |
212 | while ((pos = uri.find ('&', last)) != std::string::npos) | |
213 | { | |
214 | tokens.emplace_back (uri.substr (last, pos - last)); | |
215 | last = pos + 1; | |
216 | } | |
217 | if (last != std::string::npos) | |
218 | tokens.emplace_back (uri.substr (last)); | |
219 | ||
220 | /* Create a tag-value map from the tokenized query/fragment. */ | |
221 | std::unordered_map<std::string, std::string> params; | |
222 | std::for_each (tokens.begin (), tokens.end (), [&] (std::string &token) { | |
223 | size_t delim = token.find ('='); | |
224 | if (delim != std::string::npos) | |
225 | params.emplace (token.substr (0, delim), token.substr (delim + 1)); | |
226 | }); | |
227 | ||
228 | gdb::unique_xmalloc_ptr<rocm_code_object_stream> stream ( | |
229 | XCNEW (rocm_code_object_stream)); | |
230 | try | |
231 | { | |
232 | auto offset_it = params.find ("offset"); | |
233 | if (offset_it != params.end ()) | |
234 | stream->offset = std::stoul (offset_it->second, nullptr, 0); | |
235 | ||
236 | auto size_it = params.find ("size"); | |
237 | if (size_it != params.end ()) | |
238 | if (!(stream->size = std::stoul (size_it->second, nullptr, 0))) | |
239 | { | |
240 | bfd_set_error (bfd_error_bad_value); | |
241 | return nullptr; | |
242 | } | |
243 | } | |
244 | catch (...) | |
245 | { | |
246 | bfd_set_error (bfd_error_bad_value); | |
247 | return nullptr; | |
248 | } | |
249 | ||
250 | int fd, target_errno; | |
251 | fd = target_fileio_open (static_cast<struct inferior *> (inferior), | |
252 | decoded_path.c_str (), FILEIO_O_RDONLY, 0, | |
253 | &target_errno); | |
254 | if (fd == -1) | |
255 | { | |
256 | /* FIXME: Should we set errno? Move fileio_errno_to_host from gdb_bfd.c | |
257 | to fileio.cc */ | |
258 | /* errno = fileio_errno_to_host (target_errno); */ | |
259 | bfd_set_error (bfd_error_system_call); | |
260 | return nullptr; | |
261 | } | |
262 | ||
263 | stream->fd = fd; | |
264 | return stream.release (); | |
abeeff98 LM |
265 | } |
266 | ||
267 | static int | |
84281c0e | 268 | rocm_bfd_iovec_close (bfd *nbfd, void *data) |
abeeff98 | 269 | { |
84281c0e LM |
270 | auto *stream = static_cast<rocm_code_object_stream *> (data); |
271 | ||
272 | int target_errno; | |
273 | target_fileio_close (stream->fd, &target_errno); | |
274 | ||
abeeff98 LM |
275 | xfree (stream); |
276 | return 0; | |
277 | } | |
278 | ||
279 | static file_ptr | |
84281c0e | 280 | rocm_bfd_iovec_pread (bfd *abfd, void *data, void *buf, file_ptr size, |
abeeff98 LM |
281 | file_ptr offset) |
282 | { | |
84281c0e LM |
283 | auto *stream = static_cast<rocm_code_object_stream *> (data); |
284 | int target_errno; | |
abeeff98 | 285 | |
84281c0e LM |
286 | file_ptr nbytes = 0; |
287 | while (size > 0) | |
abeeff98 | 288 | { |
84281c0e LM |
289 | QUIT; |
290 | ||
291 | file_ptr bytes_read = target_fileio_pread ( | |
292 | stream->fd, static_cast<gdb_byte *> (buf) + nbytes, size, | |
293 | stream->offset + offset + nbytes, &target_errno); | |
294 | ||
295 | if (bytes_read == 0) | |
296 | break; | |
297 | ||
298 | if (bytes_read < 0) | |
299 | { | |
300 | /* FIXME: Should we set errno? */ | |
301 | /* errno = fileio_errno_to_host (target_errno); */ | |
302 | bfd_set_error (bfd_error_system_call); | |
303 | return -1; | |
304 | } | |
305 | ||
306 | nbytes += bytes_read; | |
307 | size -= bytes_read; | |
abeeff98 LM |
308 | } |
309 | ||
310 | return nbytes; | |
311 | } | |
312 | ||
313 | static int | |
84281c0e | 314 | rocm_bfd_iovec_stat (bfd *abfd, void *data, struct stat *sb) |
abeeff98 | 315 | { |
84281c0e LM |
316 | auto *stream = static_cast<rocm_code_object_stream *> (data); |
317 | int target_errno; | |
318 | ||
319 | /* If stream->size is 0, the URI size parameter was not set. */ | |
320 | if (!stream->size) | |
321 | { | |
322 | struct stat stat; | |
323 | if (target_fileio_fstat (stream->fd, &stat, &target_errno) < 0) | |
324 | { | |
325 | /* FIXME: Should we set errno? */ | |
326 | /* errno = fileio_errno_to_host (target_errno); */ | |
327 | bfd_set_error (bfd_error_system_call); | |
328 | return -1; | |
329 | } | |
330 | ||
331 | /* Check that the offset is valid. */ | |
332 | if (stream->offset >= stat.st_size) | |
333 | { | |
334 | bfd_set_error (bfd_error_bad_value); | |
335 | return -1; | |
336 | } | |
337 | ||
338 | stream->size = stat.st_size - stream->offset; | |
339 | } | |
340 | ||
abeeff98 | 341 | memset (sb, '\0', sizeof (struct stat)); |
84281c0e | 342 | sb->st_size = stream->size; |
abeeff98 LM |
343 | return 0; |
344 | } | |
345 | ||
346 | static gdb_bfd_ref_ptr | |
347 | rocm_solib_bfd_open (const char *pathname) | |
348 | { | |
84281c0e LM |
349 | /* Handle regular files with SVR4 open. */ |
350 | if (!strstr (pathname, "://")) | |
abeeff98 LM |
351 | return svr4_so_ops.bfd_open (pathname); |
352 | ||
abeeff98 | 353 | gdb_bfd_ref_ptr abfd (gdb_bfd_openr_iovec ( |
84281c0e | 354 | pathname, "elf64-amdgcn", rocm_bfd_iovec_open, current_inferior (), |
abeeff98 LM |
355 | rocm_bfd_iovec_pread, rocm_bfd_iovec_close, rocm_bfd_iovec_stat)); |
356 | ||
84281c0e LM |
357 | if (abfd == nullptr) |
358 | error (_ ("Could not open `%s' as an executable file: %s"), pathname, | |
359 | bfd_errmsg (bfd_get_error ())); | |
360 | ||
abeeff98 LM |
361 | /* Check bfd format. */ |
362 | if (!bfd_check_format (abfd.get (), bfd_object)) | |
363 | error (_ ("`%s': not in executable format: %s"), | |
364 | bfd_get_filename (abfd.get ()), bfd_errmsg (bfd_get_error ())); | |
365 | ||
366 | unsigned char osabi = elf_elfheader (abfd)->e_ident[EI_OSABI]; | |
367 | unsigned char osabiversion = elf_elfheader (abfd)->e_ident[EI_ABIVERSION]; | |
368 | ||
369 | /* Make a check if the code object in the elf is V3. ROCM-gdb has | |
370 | support only for V3. */ | |
371 | if (osabi != ELFOSABI_AMDGPU_HSA) | |
372 | error (_ ("`%s': ELF file OS ABI invalid (%d)."), | |
373 | bfd_get_filename (abfd.get ()), osabi); | |
374 | ||
375 | if (osabi == ELFOSABI_AMDGPU_HSA && osabiversion < 1) | |
376 | error (_ ("`%s': ELF file ABI version (%d) is not supported."), | |
377 | bfd_get_filename (abfd.get ()), osabiversion); | |
378 | ||
379 | return abfd; | |
380 | } | |
381 | ||
382 | static void | |
383 | rocm_solib_create_inferior_hook (int from_tty) | |
384 | { | |
385 | rocm_free_solib_list (get_solib_info ()); | |
386 | ||
387 | svr4_so_ops.solib_create_inferior_hook (from_tty); | |
388 | } | |
389 | ||
390 | static void | |
391 | rocm_update_solib_list () | |
392 | { | |
393 | amd_dbgapi_process_id_t process_id = get_amd_dbgapi_process_id (); | |
84281c0e LM |
394 | if (process_id.handle == AMD_DBGAPI_PROCESS_NONE.handle) |
395 | return; | |
396 | ||
abeeff98 LM |
397 | solib_info *info = get_solib_info (); |
398 | amd_dbgapi_status_t status; | |
399 | ||
400 | rocm_free_solib_list (info); | |
401 | struct so_list **link = &info->solib_list; | |
402 | ||
403 | amd_dbgapi_code_object_id_t *code_object_list; | |
404 | size_t count; | |
405 | ||
406 | if ((status = amd_dbgapi_code_object_list (process_id, &count, | |
407 | &code_object_list, nullptr)) | |
408 | != AMD_DBGAPI_STATUS_SUCCESS) | |
409 | { | |
410 | warning (_ ("amd_dbgapi_code_object_list failed (%d)"), status); | |
411 | return; | |
412 | } | |
413 | ||
414 | for (size_t i = 0; i < count; ++i) | |
415 | { | |
416 | struct so_list *so = XCNEW (struct so_list); | |
417 | lm_info_svr4 *li = new lm_info_svr4; | |
418 | so->lm_info = li; | |
419 | ||
420 | char *uri_bytes; | |
421 | ||
422 | if (amd_dbgapi_code_object_get_info ( | |
423 | process_id, code_object_list[i], | |
424 | AMD_DBGAPI_CODE_OBJECT_INFO_LOAD_ADDRESS, sizeof (li->l_addr), | |
425 | &li->l_addr) | |
426 | != AMD_DBGAPI_STATUS_SUCCESS | |
427 | || amd_dbgapi_code_object_get_info ( | |
428 | process_id, code_object_list[i], | |
429 | AMD_DBGAPI_CODE_OBJECT_INFO_URI_NAME, sizeof (uri_bytes), | |
430 | &uri_bytes) | |
431 | != AMD_DBGAPI_STATUS_SUCCESS) | |
432 | continue; | |
433 | ||
84281c0e LM |
434 | strncpy (so->so_name, uri_bytes, sizeof (so->so_name)); |
435 | so->so_name[sizeof (so->so_name) - 1] = '\0'; | |
abeeff98 LM |
436 | xfree (uri_bytes); |
437 | ||
abeeff98 LM |
438 | strcpy (so->so_original_name, so->so_name); |
439 | ||
440 | so->next = nullptr; | |
441 | *link = so; | |
442 | link = &so->next; | |
443 | } | |
444 | ||
445 | xfree (code_object_list); | |
446 | ||
447 | /* Force GDB to reload the solibs. */ | |
448 | clear_program_space_solib_cache (current_inferior ()->pspace); | |
449 | ||
450 | /* Switch terminal for any messages produced by | |
451 | breakpoint_re_set. */ | |
452 | target_terminal::ours_for_output (); | |
453 | ||
454 | solib_add (NULL, 0, auto_solib_add); | |
455 | ||
456 | /* Switch it back. */ | |
457 | target_terminal::inferior (); | |
458 | } | |
459 | ||
460 | static void | |
461 | rocm_solib_dbgapi_activated () | |
462 | { | |
463 | if (rocm_solib_ops.current_sos == NULL) | |
464 | { | |
465 | /* Override what we need to */ | |
466 | rocm_solib_ops = svr4_so_ops; | |
467 | rocm_solib_ops.current_sos = rocm_solib_current_sos; | |
468 | rocm_solib_ops.solib_create_inferior_hook | |
469 | = rocm_solib_create_inferior_hook; | |
470 | rocm_solib_ops.bfd_open = rocm_solib_bfd_open; | |
471 | rocm_solib_ops.relocate_section_addresses | |
472 | = rocm_solib_relocate_section_addresses; | |
473 | } | |
474 | ||
475 | /* Engage the ROCm so_ops. */ | |
476 | set_solib_ops (target_gdbarch (), &rocm_solib_ops); | |
477 | } | |
478 | ||
479 | static void | |
480 | rocm_solib_dbgapi_deactivated () | |
481 | { | |
482 | /* Disengage the ROCm so_ops. */ | |
483 | set_solib_ops (target_gdbarch (), &svr4_so_ops); | |
0fdeaa80 | 484 | rocm_update_solib_list (); |
abeeff98 LM |
485 | } |
486 | ||
487 | static void | |
488 | rocm_solib_target_inferior_created (struct target_ops *target, int from_tty) | |
489 | { | |
490 | rocm_free_solib_list (get_solib_info ()); | |
491 | rocm_update_solib_list (); | |
492 | } | |
493 | ||
494 | /* -Wmissing-prototypes */ | |
495 | extern initialize_file_ftype _initialize_rocm_solib; | |
496 | ||
497 | void | |
498 | _initialize_rocm_solib (void) | |
499 | { | |
500 | /* Install our observers. */ | |
501 | amd_dbgapi_activated.attach (rocm_solib_dbgapi_activated); | |
502 | amd_dbgapi_deactivated.attach (rocm_solib_dbgapi_deactivated); | |
503 | amd_dbgapi_code_object_list_updated.attach (rocm_update_solib_list); | |
504 | ||
505 | /* FIXME: remove this when we can clear the solist in | |
506 | rocm_solib_create_inferior_hook. */ | |
507 | gdb::observers::inferior_created.attach (rocm_solib_target_inferior_created); | |
508 | } |