mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-21 01:12:32 +08:00
14ade91660
The copyright years in the ROCm files (e.g. solib-rocm.c) are wrong, they end in 2022 instead of 2023. I suppose because I posted (or at least prepared) the patches in 2022 but merged them in 2023, and forgot to update the year. I found a bunch of other files that are in the same situation. Fix them all up. Change-Id: Ia55f5b563606c2ba6a89046f22bc0bf1c0ff2e10 Reviewed-By: Tom Tromey <tom@tromey.com>
680 lines
18 KiB
C
680 lines
18 KiB
C
/* Handle ROCm Code Objects for GDB, the GNU Debugger.
|
|
|
|
Copyright (C) 2019-2023 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "defs.h"
|
|
|
|
#include "amd-dbgapi-target.h"
|
|
#include "amdgpu-tdep.h"
|
|
#include "arch-utils.h"
|
|
#include "elf-bfd.h"
|
|
#include "elf/amdgpu.h"
|
|
#include "gdbsupport/fileio.h"
|
|
#include "inferior.h"
|
|
#include "observable.h"
|
|
#include "solib.h"
|
|
#include "solib-svr4.h"
|
|
#include "solist.h"
|
|
#include "symfile.h"
|
|
|
|
/* ROCm-specific inferior data. */
|
|
|
|
struct solib_info
|
|
{
|
|
/* List of code objects loaded into the inferior. */
|
|
so_list *solib_list;
|
|
};
|
|
|
|
/* Per-inferior data key. */
|
|
static const registry<inferior>::key<solib_info> rocm_solib_data;
|
|
|
|
static target_so_ops rocm_solib_ops;
|
|
|
|
/* Free the solib linked list. */
|
|
|
|
static void
|
|
rocm_free_solib_list (struct solib_info *info)
|
|
{
|
|
while (info->solib_list != nullptr)
|
|
{
|
|
struct so_list *next = info->solib_list->next;
|
|
|
|
free_so (info->solib_list);
|
|
info->solib_list = next;
|
|
}
|
|
|
|
info->solib_list = nullptr;
|
|
}
|
|
|
|
|
|
/* Fetch the solib_info data for INF. */
|
|
|
|
static struct solib_info *
|
|
get_solib_info (inferior *inf)
|
|
{
|
|
solib_info *info = rocm_solib_data.get (inf);
|
|
|
|
if (info == nullptr)
|
|
info = rocm_solib_data.emplace (inf);
|
|
|
|
return info;
|
|
}
|
|
|
|
/* Relocate section addresses. */
|
|
|
|
static void
|
|
rocm_solib_relocate_section_addresses (struct so_list *so,
|
|
struct target_section *sec)
|
|
{
|
|
if (!is_amdgpu_arch (gdbarch_from_bfd (so->abfd)))
|
|
{
|
|
svr4_so_ops.relocate_section_addresses (so, sec);
|
|
return;
|
|
}
|
|
|
|
lm_info_svr4 *li = (lm_info_svr4 *) so->lm_info;
|
|
sec->addr = sec->addr + li->l_addr;
|
|
sec->endaddr = sec->endaddr + li->l_addr;
|
|
}
|
|
|
|
static void rocm_update_solib_list ();
|
|
|
|
static void
|
|
rocm_solib_handle_event ()
|
|
{
|
|
/* Since we sit on top of svr4_so_ops, we might get called following an event
|
|
concerning host libraries. We must therefore forward the call. If the
|
|
event was for a ROCm code object, it will be a no-op. On the other hand,
|
|
if the event was for host libraries, rocm_update_solib_list will be
|
|
essentially be a no-op (it will reload the same code object list as was
|
|
previously loaded). */
|
|
svr4_so_ops.handle_event ();
|
|
|
|
rocm_update_solib_list ();
|
|
}
|
|
|
|
/* Make a deep copy of the solib linked list. */
|
|
|
|
static so_list *
|
|
rocm_solib_copy_list (const so_list *src)
|
|
{
|
|
struct so_list *dst = nullptr;
|
|
struct so_list **link = &dst;
|
|
|
|
while (src != nullptr)
|
|
{
|
|
struct so_list *newobj;
|
|
|
|
newobj = XNEW (struct so_list);
|
|
memcpy (newobj, src, sizeof (struct so_list));
|
|
|
|
lm_info_svr4 *src_li = (lm_info_svr4 *) src->lm_info;
|
|
newobj->lm_info = new lm_info_svr4 (*src_li);
|
|
|
|
newobj->next = nullptr;
|
|
*link = newobj;
|
|
link = &newobj->next;
|
|
|
|
src = src->next;
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
/* Build a list of `struct so_list' objects describing the shared
|
|
objects currently loaded in the inferior. */
|
|
|
|
static struct so_list *
|
|
rocm_solib_current_sos ()
|
|
{
|
|
/* First, retrieve the host-side shared library list. */
|
|
so_list *head = svr4_so_ops.current_sos ();
|
|
|
|
/* Then, the device-side shared library list. */
|
|
so_list *list = get_solib_info (current_inferior ())->solib_list;
|
|
|
|
if (list == nullptr)
|
|
return head;
|
|
|
|
list = rocm_solib_copy_list (list);
|
|
|
|
if (head == nullptr)
|
|
return list;
|
|
|
|
/* Append our libraries to the end of the list. */
|
|
so_list *tail;
|
|
for (tail = head; tail->next; tail = tail->next)
|
|
/* Nothing. */;
|
|
tail->next = list;
|
|
|
|
return head;
|
|
}
|
|
|
|
namespace {
|
|
|
|
/* Interface to interact with a ROCm code object stream. */
|
|
|
|
struct rocm_code_object_stream
|
|
{
|
|
DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream);
|
|
|
|
/* Copy SIZE bytes from the underlying objfile storage starting at OFFSET
|
|
into the user provided buffer BUF.
|
|
|
|
Return the number of bytes actually copied (might be inferior to SIZE if
|
|
the end of the stream is reached). */
|
|
virtual file_ptr read (void *buf, file_ptr size, file_ptr offset) = 0;
|
|
|
|
/* Retrieve file information in SB.
|
|
|
|
Return 0 on success. On failure, set the appropriate bfd error number
|
|
(using bfd_set_error) and return -1. */
|
|
int stat (struct stat *sb);
|
|
|
|
virtual ~rocm_code_object_stream () = default;
|
|
|
|
protected:
|
|
rocm_code_object_stream () = default;
|
|
|
|
/* Return the size of the object file, or -1 if the size cannot be
|
|
determined.
|
|
|
|
This is a helper function for stat. */
|
|
virtual LONGEST size () = 0;
|
|
};
|
|
|
|
int
|
|
rocm_code_object_stream::stat (struct stat *sb)
|
|
{
|
|
const LONGEST size = this->size ();
|
|
if (size == -1)
|
|
return -1;
|
|
|
|
memset (sb, '\0', sizeof (struct stat));
|
|
sb->st_size = size;
|
|
return 0;
|
|
}
|
|
|
|
/* Interface to a ROCm object stream which is embedded in an ELF file
|
|
accessible to the debugger. */
|
|
|
|
struct rocm_code_object_stream_file final : rocm_code_object_stream
|
|
{
|
|
DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_file);
|
|
|
|
rocm_code_object_stream_file (int fd, ULONGEST offset, ULONGEST size);
|
|
|
|
file_ptr read (void *buf, file_ptr size, file_ptr offset) override;
|
|
|
|
LONGEST size () override;
|
|
|
|
~rocm_code_object_stream_file () override;
|
|
|
|
protected:
|
|
|
|
/* The target file descriptor for this stream. */
|
|
int m_fd;
|
|
|
|
/* The offset of the ELF file image in the target file. */
|
|
ULONGEST m_offset;
|
|
|
|
/* The size of the ELF file image. The value 0 means that it was
|
|
unspecified in the URI descriptor. */
|
|
ULONGEST m_size;
|
|
};
|
|
|
|
rocm_code_object_stream_file::rocm_code_object_stream_file
|
|
(int fd, ULONGEST offset, ULONGEST size)
|
|
: m_fd (fd), m_offset (offset), m_size (size)
|
|
{
|
|
}
|
|
|
|
file_ptr
|
|
rocm_code_object_stream_file::read (void *buf, file_ptr size,
|
|
file_ptr offset)
|
|
{
|
|
fileio_error target_errno;
|
|
file_ptr nbytes = 0;
|
|
while (size > 0)
|
|
{
|
|
QUIT;
|
|
|
|
file_ptr bytes_read
|
|
= target_fileio_pread (m_fd, static_cast<gdb_byte *> (buf) + nbytes,
|
|
size, m_offset + offset + nbytes,
|
|
&target_errno);
|
|
|
|
if (bytes_read == 0)
|
|
break;
|
|
|
|
if (bytes_read < 0)
|
|
{
|
|
errno = fileio_error_to_host (target_errno);
|
|
bfd_set_error (bfd_error_system_call);
|
|
return -1;
|
|
}
|
|
|
|
nbytes += bytes_read;
|
|
size -= bytes_read;
|
|
}
|
|
|
|
return nbytes;
|
|
}
|
|
|
|
LONGEST
|
|
rocm_code_object_stream_file::size ()
|
|
{
|
|
if (m_size == 0)
|
|
{
|
|
fileio_error target_errno;
|
|
struct stat stat;
|
|
if (target_fileio_fstat (m_fd, &stat, &target_errno) < 0)
|
|
{
|
|
errno = fileio_error_to_host (target_errno);
|
|
bfd_set_error (bfd_error_system_call);
|
|
return -1;
|
|
}
|
|
|
|
/* Check that the offset is valid. */
|
|
if (m_offset >= stat.st_size)
|
|
{
|
|
bfd_set_error (bfd_error_bad_value);
|
|
return -1;
|
|
}
|
|
|
|
m_size = stat.st_size - m_offset;
|
|
}
|
|
|
|
return m_size;
|
|
}
|
|
|
|
rocm_code_object_stream_file::~rocm_code_object_stream_file ()
|
|
{
|
|
fileio_error target_errno;
|
|
target_fileio_close (m_fd, &target_errno);
|
|
}
|
|
|
|
/* Interface to a code object which lives in the inferior's memory. */
|
|
|
|
struct rocm_code_object_stream_memory final : public rocm_code_object_stream
|
|
{
|
|
DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_memory);
|
|
|
|
rocm_code_object_stream_memory (gdb::byte_vector buffer);
|
|
|
|
file_ptr read (void *buf, file_ptr size, file_ptr offset) override;
|
|
|
|
protected:
|
|
|
|
/* Snapshot of the original ELF image taken during load. This is done to
|
|
support the situation where an inferior uses an in-memory image, and
|
|
releases or re-uses this memory before GDB is done using it. */
|
|
gdb::byte_vector m_objfile_image;
|
|
|
|
LONGEST size () override
|
|
{
|
|
return m_objfile_image.size ();
|
|
}
|
|
};
|
|
|
|
rocm_code_object_stream_memory::rocm_code_object_stream_memory
|
|
(gdb::byte_vector buffer)
|
|
: m_objfile_image (std::move (buffer))
|
|
{
|
|
}
|
|
|
|
file_ptr
|
|
rocm_code_object_stream_memory::read (void *buf, file_ptr size,
|
|
file_ptr offset)
|
|
{
|
|
if (size > m_objfile_image.size () - offset)
|
|
size = m_objfile_image.size () - offset;
|
|
|
|
memcpy (buf, m_objfile_image.data () + offset, size);
|
|
return size;
|
|
}
|
|
|
|
} /* anonymous namespace */
|
|
|
|
static void *
|
|
rocm_bfd_iovec_open (bfd *abfd, void *inferior_void)
|
|
{
|
|
gdb::string_view uri (bfd_get_filename (abfd));
|
|
gdb::string_view protocol_delim = "://";
|
|
size_t protocol_end = uri.find (protocol_delim);
|
|
std::string protocol = gdb::to_string (uri.substr (0, protocol_end));
|
|
protocol_end += protocol_delim.length ();
|
|
|
|
std::transform (protocol.begin (), protocol.end (), protocol.begin (),
|
|
[] (unsigned char c) { return std::tolower (c); });
|
|
|
|
gdb::string_view path;
|
|
size_t path_end = uri.find_first_of ("#?", protocol_end);
|
|
if (path_end != std::string::npos)
|
|
path = uri.substr (protocol_end, path_end++ - protocol_end);
|
|
else
|
|
path = uri.substr (protocol_end);
|
|
|
|
/* %-decode the string. */
|
|
std::string decoded_path;
|
|
decoded_path.reserve (path.length ());
|
|
for (size_t i = 0; i < path.length (); ++i)
|
|
if (path[i] == '%'
|
|
&& i < path.length () - 2
|
|
&& std::isxdigit (path[i + 1])
|
|
&& std::isxdigit (path[i + 2]))
|
|
{
|
|
gdb::string_view hex_digits = path.substr (i + 1, 2);
|
|
decoded_path += std::stoi (gdb::to_string (hex_digits), 0, 16);
|
|
i += 2;
|
|
}
|
|
else
|
|
decoded_path += path[i];
|
|
|
|
/* Tokenize the query/fragment. */
|
|
std::vector<gdb::string_view> tokens;
|
|
size_t pos, last = path_end;
|
|
while ((pos = uri.find ('&', last)) != std::string::npos)
|
|
{
|
|
tokens.emplace_back (uri.substr (last, pos - last));
|
|
last = pos + 1;
|
|
}
|
|
|
|
if (last != std::string::npos)
|
|
tokens.emplace_back (uri.substr (last));
|
|
|
|
/* Create a tag-value map from the tokenized query/fragment. */
|
|
std::unordered_map<gdb::string_view, gdb::string_view,
|
|
gdb::string_view_hash> params;
|
|
for (gdb::string_view token : tokens)
|
|
{
|
|
size_t delim = token.find ('=');
|
|
if (delim != std::string::npos)
|
|
{
|
|
gdb::string_view tag = token.substr (0, delim);
|
|
gdb::string_view val = token.substr (delim + 1);
|
|
params.emplace (tag, val);
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
ULONGEST offset = 0;
|
|
ULONGEST size = 0;
|
|
inferior *inferior = static_cast<struct inferior *> (inferior_void);
|
|
|
|
auto try_strtoulst = [] (gdb::string_view v)
|
|
{
|
|
errno = 0;
|
|
ULONGEST value = strtoulst (v.data (), nullptr, 0);
|
|
if (errno != 0)
|
|
{
|
|
/* The actual message doesn't matter, the exception is caught
|
|
below, transformed in a BFD error, and the message is lost. */
|
|
error (_("Failed to parse integer."));
|
|
}
|
|
|
|
return value;
|
|
};
|
|
|
|
auto offset_it = params.find ("offset");
|
|
if (offset_it != params.end ())
|
|
offset = try_strtoulst (offset_it->second);
|
|
|
|
auto size_it = params.find ("size");
|
|
if (size_it != params.end ())
|
|
{
|
|
size = try_strtoulst (size_it->second);
|
|
if (size == 0)
|
|
error (_("Invalid size value"));
|
|
}
|
|
|
|
if (protocol == "file")
|
|
{
|
|
fileio_error target_errno;
|
|
int fd
|
|
= target_fileio_open (static_cast<struct inferior *> (inferior),
|
|
decoded_path.c_str (), FILEIO_O_RDONLY,
|
|
false, 0, &target_errno);
|
|
|
|
if (fd == -1)
|
|
{
|
|
errno = fileio_error_to_host (target_errno);
|
|
bfd_set_error (bfd_error_system_call);
|
|
return nullptr;
|
|
}
|
|
|
|
return new rocm_code_object_stream_file (fd, offset, size);
|
|
}
|
|
|
|
if (protocol == "memory")
|
|
{
|
|
ULONGEST pid = try_strtoulst (path);
|
|
if (pid != inferior->pid)
|
|
{
|
|
warning (_("`%s': code object is from another inferior"),
|
|
gdb::to_string (uri).c_str ());
|
|
bfd_set_error (bfd_error_bad_value);
|
|
return nullptr;
|
|
}
|
|
|
|
gdb::byte_vector buffer (size);
|
|
if (target_read_memory (offset, buffer.data (), size) != 0)
|
|
{
|
|
warning (_("Failed to copy the code object from the inferior"));
|
|
bfd_set_error (bfd_error_bad_value);
|
|
return nullptr;
|
|
}
|
|
|
|
return new rocm_code_object_stream_memory (std::move (buffer));
|
|
}
|
|
|
|
warning (_("`%s': protocol not supported: %s"),
|
|
gdb::to_string (uri).c_str (), protocol.c_str ());
|
|
bfd_set_error (bfd_error_bad_value);
|
|
return nullptr;
|
|
}
|
|
catch (const gdb_exception_quit &ex)
|
|
{
|
|
set_quit_flag ();
|
|
bfd_set_error (bfd_error_bad_value);
|
|
return nullptr;
|
|
}
|
|
catch (const gdb_exception &ex)
|
|
{
|
|
bfd_set_error (bfd_error_bad_value);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
static int
|
|
rocm_bfd_iovec_close (bfd *nbfd, void *data)
|
|
{
|
|
delete static_cast<rocm_code_object_stream *> (data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static file_ptr
|
|
rocm_bfd_iovec_pread (bfd *abfd, void *data, void *buf, file_ptr size,
|
|
file_ptr offset)
|
|
{
|
|
return static_cast<rocm_code_object_stream *> (data)->read (buf, size,
|
|
offset);
|
|
}
|
|
|
|
static int
|
|
rocm_bfd_iovec_stat (bfd *abfd, void *data, struct stat *sb)
|
|
{
|
|
return static_cast<rocm_code_object_stream *> (data)->stat (sb);
|
|
}
|
|
|
|
static gdb_bfd_ref_ptr
|
|
rocm_solib_bfd_open (const char *pathname)
|
|
{
|
|
/* Handle regular files with SVR4 open. */
|
|
if (strstr (pathname, "://") == nullptr)
|
|
return svr4_so_ops.bfd_open (pathname);
|
|
|
|
gdb_bfd_ref_ptr abfd
|
|
= gdb_bfd_openr_iovec (pathname, "elf64-amdgcn", rocm_bfd_iovec_open,
|
|
current_inferior (), rocm_bfd_iovec_pread,
|
|
rocm_bfd_iovec_close, rocm_bfd_iovec_stat);
|
|
|
|
if (abfd == nullptr)
|
|
error (_("Could not open `%s' as an executable file: %s"), pathname,
|
|
bfd_errmsg (bfd_get_error ()));
|
|
|
|
/* Check bfd format. */
|
|
if (!bfd_check_format (abfd.get (), bfd_object))
|
|
error (_("`%s': not in executable format: %s"),
|
|
bfd_get_filename (abfd.get ()), bfd_errmsg (bfd_get_error ()));
|
|
|
|
unsigned char osabi = elf_elfheader (abfd)->e_ident[EI_OSABI];
|
|
unsigned char osabiversion = elf_elfheader (abfd)->e_ident[EI_ABIVERSION];
|
|
|
|
/* Check that the code object is using the HSA OS ABI. */
|
|
if (osabi != ELFOSABI_AMDGPU_HSA)
|
|
error (_("`%s': ELF file OS ABI is not supported (%d)."),
|
|
bfd_get_filename (abfd.get ()), osabi);
|
|
|
|
/* We support HSA code objects V3 and greater. */
|
|
if (osabiversion < ELFABIVERSION_AMDGPU_HSA_V3)
|
|
error (_("`%s': ELF file HSA OS ABI version is not supported (%d)."),
|
|
bfd_get_filename (abfd.get ()), osabiversion);
|
|
|
|
return abfd;
|
|
}
|
|
|
|
static void
|
|
rocm_solib_create_inferior_hook (int from_tty)
|
|
{
|
|
rocm_free_solib_list (get_solib_info (current_inferior ()));
|
|
|
|
svr4_so_ops.solib_create_inferior_hook (from_tty);
|
|
}
|
|
|
|
static void
|
|
rocm_update_solib_list ()
|
|
{
|
|
inferior *inf = current_inferior ();
|
|
|
|
amd_dbgapi_process_id_t process_id = get_amd_dbgapi_process_id (inf);
|
|
if (process_id.handle == AMD_DBGAPI_PROCESS_NONE.handle)
|
|
return;
|
|
|
|
solib_info *info = get_solib_info (inf);
|
|
|
|
rocm_free_solib_list (info);
|
|
struct so_list **link = &info->solib_list;
|
|
|
|
amd_dbgapi_code_object_id_t *code_object_list;
|
|
size_t count;
|
|
|
|
amd_dbgapi_status_t status
|
|
= amd_dbgapi_process_code_object_list (process_id, &count,
|
|
&code_object_list, nullptr);
|
|
if (status != AMD_DBGAPI_STATUS_SUCCESS)
|
|
{
|
|
warning (_("amd_dbgapi_process_code_object_list failed (%s)"),
|
|
get_status_string (status));
|
|
return;
|
|
}
|
|
|
|
for (size_t i = 0; i < count; ++i)
|
|
{
|
|
CORE_ADDR l_addr;
|
|
char *uri_bytes;
|
|
|
|
status = amd_dbgapi_code_object_get_info
|
|
(code_object_list[i], AMD_DBGAPI_CODE_OBJECT_INFO_LOAD_ADDRESS,
|
|
sizeof (l_addr), &l_addr);
|
|
if (status != AMD_DBGAPI_STATUS_SUCCESS)
|
|
continue;
|
|
|
|
status = amd_dbgapi_code_object_get_info
|
|
(code_object_list[i], AMD_DBGAPI_CODE_OBJECT_INFO_URI_NAME,
|
|
sizeof (uri_bytes), &uri_bytes);
|
|
if (status != AMD_DBGAPI_STATUS_SUCCESS)
|
|
continue;
|
|
|
|
struct so_list *so = XCNEW (struct so_list);
|
|
lm_info_svr4 *li = new lm_info_svr4;
|
|
li->l_addr = l_addr;
|
|
so->lm_info = li;
|
|
|
|
strncpy (so->so_name, uri_bytes, sizeof (so->so_name));
|
|
so->so_name[sizeof (so->so_name) - 1] = '\0';
|
|
xfree (uri_bytes);
|
|
|
|
/* Make so_original_name unique so that code objects with the same URI
|
|
but different load addresses are seen by gdb core as different shared
|
|
objects. */
|
|
xsnprintf (so->so_original_name, sizeof (so->so_original_name),
|
|
"code_object_%ld", code_object_list[i].handle);
|
|
|
|
so->next = nullptr;
|
|
*link = so;
|
|
link = &so->next;
|
|
}
|
|
|
|
xfree (code_object_list);
|
|
|
|
if (rocm_solib_ops.current_sos == NULL)
|
|
{
|
|
/* Override what we need to. */
|
|
rocm_solib_ops = svr4_so_ops;
|
|
rocm_solib_ops.current_sos = rocm_solib_current_sos;
|
|
rocm_solib_ops.solib_create_inferior_hook
|
|
= rocm_solib_create_inferior_hook;
|
|
rocm_solib_ops.bfd_open = rocm_solib_bfd_open;
|
|
rocm_solib_ops.relocate_section_addresses
|
|
= rocm_solib_relocate_section_addresses;
|
|
rocm_solib_ops.handle_event = rocm_solib_handle_event;
|
|
|
|
/* Engage the ROCm so_ops. */
|
|
set_gdbarch_so_ops (current_inferior ()->gdbarch, &rocm_solib_ops);
|
|
}
|
|
}
|
|
|
|
static void
|
|
rocm_solib_target_inferior_created (inferior *inf)
|
|
{
|
|
rocm_free_solib_list (get_solib_info (inf));
|
|
rocm_update_solib_list ();
|
|
|
|
/* Force GDB to reload the solibs. */
|
|
current_inferior ()->pspace->clear_solib_cache ();
|
|
solib_add (nullptr, 0, auto_solib_add);
|
|
}
|
|
|
|
/* -Wmissing-prototypes */
|
|
extern initialize_file_ftype _initialize_rocm_solib;
|
|
|
|
void
|
|
_initialize_rocm_solib ()
|
|
{
|
|
/* The dependency on the amd-dbgapi exists because solib-rocm's
|
|
inferior_created observer needs amd-dbgapi to have attached the process,
|
|
which happens in amd_dbgapi_target's inferior_created observer. */
|
|
gdb::observers::inferior_created.attach
|
|
(rocm_solib_target_inferior_created,
|
|
"solib-rocm",
|
|
{ &get_amd_dbgapi_target_inferior_created_observer_token () });
|
|
}
|