Fix null pointer dereference in process_debug_info()
[binutils-gdb.git] / gdb / solib-rocm.c
blobb714e4ef6ccec40da1a8892f97ceb64a6eac5769
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"
24 #include "elf-bfd.h"
25 #include "elf/amdgpu.h"
26 #include "gdbsupport/fileio.h"
27 #include "inferior.h"
28 #include "observable.h"
29 #include "solib.h"
30 #include "solib-svr4.h"
31 #include "solist.h"
32 #include "symfile.h"
34 #include <unordered_map>
36 namespace {
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
48 descriptor otherwise.
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
54 reaches 0.
56 On success, return 0. On error, return -1 and set TARGET_ERRNO. */
57 int close (int fd, fileio_error *target_errno);
59 private:
60 struct refcnt_fd
62 DISABLE_COPY_AND_ASSIGN (refcnt_fd);
63 refcnt_fd (int fd, int refcnt) : fd (fd), refcnt (refcnt) {}
65 int fd = -1;
66 int refcnt = 0;
69 inferior *m_inferior;
70 std::unordered_map<std::string, refcnt_fd> m_cache;
73 int
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. */
81 int fd
82 = target_fileio_open (m_inferior, filename.c_str (), FILEIO_O_RDONLY,
83 false, 0, target_errno);
84 if (fd != -1)
85 m_cache.emplace (std::piecewise_construct,
86 std::forward_as_tuple (filename),
87 std::forward_as_tuple (fd, 1));
88 return fd;
90 else
92 /* The file is already opened. Increment the refcnt and return the
93 already opened FD. */
94 it->second.refcnt++;
95 gdb_assert (it->second.fd != -1);
96 return it->second.fd;
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;
104 auto it
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 ());
110 it->second.refcnt--;
111 if (it->second.refcnt == 0)
113 int ret = target_fileio_close (it->second.fd, target_errno);
114 m_cache.erase (it);
115 return ret;
117 else
119 /* Keep the FD open for the other users, return success. */
120 return 0;
124 } /* Anonymous namespace. */
126 /* ROCm-specific inferior data. */
128 struct rocm_so
130 rocm_so (const char *name, std::string unique_name, lm_info_svr4_up lm_info)
131 : name (name),
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;
140 struct solib_info
142 explicit solib_info (inferior *inf)
143 : fd_cache (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);
165 if (info == nullptr)
166 info = rocm_solib_data.emplace (inf, inf);
168 return info;
171 /* Relocate section addresses. */
173 static void
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);
180 return;
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 ();
190 static void
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);
222 return dst;
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 ())
238 return sos;
240 intrusive_list<solib> dev_so_list = so_list_from_rocm_sos (dev_sos);
242 if (sos.empty ())
243 return dev_so_list;
245 /* Append our libraries to the end of the list. */
246 sos.splice (std::move (dev_so_list));
248 return sos;
251 namespace {
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;
263 protected:
264 rocm_code_object_stream () = default;
266 /* Return the size of the object file, or -1 if the size cannot be
267 determined.
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 ();
277 if (size == -1)
278 return -1;
280 memset (sb, '\0', sizeof (struct stat));
281 sb->st_size = size;
282 return 0;
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,
293 ULONGEST size);
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;
302 protected:
304 /* The inferior owning this code object stream. */
305 inferior *m_inf;
307 /* The target file descriptor for this stream. */
308 int m_fd;
310 /* The offset of the ELF file image in the target file. */
311 ULONGEST m_offset;
313 /* The size of the ELF file image. The value 0 means that it was
314 unspecified in the URI descriptor. */
315 ULONGEST m_size;
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)
324 file_ptr
325 rocm_code_object_stream_file::read (bfd *, void *buf, file_ptr size,
326 file_ptr offset)
328 fileio_error target_errno;
329 file_ptr nbytes = 0;
330 while (size > 0)
332 QUIT;
334 file_ptr bytes_read
335 = target_fileio_pread (m_fd, static_cast<gdb_byte *> (buf) + nbytes,
336 size, m_offset + offset + nbytes,
337 &target_errno);
339 if (bytes_read == 0)
340 break;
342 if (bytes_read < 0)
344 errno = fileio_error_to_host (target_errno);
345 bfd_set_error (bfd_error_system_call);
346 return -1;
349 nbytes += bytes_read;
350 size -= bytes_read;
353 return nbytes;
356 LONGEST
357 rocm_code_object_stream_file::size ()
359 if (m_size == 0)
361 fileio_error target_errno;
362 struct stat stat;
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);
367 return -1;
370 /* Check that the offset is valid. */
371 if (m_offset >= stat.st_size)
373 bfd_set_error (bfd_error_bad_value);
374 return -1;
377 m_size = stat.st_size - m_offset;
380 return m_size;
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;
403 protected:
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))
422 file_ptr
423 rocm_code_object_stream_memory::read (bfd *, void *buf, file_ptr size,
424 file_ptr offset)
426 if (size > m_objfile_image.size () - offset)
427 size = m_objfile_image.size () - offset;
429 memcpy (buf, m_objfile_image.data () + offset, size);
430 return 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);
451 else
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)
458 if (path[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);
465 i += 2;
467 else
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));
476 last = pos + 1;
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);
498 ULONGEST offset = 0;
499 ULONGEST size = 0;
501 auto try_strtoulst = [] (std::string_view v)
503 errno = 0;
504 ULONGEST value = strtoulst (v.data (), nullptr, 0);
505 if (errno != 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."));
512 return value;
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);
523 if (size == 0)
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);
533 if (fd == -1)
535 errno = fileio_error_to_host (target_errno);
536 bfd_set_error (bfd_error_system_call);
537 return nullptr;
540 return new rocm_code_object_stream_file (inferior, fd, offset,
541 size);
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);
552 return nullptr;
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);
560 return nullptr;
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);
569 return nullptr;
571 catch (const gdb_exception_quit &ex)
573 set_quit_flag ();
574 bfd_set_error (bfd_error_bad_value);
575 return nullptr;
577 catch (const gdb_exception &ex)
579 bfd_set_error (bfd_error_bad_value);
580 return nullptr;
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);
598 if (abfd == nullptr)
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 "
646 "amd-dbgapi."),
647 bfd_get_filename (abfd.get ()),
648 bfd_arch_info->printable_name);
650 else
652 gdb_assert (dbgapi_query_arch == AMD_DBGAPI_STATUS_SUCCESS);
653 char *arch_name;
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 "
658 "%#02x.", gfx_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 ()),
663 arch_name);
667 gdb_assert (gdbarch_from_bfd (abfd.get ()) != nullptr);
668 gdb_assert (is_amdgpu_arch (gdbarch_from_bfd (abfd.get ())));
670 return abfd;
673 static void
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);
681 static void
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)
688 return;
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;
696 size_t count;
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));
705 return;
708 for (size_t i = 0; i < count; ++i)
710 CORE_ADDR l_addr;
711 char *uri_bytes;
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)
717 continue;
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)
723 continue;
725 gdb::unique_xmalloc_ptr<char> uri_bytes_holder (uri_bytes);
727 lm_info_svr4_up li = std::make_unique<lm_info_svr4> ();
728 li->l_addr = l_addr;
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
732 objects. */
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);
758 static void
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;
773 void
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,
781 "solib-rocm",
782 { &get_amd_dbgapi_target_inferior_created_observer_token () });