Updated Swedish translation for the opcodes directory
[binutils-gdb.git] / gdb / solib-rocm.c
blob0ea3702e9e3e06270bcfcee114766659e7ce2ec7
1 /* Handle ROCm Code Objects for GDB, the GNU Debugger.
3 Copyright (C) 2019-2023 Free Software Foundation, Inc.
5 This file is part of GDB.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 #include "defs.h"
22 #include "amd-dbgapi-target.h"
23 #include "amdgpu-tdep.h"
24 #include "arch-utils.h"
25 #include "elf-bfd.h"
26 #include "elf/amdgpu.h"
27 #include "gdbsupport/fileio.h"
28 #include "inferior.h"
29 #include "observable.h"
30 #include "solib.h"
31 #include "solib-svr4.h"
32 #include "solist.h"
33 #include "symfile.h"
35 /* ROCm-specific inferior data. */
37 struct solib_info
39 /* List of code objects loaded into the inferior. */
40 so_list *solib_list;
43 /* Per-inferior data key. */
44 static const registry<inferior>::key<solib_info> rocm_solib_data;
46 static target_so_ops rocm_solib_ops;
48 /* Free the solib linked list. */
50 static void
51 rocm_free_solib_list (struct solib_info *info)
53 while (info->solib_list != nullptr)
55 struct so_list *next = info->solib_list->next;
57 free_so (info->solib_list);
58 info->solib_list = next;
61 info->solib_list = nullptr;
65 /* Fetch the solib_info data for INF. */
67 static struct solib_info *
68 get_solib_info (inferior *inf)
70 solib_info *info = rocm_solib_data.get (inf);
72 if (info == nullptr)
73 info = rocm_solib_data.emplace (inf);
75 return info;
78 /* Relocate section addresses. */
80 static void
81 rocm_solib_relocate_section_addresses (struct so_list *so,
82 struct target_section *sec)
84 if (!is_amdgpu_arch (gdbarch_from_bfd (so->abfd)))
86 svr4_so_ops.relocate_section_addresses (so, sec);
87 return;
90 lm_info_svr4 *li = (lm_info_svr4 *) so->lm_info;
91 sec->addr = sec->addr + li->l_addr;
92 sec->endaddr = sec->endaddr + li->l_addr;
95 static void rocm_update_solib_list ();
97 static void
98 rocm_solib_handle_event ()
100 /* Since we sit on top of svr4_so_ops, we might get called following an event
101 concerning host libraries. We must therefore forward the call. If the
102 event was for a ROCm code object, it will be a no-op. On the other hand,
103 if the event was for host libraries, rocm_update_solib_list will be
104 essentially be a no-op (it will reload the same code object list as was
105 previously loaded). */
106 svr4_so_ops.handle_event ();
108 rocm_update_solib_list ();
111 /* Make a deep copy of the solib linked list. */
113 static so_list *
114 rocm_solib_copy_list (const so_list *src)
116 struct so_list *dst = nullptr;
117 struct so_list **link = &dst;
119 while (src != nullptr)
121 struct so_list *newobj;
123 newobj = XNEW (struct so_list);
124 memcpy (newobj, src, sizeof (struct so_list));
126 lm_info_svr4 *src_li = (lm_info_svr4 *) src->lm_info;
127 newobj->lm_info = new lm_info_svr4 (*src_li);
129 newobj->next = nullptr;
130 *link = newobj;
131 link = &newobj->next;
133 src = src->next;
136 return dst;
139 /* Build a list of `struct so_list' objects describing the shared
140 objects currently loaded in the inferior. */
142 static struct so_list *
143 rocm_solib_current_sos ()
145 /* First, retrieve the host-side shared library list. */
146 so_list *head = svr4_so_ops.current_sos ();
148 /* Then, the device-side shared library list. */
149 so_list *list = get_solib_info (current_inferior ())->solib_list;
151 if (list == nullptr)
152 return head;
154 list = rocm_solib_copy_list (list);
156 if (head == nullptr)
157 return list;
159 /* Append our libraries to the end of the list. */
160 so_list *tail;
161 for (tail = head; tail->next; tail = tail->next)
162 /* Nothing. */;
163 tail->next = list;
165 return head;
168 namespace {
170 /* Interface to interact with a ROCm code object stream. */
172 struct rocm_code_object_stream
174 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream);
176 /* Copy SIZE bytes from the underlying objfile storage starting at OFFSET
177 into the user provided buffer BUF.
179 Return the number of bytes actually copied (might be inferior to SIZE if
180 the end of the stream is reached). */
181 virtual file_ptr read (void *buf, file_ptr size, file_ptr offset) = 0;
183 /* Retrieve file information in SB.
185 Return 0 on success. On failure, set the appropriate bfd error number
186 (using bfd_set_error) and return -1. */
187 int stat (struct stat *sb);
189 virtual ~rocm_code_object_stream () = default;
191 protected:
192 rocm_code_object_stream () = default;
194 /* Return the size of the object file, or -1 if the size cannot be
195 determined.
197 This is a helper function for stat. */
198 virtual LONGEST size () = 0;
202 rocm_code_object_stream::stat (struct stat *sb)
204 const LONGEST size = this->size ();
205 if (size == -1)
206 return -1;
208 memset (sb, '\0', sizeof (struct stat));
209 sb->st_size = size;
210 return 0;
213 /* Interface to a ROCm object stream which is embedded in an ELF file
214 accessible to the debugger. */
216 struct rocm_code_object_stream_file final : rocm_code_object_stream
218 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_file);
220 rocm_code_object_stream_file (int fd, ULONGEST offset, ULONGEST size);
222 file_ptr read (void *buf, file_ptr size, file_ptr offset) override;
224 LONGEST size () override;
226 ~rocm_code_object_stream_file () override;
228 protected:
230 /* The target file descriptor for this stream. */
231 int m_fd;
233 /* The offset of the ELF file image in the target file. */
234 ULONGEST m_offset;
236 /* The size of the ELF file image. The value 0 means that it was
237 unspecified in the URI descriptor. */
238 ULONGEST m_size;
241 rocm_code_object_stream_file::rocm_code_object_stream_file
242 (int fd, ULONGEST offset, ULONGEST size)
243 : m_fd (fd), m_offset (offset), m_size (size)
247 file_ptr
248 rocm_code_object_stream_file::read (void *buf, file_ptr size,
249 file_ptr offset)
251 fileio_error target_errno;
252 file_ptr nbytes = 0;
253 while (size > 0)
255 QUIT;
257 file_ptr bytes_read
258 = target_fileio_pread (m_fd, static_cast<gdb_byte *> (buf) + nbytes,
259 size, m_offset + offset + nbytes,
260 &target_errno);
262 if (bytes_read == 0)
263 break;
265 if (bytes_read < 0)
267 errno = fileio_error_to_host (target_errno);
268 bfd_set_error (bfd_error_system_call);
269 return -1;
272 nbytes += bytes_read;
273 size -= bytes_read;
276 return nbytes;
279 LONGEST
280 rocm_code_object_stream_file::size ()
282 if (m_size == 0)
284 fileio_error target_errno;
285 struct stat stat;
286 if (target_fileio_fstat (m_fd, &stat, &target_errno) < 0)
288 errno = fileio_error_to_host (target_errno);
289 bfd_set_error (bfd_error_system_call);
290 return -1;
293 /* Check that the offset is valid. */
294 if (m_offset >= stat.st_size)
296 bfd_set_error (bfd_error_bad_value);
297 return -1;
300 m_size = stat.st_size - m_offset;
303 return m_size;
306 rocm_code_object_stream_file::~rocm_code_object_stream_file ()
308 fileio_error target_errno;
309 target_fileio_close (m_fd, &target_errno);
312 /* Interface to a code object which lives in the inferior's memory. */
314 struct rocm_code_object_stream_memory final : public rocm_code_object_stream
316 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_memory);
318 rocm_code_object_stream_memory (gdb::byte_vector buffer);
320 file_ptr read (void *buf, file_ptr size, file_ptr offset) override;
322 protected:
324 /* Snapshot of the original ELF image taken during load. This is done to
325 support the situation where an inferior uses an in-memory image, and
326 releases or re-uses this memory before GDB is done using it. */
327 gdb::byte_vector m_objfile_image;
329 LONGEST size () override
331 return m_objfile_image.size ();
335 rocm_code_object_stream_memory::rocm_code_object_stream_memory
336 (gdb::byte_vector buffer)
337 : m_objfile_image (std::move (buffer))
341 file_ptr
342 rocm_code_object_stream_memory::read (void *buf, file_ptr size,
343 file_ptr offset)
345 if (size > m_objfile_image.size () - offset)
346 size = m_objfile_image.size () - offset;
348 memcpy (buf, m_objfile_image.data () + offset, size);
349 return size;
352 } /* anonymous namespace */
354 static void *
355 rocm_bfd_iovec_open (bfd *abfd, void *inferior_void)
357 gdb::string_view uri (bfd_get_filename (abfd));
358 gdb::string_view protocol_delim = "://";
359 size_t protocol_end = uri.find (protocol_delim);
360 std::string protocol = gdb::to_string (uri.substr (0, protocol_end));
361 protocol_end += protocol_delim.length ();
363 std::transform (protocol.begin (), protocol.end (), protocol.begin (),
364 [] (unsigned char c) { return std::tolower (c); });
366 gdb::string_view path;
367 size_t path_end = uri.find_first_of ("#?", protocol_end);
368 if (path_end != std::string::npos)
369 path = uri.substr (protocol_end, path_end++ - protocol_end);
370 else
371 path = uri.substr (protocol_end);
373 /* %-decode the string. */
374 std::string decoded_path;
375 decoded_path.reserve (path.length ());
376 for (size_t i = 0; i < path.length (); ++i)
377 if (path[i] == '%'
378 && i < path.length () - 2
379 && std::isxdigit (path[i + 1])
380 && std::isxdigit (path[i + 2]))
382 gdb::string_view hex_digits = path.substr (i + 1, 2);
383 decoded_path += std::stoi (gdb::to_string (hex_digits), 0, 16);
384 i += 2;
386 else
387 decoded_path += path[i];
389 /* Tokenize the query/fragment. */
390 std::vector<gdb::string_view> tokens;
391 size_t pos, last = path_end;
392 while ((pos = uri.find ('&', last)) != std::string::npos)
394 tokens.emplace_back (uri.substr (last, pos - last));
395 last = pos + 1;
398 if (last != std::string::npos)
399 tokens.emplace_back (uri.substr (last));
401 /* Create a tag-value map from the tokenized query/fragment. */
402 std::unordered_map<gdb::string_view, gdb::string_view,
403 gdb::string_view_hash> params;
404 for (gdb::string_view token : tokens)
406 size_t delim = token.find ('=');
407 if (delim != std::string::npos)
409 gdb::string_view tag = token.substr (0, delim);
410 gdb::string_view val = token.substr (delim + 1);
411 params.emplace (tag, val);
417 ULONGEST offset = 0;
418 ULONGEST size = 0;
419 inferior *inferior = static_cast<struct inferior *> (inferior_void);
421 auto try_strtoulst = [] (gdb::string_view v)
423 errno = 0;
424 ULONGEST value = strtoulst (v.data (), nullptr, 0);
425 if (errno != 0)
427 /* The actual message doesn't matter, the exception is caught
428 below, transformed in a BFD error, and the message is lost. */
429 error (_("Failed to parse integer."));
432 return value;
435 auto offset_it = params.find ("offset");
436 if (offset_it != params.end ())
437 offset = try_strtoulst (offset_it->second);
439 auto size_it = params.find ("size");
440 if (size_it != params.end ())
442 size = try_strtoulst (size_it->second);
443 if (size == 0)
444 error (_("Invalid size value"));
447 if (protocol == "file")
449 fileio_error target_errno;
450 int fd
451 = target_fileio_open (static_cast<struct inferior *> (inferior),
452 decoded_path.c_str (), FILEIO_O_RDONLY,
453 false, 0, &target_errno);
455 if (fd == -1)
457 errno = fileio_error_to_host (target_errno);
458 bfd_set_error (bfd_error_system_call);
459 return nullptr;
462 return new rocm_code_object_stream_file (fd, offset, size);
465 if (protocol == "memory")
467 ULONGEST pid = try_strtoulst (path);
468 if (pid != inferior->pid)
470 warning (_("`%s': code object is from another inferior"),
471 gdb::to_string (uri).c_str ());
472 bfd_set_error (bfd_error_bad_value);
473 return nullptr;
476 gdb::byte_vector buffer (size);
477 if (target_read_memory (offset, buffer.data (), size) != 0)
479 warning (_("Failed to copy the code object from the inferior"));
480 bfd_set_error (bfd_error_bad_value);
481 return nullptr;
484 return new rocm_code_object_stream_memory (std::move (buffer));
487 warning (_("`%s': protocol not supported: %s"),
488 gdb::to_string (uri).c_str (), protocol.c_str ());
489 bfd_set_error (bfd_error_bad_value);
490 return nullptr;
492 catch (const gdb_exception_quit &ex)
494 set_quit_flag ();
495 bfd_set_error (bfd_error_bad_value);
496 return nullptr;
498 catch (const gdb_exception &ex)
500 bfd_set_error (bfd_error_bad_value);
501 return nullptr;
505 static int
506 rocm_bfd_iovec_close (bfd *nbfd, void *data)
508 delete static_cast<rocm_code_object_stream *> (data);
510 return 0;
513 static file_ptr
514 rocm_bfd_iovec_pread (bfd *abfd, void *data, void *buf, file_ptr size,
515 file_ptr offset)
517 return static_cast<rocm_code_object_stream *> (data)->read (buf, size,
518 offset);
521 static int
522 rocm_bfd_iovec_stat (bfd *abfd, void *data, struct stat *sb)
524 return static_cast<rocm_code_object_stream *> (data)->stat (sb);
527 static gdb_bfd_ref_ptr
528 rocm_solib_bfd_open (const char *pathname)
530 /* Handle regular files with SVR4 open. */
531 if (strstr (pathname, "://") == nullptr)
532 return svr4_so_ops.bfd_open (pathname);
534 gdb_bfd_ref_ptr abfd
535 = gdb_bfd_openr_iovec (pathname, "elf64-amdgcn", rocm_bfd_iovec_open,
536 current_inferior (), rocm_bfd_iovec_pread,
537 rocm_bfd_iovec_close, rocm_bfd_iovec_stat);
539 if (abfd == nullptr)
540 error (_("Could not open `%s' as an executable file: %s"), pathname,
541 bfd_errmsg (bfd_get_error ()));
543 /* Check bfd format. */
544 if (!bfd_check_format (abfd.get (), bfd_object))
545 error (_("`%s': not in executable format: %s"),
546 bfd_get_filename (abfd.get ()), bfd_errmsg (bfd_get_error ()));
548 unsigned char osabi = elf_elfheader (abfd)->e_ident[EI_OSABI];
549 unsigned char osabiversion = elf_elfheader (abfd)->e_ident[EI_ABIVERSION];
551 /* Check that the code object is using the HSA OS ABI. */
552 if (osabi != ELFOSABI_AMDGPU_HSA)
553 error (_("`%s': ELF file OS ABI is not supported (%d)."),
554 bfd_get_filename (abfd.get ()), osabi);
556 /* We support HSA code objects V3 and greater. */
557 if (osabiversion < ELFABIVERSION_AMDGPU_HSA_V3)
558 error (_("`%s': ELF file HSA OS ABI version is not supported (%d)."),
559 bfd_get_filename (abfd.get ()), osabiversion);
561 return abfd;
564 static void
565 rocm_solib_create_inferior_hook (int from_tty)
567 rocm_free_solib_list (get_solib_info (current_inferior ()));
569 svr4_so_ops.solib_create_inferior_hook (from_tty);
572 static void
573 rocm_update_solib_list ()
575 inferior *inf = current_inferior ();
577 amd_dbgapi_process_id_t process_id = get_amd_dbgapi_process_id (inf);
578 if (process_id.handle == AMD_DBGAPI_PROCESS_NONE.handle)
579 return;
581 solib_info *info = get_solib_info (inf);
583 rocm_free_solib_list (info);
584 struct so_list **link = &info->solib_list;
586 amd_dbgapi_code_object_id_t *code_object_list;
587 size_t count;
589 amd_dbgapi_status_t status
590 = amd_dbgapi_process_code_object_list (process_id, &count,
591 &code_object_list, nullptr);
592 if (status != AMD_DBGAPI_STATUS_SUCCESS)
594 warning (_("amd_dbgapi_process_code_object_list failed (%s)"),
595 get_status_string (status));
596 return;
599 for (size_t i = 0; i < count; ++i)
601 CORE_ADDR l_addr;
602 char *uri_bytes;
604 status = amd_dbgapi_code_object_get_info
605 (code_object_list[i], AMD_DBGAPI_CODE_OBJECT_INFO_LOAD_ADDRESS,
606 sizeof (l_addr), &l_addr);
607 if (status != AMD_DBGAPI_STATUS_SUCCESS)
608 continue;
610 status = amd_dbgapi_code_object_get_info
611 (code_object_list[i], AMD_DBGAPI_CODE_OBJECT_INFO_URI_NAME,
612 sizeof (uri_bytes), &uri_bytes);
613 if (status != AMD_DBGAPI_STATUS_SUCCESS)
614 continue;
616 struct so_list *so = XCNEW (struct so_list);
617 lm_info_svr4 *li = new lm_info_svr4;
618 li->l_addr = l_addr;
619 so->lm_info = li;
621 strncpy (so->so_name, uri_bytes, sizeof (so->so_name));
622 so->so_name[sizeof (so->so_name) - 1] = '\0';
623 xfree (uri_bytes);
625 /* Make so_original_name unique so that code objects with the same URI
626 but different load addresses are seen by gdb core as different shared
627 objects. */
628 xsnprintf (so->so_original_name, sizeof (so->so_original_name),
629 "code_object_%ld", code_object_list[i].handle);
631 so->next = nullptr;
632 *link = so;
633 link = &so->next;
636 xfree (code_object_list);
638 if (rocm_solib_ops.current_sos == NULL)
640 /* Override what we need to. */
641 rocm_solib_ops = svr4_so_ops;
642 rocm_solib_ops.current_sos = rocm_solib_current_sos;
643 rocm_solib_ops.solib_create_inferior_hook
644 = rocm_solib_create_inferior_hook;
645 rocm_solib_ops.bfd_open = rocm_solib_bfd_open;
646 rocm_solib_ops.relocate_section_addresses
647 = rocm_solib_relocate_section_addresses;
648 rocm_solib_ops.handle_event = rocm_solib_handle_event;
650 /* Engage the ROCm so_ops. */
651 set_gdbarch_so_ops (current_inferior ()->gdbarch, &rocm_solib_ops);
655 static void
656 rocm_solib_target_inferior_created (inferior *inf)
658 rocm_free_solib_list (get_solib_info (inf));
659 rocm_update_solib_list ();
661 /* Force GDB to reload the solibs. */
662 current_inferior ()->pspace->clear_solib_cache ();
663 solib_add (nullptr, 0, auto_solib_add);
666 /* -Wmissing-prototypes */
667 extern initialize_file_ftype _initialize_rocm_solib;
669 void
670 _initialize_rocm_solib ()
672 /* The dependency on the amd-dbgapi exists because solib-rocm's
673 inferior_created observer needs amd-dbgapi to have attached the process,
674 which happens in amd_dbgapi_target's inferior_created observer. */
675 gdb::observers::inferior_created.attach
676 (rocm_solib_target_inferior_created,
677 "solib-rocm",
678 { &get_amd_dbgapi_target_inferior_created_observer_token () });