Bump GDB's version number to 15.0.91.DATE-git.
[binutils-gdb.git] / gdb / solib-rocm.c
blob9b995c7a3abe511269865022289897dee3e9077c
1 /* Handle ROCm Code Objects for GDB, the GNU Debugger.
3 Copyright (C) 2019-2024 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/>. */
21 #include "amd-dbgapi-target.h"
22 #include "amdgpu-tdep.h"
23 #include "arch-utils.h"
24 #include "elf-bfd.h"
25 #include "elf/amdgpu.h"
26 #include "event-top.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 #include <unordered_map>
37 namespace {
39 /* Per inferior cache of opened file descriptors. */
40 struct rocm_solib_fd_cache
42 explicit rocm_solib_fd_cache (inferior *inf) : m_inferior (inf) {}
43 DISABLE_COPY_AND_ASSIGN (rocm_solib_fd_cache);
45 /* Return a read-only file descriptor to FILENAME and increment the
46 associated reference count.
48 Open the file FILENAME if it is not already opened, reuse the existing file
49 descriptor otherwise.
51 On error -1 is returned, and TARGET_ERRNO is set. */
52 int open (const std::string &filename, fileio_error *target_errno);
54 /* Decrement the reference count to FD and close FD if the reference count
55 reaches 0.
57 On success, return 0. On error, return -1 and set TARGET_ERRNO. */
58 int close (int fd, fileio_error *target_errno);
60 private:
61 struct refcnt_fd
63 DISABLE_COPY_AND_ASSIGN (refcnt_fd);
64 refcnt_fd (int fd, int refcnt) : fd (fd), refcnt (refcnt) {}
66 int fd = -1;
67 int refcnt = 0;
70 inferior *m_inferior;
71 std::unordered_map<std::string, refcnt_fd> m_cache;
74 int
75 rocm_solib_fd_cache::open (const std::string &filename,
76 fileio_error *target_errno)
78 auto it = m_cache.find (filename);
79 if (it == m_cache.end ())
81 /* The file is not yet opened on the target. */
82 int fd
83 = target_fileio_open (m_inferior, filename.c_str (), FILEIO_O_RDONLY,
84 false, 0, target_errno);
85 if (fd != -1)
86 m_cache.emplace (std::piecewise_construct,
87 std::forward_as_tuple (filename),
88 std::forward_as_tuple (fd, 1));
89 return fd;
91 else
93 /* The file is already opened. Increment the refcnt and return the
94 already opened FD. */
95 it->second.refcnt++;
96 gdb_assert (it->second.fd != -1);
97 return it->second.fd;
102 rocm_solib_fd_cache::close (int fd, fileio_error *target_errno)
104 using cache_val = std::unordered_map<std::string, refcnt_fd>::value_type;
105 auto it
106 = std::find_if (m_cache.begin (), m_cache.end (),
107 [fd](const cache_val &s) { return s.second.fd == fd; });
109 gdb_assert (it != m_cache.end ());
111 it->second.refcnt--;
112 if (it->second.refcnt == 0)
114 int ret = target_fileio_close (it->second.fd, target_errno);
115 m_cache.erase (it);
116 return ret;
118 else
120 /* Keep the FD open for the other users, return success. */
121 return 0;
125 } /* Anonymous namespace. */
127 /* ROCm-specific inferior data. */
129 struct rocm_so
131 rocm_so (const char *name, std::string unique_name, lm_info_svr4_up lm_info)
132 : name (name),
133 unique_name (std::move (unique_name)),
134 lm_info (std::move (lm_info))
137 std::string name, unique_name;
138 lm_info_svr4_up lm_info;
141 struct solib_info
143 explicit solib_info (inferior *inf)
144 : fd_cache (inf)
147 /* List of code objects loaded into the inferior. */
148 std::vector<rocm_so> solib_list;
150 /* Cache of opened FD in the inferior. */
151 rocm_solib_fd_cache fd_cache;
154 /* Per-inferior data key. */
155 static const registry<inferior>::key<solib_info> rocm_solib_data;
157 static solib_ops rocm_solib_ops;
159 /* Fetch the solib_info data for INF. */
161 static struct solib_info *
162 get_solib_info (inferior *inf)
164 solib_info *info = rocm_solib_data.get (inf);
166 if (info == nullptr)
167 info = rocm_solib_data.emplace (inf, inf);
169 return info;
172 /* Relocate section addresses. */
174 static void
175 rocm_solib_relocate_section_addresses (solib &so,
176 struct target_section *sec)
178 if (!is_amdgpu_arch (gdbarch_from_bfd (so.abfd.get ())))
180 svr4_so_ops.relocate_section_addresses (so, sec);
181 return;
184 auto *li = gdb::checked_static_cast<lm_info_svr4 *> (so.lm_info.get ());
185 sec->addr = sec->addr + li->l_addr;
186 sec->endaddr = sec->endaddr + li->l_addr;
189 static void rocm_update_solib_list ();
191 static void
192 rocm_solib_handle_event ()
194 /* Since we sit on top of svr4_so_ops, we might get called following an event
195 concerning host libraries. We must therefore forward the call. If the
196 event was for a ROCm code object, it will be a no-op. On the other hand,
197 if the event was for host libraries, rocm_update_solib_list will be
198 essentially be a no-op (it will reload the same code object list as was
199 previously loaded). */
200 svr4_so_ops.handle_event ();
202 rocm_update_solib_list ();
205 /* Create so_list objects from rocm_so objects in SOS. */
207 static intrusive_list<solib>
208 so_list_from_rocm_sos (const std::vector<rocm_so> &sos)
210 intrusive_list<solib> dst;
212 for (const rocm_so &so : sos)
214 solib *newobj = new solib;
215 newobj->lm_info = std::make_unique<lm_info_svr4> (*so.lm_info);
217 newobj->so_name = so.name;
218 newobj->so_original_name = so.unique_name;
220 dst.push_back (*newobj);
223 return dst;
226 /* Build a list of `struct solib' objects describing the shared
227 objects currently loaded in the inferior. */
229 static intrusive_list<solib>
230 rocm_solib_current_sos ()
232 /* First, retrieve the host-side shared library list. */
233 intrusive_list<solib> sos = svr4_so_ops.current_sos ();
235 /* Then, the device-side shared library list. */
236 std::vector<rocm_so> &dev_sos = get_solib_info (current_inferior ())->solib_list;
238 if (dev_sos.empty ())
239 return sos;
241 intrusive_list<solib> dev_so_list = so_list_from_rocm_sos (dev_sos);
243 if (sos.empty ())
244 return dev_so_list;
246 /* Append our libraries to the end of the list. */
247 sos.splice (std::move (dev_so_list));
249 return sos;
252 namespace {
254 /* Interface to interact with a ROCm code object stream. */
256 struct rocm_code_object_stream : public gdb_bfd_iovec_base
258 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream);
260 int stat (bfd *abfd, struct stat *sb) final override;
262 ~rocm_code_object_stream () override = default;
264 protected:
265 rocm_code_object_stream () = default;
267 /* Return the size of the object file, or -1 if the size cannot be
268 determined.
270 This is a helper function for stat. */
271 virtual LONGEST size () = 0;
275 rocm_code_object_stream::stat (bfd *, struct stat *sb)
277 const LONGEST size = this->size ();
278 if (size == -1)
279 return -1;
281 memset (sb, '\0', sizeof (struct stat));
282 sb->st_size = size;
283 return 0;
286 /* Interface to a ROCm object stream which is embedded in an ELF file
287 accessible to the debugger. */
289 struct rocm_code_object_stream_file final : rocm_code_object_stream
291 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_file);
293 rocm_code_object_stream_file (inferior *inf, int fd, ULONGEST offset,
294 ULONGEST size);
296 file_ptr read (bfd *abfd, void *buf, file_ptr size,
297 file_ptr offset) override;
299 LONGEST size () override;
301 ~rocm_code_object_stream_file () override;
303 protected:
305 /* The inferior owning this code object stream. */
306 inferior *m_inf;
308 /* The target file descriptor for this stream. */
309 int m_fd;
311 /* The offset of the ELF file image in the target file. */
312 ULONGEST m_offset;
314 /* The size of the ELF file image. The value 0 means that it was
315 unspecified in the URI descriptor. */
316 ULONGEST m_size;
319 rocm_code_object_stream_file::rocm_code_object_stream_file
320 (inferior *inf, int fd, ULONGEST offset, ULONGEST size)
321 : m_inf (inf), m_fd (fd), m_offset (offset), m_size (size)
325 file_ptr
326 rocm_code_object_stream_file::read (bfd *, void *buf, file_ptr size,
327 file_ptr offset)
329 fileio_error target_errno;
330 file_ptr nbytes = 0;
331 while (size > 0)
333 QUIT;
335 file_ptr bytes_read
336 = target_fileio_pread (m_fd, static_cast<gdb_byte *> (buf) + nbytes,
337 size, m_offset + offset + nbytes,
338 &target_errno);
340 if (bytes_read == 0)
341 break;
343 if (bytes_read < 0)
345 errno = fileio_error_to_host (target_errno);
346 bfd_set_error (bfd_error_system_call);
347 return -1;
350 nbytes += bytes_read;
351 size -= bytes_read;
354 return nbytes;
357 LONGEST
358 rocm_code_object_stream_file::size ()
360 if (m_size == 0)
362 fileio_error target_errno;
363 struct stat stat;
364 if (target_fileio_fstat (m_fd, &stat, &target_errno) < 0)
366 errno = fileio_error_to_host (target_errno);
367 bfd_set_error (bfd_error_system_call);
368 return -1;
371 /* Check that the offset is valid. */
372 if (m_offset >= stat.st_size)
374 bfd_set_error (bfd_error_bad_value);
375 return -1;
378 m_size = stat.st_size - m_offset;
381 return m_size;
384 rocm_code_object_stream_file::~rocm_code_object_stream_file ()
386 auto info = get_solib_info (m_inf);
387 fileio_error target_errno;
388 if (info->fd_cache.close (m_fd, &target_errno) != 0)
389 warning (_("Failed to close solib: %s"),
390 strerror (fileio_error_to_host (target_errno)));
393 /* Interface to a code object which lives in the inferior's memory. */
395 struct rocm_code_object_stream_memory final : public rocm_code_object_stream
397 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_memory);
399 rocm_code_object_stream_memory (gdb::byte_vector buffer);
401 file_ptr read (bfd *abfd, void *buf, file_ptr size,
402 file_ptr offset) override;
404 protected:
406 /* Snapshot of the original ELF image taken during load. This is done to
407 support the situation where an inferior uses an in-memory image, and
408 releases or re-uses this memory before GDB is done using it. */
409 gdb::byte_vector m_objfile_image;
411 LONGEST size () override
413 return m_objfile_image.size ();
417 rocm_code_object_stream_memory::rocm_code_object_stream_memory
418 (gdb::byte_vector buffer)
419 : m_objfile_image (std::move (buffer))
423 file_ptr
424 rocm_code_object_stream_memory::read (bfd *, void *buf, file_ptr size,
425 file_ptr offset)
427 if (size > m_objfile_image.size () - offset)
428 size = m_objfile_image.size () - offset;
430 memcpy (buf, m_objfile_image.data () + offset, size);
431 return size;
434 } /* anonymous namespace */
436 static gdb_bfd_iovec_base *
437 rocm_bfd_iovec_open (bfd *abfd, inferior *inferior)
439 std::string_view uri (bfd_get_filename (abfd));
440 std::string_view protocol_delim = "://";
441 size_t protocol_end = uri.find (protocol_delim);
442 std::string protocol (uri.substr (0, protocol_end));
443 protocol_end += protocol_delim.length ();
445 std::transform (protocol.begin (), protocol.end (), protocol.begin (),
446 [] (unsigned char c) { return std::tolower (c); });
448 std::string_view path;
449 size_t path_end = uri.find_first_of ("#?", protocol_end);
450 if (path_end != std::string::npos)
451 path = uri.substr (protocol_end, path_end++ - protocol_end);
452 else
453 path = uri.substr (protocol_end);
455 /* %-decode the string. */
456 std::string decoded_path;
457 decoded_path.reserve (path.length ());
458 for (size_t i = 0; i < path.length (); ++i)
459 if (path[i] == '%'
460 && i < path.length () - 2
461 && std::isxdigit (path[i + 1])
462 && std::isxdigit (path[i + 2]))
464 std::string_view hex_digits = path.substr (i + 1, 2);
465 decoded_path += std::stoi (std::string (hex_digits), 0, 16);
466 i += 2;
468 else
469 decoded_path += path[i];
471 /* Tokenize the query/fragment. */
472 std::vector<std::string_view> tokens;
473 size_t pos, last = path_end;
474 while ((pos = uri.find ('&', last)) != std::string::npos)
476 tokens.emplace_back (uri.substr (last, pos - last));
477 last = pos + 1;
480 if (last != std::string::npos)
481 tokens.emplace_back (uri.substr (last));
483 /* Create a tag-value map from the tokenized query/fragment. */
484 std::unordered_map<std::string_view, std::string_view,
485 gdb::string_view_hash> params;
486 for (std::string_view token : tokens)
488 size_t delim = token.find ('=');
489 if (delim != std::string::npos)
491 std::string_view tag = token.substr (0, delim);
492 std::string_view val = token.substr (delim + 1);
493 params.emplace (tag, val);
499 ULONGEST offset = 0;
500 ULONGEST size = 0;
502 auto try_strtoulst = [] (std::string_view v)
504 errno = 0;
505 ULONGEST value = strtoulst (v.data (), nullptr, 0);
506 if (errno != 0)
508 /* The actual message doesn't matter, the exception is caught
509 below, transformed in a BFD error, and the message is lost. */
510 error (_("Failed to parse integer."));
513 return value;
516 auto offset_it = params.find ("offset");
517 if (offset_it != params.end ())
518 offset = try_strtoulst (offset_it->second);
520 auto size_it = params.find ("size");
521 if (size_it != params.end ())
523 size = try_strtoulst (size_it->second);
524 if (size == 0)
525 error (_("Invalid size value"));
528 if (protocol == "file")
530 auto info = get_solib_info (inferior);
531 fileio_error target_errno;
532 int fd = info->fd_cache.open (decoded_path, &target_errno);
534 if (fd == -1)
536 errno = fileio_error_to_host (target_errno);
537 bfd_set_error (bfd_error_system_call);
538 return nullptr;
541 return new rocm_code_object_stream_file (inferior, fd, offset,
542 size);
545 if (protocol == "memory")
547 ULONGEST pid = try_strtoulst (path);
548 if (pid != inferior->pid)
550 warning (_("`%s': code object is from another inferior"),
551 std::string (uri).c_str ());
552 bfd_set_error (bfd_error_bad_value);
553 return nullptr;
556 gdb::byte_vector buffer (size);
557 if (target_read_memory (offset, buffer.data (), size) != 0)
559 warning (_("Failed to copy the code object from the inferior"));
560 bfd_set_error (bfd_error_bad_value);
561 return nullptr;
564 return new rocm_code_object_stream_memory (std::move (buffer));
567 warning (_("`%s': protocol not supported: %s"),
568 std::string (uri).c_str (), protocol.c_str ());
569 bfd_set_error (bfd_error_bad_value);
570 return nullptr;
572 catch (const gdb_exception_quit &ex)
574 set_quit_flag ();
575 bfd_set_error (bfd_error_bad_value);
576 return nullptr;
578 catch (const gdb_exception &ex)
580 bfd_set_error (bfd_error_bad_value);
581 return nullptr;
585 static gdb_bfd_ref_ptr
586 rocm_solib_bfd_open (const char *pathname)
588 /* Handle regular files with SVR4 open. */
589 if (strstr (pathname, "://") == nullptr)
590 return svr4_so_ops.bfd_open (pathname);
592 auto open = [] (bfd *nbfd) -> gdb_bfd_iovec_base *
594 return rocm_bfd_iovec_open (nbfd, current_inferior ());
597 gdb_bfd_ref_ptr abfd = gdb_bfd_openr_iovec (pathname, "elf64-amdgcn", open);
599 if (abfd == nullptr)
600 error (_("Could not open `%s' as an executable file: %s"), pathname,
601 bfd_errmsg (bfd_get_error ()));
603 /* Check bfd format. */
604 if (!bfd_check_format (abfd.get (), bfd_object))
605 error (_("`%s': not in executable format: %s"),
606 bfd_get_filename (abfd.get ()), bfd_errmsg (bfd_get_error ()));
608 unsigned char osabi = elf_elfheader (abfd)->e_ident[EI_OSABI];
609 unsigned char osabiversion = elf_elfheader (abfd)->e_ident[EI_ABIVERSION];
611 /* Check that the code object is using the HSA OS ABI. */
612 if (osabi != ELFOSABI_AMDGPU_HSA)
613 error (_("`%s': ELF file OS ABI is not supported (%d)."),
614 bfd_get_filename (abfd.get ()), osabi);
616 /* We support HSA code objects V3 and greater. */
617 if (osabiversion < ELFABIVERSION_AMDGPU_HSA_V3)
618 error (_("`%s': ELF file HSA OS ABI version is not supported (%d)."),
619 bfd_get_filename (abfd.get ()), osabiversion);
621 /* For GDB to be able to use this solib, the exact AMDGPU processor type
622 must be supported by both BFD and the amd-dbgapi library. */
623 const unsigned char gfx_arch
624 = elf_elfheader (abfd)->e_flags & EF_AMDGPU_MACH ;
625 const bfd_arch_info_type *bfd_arch_info
626 = bfd_lookup_arch (bfd_arch_amdgcn, gfx_arch);
628 amd_dbgapi_architecture_id_t architecture_id;
629 amd_dbgapi_status_t dbgapi_query_arch
630 = amd_dbgapi_get_architecture (gfx_arch, &architecture_id);
632 if (dbgapi_query_arch != AMD_DBGAPI_STATUS_SUCCESS
633 || bfd_arch_info == nullptr)
635 if (dbgapi_query_arch != AMD_DBGAPI_STATUS_SUCCESS
636 && bfd_arch_info == nullptr)
638 /* Neither of the libraries knows about this arch, so we cannot
639 provide a human readable name for it. */
640 error (_("'%s': AMDGCN architecture %#02x is not supported."),
641 bfd_get_filename (abfd.get ()), gfx_arch);
643 else if (dbgapi_query_arch != AMD_DBGAPI_STATUS_SUCCESS)
645 gdb_assert (bfd_arch_info != nullptr);
646 error (_("'%s': AMDGCN architecture %s not supported by "
647 "amd-dbgapi."),
648 bfd_get_filename (abfd.get ()),
649 bfd_arch_info->printable_name);
651 else
653 gdb_assert (dbgapi_query_arch == AMD_DBGAPI_STATUS_SUCCESS);
654 char *arch_name;
655 if (amd_dbgapi_architecture_get_info
656 (architecture_id, AMD_DBGAPI_ARCHITECTURE_INFO_NAME,
657 sizeof (arch_name), &arch_name) != AMD_DBGAPI_STATUS_SUCCESS)
658 error ("amd_dbgapi_architecture_get_info call failed for arch "
659 "%#02x.", gfx_arch);
660 gdb::unique_xmalloc_ptr<char> arch_name_cleaner (arch_name);
662 error (_("'%s': AMDGCN architecture %s not supported."),
663 bfd_get_filename (abfd.get ()),
664 arch_name);
668 gdb_assert (gdbarch_from_bfd (abfd.get ()) != nullptr);
669 gdb_assert (is_amdgpu_arch (gdbarch_from_bfd (abfd.get ())));
671 return abfd;
674 static void
675 rocm_solib_create_inferior_hook (int from_tty)
677 get_solib_info (current_inferior ())->solib_list.clear ();
679 svr4_so_ops.solib_create_inferior_hook (from_tty);
682 static void
683 rocm_update_solib_list ()
685 inferior *inf = current_inferior ();
687 amd_dbgapi_process_id_t process_id = get_amd_dbgapi_process_id (inf);
688 if (process_id.handle == AMD_DBGAPI_PROCESS_NONE.handle)
689 return;
691 solib_info *info = get_solib_info (inf);
693 info->solib_list.clear ();
694 std::vector<rocm_so> &sos = info->solib_list;
696 amd_dbgapi_code_object_id_t *code_object_list;
697 size_t count;
699 amd_dbgapi_status_t status
700 = amd_dbgapi_process_code_object_list (process_id, &count,
701 &code_object_list, nullptr);
702 if (status != AMD_DBGAPI_STATUS_SUCCESS)
704 warning (_("amd_dbgapi_process_code_object_list failed (%s)"),
705 get_status_string (status));
706 return;
709 for (size_t i = 0; i < count; ++i)
711 CORE_ADDR l_addr;
712 char *uri_bytes;
714 status = amd_dbgapi_code_object_get_info
715 (code_object_list[i], AMD_DBGAPI_CODE_OBJECT_INFO_LOAD_ADDRESS,
716 sizeof (l_addr), &l_addr);
717 if (status != AMD_DBGAPI_STATUS_SUCCESS)
718 continue;
720 status = amd_dbgapi_code_object_get_info
721 (code_object_list[i], AMD_DBGAPI_CODE_OBJECT_INFO_URI_NAME,
722 sizeof (uri_bytes), &uri_bytes);
723 if (status != AMD_DBGAPI_STATUS_SUCCESS)
724 continue;
726 gdb::unique_xmalloc_ptr<char> uri_bytes_holder (uri_bytes);
728 lm_info_svr4_up li = std::make_unique<lm_info_svr4> ();
729 li->l_addr = l_addr;
731 /* Generate a unique name so that code objects with the same URI but
732 different load addresses are seen by gdb core as different shared
733 objects. */
734 std::string unique_name
735 = string_printf ("code_object_%ld", code_object_list[i].handle);
737 sos.emplace_back (uri_bytes, std::move (unique_name), std::move (li));
740 xfree (code_object_list);
742 if (rocm_solib_ops.current_sos == NULL)
744 /* Override what we need to. */
745 rocm_solib_ops = svr4_so_ops;
746 rocm_solib_ops.current_sos = rocm_solib_current_sos;
747 rocm_solib_ops.solib_create_inferior_hook
748 = rocm_solib_create_inferior_hook;
749 rocm_solib_ops.bfd_open = rocm_solib_bfd_open;
750 rocm_solib_ops.relocate_section_addresses
751 = rocm_solib_relocate_section_addresses;
752 rocm_solib_ops.handle_event = rocm_solib_handle_event;
754 /* Engage the ROCm so_ops. */
755 set_gdbarch_so_ops (current_inferior ()->arch (), &rocm_solib_ops);
759 static void
760 rocm_solib_target_inferior_created (inferior *inf)
762 get_solib_info (inf)->solib_list.clear ();
764 rocm_update_solib_list ();
766 /* Force GDB to reload the solibs. */
767 current_inferior ()->pspace->clear_solib_cache ();
768 solib_add (nullptr, 0, auto_solib_add);
771 /* -Wmissing-prototypes */
772 extern initialize_file_ftype _initialize_rocm_solib;
774 void
775 _initialize_rocm_solib ()
777 /* The dependency on the amd-dbgapi exists because solib-rocm's
778 inferior_created observer needs amd-dbgapi to have attached the process,
779 which happens in amd_dbgapi_target's inferior_created observer. */
780 gdb::observers::inferior_created.attach
781 (rocm_solib_target_inferior_created,
782 "solib-rocm",
783 { &get_amd_dbgapi_target_inferior_created_observer_token () });