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 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
);
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 ())
241 intrusive_list
<solib
> dev_so_list
= so_list_from_rocm_sos (dev_sos
);
246 /* Append our libraries to the end of the list. */
247 sos
.splice (std::move (dev_so_list
));
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;
265 rocm_code_object_stream () = default;
267 /* Return the size of the object file, or -1 if the size cannot be
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 ();
281 memset (sb
, '\0', sizeof (struct stat
));
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
,
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
;
305 /* The inferior owning this code object stream. */
308 /* The target file descriptor for this stream. */
311 /* The offset of the ELF file image in the target file. */
314 /* The size of the ELF file image. The value 0 means that it was
315 unspecified in the URI descriptor. */
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
)
326 rocm_code_object_stream_file::read (bfd
*, void *buf
, file_ptr size
,
329 fileio_error target_errno
;
336 = target_fileio_pread (m_fd
, static_cast<gdb_byte
*> (buf
) + nbytes
,
337 size
, m_offset
+ offset
+ nbytes
,
345 errno
= fileio_error_to_host (target_errno
);
346 bfd_set_error (bfd_error_system_call
);
350 nbytes
+= bytes_read
;
358 rocm_code_object_stream_file::size ()
362 fileio_error target_errno
;
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
);
371 /* Check that the offset is valid. */
372 if (m_offset
>= stat
.st_size
)
374 bfd_set_error (bfd_error_bad_value
);
378 m_size
= stat
.st_size
- m_offset
;
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
;
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
))
424 rocm_code_object_stream_memory::read (bfd
*, void *buf
, file_ptr size
,
427 if (size
> m_objfile_image
.size () - offset
)
428 size
= m_objfile_image
.size () - offset
;
430 memcpy (buf
, m_objfile_image
.data () + offset
, 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
);
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
)
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);
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
));
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
);
502 auto try_strtoulst
= [] (std::string_view v
)
505 ULONGEST value
= strtoulst (v
.data (), nullptr, 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."));
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
);
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
);
536 errno
= fileio_error_to_host (target_errno
);
537 bfd_set_error (bfd_error_system_call
);
541 return new rocm_code_object_stream_file (inferior
, fd
, offset
,
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
);
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
);
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
);
572 catch (const gdb_exception_quit
&ex
)
575 bfd_set_error (bfd_error_bad_value
);
578 catch (const gdb_exception
&ex
)
580 bfd_set_error (bfd_error_bad_value
);
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
);
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 "
648 bfd_get_filename (abfd
.get ()),
649 bfd_arch_info
->printable_name
);
653 gdb_assert (dbgapi_query_arch
== AMD_DBGAPI_STATUS_SUCCESS
);
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 "
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 ()),
668 gdb_assert (gdbarch_from_bfd (abfd
.get ()) != nullptr);
669 gdb_assert (is_amdgpu_arch (gdbarch_from_bfd (abfd
.get ())));
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
);
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
)
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
;
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
));
709 for (size_t i
= 0; i
< count
; ++i
)
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
)
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
)
726 gdb::unique_xmalloc_ptr
<char> uri_bytes_holder (uri_bytes
);
728 lm_info_svr4_up li
= std::make_unique
<lm_info_svr4
> ();
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
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
);
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
;
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
,
783 { &get_amd_dbgapi_target_inferior_created_observer_token () });