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 /* ROCm-specific inferior data. */
39 /* List of code objects loaded into the inferior. */
43 /* Per-inferior data key. */
44 static const registry
<inferior
>::key
<solib_info
> rocm_solib_data
;
46 static target_so_ops rocm_solib_ops
;
48 /* Free the solib linked list. */
51 rocm_free_solib_list (struct solib_info
*info
)
53 while (info
->solib_list
!= nullptr)
55 struct so_list
*next
= info
->solib_list
->next
;
57 free_so (info
->solib_list
);
58 info
->solib_list
= next
;
61 info
->solib_list
= nullptr;
65 /* Fetch the solib_info data for INF. */
67 static struct solib_info
*
68 get_solib_info (inferior
*inf
)
70 solib_info
*info
= rocm_solib_data
.get (inf
);
73 info
= rocm_solib_data
.emplace (inf
);
78 /* Relocate section addresses. */
81 rocm_solib_relocate_section_addresses (struct so_list
*so
,
82 struct target_section
*sec
)
84 if (!is_amdgpu_arch (gdbarch_from_bfd (so
->abfd
)))
86 svr4_so_ops
.relocate_section_addresses (so
, sec
);
90 lm_info_svr4
*li
= (lm_info_svr4
*) so
->lm_info
;
91 sec
->addr
= sec
->addr
+ li
->l_addr
;
92 sec
->endaddr
= sec
->endaddr
+ li
->l_addr
;
95 static void rocm_update_solib_list ();
98 rocm_solib_handle_event ()
100 /* Since we sit on top of svr4_so_ops, we might get called following an event
101 concerning host libraries. We must therefore forward the call. If the
102 event was for a ROCm code object, it will be a no-op. On the other hand,
103 if the event was for host libraries, rocm_update_solib_list will be
104 essentially be a no-op (it will reload the same code object list as was
105 previously loaded). */
106 svr4_so_ops
.handle_event ();
108 rocm_update_solib_list ();
111 /* Make a deep copy of the solib linked list. */
114 rocm_solib_copy_list (const so_list
*src
)
116 struct so_list
*dst
= nullptr;
117 struct so_list
**link
= &dst
;
119 while (src
!= nullptr)
121 struct so_list
*newobj
;
123 newobj
= XNEW (struct so_list
);
124 memcpy (newobj
, src
, sizeof (struct so_list
));
126 lm_info_svr4
*src_li
= (lm_info_svr4
*) src
->lm_info
;
127 newobj
->lm_info
= new lm_info_svr4 (*src_li
);
129 newobj
->next
= nullptr;
131 link
= &newobj
->next
;
139 /* Build a list of `struct so_list' objects describing the shared
140 objects currently loaded in the inferior. */
142 static struct so_list
*
143 rocm_solib_current_sos ()
145 /* First, retrieve the host-side shared library list. */
146 so_list
*head
= svr4_so_ops
.current_sos ();
148 /* Then, the device-side shared library list. */
149 so_list
*list
= get_solib_info (current_inferior ())->solib_list
;
154 list
= rocm_solib_copy_list (list
);
159 /* Append our libraries to the end of the list. */
161 for (tail
= head
; tail
->next
; tail
= tail
->next
)
170 /* Interface to interact with a ROCm code object stream. */
172 struct rocm_code_object_stream
174 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream
);
176 /* Copy SIZE bytes from the underlying objfile storage starting at OFFSET
177 into the user provided buffer BUF.
179 Return the number of bytes actually copied (might be inferior to SIZE if
180 the end of the stream is reached). */
181 virtual file_ptr
read (void *buf
, file_ptr size
, file_ptr offset
) = 0;
183 /* Retrieve file information in SB.
185 Return 0 on success. On failure, set the appropriate bfd error number
186 (using bfd_set_error) and return -1. */
187 int stat (struct stat
*sb
);
189 virtual ~rocm_code_object_stream () = default;
192 rocm_code_object_stream () = default;
194 /* Return the size of the object file, or -1 if the size cannot be
197 This is a helper function for stat. */
198 virtual LONGEST
size () = 0;
202 rocm_code_object_stream::stat (struct stat
*sb
)
204 const LONGEST size
= this->size ();
208 memset (sb
, '\0', sizeof (struct stat
));
213 /* Interface to a ROCm object stream which is embedded in an ELF file
214 accessible to the debugger. */
216 struct rocm_code_object_stream_file final
: rocm_code_object_stream
218 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_file
);
220 rocm_code_object_stream_file (int fd
, ULONGEST offset
, ULONGEST size
);
222 file_ptr
read (void *buf
, file_ptr size
, file_ptr offset
) override
;
224 LONGEST
size () override
;
226 ~rocm_code_object_stream_file () override
;
230 /* The target file descriptor for this stream. */
233 /* The offset of the ELF file image in the target file. */
236 /* The size of the ELF file image. The value 0 means that it was
237 unspecified in the URI descriptor. */
241 rocm_code_object_stream_file::rocm_code_object_stream_file
242 (int fd
, ULONGEST offset
, ULONGEST size
)
243 : m_fd (fd
), m_offset (offset
), m_size (size
)
248 rocm_code_object_stream_file::read (void *buf
, file_ptr size
,
251 fileio_error target_errno
;
258 = target_fileio_pread (m_fd
, static_cast<gdb_byte
*> (buf
) + nbytes
,
259 size
, m_offset
+ offset
+ nbytes
,
267 errno
= fileio_error_to_host (target_errno
);
268 bfd_set_error (bfd_error_system_call
);
272 nbytes
+= bytes_read
;
280 rocm_code_object_stream_file::size ()
284 fileio_error target_errno
;
286 if (target_fileio_fstat (m_fd
, &stat
, &target_errno
) < 0)
288 errno
= fileio_error_to_host (target_errno
);
289 bfd_set_error (bfd_error_system_call
);
293 /* Check that the offset is valid. */
294 if (m_offset
>= stat
.st_size
)
296 bfd_set_error (bfd_error_bad_value
);
300 m_size
= stat
.st_size
- m_offset
;
306 rocm_code_object_stream_file::~rocm_code_object_stream_file ()
308 fileio_error target_errno
;
309 target_fileio_close (m_fd
, &target_errno
);
312 /* Interface to a code object which lives in the inferior's memory. */
314 struct rocm_code_object_stream_memory final
: public rocm_code_object_stream
316 DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_memory
);
318 rocm_code_object_stream_memory (gdb::byte_vector buffer
);
320 file_ptr
read (void *buf
, file_ptr size
, file_ptr offset
) override
;
324 /* Snapshot of the original ELF image taken during load. This is done to
325 support the situation where an inferior uses an in-memory image, and
326 releases or re-uses this memory before GDB is done using it. */
327 gdb::byte_vector m_objfile_image
;
329 LONGEST
size () override
331 return m_objfile_image
.size ();
335 rocm_code_object_stream_memory::rocm_code_object_stream_memory
336 (gdb::byte_vector buffer
)
337 : m_objfile_image (std::move (buffer
))
342 rocm_code_object_stream_memory::read (void *buf
, file_ptr size
,
345 if (size
> m_objfile_image
.size () - offset
)
346 size
= m_objfile_image
.size () - offset
;
348 memcpy (buf
, m_objfile_image
.data () + offset
, size
);
352 } /* anonymous namespace */
355 rocm_bfd_iovec_open (bfd
*abfd
, void *inferior_void
)
357 gdb::string_view
uri (bfd_get_filename (abfd
));
358 gdb::string_view protocol_delim
= "://";
359 size_t protocol_end
= uri
.find (protocol_delim
);
360 std::string protocol
= gdb::to_string (uri
.substr (0, protocol_end
));
361 protocol_end
+= protocol_delim
.length ();
363 std::transform (protocol
.begin (), protocol
.end (), protocol
.begin (),
364 [] (unsigned char c
) { return std::tolower (c
); });
366 gdb::string_view path
;
367 size_t path_end
= uri
.find_first_of ("#?", protocol_end
);
368 if (path_end
!= std::string::npos
)
369 path
= uri
.substr (protocol_end
, path_end
++ - protocol_end
);
371 path
= uri
.substr (protocol_end
);
373 /* %-decode the string. */
374 std::string decoded_path
;
375 decoded_path
.reserve (path
.length ());
376 for (size_t i
= 0; i
< path
.length (); ++i
)
378 && i
< path
.length () - 2
379 && std::isxdigit (path
[i
+ 1])
380 && std::isxdigit (path
[i
+ 2]))
382 gdb::string_view hex_digits
= path
.substr (i
+ 1, 2);
383 decoded_path
+= std::stoi (gdb::to_string (hex_digits
), 0, 16);
387 decoded_path
+= path
[i
];
389 /* Tokenize the query/fragment. */
390 std::vector
<gdb::string_view
> tokens
;
391 size_t pos
, last
= path_end
;
392 while ((pos
= uri
.find ('&', last
)) != std::string::npos
)
394 tokens
.emplace_back (uri
.substr (last
, pos
- last
));
398 if (last
!= std::string::npos
)
399 tokens
.emplace_back (uri
.substr (last
));
401 /* Create a tag-value map from the tokenized query/fragment. */
402 std::unordered_map
<gdb::string_view
, gdb::string_view
,
403 gdb::string_view_hash
> params
;
404 for (gdb::string_view token
: tokens
)
406 size_t delim
= token
.find ('=');
407 if (delim
!= std::string::npos
)
409 gdb::string_view tag
= token
.substr (0, delim
);
410 gdb::string_view val
= token
.substr (delim
+ 1);
411 params
.emplace (tag
, val
);
419 inferior
*inferior
= static_cast<struct inferior
*> (inferior_void
);
421 auto try_strtoulst
= [] (gdb::string_view v
)
424 ULONGEST value
= strtoulst (v
.data (), nullptr, 0);
427 /* The actual message doesn't matter, the exception is caught
428 below, transformed in a BFD error, and the message is lost. */
429 error (_("Failed to parse integer."));
435 auto offset_it
= params
.find ("offset");
436 if (offset_it
!= params
.end ())
437 offset
= try_strtoulst (offset_it
->second
);
439 auto size_it
= params
.find ("size");
440 if (size_it
!= params
.end ())
442 size
= try_strtoulst (size_it
->second
);
444 error (_("Invalid size value"));
447 if (protocol
== "file")
449 fileio_error target_errno
;
451 = target_fileio_open (static_cast<struct inferior
*> (inferior
),
452 decoded_path
.c_str (), FILEIO_O_RDONLY
,
453 false, 0, &target_errno
);
457 errno
= fileio_error_to_host (target_errno
);
458 bfd_set_error (bfd_error_system_call
);
462 return new rocm_code_object_stream_file (fd
, offset
, size
);
465 if (protocol
== "memory")
467 ULONGEST pid
= try_strtoulst (path
);
468 if (pid
!= inferior
->pid
)
470 warning (_("`%s': code object is from another inferior"),
471 gdb::to_string (uri
).c_str ());
472 bfd_set_error (bfd_error_bad_value
);
476 gdb::byte_vector
buffer (size
);
477 if (target_read_memory (offset
, buffer
.data (), size
) != 0)
479 warning (_("Failed to copy the code object from the inferior"));
480 bfd_set_error (bfd_error_bad_value
);
484 return new rocm_code_object_stream_memory (std::move (buffer
));
487 warning (_("`%s': protocol not supported: %s"),
488 gdb::to_string (uri
).c_str (), protocol
.c_str ());
489 bfd_set_error (bfd_error_bad_value
);
492 catch (const gdb_exception_quit
&ex
)
495 bfd_set_error (bfd_error_bad_value
);
498 catch (const gdb_exception
&ex
)
500 bfd_set_error (bfd_error_bad_value
);
506 rocm_bfd_iovec_close (bfd
*nbfd
, void *data
)
508 delete static_cast<rocm_code_object_stream
*> (data
);
514 rocm_bfd_iovec_pread (bfd
*abfd
, void *data
, void *buf
, file_ptr size
,
517 return static_cast<rocm_code_object_stream
*> (data
)->read (buf
, size
,
522 rocm_bfd_iovec_stat (bfd
*abfd
, void *data
, struct stat
*sb
)
524 return static_cast<rocm_code_object_stream
*> (data
)->stat (sb
);
527 static gdb_bfd_ref_ptr
528 rocm_solib_bfd_open (const char *pathname
)
530 /* Handle regular files with SVR4 open. */
531 if (strstr (pathname
, "://") == nullptr)
532 return svr4_so_ops
.bfd_open (pathname
);
535 = gdb_bfd_openr_iovec (pathname
, "elf64-amdgcn", rocm_bfd_iovec_open
,
536 current_inferior (), rocm_bfd_iovec_pread
,
537 rocm_bfd_iovec_close
, rocm_bfd_iovec_stat
);
540 error (_("Could not open `%s' as an executable file: %s"), pathname
,
541 bfd_errmsg (bfd_get_error ()));
543 /* Check bfd format. */
544 if (!bfd_check_format (abfd
.get (), bfd_object
))
545 error (_("`%s': not in executable format: %s"),
546 bfd_get_filename (abfd
.get ()), bfd_errmsg (bfd_get_error ()));
548 unsigned char osabi
= elf_elfheader (abfd
)->e_ident
[EI_OSABI
];
549 unsigned char osabiversion
= elf_elfheader (abfd
)->e_ident
[EI_ABIVERSION
];
551 /* Check that the code object is using the HSA OS ABI. */
552 if (osabi
!= ELFOSABI_AMDGPU_HSA
)
553 error (_("`%s': ELF file OS ABI is not supported (%d)."),
554 bfd_get_filename (abfd
.get ()), osabi
);
556 /* We support HSA code objects V3 and greater. */
557 if (osabiversion
< ELFABIVERSION_AMDGPU_HSA_V3
)
558 error (_("`%s': ELF file HSA OS ABI version is not supported (%d)."),
559 bfd_get_filename (abfd
.get ()), osabiversion
);
565 rocm_solib_create_inferior_hook (int from_tty
)
567 rocm_free_solib_list (get_solib_info (current_inferior ()));
569 svr4_so_ops
.solib_create_inferior_hook (from_tty
);
573 rocm_update_solib_list ()
575 inferior
*inf
= current_inferior ();
577 amd_dbgapi_process_id_t process_id
= get_amd_dbgapi_process_id (inf
);
578 if (process_id
.handle
== AMD_DBGAPI_PROCESS_NONE
.handle
)
581 solib_info
*info
= get_solib_info (inf
);
583 rocm_free_solib_list (info
);
584 struct so_list
**link
= &info
->solib_list
;
586 amd_dbgapi_code_object_id_t
*code_object_list
;
589 amd_dbgapi_status_t status
590 = amd_dbgapi_process_code_object_list (process_id
, &count
,
591 &code_object_list
, nullptr);
592 if (status
!= AMD_DBGAPI_STATUS_SUCCESS
)
594 warning (_("amd_dbgapi_process_code_object_list failed (%s)"),
595 get_status_string (status
));
599 for (size_t i
= 0; i
< count
; ++i
)
604 status
= amd_dbgapi_code_object_get_info
605 (code_object_list
[i
], AMD_DBGAPI_CODE_OBJECT_INFO_LOAD_ADDRESS
,
606 sizeof (l_addr
), &l_addr
);
607 if (status
!= AMD_DBGAPI_STATUS_SUCCESS
)
610 status
= amd_dbgapi_code_object_get_info
611 (code_object_list
[i
], AMD_DBGAPI_CODE_OBJECT_INFO_URI_NAME
,
612 sizeof (uri_bytes
), &uri_bytes
);
613 if (status
!= AMD_DBGAPI_STATUS_SUCCESS
)
616 struct so_list
*so
= XCNEW (struct so_list
);
617 lm_info_svr4
*li
= new lm_info_svr4
;
621 strncpy (so
->so_name
, uri_bytes
, sizeof (so
->so_name
));
622 so
->so_name
[sizeof (so
->so_name
) - 1] = '\0';
625 /* Make so_original_name unique so that code objects with the same URI
626 but different load addresses are seen by gdb core as different shared
628 xsnprintf (so
->so_original_name
, sizeof (so
->so_original_name
),
629 "code_object_%ld", code_object_list
[i
].handle
);
636 xfree (code_object_list
);
638 if (rocm_solib_ops
.current_sos
== NULL
)
640 /* Override what we need to. */
641 rocm_solib_ops
= svr4_so_ops
;
642 rocm_solib_ops
.current_sos
= rocm_solib_current_sos
;
643 rocm_solib_ops
.solib_create_inferior_hook
644 = rocm_solib_create_inferior_hook
;
645 rocm_solib_ops
.bfd_open
= rocm_solib_bfd_open
;
646 rocm_solib_ops
.relocate_section_addresses
647 = rocm_solib_relocate_section_addresses
;
648 rocm_solib_ops
.handle_event
= rocm_solib_handle_event
;
650 /* Engage the ROCm so_ops. */
651 set_gdbarch_so_ops (current_inferior ()->gdbarch
, &rocm_solib_ops
);
656 rocm_solib_target_inferior_created (inferior
*inf
)
658 rocm_free_solib_list (get_solib_info (inf
));
659 rocm_update_solib_list ();
661 /* Force GDB to reload the solibs. */
662 current_inferior ()->pspace
->clear_solib_cache ();
663 solib_add (nullptr, 0, auto_solib_add
);
666 /* -Wmissing-prototypes */
667 extern initialize_file_ftype _initialize_rocm_solib
;
670 _initialize_rocm_solib ()
672 /* The dependency on the amd-dbgapi exists because solib-rocm's
673 inferior_created observer needs amd-dbgapi to have attached the process,
674 which happens in amd_dbgapi_target's inferior_created observer. */
675 gdb::observers::inferior_created
.attach
676 (rocm_solib_target_inferior_created
,
678 { &get_amd_dbgapi_target_inferior_created_observer_token () });