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 "gdbsupport/fileio.h"
28 #include "observable.h"
30 #include "solib-svr4.h"
34 #include <unordered_map>
38 /* Per inferior cache of opened file descriptors. */
39 struct rocm_solib_fd_cache
41 explicit rocm_solib_fd_cache (inferior
*inf
) : m_inferior (inf
) {}
42 DISABLE_COPY_AND_ASSIGN (rocm_solib_fd_cache
);
44 /* Return a read-only file descriptor to FILENAME and increment the
45 associated reference count.
47 Open the file FILENAME if it is not already opened, reuse the existing file
50 On error -1 is returned, and TARGET_ERRNO is set. */
51 int open (const std::string
&filename
, fileio_error
*target_errno
);
53 /* Decrement the reference count to FD and close FD if the reference count
56 On success, return 0. On error, return -1 and set TARGET_ERRNO. */
57 int close (int fd
, fileio_error
*target_errno
);
62 DISABLE_COPY_AND_ASSIGN (refcnt_fd
);
63 refcnt_fd (int fd
, int refcnt
) : fd (fd
), refcnt (refcnt
) {}
70 std::unordered_map
<std::string
, refcnt_fd
> m_cache
;
74 rocm_solib_fd_cache::open (const std::string
&filename
,
75 fileio_error
*target_errno
)
77 auto it
= m_cache
.find (filename
);
78 if (it
== m_cache
.end ())
80 /* The file is not yet opened on the target. */
82 = target_fileio_open (m_inferior
, filename
.c_str (), FILEIO_O_RDONLY
,
83 false, 0, target_errno
);
85 m_cache
.emplace (std::piecewise_construct
,
86 std::forward_as_tuple (filename
),
87 std::forward_as_tuple (fd
, 1));
92 /* The file is already opened. Increment the refcnt and return the
95 gdb_assert (it
->second
.fd
!= -1);
101 rocm_solib_fd_cache::close (int fd
, fileio_error
*target_errno
)
103 using cache_val
= std::unordered_map
<std::string
, refcnt_fd
>::value_type
;
105 = std::find_if (m_cache
.begin (), m_cache
.end (),
106 [fd
](const cache_val
&s
) { return s
.second
.fd
== fd
; });
108 gdb_assert (it
!= m_cache
.end ());
111 if (it
->second
.refcnt
== 0)
113 int ret
= target_fileio_close (it
->second
.fd
, target_errno
);
119 /* Keep the FD open for the other users, return success. */
124 } /* Anonymous namespace. */
126 /* ROCm-specific inferior data. */
130 rocm_so (const char *name
, std::string unique_name
, lm_info_svr4_up lm_info
)
132 unique_name (std::move (unique_name
)),
133 lm_info (std::move (lm_info
))
136 std::string name
, unique_name
;
137 lm_info_svr4_up lm_info
;
142 explicit solib_info (inferior
*inf
)
146 /* List of code objects loaded into the inferior. */
147 std::vector
<rocm_so
> solib_list
;
149 /* Cache of opened FD in the inferior. */
150 rocm_solib_fd_cache fd_cache
;
153 /* Per-inferior data key. */
154 static const registry
<inferior
>::key
<solib_info
> rocm_solib_data
;
156 static solib_ops rocm_solib_ops
;
158 /* Fetch the solib_info data for INF. */
160 static struct solib_info
*
161 get_solib_info (inferior
*inf
)
163 solib_info
*info
= rocm_solib_data
.get (inf
);
166 info
= rocm_solib_data
.emplace (inf
, inf
);
171 /* Relocate section addresses. */
174 rocm_solib_relocate_section_addresses (solib
&so
,
175 struct target_section
*sec
)
177 if (!is_amdgpu_arch (gdbarch_from_bfd (so
.abfd
.get ())))
179 svr4_so_ops
.relocate_section_addresses (so
, sec
);
183 auto *li
= gdb::checked_static_cast
<lm_info_svr4
*> (so
.lm_info
.get ());
184 sec
->addr
= sec
->addr
+ li
->l_addr
;
185 sec
->endaddr
= sec
->endaddr
+ li
->l_addr
;
188 static void rocm_update_solib_list ();
191 rocm_solib_handle_event ()
193 /* Since we sit on top of svr4_so_ops, we might get called following an event
194 concerning host libraries. We must therefore forward the call. If the
195 event was for a ROCm code object, it will be a no-op. On the other hand,
196 if the event was for host libraries, rocm_update_solib_list will be
197 essentially be a no-op (it will reload the same code object list as was
198 previously loaded). */
199 svr4_so_ops
.handle_event ();
201 rocm_update_solib_list ();
204 /* Create so_list objects from rocm_so objects in SOS. */
206 static intrusive_list
<solib
>
207 so_list_from_rocm_sos (const std::vector
<rocm_so
> &sos
)
209 intrusive_list
<solib
> dst
;
211 for (const rocm_so
&so
: sos
)
213 solib
*newobj
= new solib
;
214 newobj
->lm_info
= std::make_unique
<lm_info_svr4
> (*so
.lm_info
);
216 newobj
->so_name
= so
.name
;
217 newobj
->so_original_name
= so
.unique_name
;
219 dst
.push_back (*newobj
);
225 /* Build a list of `struct solib' objects describing the shared
226 objects currently loaded in the inferior. */
228 static intrusive_list
<solib
>
229 rocm_solib_current_sos ()
231 /* First, retrieve the host-side shared library list. */
232 intrusive_list
<solib
> sos
= svr4_so_ops
.current_sos ();
234 /* Then, the device-side shared library list. */
235 std::vector
<rocm_so
> &dev_sos
= get_solib_info (current_inferior ())->solib_list
;
237 if (dev_sos
.empty ())
240 intrusive_list
<solib
> dev_so_list
= so_list_from_rocm_sos (dev_sos
);
245 /* Append our libraries to the end of the list. */
246 sos
.splice (std::move (dev_so_list
));
253 /* Interface to interact with a ROCm code object stream. */
255 struct rocm_code_object_stream
: public gdb_bfd_iovec_base
257 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream
);
259 int stat (bfd
*abfd
, struct stat
*sb
) final override
;
261 ~rocm_code_object_stream () override
= default;
264 rocm_code_object_stream () = default;
266 /* Return the size of the object file, or -1 if the size cannot be
269 This is a helper function for stat. */
270 virtual LONGEST
size () = 0;
274 rocm_code_object_stream::stat (bfd
*, struct stat
*sb
)
276 const LONGEST size
= this->size ();
280 memset (sb
, '\0', sizeof (struct stat
));
285 /* Interface to a ROCm object stream which is embedded in an ELF file
286 accessible to the debugger. */
288 struct rocm_code_object_stream_file final
: rocm_code_object_stream
290 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_file
);
292 rocm_code_object_stream_file (inferior
*inf
, int fd
, ULONGEST offset
,
295 file_ptr
read (bfd
*abfd
, void *buf
, file_ptr size
,
296 file_ptr offset
) override
;
298 LONGEST
size () override
;
300 ~rocm_code_object_stream_file () override
;
304 /* The inferior owning this code object stream. */
307 /* The target file descriptor for this stream. */
310 /* The offset of the ELF file image in the target file. */
313 /* The size of the ELF file image. The value 0 means that it was
314 unspecified in the URI descriptor. */
318 rocm_code_object_stream_file::rocm_code_object_stream_file
319 (inferior
*inf
, int fd
, ULONGEST offset
, ULONGEST size
)
320 : m_inf (inf
), m_fd (fd
), m_offset (offset
), m_size (size
)
325 rocm_code_object_stream_file::read (bfd
*, void *buf
, file_ptr size
,
328 fileio_error target_errno
;
335 = target_fileio_pread (m_fd
, static_cast<gdb_byte
*> (buf
) + nbytes
,
336 size
, m_offset
+ offset
+ nbytes
,
344 errno
= fileio_error_to_host (target_errno
);
345 bfd_set_error (bfd_error_system_call
);
349 nbytes
+= bytes_read
;
357 rocm_code_object_stream_file::size ()
361 fileio_error target_errno
;
363 if (target_fileio_fstat (m_fd
, &stat
, &target_errno
) < 0)
365 errno
= fileio_error_to_host (target_errno
);
366 bfd_set_error (bfd_error_system_call
);
370 /* Check that the offset is valid. */
371 if (m_offset
>= stat
.st_size
)
373 bfd_set_error (bfd_error_bad_value
);
377 m_size
= stat
.st_size
- m_offset
;
383 rocm_code_object_stream_file::~rocm_code_object_stream_file ()
385 auto info
= get_solib_info (m_inf
);
386 fileio_error target_errno
;
387 if (info
->fd_cache
.close (m_fd
, &target_errno
) != 0)
388 warning (_("Failed to close solib: %s"),
389 strerror (fileio_error_to_host (target_errno
)));
392 /* Interface to a code object which lives in the inferior's memory. */
394 struct rocm_code_object_stream_memory final
: public rocm_code_object_stream
396 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_memory
);
398 rocm_code_object_stream_memory (gdb::byte_vector buffer
);
400 file_ptr
read (bfd
*abfd
, void *buf
, file_ptr size
,
401 file_ptr offset
) override
;
405 /* Snapshot of the original ELF image taken during load. This is done to
406 support the situation where an inferior uses an in-memory image, and
407 releases or re-uses this memory before GDB is done using it. */
408 gdb::byte_vector m_objfile_image
;
410 LONGEST
size () override
412 return m_objfile_image
.size ();
416 rocm_code_object_stream_memory::rocm_code_object_stream_memory
417 (gdb::byte_vector buffer
)
418 : m_objfile_image (std::move (buffer
))
423 rocm_code_object_stream_memory::read (bfd
*, void *buf
, file_ptr size
,
426 if (size
> m_objfile_image
.size () - offset
)
427 size
= m_objfile_image
.size () - offset
;
429 memcpy (buf
, m_objfile_image
.data () + offset
, size
);
433 } /* anonymous namespace */
435 static gdb_bfd_iovec_base
*
436 rocm_bfd_iovec_open (bfd
*abfd
, inferior
*inferior
)
438 std::string_view
uri (bfd_get_filename (abfd
));
439 std::string_view protocol_delim
= "://";
440 size_t protocol_end
= uri
.find (protocol_delim
);
441 std::string
protocol (uri
.substr (0, protocol_end
));
442 protocol_end
+= protocol_delim
.length ();
444 std::transform (protocol
.begin (), protocol
.end (), protocol
.begin (),
445 [] (unsigned char c
) { return std::tolower (c
); });
447 std::string_view path
;
448 size_t path_end
= uri
.find_first_of ("#?", protocol_end
);
449 if (path_end
!= std::string::npos
)
450 path
= uri
.substr (protocol_end
, path_end
++ - protocol_end
);
452 path
= uri
.substr (protocol_end
);
454 /* %-decode the string. */
455 std::string decoded_path
;
456 decoded_path
.reserve (path
.length ());
457 for (size_t i
= 0; i
< path
.length (); ++i
)
459 && i
< path
.length () - 2
460 && std::isxdigit (path
[i
+ 1])
461 && std::isxdigit (path
[i
+ 2]))
463 std::string_view hex_digits
= path
.substr (i
+ 1, 2);
464 decoded_path
+= std::stoi (std::string (hex_digits
), 0, 16);
468 decoded_path
+= path
[i
];
470 /* Tokenize the query/fragment. */
471 std::vector
<std::string_view
> tokens
;
472 size_t pos
, last
= path_end
;
473 while ((pos
= uri
.find ('&', last
)) != std::string::npos
)
475 tokens
.emplace_back (uri
.substr (last
, pos
- last
));
479 if (last
!= std::string::npos
)
480 tokens
.emplace_back (uri
.substr (last
));
482 /* Create a tag-value map from the tokenized query/fragment. */
483 std::unordered_map
<std::string_view
, std::string_view
,
484 gdb::string_view_hash
> params
;
485 for (std::string_view token
: tokens
)
487 size_t delim
= token
.find ('=');
488 if (delim
!= std::string::npos
)
490 std::string_view tag
= token
.substr (0, delim
);
491 std::string_view val
= token
.substr (delim
+ 1);
492 params
.emplace (tag
, val
);
501 auto try_strtoulst
= [] (std::string_view v
)
504 ULONGEST value
= strtoulst (v
.data (), nullptr, 0);
507 /* The actual message doesn't matter, the exception is caught
508 below, transformed in a BFD error, and the message is lost. */
509 error (_("Failed to parse integer."));
515 auto offset_it
= params
.find ("offset");
516 if (offset_it
!= params
.end ())
517 offset
= try_strtoulst (offset_it
->second
);
519 auto size_it
= params
.find ("size");
520 if (size_it
!= params
.end ())
522 size
= try_strtoulst (size_it
->second
);
524 error (_("Invalid size value"));
527 if (protocol
== "file")
529 auto info
= get_solib_info (inferior
);
530 fileio_error target_errno
;
531 int fd
= info
->fd_cache
.open (decoded_path
, &target_errno
);
535 errno
= fileio_error_to_host (target_errno
);
536 bfd_set_error (bfd_error_system_call
);
540 return new rocm_code_object_stream_file (inferior
, fd
, offset
,
544 if (protocol
== "memory")
546 ULONGEST pid
= try_strtoulst (path
);
547 if (pid
!= inferior
->pid
)
549 warning (_("`%s': code object is from another inferior"),
550 std::string (uri
).c_str ());
551 bfd_set_error (bfd_error_bad_value
);
555 gdb::byte_vector
buffer (size
);
556 if (target_read_memory (offset
, buffer
.data (), size
) != 0)
558 warning (_("Failed to copy the code object from the inferior"));
559 bfd_set_error (bfd_error_bad_value
);
563 return new rocm_code_object_stream_memory (std::move (buffer
));
566 warning (_("`%s': protocol not supported: %s"),
567 std::string (uri
).c_str (), protocol
.c_str ());
568 bfd_set_error (bfd_error_bad_value
);
571 catch (const gdb_exception_quit
&ex
)
574 bfd_set_error (bfd_error_bad_value
);
577 catch (const gdb_exception
&ex
)
579 bfd_set_error (bfd_error_bad_value
);
584 static gdb_bfd_ref_ptr
585 rocm_solib_bfd_open (const char *pathname
)
587 /* Handle regular files with SVR4 open. */
588 if (strstr (pathname
, "://") == nullptr)
589 return svr4_so_ops
.bfd_open (pathname
);
591 auto open
= [] (bfd
*nbfd
) -> gdb_bfd_iovec_base
*
593 return rocm_bfd_iovec_open (nbfd
, current_inferior ());
596 gdb_bfd_ref_ptr abfd
= gdb_bfd_openr_iovec (pathname
, "elf64-amdgcn", open
);
599 error (_("Could not open `%s' as an executable file: %s"), pathname
,
600 bfd_errmsg (bfd_get_error ()));
602 /* Check bfd format. */
603 if (!bfd_check_format (abfd
.get (), bfd_object
))
604 error (_("`%s': not in executable format: %s"),
605 bfd_get_filename (abfd
.get ()), bfd_errmsg (bfd_get_error ()));
607 unsigned char osabi
= elf_elfheader (abfd
)->e_ident
[EI_OSABI
];
608 unsigned char osabiversion
= elf_elfheader (abfd
)->e_ident
[EI_ABIVERSION
];
610 /* Check that the code object is using the HSA OS ABI. */
611 if (osabi
!= ELFOSABI_AMDGPU_HSA
)
612 error (_("`%s': ELF file OS ABI is not supported (%d)."),
613 bfd_get_filename (abfd
.get ()), osabi
);
615 /* We support HSA code objects V3 and greater. */
616 if (osabiversion
< ELFABIVERSION_AMDGPU_HSA_V3
)
617 error (_("`%s': ELF file HSA OS ABI version is not supported (%d)."),
618 bfd_get_filename (abfd
.get ()), osabiversion
);
620 /* For GDB to be able to use this solib, the exact AMDGPU processor type
621 must be supported by both BFD and the amd-dbgapi library. */
622 const unsigned char gfx_arch
623 = elf_elfheader (abfd
)->e_flags
& EF_AMDGPU_MACH
;
624 const bfd_arch_info_type
*bfd_arch_info
625 = bfd_lookup_arch (bfd_arch_amdgcn
, gfx_arch
);
627 amd_dbgapi_architecture_id_t architecture_id
;
628 amd_dbgapi_status_t dbgapi_query_arch
629 = amd_dbgapi_get_architecture (gfx_arch
, &architecture_id
);
631 if (dbgapi_query_arch
!= AMD_DBGAPI_STATUS_SUCCESS
632 || bfd_arch_info
== nullptr)
634 if (dbgapi_query_arch
!= AMD_DBGAPI_STATUS_SUCCESS
635 && bfd_arch_info
== nullptr)
637 /* Neither of the libraries knows about this arch, so we cannot
638 provide a human readable name for it. */
639 error (_("'%s': AMDGCN architecture %#02x is not supported."),
640 bfd_get_filename (abfd
.get ()), gfx_arch
);
642 else if (dbgapi_query_arch
!= AMD_DBGAPI_STATUS_SUCCESS
)
644 gdb_assert (bfd_arch_info
!= nullptr);
645 error (_("'%s': AMDGCN architecture %s not supported by "
647 bfd_get_filename (abfd
.get ()),
648 bfd_arch_info
->printable_name
);
652 gdb_assert (dbgapi_query_arch
== AMD_DBGAPI_STATUS_SUCCESS
);
654 if (amd_dbgapi_architecture_get_info
655 (architecture_id
, AMD_DBGAPI_ARCHITECTURE_INFO_NAME
,
656 sizeof (arch_name
), &arch_name
) != AMD_DBGAPI_STATUS_SUCCESS
)
657 error ("amd_dbgapi_architecture_get_info call failed for arch "
659 gdb::unique_xmalloc_ptr
<char> arch_name_cleaner (arch_name
);
661 error (_("'%s': AMDGCN architecture %s not supported."),
662 bfd_get_filename (abfd
.get ()),
667 gdb_assert (gdbarch_from_bfd (abfd
.get ()) != nullptr);
668 gdb_assert (is_amdgpu_arch (gdbarch_from_bfd (abfd
.get ())));
674 rocm_solib_create_inferior_hook (int from_tty
)
676 get_solib_info (current_inferior ())->solib_list
.clear ();
678 svr4_so_ops
.solib_create_inferior_hook (from_tty
);
682 rocm_update_solib_list ()
684 inferior
*inf
= current_inferior ();
686 amd_dbgapi_process_id_t process_id
= get_amd_dbgapi_process_id (inf
);
687 if (process_id
.handle
== AMD_DBGAPI_PROCESS_NONE
.handle
)
690 solib_info
*info
= get_solib_info (inf
);
692 info
->solib_list
.clear ();
693 std::vector
<rocm_so
> &sos
= info
->solib_list
;
695 amd_dbgapi_code_object_id_t
*code_object_list
;
698 amd_dbgapi_status_t status
699 = amd_dbgapi_process_code_object_list (process_id
, &count
,
700 &code_object_list
, nullptr);
701 if (status
!= AMD_DBGAPI_STATUS_SUCCESS
)
703 warning (_("amd_dbgapi_process_code_object_list failed (%s)"),
704 get_status_string (status
));
708 for (size_t i
= 0; i
< count
; ++i
)
713 status
= amd_dbgapi_code_object_get_info
714 (code_object_list
[i
], AMD_DBGAPI_CODE_OBJECT_INFO_LOAD_ADDRESS
,
715 sizeof (l_addr
), &l_addr
);
716 if (status
!= AMD_DBGAPI_STATUS_SUCCESS
)
719 status
= amd_dbgapi_code_object_get_info
720 (code_object_list
[i
], AMD_DBGAPI_CODE_OBJECT_INFO_URI_NAME
,
721 sizeof (uri_bytes
), &uri_bytes
);
722 if (status
!= AMD_DBGAPI_STATUS_SUCCESS
)
725 gdb::unique_xmalloc_ptr
<char> uri_bytes_holder (uri_bytes
);
727 lm_info_svr4_up li
= std::make_unique
<lm_info_svr4
> ();
730 /* Generate a unique name so that code objects with the same URI but
731 different load addresses are seen by gdb core as different shared
733 std::string unique_name
734 = string_printf ("code_object_%ld", code_object_list
[i
].handle
);
736 sos
.emplace_back (uri_bytes
, std::move (unique_name
), std::move (li
));
739 xfree (code_object_list
);
741 if (rocm_solib_ops
.current_sos
== NULL
)
743 /* Override what we need to. */
744 rocm_solib_ops
= svr4_so_ops
;
745 rocm_solib_ops
.current_sos
= rocm_solib_current_sos
;
746 rocm_solib_ops
.solib_create_inferior_hook
747 = rocm_solib_create_inferior_hook
;
748 rocm_solib_ops
.bfd_open
= rocm_solib_bfd_open
;
749 rocm_solib_ops
.relocate_section_addresses
750 = rocm_solib_relocate_section_addresses
;
751 rocm_solib_ops
.handle_event
= rocm_solib_handle_event
;
753 /* Engage the ROCm so_ops. */
754 set_gdbarch_so_ops (current_inferior ()->arch (), &rocm_solib_ops
);
759 rocm_solib_target_inferior_created (inferior
*inf
)
761 get_solib_info (inf
)->solib_list
.clear ();
763 rocm_update_solib_list ();
765 /* Force GDB to reload the solibs. */
766 current_inferior ()->pspace
->clear_solib_cache ();
767 solib_add (nullptr, 0, auto_solib_add
);
770 /* -Wmissing-prototypes */
771 extern initialize_file_ftype _initialize_rocm_solib
;
774 _initialize_rocm_solib ()
776 /* The dependency on the amd-dbgapi exists because solib-rocm's
777 inferior_created observer needs amd-dbgapi to have attached the process,
778 which happens in amd_dbgapi_target's inferior_created observer. */
779 gdb::observers::inferior_created
.attach
780 (rocm_solib_target_inferior_created
,
782 { &get_amd_dbgapi_target_inferior_created_observer_token () });