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"
25 #include "elf/amdgpu.h"
26 #include "event-top.h"
27 #include "gdbsupport/fileio.h"
29 #include "observable.h"
31 #include "solib-svr4.h"
35 #include <unordered_map>
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
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
57 On success, return 0. On error, return -1 and set TARGET_ERRNO. */
58 int close (int fd
, fileio_error
*target_errno
);
63 DISABLE_COPY_AND_ASSIGN (refcnt_fd
);
64 refcnt_fd (int fd
, int refcnt
) : fd (fd
), refcnt (refcnt
) {}
71 std::unordered_map
<std::string
, refcnt_fd
> m_cache
;
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. */
83 = target_fileio_open (m_inferior
, filename
.c_str (), FILEIO_O_RDONLY
,
84 false, 0, target_errno
);
86 m_cache
.emplace (std::piecewise_construct
,
87 std::forward_as_tuple (filename
),
88 std::forward_as_tuple (fd
, 1));
93 /* The file is already opened. Increment the refcnt and return the
96 gdb_assert (it
->second
.fd
!= -1);
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
;
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 ());
112 if (it
->second
.refcnt
== 0)
114 int ret
= target_fileio_close (it
->second
.fd
, target_errno
);
120 /* Keep the FD open for the other users, return success. */
125 } /* Anonymous namespace. */
127 /* ROCm-specific inferior data. */
131 rocm_so (const char *name
, std::string unique_name
, lm_info_svr4_up lm_info
)
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
;
143 explicit solib_info (inferior
*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
);
167 info
= rocm_solib_data
.emplace (inf
, inf
);
172 /* Relocate section addresses. */
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
);
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 ();
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
;
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 ())
239 owning_intrusive_list
<solib
> dev_so_list
= so_list_from_rocm_sos (dev_sos
);
244 /* Append our libraries to the end of the list. */
245 sos
.splice (std::move (dev_so_list
));
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;
263 rocm_code_object_stream () = default;
265 /* Return the size of the object file, or -1 if the size cannot be
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 ();
279 memset (sb
, '\0', sizeof (struct stat
));
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
,
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
;
303 /* The inferior owning this code object stream. */
306 /* The target file descriptor for this stream. */
309 /* The offset of the ELF file image in the target file. */
312 /* The size of the ELF file image. The value 0 means that it was
313 unspecified in the URI descriptor. */
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
)
324 rocm_code_object_stream_file::read (bfd
*, void *buf
, file_ptr size
,
327 fileio_error target_errno
;
334 = target_fileio_pread (m_fd
, static_cast<gdb_byte
*> (buf
) + nbytes
,
335 size
, m_offset
+ offset
+ nbytes
,
343 errno
= fileio_error_to_host (target_errno
);
344 bfd_set_error (bfd_error_system_call
);
348 nbytes
+= bytes_read
;
356 rocm_code_object_stream_file::size ()
360 fileio_error target_errno
;
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
);
369 /* Check that the offset is valid. */
370 if (m_offset
>= stat
.st_size
)
372 bfd_set_error (bfd_error_bad_value
);
376 m_size
= stat
.st_size
- m_offset
;
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
;
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
))
422 rocm_code_object_stream_memory::read (bfd
*, void *buf
, file_ptr size
,
425 if (size
> m_objfile_image
.size () - offset
)
426 size
= m_objfile_image
.size () - offset
;
428 memcpy (buf
, m_objfile_image
.data () + offset
, 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
);
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
)
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);
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
));
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
);
500 auto try_strtoulst
= [] (std::string_view v
)
503 ULONGEST value
= strtoulst (v
.data (), nullptr, 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."));
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
);
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
);
534 errno
= fileio_error_to_host (target_errno
);
535 bfd_set_error (bfd_error_system_call
);
539 return new rocm_code_object_stream_file (inferior
, fd
, offset
,
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
);
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
);
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
);
570 catch (const gdb_exception_quit
&ex
)
573 bfd_set_error (bfd_error_bad_value
);
576 catch (const gdb_exception
&ex
)
578 bfd_set_error (bfd_error_bad_value
);
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
);
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 "
646 bfd_get_filename (abfd
.get ()),
647 bfd_arch_info
->printable_name
);
651 gdb_assert (dbgapi_query_arch
== AMD_DBGAPI_STATUS_SUCCESS
);
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 "
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 ()),
666 gdb_assert (gdbarch_from_bfd (abfd
.get ()) != nullptr);
667 gdb_assert (is_amdgpu_arch (gdbarch_from_bfd (abfd
.get ())));
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
);
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
)
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
;
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
));
707 for (size_t i
= 0; i
< count
; ++i
)
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
)
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
)
724 gdb::unique_xmalloc_ptr
<char> uri_bytes_holder (uri_bytes
);
726 lm_info_svr4_up li
= std::make_unique
<lm_info_svr4
> ();
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
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
);
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
;
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
,
781 { &get_amd_dbgapi_target_inferior_created_observer_token () });