1 /* Handle ROCm Code Objects for GDB, the GNU Debugger.
3 Copyright (C) 2019-2023 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/>. */
22 #include "amd-dbgapi-target.h"
23 #include "amdgpu-tdep.h"
24 #include "arch-utils.h"
26 #include "elf/amdgpu.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 explicit solib_info (inferior
*inf
)
132 : solib_list (nullptr), fd_cache (inf
)
135 /* List of code objects loaded into the inferior. */
138 /* Cache of opened FD in the inferior. */
139 rocm_solib_fd_cache fd_cache
;
142 /* Per-inferior data key. */
143 static const registry
<inferior
>::key
<solib_info
> rocm_solib_data
;
145 static target_so_ops rocm_solib_ops
;
147 /* Free the solib linked list. */
150 rocm_free_solib_list (struct solib_info
*info
)
152 while (info
->solib_list
!= nullptr)
154 struct so_list
*next
= info
->solib_list
->next
;
156 free_so (info
->solib_list
);
157 info
->solib_list
= next
;
160 info
->solib_list
= nullptr;
164 /* Fetch the solib_info data for INF. */
166 static struct solib_info
*
167 get_solib_info (inferior
*inf
)
169 solib_info
*info
= rocm_solib_data
.get (inf
);
172 info
= rocm_solib_data
.emplace (inf
, inf
);
177 /* Relocate section addresses. */
180 rocm_solib_relocate_section_addresses (struct so_list
*so
,
181 struct target_section
*sec
)
183 if (!is_amdgpu_arch (gdbarch_from_bfd (so
->abfd
)))
185 svr4_so_ops
.relocate_section_addresses (so
, sec
);
189 lm_info_svr4
*li
= (lm_info_svr4
*) so
->lm_info
;
190 sec
->addr
= sec
->addr
+ li
->l_addr
;
191 sec
->endaddr
= sec
->endaddr
+ li
->l_addr
;
194 static void rocm_update_solib_list ();
197 rocm_solib_handle_event ()
199 /* Since we sit on top of svr4_so_ops, we might get called following an event
200 concerning host libraries. We must therefore forward the call. If the
201 event was for a ROCm code object, it will be a no-op. On the other hand,
202 if the event was for host libraries, rocm_update_solib_list will be
203 essentially be a no-op (it will reload the same code object list as was
204 previously loaded). */
205 svr4_so_ops
.handle_event ();
207 rocm_update_solib_list ();
210 /* Make a deep copy of the solib linked list. */
213 rocm_solib_copy_list (const so_list
*src
)
215 struct so_list
*dst
= nullptr;
216 struct so_list
**link
= &dst
;
218 while (src
!= nullptr)
220 struct so_list
*newobj
;
222 newobj
= XNEW (struct so_list
);
223 memcpy (newobj
, src
, sizeof (struct so_list
));
225 lm_info_svr4
*src_li
= (lm_info_svr4
*) src
->lm_info
;
226 newobj
->lm_info
= new lm_info_svr4 (*src_li
);
228 newobj
->next
= nullptr;
230 link
= &newobj
->next
;
238 /* Build a list of `struct so_list' objects describing the shared
239 objects currently loaded in the inferior. */
241 static struct so_list
*
242 rocm_solib_current_sos ()
244 /* First, retrieve the host-side shared library list. */
245 so_list
*head
= svr4_so_ops
.current_sos ();
247 /* Then, the device-side shared library list. */
248 so_list
*list
= get_solib_info (current_inferior ())->solib_list
;
253 list
= rocm_solib_copy_list (list
);
258 /* Append our libraries to the end of the list. */
260 for (tail
= head
; tail
->next
; tail
= tail
->next
)
269 /* Interface to interact with a ROCm code object stream. */
271 struct rocm_code_object_stream
: public gdb_bfd_iovec_base
273 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream
);
275 int stat (bfd
*abfd
, struct stat
*sb
) final override
;
277 ~rocm_code_object_stream () override
= default;
280 rocm_code_object_stream () = default;
282 /* Return the size of the object file, or -1 if the size cannot be
285 This is a helper function for stat. */
286 virtual LONGEST
size () = 0;
290 rocm_code_object_stream::stat (bfd
*, struct stat
*sb
)
292 const LONGEST size
= this->size ();
296 memset (sb
, '\0', sizeof (struct stat
));
301 /* Interface to a ROCm object stream which is embedded in an ELF file
302 accessible to the debugger. */
304 struct rocm_code_object_stream_file final
: rocm_code_object_stream
306 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_file
);
308 rocm_code_object_stream_file (inferior
*inf
, int fd
, ULONGEST offset
,
311 file_ptr
read (bfd
*abfd
, void *buf
, file_ptr size
,
312 file_ptr offset
) override
;
314 LONGEST
size () override
;
316 ~rocm_code_object_stream_file () override
;
320 /* The inferior owning this code object stream. */
323 /* The target file descriptor for this stream. */
326 /* The offset of the ELF file image in the target file. */
329 /* The size of the ELF file image. The value 0 means that it was
330 unspecified in the URI descriptor. */
334 rocm_code_object_stream_file::rocm_code_object_stream_file
335 (inferior
*inf
, int fd
, ULONGEST offset
, ULONGEST size
)
336 : m_inf (inf
), m_fd (fd
), m_offset (offset
), m_size (size
)
341 rocm_code_object_stream_file::read (bfd
*, void *buf
, file_ptr size
,
344 fileio_error target_errno
;
351 = target_fileio_pread (m_fd
, static_cast<gdb_byte
*> (buf
) + nbytes
,
352 size
, m_offset
+ offset
+ nbytes
,
360 errno
= fileio_error_to_host (target_errno
);
361 bfd_set_error (bfd_error_system_call
);
365 nbytes
+= bytes_read
;
373 rocm_code_object_stream_file::size ()
377 fileio_error target_errno
;
379 if (target_fileio_fstat (m_fd
, &stat
, &target_errno
) < 0)
381 errno
= fileio_error_to_host (target_errno
);
382 bfd_set_error (bfd_error_system_call
);
386 /* Check that the offset is valid. */
387 if (m_offset
>= stat
.st_size
)
389 bfd_set_error (bfd_error_bad_value
);
393 m_size
= stat
.st_size
- m_offset
;
399 rocm_code_object_stream_file::~rocm_code_object_stream_file ()
401 auto info
= get_solib_info (m_inf
);
402 fileio_error target_errno
;
403 if (info
->fd_cache
.close (m_fd
, &target_errno
) != 0)
404 warning (_("Failed to close solib: %s"),
405 strerror (fileio_error_to_host (target_errno
)));
408 /* Interface to a code object which lives in the inferior's memory. */
410 struct rocm_code_object_stream_memory final
: public rocm_code_object_stream
412 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_memory
);
414 rocm_code_object_stream_memory (gdb::byte_vector buffer
);
416 file_ptr
read (bfd
*abfd
, void *buf
, file_ptr size
,
417 file_ptr offset
) override
;
421 /* Snapshot of the original ELF image taken during load. This is done to
422 support the situation where an inferior uses an in-memory image, and
423 releases or re-uses this memory before GDB is done using it. */
424 gdb::byte_vector m_objfile_image
;
426 LONGEST
size () override
428 return m_objfile_image
.size ();
432 rocm_code_object_stream_memory::rocm_code_object_stream_memory
433 (gdb::byte_vector buffer
)
434 : m_objfile_image (std::move (buffer
))
439 rocm_code_object_stream_memory::read (bfd
*, void *buf
, file_ptr size
,
442 if (size
> m_objfile_image
.size () - offset
)
443 size
= m_objfile_image
.size () - offset
;
445 memcpy (buf
, m_objfile_image
.data () + offset
, size
);
449 } /* anonymous namespace */
451 static gdb_bfd_iovec_base
*
452 rocm_bfd_iovec_open (bfd
*abfd
, inferior
*inferior
)
454 gdb::string_view
uri (bfd_get_filename (abfd
));
455 gdb::string_view protocol_delim
= "://";
456 size_t protocol_end
= uri
.find (protocol_delim
);
457 std::string protocol
= gdb::to_string (uri
.substr (0, protocol_end
));
458 protocol_end
+= protocol_delim
.length ();
460 std::transform (protocol
.begin (), protocol
.end (), protocol
.begin (),
461 [] (unsigned char c
) { return std::tolower (c
); });
463 gdb::string_view path
;
464 size_t path_end
= uri
.find_first_of ("#?", protocol_end
);
465 if (path_end
!= std::string::npos
)
466 path
= uri
.substr (protocol_end
, path_end
++ - protocol_end
);
468 path
= uri
.substr (protocol_end
);
470 /* %-decode the string. */
471 std::string decoded_path
;
472 decoded_path
.reserve (path
.length ());
473 for (size_t i
= 0; i
< path
.length (); ++i
)
475 && i
< path
.length () - 2
476 && std::isxdigit (path
[i
+ 1])
477 && std::isxdigit (path
[i
+ 2]))
479 gdb::string_view hex_digits
= path
.substr (i
+ 1, 2);
480 decoded_path
+= std::stoi (gdb::to_string (hex_digits
), 0, 16);
484 decoded_path
+= path
[i
];
486 /* Tokenize the query/fragment. */
487 std::vector
<gdb::string_view
> tokens
;
488 size_t pos
, last
= path_end
;
489 while ((pos
= uri
.find ('&', last
)) != std::string::npos
)
491 tokens
.emplace_back (uri
.substr (last
, pos
- last
));
495 if (last
!= std::string::npos
)
496 tokens
.emplace_back (uri
.substr (last
));
498 /* Create a tag-value map from the tokenized query/fragment. */
499 std::unordered_map
<gdb::string_view
, gdb::string_view
,
500 gdb::string_view_hash
> params
;
501 for (gdb::string_view token
: tokens
)
503 size_t delim
= token
.find ('=');
504 if (delim
!= std::string::npos
)
506 gdb::string_view tag
= token
.substr (0, delim
);
507 gdb::string_view val
= token
.substr (delim
+ 1);
508 params
.emplace (tag
, val
);
517 auto try_strtoulst
= [] (gdb::string_view v
)
520 ULONGEST value
= strtoulst (v
.data (), nullptr, 0);
523 /* The actual message doesn't matter, the exception is caught
524 below, transformed in a BFD error, and the message is lost. */
525 error (_("Failed to parse integer."));
531 auto offset_it
= params
.find ("offset");
532 if (offset_it
!= params
.end ())
533 offset
= try_strtoulst (offset_it
->second
);
535 auto size_it
= params
.find ("size");
536 if (size_it
!= params
.end ())
538 size
= try_strtoulst (size_it
->second
);
540 error (_("Invalid size value"));
543 if (protocol
== "file")
545 auto info
= get_solib_info (inferior
);
546 fileio_error target_errno
;
547 int fd
= info
->fd_cache
.open (decoded_path
, &target_errno
);
551 errno
= fileio_error_to_host (target_errno
);
552 bfd_set_error (bfd_error_system_call
);
556 return new rocm_code_object_stream_file (inferior
, fd
, offset
,
560 if (protocol
== "memory")
562 ULONGEST pid
= try_strtoulst (path
);
563 if (pid
!= inferior
->pid
)
565 warning (_("`%s': code object is from another inferior"),
566 gdb::to_string (uri
).c_str ());
567 bfd_set_error (bfd_error_bad_value
);
571 gdb::byte_vector
buffer (size
);
572 if (target_read_memory (offset
, buffer
.data (), size
) != 0)
574 warning (_("Failed to copy the code object from the inferior"));
575 bfd_set_error (bfd_error_bad_value
);
579 return new rocm_code_object_stream_memory (std::move (buffer
));
582 warning (_("`%s': protocol not supported: %s"),
583 gdb::to_string (uri
).c_str (), protocol
.c_str ());
584 bfd_set_error (bfd_error_bad_value
);
587 catch (const gdb_exception_quit
&ex
)
590 bfd_set_error (bfd_error_bad_value
);
593 catch (const gdb_exception
&ex
)
595 bfd_set_error (bfd_error_bad_value
);
600 static gdb_bfd_ref_ptr
601 rocm_solib_bfd_open (const char *pathname
)
603 /* Handle regular files with SVR4 open. */
604 if (strstr (pathname
, "://") == nullptr)
605 return svr4_so_ops
.bfd_open (pathname
);
607 auto open
= [] (bfd
*nbfd
) -> gdb_bfd_iovec_base
*
609 return rocm_bfd_iovec_open (nbfd
, current_inferior ());
612 gdb_bfd_ref_ptr abfd
= gdb_bfd_openr_iovec (pathname
, "elf64-amdgcn", open
);
615 error (_("Could not open `%s' as an executable file: %s"), pathname
,
616 bfd_errmsg (bfd_get_error ()));
618 /* Check bfd format. */
619 if (!bfd_check_format (abfd
.get (), bfd_object
))
620 error (_("`%s': not in executable format: %s"),
621 bfd_get_filename (abfd
.get ()), bfd_errmsg (bfd_get_error ()));
623 unsigned char osabi
= elf_elfheader (abfd
)->e_ident
[EI_OSABI
];
624 unsigned char osabiversion
= elf_elfheader (abfd
)->e_ident
[EI_ABIVERSION
];
626 /* Check that the code object is using the HSA OS ABI. */
627 if (osabi
!= ELFOSABI_AMDGPU_HSA
)
628 error (_("`%s': ELF file OS ABI is not supported (%d)."),
629 bfd_get_filename (abfd
.get ()), osabi
);
631 /* We support HSA code objects V3 and greater. */
632 if (osabiversion
< ELFABIVERSION_AMDGPU_HSA_V3
)
633 error (_("`%s': ELF file HSA OS ABI version is not supported (%d)."),
634 bfd_get_filename (abfd
.get ()), osabiversion
);
636 /* For GDB to be able to use this solib, the exact AMDGPU processor type
637 must be supported by both BFD and the amd-dbgapi library. */
638 const unsigned char gfx_arch
639 = elf_elfheader (abfd
)->e_flags
& EF_AMDGPU_MACH
;
640 const bfd_arch_info_type
*bfd_arch_info
641 = bfd_lookup_arch (bfd_arch_amdgcn
, gfx_arch
);
643 amd_dbgapi_architecture_id_t architecture_id
;
644 amd_dbgapi_status_t dbgapi_query_arch
645 = amd_dbgapi_get_architecture (gfx_arch
, &architecture_id
);
647 if (dbgapi_query_arch
!= AMD_DBGAPI_STATUS_SUCCESS
648 || bfd_arch_info
== nullptr)
650 if (dbgapi_query_arch
!= AMD_DBGAPI_STATUS_SUCCESS
651 && bfd_arch_info
== nullptr)
653 /* Neither of the libraries knows about this arch, so we cannot
654 provide a human readable name for it. */
655 error (_("'%s': AMDGCN architecture %#02x is not supported."),
656 bfd_get_filename (abfd
.get ()), gfx_arch
);
658 else if (dbgapi_query_arch
!= AMD_DBGAPI_STATUS_SUCCESS
)
660 gdb_assert (bfd_arch_info
!= nullptr);
661 error (_("'%s': AMDGCN architecture %s not supported by "
663 bfd_get_filename (abfd
.get ()),
664 bfd_arch_info
->printable_name
);
668 gdb_assert (dbgapi_query_arch
== AMD_DBGAPI_STATUS_SUCCESS
);
670 if (amd_dbgapi_architecture_get_info
671 (architecture_id
, AMD_DBGAPI_ARCHITECTURE_INFO_NAME
,
672 sizeof (arch_name
), &arch_name
) != AMD_DBGAPI_STATUS_SUCCESS
)
673 error ("amd_dbgapi_architecture_get_info call failed for arch "
675 gdb::unique_xmalloc_ptr
<char> arch_name_cleaner (arch_name
);
677 error (_("'%s': AMDGCN architecture %s not supported."),
678 bfd_get_filename (abfd
.get ()),
683 gdb_assert (gdbarch_from_bfd (abfd
.get ()) != nullptr);
684 gdb_assert (is_amdgpu_arch (gdbarch_from_bfd (abfd
.get ())));
690 rocm_solib_create_inferior_hook (int from_tty
)
692 rocm_free_solib_list (get_solib_info (current_inferior ()));
694 svr4_so_ops
.solib_create_inferior_hook (from_tty
);
698 rocm_update_solib_list ()
700 inferior
*inf
= current_inferior ();
702 amd_dbgapi_process_id_t process_id
= get_amd_dbgapi_process_id (inf
);
703 if (process_id
.handle
== AMD_DBGAPI_PROCESS_NONE
.handle
)
706 solib_info
*info
= get_solib_info (inf
);
708 rocm_free_solib_list (info
);
709 struct so_list
**link
= &info
->solib_list
;
711 amd_dbgapi_code_object_id_t
*code_object_list
;
714 amd_dbgapi_status_t status
715 = amd_dbgapi_process_code_object_list (process_id
, &count
,
716 &code_object_list
, nullptr);
717 if (status
!= AMD_DBGAPI_STATUS_SUCCESS
)
719 warning (_("amd_dbgapi_process_code_object_list failed (%s)"),
720 get_status_string (status
));
724 for (size_t i
= 0; i
< count
; ++i
)
729 status
= amd_dbgapi_code_object_get_info
730 (code_object_list
[i
], AMD_DBGAPI_CODE_OBJECT_INFO_LOAD_ADDRESS
,
731 sizeof (l_addr
), &l_addr
);
732 if (status
!= AMD_DBGAPI_STATUS_SUCCESS
)
735 status
= amd_dbgapi_code_object_get_info
736 (code_object_list
[i
], AMD_DBGAPI_CODE_OBJECT_INFO_URI_NAME
,
737 sizeof (uri_bytes
), &uri_bytes
);
738 if (status
!= AMD_DBGAPI_STATUS_SUCCESS
)
741 struct so_list
*so
= XCNEW (struct so_list
);
742 lm_info_svr4
*li
= new lm_info_svr4
;
746 strncpy (so
->so_name
, uri_bytes
, sizeof (so
->so_name
));
747 so
->so_name
[sizeof (so
->so_name
) - 1] = '\0';
750 /* Make so_original_name unique so that code objects with the same URI
751 but different load addresses are seen by gdb core as different shared
753 xsnprintf (so
->so_original_name
, sizeof (so
->so_original_name
),
754 "code_object_%ld", code_object_list
[i
].handle
);
761 xfree (code_object_list
);
763 if (rocm_solib_ops
.current_sos
== NULL
)
765 /* Override what we need to. */
766 rocm_solib_ops
= svr4_so_ops
;
767 rocm_solib_ops
.current_sos
= rocm_solib_current_sos
;
768 rocm_solib_ops
.solib_create_inferior_hook
769 = rocm_solib_create_inferior_hook
;
770 rocm_solib_ops
.bfd_open
= rocm_solib_bfd_open
;
771 rocm_solib_ops
.relocate_section_addresses
772 = rocm_solib_relocate_section_addresses
;
773 rocm_solib_ops
.handle_event
= rocm_solib_handle_event
;
775 /* Engage the ROCm so_ops. */
776 set_gdbarch_so_ops (current_inferior ()->arch (), &rocm_solib_ops
);
781 rocm_solib_target_inferior_created (inferior
*inf
)
783 rocm_free_solib_list (get_solib_info (inf
));
784 rocm_update_solib_list ();
786 /* Force GDB to reload the solibs. */
787 current_inferior ()->pspace
->clear_solib_cache ();
788 solib_add (nullptr, 0, auto_solib_add
);
791 /* -Wmissing-prototypes */
792 extern initialize_file_ftype _initialize_rocm_solib
;
795 _initialize_rocm_solib ()
797 /* The dependency on the amd-dbgapi exists because solib-rocm's
798 inferior_created observer needs amd-dbgapi to have attached the process,
799 which happens in amd_dbgapi_target's inferior_created observer. */
800 gdb::observers::inferior_created
.attach
801 (rocm_solib_target_inferior_created
,
803 { &get_amd_dbgapi_target_inferior_created_observer_token () });