Add translations for various sub-directories
[binutils-gdb.git] / gdb / solib-rocm.c
blob156b36a42634112797114b159bcecdfd9f08c9a5
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 owning_intrusive_list<solib>
208 so_list_from_rocm_sos (const std::vector<rocm_so> &sos)
210 owning_intrusive_list<solib> dst;
212 for (const rocm_so &so : sos)
214 auto &newobj = dst.emplace_back ();
216 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;
221 return dst;
224 /* Build a list of `struct solib' objects describing the shared
225 objects currently loaded in the inferior. */
227 static owning_intrusive_list<solib>
228 rocm_solib_current_sos ()
230 /* First, retrieve the host-side shared library list. */
231 owning_intrusive_list<solib> sos = svr4_so_ops.current_sos ();
233 /* Then, the device-side shared library list. */
234 std::vector<rocm_so> &dev_sos = get_solib_info (current_inferior ())->solib_list;
236 if (dev_sos.empty ())
237 return sos;
239 owning_intrusive_list<solib> dev_so_list = so_list_from_rocm_sos (dev_sos);
241 if (sos.empty ())
242 return dev_so_list;
244 /* Append our libraries to the end of the list. */
245 sos.splice (std::move (dev_so_list));
247 return sos;
250 namespace {
252 /* Interface to interact with a ROCm code object stream. */
254 struct rocm_code_object_stream : public gdb_bfd_iovec_base
256 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream);
258 int stat (bfd *abfd, struct stat *sb) final override;
260 ~rocm_code_object_stream () override = default;
262 protected:
263 rocm_code_object_stream () = default;
265 /* Return the size of the object file, or -1 if the size cannot be
266 determined.
268 This is a helper function for stat. */
269 virtual LONGEST size () = 0;
273 rocm_code_object_stream::stat (bfd *, struct stat *sb)
275 const LONGEST size = this->size ();
276 if (size == -1)
277 return -1;
279 memset (sb, '\0', sizeof (struct stat));
280 sb->st_size = size;
281 return 0;
284 /* Interface to a ROCm object stream which is embedded in an ELF file
285 accessible to the debugger. */
287 struct rocm_code_object_stream_file final : rocm_code_object_stream
289 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_file);
291 rocm_code_object_stream_file (inferior *inf, int fd, ULONGEST offset,
292 ULONGEST size);
294 file_ptr read (bfd *abfd, void *buf, file_ptr size,
295 file_ptr offset) override;
297 LONGEST size () override;
299 ~rocm_code_object_stream_file () override;
301 protected:
303 /* The inferior owning this code object stream. */
304 inferior *m_inf;
306 /* The target file descriptor for this stream. */
307 int m_fd;
309 /* The offset of the ELF file image in the target file. */
310 ULONGEST m_offset;
312 /* The size of the ELF file image. The value 0 means that it was
313 unspecified in the URI descriptor. */
314 ULONGEST m_size;
317 rocm_code_object_stream_file::rocm_code_object_stream_file
318 (inferior *inf, int fd, ULONGEST offset, ULONGEST size)
319 : m_inf (inf), m_fd (fd), m_offset (offset), m_size (size)
323 file_ptr
324 rocm_code_object_stream_file::read (bfd *, void *buf, file_ptr size,
325 file_ptr offset)
327 fileio_error target_errno;
328 file_ptr nbytes = 0;
329 while (size > 0)
331 QUIT;
333 file_ptr bytes_read
334 = target_fileio_pread (m_fd, static_cast<gdb_byte *> (buf) + nbytes,
335 size, m_offset + offset + nbytes,
336 &target_errno);
338 if (bytes_read == 0)
339 break;
341 if (bytes_read < 0)
343 errno = fileio_error_to_host (target_errno);
344 bfd_set_error (bfd_error_system_call);
345 return -1;
348 nbytes += bytes_read;
349 size -= bytes_read;
352 return nbytes;
355 LONGEST
356 rocm_code_object_stream_file::size ()
358 if (m_size == 0)
360 fileio_error target_errno;
361 struct stat stat;
362 if (target_fileio_fstat (m_fd, &stat, &target_errno) < 0)
364 errno = fileio_error_to_host (target_errno);
365 bfd_set_error (bfd_error_system_call);
366 return -1;
369 /* Check that the offset is valid. */
370 if (m_offset >= stat.st_size)
372 bfd_set_error (bfd_error_bad_value);
373 return -1;
376 m_size = stat.st_size - m_offset;
379 return m_size;
382 rocm_code_object_stream_file::~rocm_code_object_stream_file ()
384 auto info = get_solib_info (m_inf);
385 fileio_error target_errno;
386 if (info->fd_cache.close (m_fd, &target_errno) != 0)
387 warning (_("Failed to close solib: %s"),
388 strerror (fileio_error_to_host (target_errno)));
391 /* Interface to a code object which lives in the inferior's memory. */
393 struct rocm_code_object_stream_memory final : public rocm_code_object_stream
395 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_memory);
397 rocm_code_object_stream_memory (gdb::byte_vector buffer);
399 file_ptr read (bfd *abfd, void *buf, file_ptr size,
400 file_ptr offset) override;
402 protected:
404 /* Snapshot of the original ELF image taken during load. This is done to
405 support the situation where an inferior uses an in-memory image, and
406 releases or re-uses this memory before GDB is done using it. */
407 gdb::byte_vector m_objfile_image;
409 LONGEST size () override
411 return m_objfile_image.size ();
415 rocm_code_object_stream_memory::rocm_code_object_stream_memory
416 (gdb::byte_vector buffer)
417 : m_objfile_image (std::move (buffer))
421 file_ptr
422 rocm_code_object_stream_memory::read (bfd *, void *buf, file_ptr size,
423 file_ptr offset)
425 if (size > m_objfile_image.size () - offset)
426 size = m_objfile_image.size () - offset;
428 memcpy (buf, m_objfile_image.data () + offset, size);
429 return size;
432 } /* anonymous namespace */
434 static gdb_bfd_iovec_base *
435 rocm_bfd_iovec_open (bfd *abfd, inferior *inferior)
437 std::string_view uri (bfd_get_filename (abfd));
438 std::string_view protocol_delim = "://";
439 size_t protocol_end = uri.find (protocol_delim);
440 std::string protocol (uri.substr (0, protocol_end));
441 protocol_end += protocol_delim.length ();
443 std::transform (protocol.begin (), protocol.end (), protocol.begin (),
444 [] (unsigned char c) { return std::tolower (c); });
446 std::string_view path;
447 size_t path_end = uri.find_first_of ("#?", protocol_end);
448 if (path_end != std::string::npos)
449 path = uri.substr (protocol_end, path_end++ - protocol_end);
450 else
451 path = uri.substr (protocol_end);
453 /* %-decode the string. */
454 std::string decoded_path;
455 decoded_path.reserve (path.length ());
456 for (size_t i = 0; i < path.length (); ++i)
457 if (path[i] == '%'
458 && i < path.length () - 2
459 && std::isxdigit (path[i + 1])
460 && std::isxdigit (path[i + 2]))
462 std::string_view hex_digits = path.substr (i + 1, 2);
463 decoded_path += std::stoi (std::string (hex_digits), 0, 16);
464 i += 2;
466 else
467 decoded_path += path[i];
469 /* Tokenize the query/fragment. */
470 std::vector<std::string_view> tokens;
471 size_t pos, last = path_end;
472 while ((pos = uri.find ('&', last)) != std::string::npos)
474 tokens.emplace_back (uri.substr (last, pos - last));
475 last = pos + 1;
478 if (last != std::string::npos)
479 tokens.emplace_back (uri.substr (last));
481 /* Create a tag-value map from the tokenized query/fragment. */
482 std::unordered_map<std::string_view, std::string_view,
483 gdb::string_view_hash> params;
484 for (std::string_view token : tokens)
486 size_t delim = token.find ('=');
487 if (delim != std::string::npos)
489 std::string_view tag = token.substr (0, delim);
490 std::string_view val = token.substr (delim + 1);
491 params.emplace (tag, val);
497 ULONGEST offset = 0;
498 ULONGEST size = 0;
500 auto try_strtoulst = [] (std::string_view v)
502 errno = 0;
503 ULONGEST value = strtoulst (v.data (), nullptr, 0);
504 if (errno != 0)
506 /* The actual message doesn't matter, the exception is caught
507 below, transformed in a BFD error, and the message is lost. */
508 error (_("Failed to parse integer."));
511 return value;
514 auto offset_it = params.find ("offset");
515 if (offset_it != params.end ())
516 offset = try_strtoulst (offset_it->second);
518 auto size_it = params.find ("size");
519 if (size_it != params.end ())
521 size = try_strtoulst (size_it->second);
522 if (size == 0)
523 error (_("Invalid size value"));
526 if (protocol == "file")
528 auto info = get_solib_info (inferior);
529 fileio_error target_errno;
530 int fd = info->fd_cache.open (decoded_path, &target_errno);
532 if (fd == -1)
534 errno = fileio_error_to_host (target_errno);
535 bfd_set_error (bfd_error_system_call);
536 return nullptr;
539 return new rocm_code_object_stream_file (inferior, fd, offset,
540 size);
543 if (protocol == "memory")
545 ULONGEST pid = try_strtoulst (path);
546 if (pid != inferior->pid)
548 warning (_("`%s': code object is from another inferior"),
549 std::string (uri).c_str ());
550 bfd_set_error (bfd_error_bad_value);
551 return nullptr;
554 gdb::byte_vector buffer (size);
555 if (target_read_memory (offset, buffer.data (), size) != 0)
557 warning (_("Failed to copy the code object from the inferior"));
558 bfd_set_error (bfd_error_bad_value);
559 return nullptr;
562 return new rocm_code_object_stream_memory (std::move (buffer));
565 warning (_("`%s': protocol not supported: %s"),
566 std::string (uri).c_str (), protocol.c_str ());
567 bfd_set_error (bfd_error_bad_value);
568 return nullptr;
570 catch (const gdb_exception_quit &ex)
572 set_quit_flag ();
573 bfd_set_error (bfd_error_bad_value);
574 return nullptr;
576 catch (const gdb_exception &ex)
578 bfd_set_error (bfd_error_bad_value);
579 return nullptr;
583 static gdb_bfd_ref_ptr
584 rocm_solib_bfd_open (const char *pathname)
586 /* Handle regular files with SVR4 open. */
587 if (strstr (pathname, "://") == nullptr)
588 return svr4_so_ops.bfd_open (pathname);
590 auto open = [] (bfd *nbfd) -> gdb_bfd_iovec_base *
592 return rocm_bfd_iovec_open (nbfd, current_inferior ());
595 gdb_bfd_ref_ptr abfd = gdb_bfd_openr_iovec (pathname, "elf64-amdgcn", open);
597 if (abfd == nullptr)
598 error (_("Could not open `%s' as an executable file: %s"), pathname,
599 bfd_errmsg (bfd_get_error ()));
601 /* Check bfd format. */
602 if (!bfd_check_format (abfd.get (), bfd_object))
603 error (_("`%s': not in executable format: %s"),
604 bfd_get_filename (abfd.get ()), bfd_errmsg (bfd_get_error ()));
606 unsigned char osabi = elf_elfheader (abfd)->e_ident[EI_OSABI];
607 unsigned char osabiversion = elf_elfheader (abfd)->e_ident[EI_ABIVERSION];
609 /* Check that the code object is using the HSA OS ABI. */
610 if (osabi != ELFOSABI_AMDGPU_HSA)
611 error (_("`%s': ELF file OS ABI is not supported (%d)."),
612 bfd_get_filename (abfd.get ()), osabi);
614 /* We support HSA code objects V3 and greater. */
615 if (osabiversion < ELFABIVERSION_AMDGPU_HSA_V3)
616 error (_("`%s': ELF file HSA OS ABI version is not supported (%d)."),
617 bfd_get_filename (abfd.get ()), osabiversion);
619 /* For GDB to be able to use this solib, the exact AMDGPU processor type
620 must be supported by both BFD and the amd-dbgapi library. */
621 const unsigned char gfx_arch
622 = elf_elfheader (abfd)->e_flags & EF_AMDGPU_MACH ;
623 const bfd_arch_info_type *bfd_arch_info
624 = bfd_lookup_arch (bfd_arch_amdgcn, gfx_arch);
626 amd_dbgapi_architecture_id_t architecture_id;
627 amd_dbgapi_status_t dbgapi_query_arch
628 = amd_dbgapi_get_architecture (gfx_arch, &architecture_id);
630 if (dbgapi_query_arch != AMD_DBGAPI_STATUS_SUCCESS
631 || bfd_arch_info == nullptr)
633 if (dbgapi_query_arch != AMD_DBGAPI_STATUS_SUCCESS
634 && bfd_arch_info == nullptr)
636 /* Neither of the libraries knows about this arch, so we cannot
637 provide a human readable name for it. */
638 error (_("'%s': AMDGCN architecture %#02x is not supported."),
639 bfd_get_filename (abfd.get ()), gfx_arch);
641 else if (dbgapi_query_arch != AMD_DBGAPI_STATUS_SUCCESS)
643 gdb_assert (bfd_arch_info != nullptr);
644 error (_("'%s': AMDGCN architecture %s not supported by "
645 "amd-dbgapi."),
646 bfd_get_filename (abfd.get ()),
647 bfd_arch_info->printable_name);
649 else
651 gdb_assert (dbgapi_query_arch == AMD_DBGAPI_STATUS_SUCCESS);
652 char *arch_name;
653 if (amd_dbgapi_architecture_get_info
654 (architecture_id, AMD_DBGAPI_ARCHITECTURE_INFO_NAME,
655 sizeof (arch_name), &arch_name) != AMD_DBGAPI_STATUS_SUCCESS)
656 error ("amd_dbgapi_architecture_get_info call failed for arch "
657 "%#02x.", gfx_arch);
658 gdb::unique_xmalloc_ptr<char> arch_name_cleaner (arch_name);
660 error (_("'%s': AMDGCN architecture %s not supported."),
661 bfd_get_filename (abfd.get ()),
662 arch_name);
666 gdb_assert (gdbarch_from_bfd (abfd.get ()) != nullptr);
667 gdb_assert (is_amdgpu_arch (gdbarch_from_bfd (abfd.get ())));
669 return abfd;
672 static void
673 rocm_solib_create_inferior_hook (int from_tty)
675 get_solib_info (current_inferior ())->solib_list.clear ();
677 svr4_so_ops.solib_create_inferior_hook (from_tty);
680 static void
681 rocm_update_solib_list ()
683 inferior *inf = current_inferior ();
685 amd_dbgapi_process_id_t process_id = get_amd_dbgapi_process_id (inf);
686 if (process_id.handle == AMD_DBGAPI_PROCESS_NONE.handle)
687 return;
689 solib_info *info = get_solib_info (inf);
691 info->solib_list.clear ();
692 std::vector<rocm_so> &sos = info->solib_list;
694 amd_dbgapi_code_object_id_t *code_object_list;
695 size_t count;
697 amd_dbgapi_status_t status
698 = amd_dbgapi_process_code_object_list (process_id, &count,
699 &code_object_list, nullptr);
700 if (status != AMD_DBGAPI_STATUS_SUCCESS)
702 warning (_("amd_dbgapi_process_code_object_list failed (%s)"),
703 get_status_string (status));
704 return;
707 for (size_t i = 0; i < count; ++i)
709 CORE_ADDR l_addr;
710 char *uri_bytes;
712 status = amd_dbgapi_code_object_get_info
713 (code_object_list[i], AMD_DBGAPI_CODE_OBJECT_INFO_LOAD_ADDRESS,
714 sizeof (l_addr), &l_addr);
715 if (status != AMD_DBGAPI_STATUS_SUCCESS)
716 continue;
718 status = amd_dbgapi_code_object_get_info
719 (code_object_list[i], AMD_DBGAPI_CODE_OBJECT_INFO_URI_NAME,
720 sizeof (uri_bytes), &uri_bytes);
721 if (status != AMD_DBGAPI_STATUS_SUCCESS)
722 continue;
724 gdb::unique_xmalloc_ptr<char> uri_bytes_holder (uri_bytes);
726 lm_info_svr4_up li = std::make_unique<lm_info_svr4> ();
727 li->l_addr = l_addr;
729 /* Generate a unique name so that code objects with the same URI but
730 different load addresses are seen by gdb core as different shared
731 objects. */
732 std::string unique_name
733 = string_printf ("code_object_%ld", code_object_list[i].handle);
735 sos.emplace_back (uri_bytes, std::move (unique_name), std::move (li));
738 xfree (code_object_list);
740 if (rocm_solib_ops.current_sos == NULL)
742 /* Override what we need to. */
743 rocm_solib_ops = svr4_so_ops;
744 rocm_solib_ops.current_sos = rocm_solib_current_sos;
745 rocm_solib_ops.solib_create_inferior_hook
746 = rocm_solib_create_inferior_hook;
747 rocm_solib_ops.bfd_open = rocm_solib_bfd_open;
748 rocm_solib_ops.relocate_section_addresses
749 = rocm_solib_relocate_section_addresses;
750 rocm_solib_ops.handle_event = rocm_solib_handle_event;
752 /* Engage the ROCm so_ops. */
753 set_gdbarch_so_ops (current_inferior ()->arch (), &rocm_solib_ops);
757 static void
758 rocm_solib_target_inferior_created (inferior *inf)
760 get_solib_info (inf)->solib_list.clear ();
762 rocm_update_solib_list ();
764 /* Force GDB to reload the solibs. */
765 current_inferior ()->pspace->clear_solib_cache ();
766 solib_add (nullptr, 0, auto_solib_add);
769 /* -Wmissing-prototypes */
770 extern initialize_file_ftype _initialize_rocm_solib;
772 void
773 _initialize_rocm_solib ()
775 /* The dependency on the amd-dbgapi exists because solib-rocm's
776 inferior_created observer needs amd-dbgapi to have attached the process,
777 which happens in amd_dbgapi_target's inferior_created observer. */
778 gdb::observers::inferior_created.attach
779 (rocm_solib_target_inferior_created,
780 "solib-rocm",
781 { &get_amd_dbgapi_target_inferior_created_observer_token () });