mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-21 04:42:53 +08:00
6cedf3bcbf
I don't like the name `target_so_ops`, because: - The name `target` is so overloaded, and in this case it's not even related to target_ops or anything else called "target". - We do have an implementation that actually fetches solibs from the target (solib_target_so_op in solib-target.c), so it's confusing for the "base class" to be called target_something as well. Rename to solib_ops. Change-Id: I46a983d44e81400470e22deb09aaf26ad8a3587f Approved-By: Tom Tromey <tom@tromey.com>
785 lines
21 KiB
C
785 lines
21 KiB
C
/* Handle ROCm Code Objects for GDB, the GNU Debugger.
|
|
|
|
Copyright (C) 2019-2024 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"
|
|
|
|
#include <unordered_map>
|
|
|
|
namespace {
|
|
|
|
/* Per inferior cache of opened file descriptors. */
|
|
struct rocm_solib_fd_cache
|
|
{
|
|
explicit rocm_solib_fd_cache (inferior *inf) : m_inferior (inf) {}
|
|
DISABLE_COPY_AND_ASSIGN (rocm_solib_fd_cache);
|
|
|
|
/* Return a read-only file descriptor to FILENAME and increment the
|
|
associated reference count.
|
|
|
|
Open the file FILENAME if it is not already opened, reuse the existing file
|
|
descriptor otherwise.
|
|
|
|
On error -1 is returned, and TARGET_ERRNO is set. */
|
|
int open (const std::string &filename, fileio_error *target_errno);
|
|
|
|
/* Decrement the reference count to FD and close FD if the reference count
|
|
reaches 0.
|
|
|
|
On success, return 0. On error, return -1 and set TARGET_ERRNO. */
|
|
int close (int fd, fileio_error *target_errno);
|
|
|
|
private:
|
|
struct refcnt_fd
|
|
{
|
|
DISABLE_COPY_AND_ASSIGN (refcnt_fd);
|
|
refcnt_fd (int fd, int refcnt) : fd (fd), refcnt (refcnt) {}
|
|
|
|
int fd = -1;
|
|
int refcnt = 0;
|
|
};
|
|
|
|
inferior *m_inferior;
|
|
std::unordered_map<std::string, refcnt_fd> m_cache;
|
|
};
|
|
|
|
int
|
|
rocm_solib_fd_cache::open (const std::string &filename,
|
|
fileio_error *target_errno)
|
|
{
|
|
auto it = m_cache.find (filename);
|
|
if (it == m_cache.end ())
|
|
{
|
|
/* The file is not yet opened on the target. */
|
|
int fd
|
|
= target_fileio_open (m_inferior, filename.c_str (), FILEIO_O_RDONLY,
|
|
false, 0, target_errno);
|
|
if (fd != -1)
|
|
m_cache.emplace (std::piecewise_construct,
|
|
std::forward_as_tuple (filename),
|
|
std::forward_as_tuple (fd, 1));
|
|
return fd;
|
|
}
|
|
else
|
|
{
|
|
/* The file is already opened. Increment the refcnt and return the
|
|
already opened FD. */
|
|
it->second.refcnt++;
|
|
gdb_assert (it->second.fd != -1);
|
|
return it->second.fd;
|
|
}
|
|
}
|
|
|
|
int
|
|
rocm_solib_fd_cache::close (int fd, fileio_error *target_errno)
|
|
{
|
|
using cache_val = std::unordered_map<std::string, refcnt_fd>::value_type;
|
|
auto it
|
|
= std::find_if (m_cache.begin (), m_cache.end (),
|
|
[fd](const cache_val &s) { return s.second.fd == fd; });
|
|
|
|
gdb_assert (it != m_cache.end ());
|
|
|
|
it->second.refcnt--;
|
|
if (it->second.refcnt == 0)
|
|
{
|
|
int ret = target_fileio_close (it->second.fd, target_errno);
|
|
m_cache.erase (it);
|
|
return ret;
|
|
}
|
|
else
|
|
{
|
|
/* Keep the FD open for the other users, return success. */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
} /* Anonymous namespace. */
|
|
|
|
/* ROCm-specific inferior data. */
|
|
|
|
struct rocm_so
|
|
{
|
|
rocm_so (const char *name, std::string unique_name, lm_info_svr4_up lm_info)
|
|
: name (name),
|
|
unique_name (std::move (unique_name)),
|
|
lm_info (std::move (lm_info))
|
|
{}
|
|
|
|
std::string name, unique_name;
|
|
lm_info_svr4_up lm_info;
|
|
};
|
|
|
|
struct solib_info
|
|
{
|
|
explicit solib_info (inferior *inf)
|
|
: fd_cache (inf)
|
|
{};
|
|
|
|
/* List of code objects loaded into the inferior. */
|
|
std::vector<rocm_so> solib_list;
|
|
|
|
/* Cache of opened FD in the inferior. */
|
|
rocm_solib_fd_cache fd_cache;
|
|
};
|
|
|
|
/* Per-inferior data key. */
|
|
static const registry<inferior>::key<solib_info> rocm_solib_data;
|
|
|
|
static solib_ops rocm_solib_ops;
|
|
|
|
/* 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, inf);
|
|
|
|
return info;
|
|
}
|
|
|
|
/* Relocate section addresses. */
|
|
|
|
static void
|
|
rocm_solib_relocate_section_addresses (solib &so,
|
|
struct target_section *sec)
|
|
{
|
|
if (!is_amdgpu_arch (gdbarch_from_bfd (so.abfd.get ())))
|
|
{
|
|
svr4_so_ops.relocate_section_addresses (so, sec);
|
|
return;
|
|
}
|
|
|
|
auto *li = gdb::checked_static_cast<lm_info_svr4 *> (so.lm_info.get ());
|
|
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 ();
|
|
}
|
|
|
|
/* Create so_list objects from rocm_so objects in SOS. */
|
|
|
|
static intrusive_list<solib>
|
|
so_list_from_rocm_sos (const std::vector<rocm_so> &sos)
|
|
{
|
|
intrusive_list<solib> dst;
|
|
|
|
for (const rocm_so &so : sos)
|
|
{
|
|
solib *newobj = new solib;
|
|
newobj->lm_info = std::make_unique<lm_info_svr4> (*so.lm_info);
|
|
|
|
newobj->so_name = so.name;
|
|
newobj->so_original_name = so.unique_name;
|
|
|
|
dst.push_back (*newobj);
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
/* Build a list of `struct solib' objects describing the shared
|
|
objects currently loaded in the inferior. */
|
|
|
|
static intrusive_list<solib>
|
|
rocm_solib_current_sos ()
|
|
{
|
|
/* First, retrieve the host-side shared library list. */
|
|
intrusive_list<solib> sos = svr4_so_ops.current_sos ();
|
|
|
|
/* Then, the device-side shared library list. */
|
|
std::vector<rocm_so> &dev_sos = get_solib_info (current_inferior ())->solib_list;
|
|
|
|
if (dev_sos.empty ())
|
|
return sos;
|
|
|
|
intrusive_list<solib> dev_so_list = so_list_from_rocm_sos (dev_sos);
|
|
|
|
if (sos.empty ())
|
|
return dev_so_list;
|
|
|
|
/* Append our libraries to the end of the list. */
|
|
sos.splice (std::move (dev_so_list));
|
|
|
|
return sos;
|
|
}
|
|
|
|
namespace {
|
|
|
|
/* Interface to interact with a ROCm code object stream. */
|
|
|
|
struct rocm_code_object_stream : public gdb_bfd_iovec_base
|
|
{
|
|
DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream);
|
|
|
|
int stat (bfd *abfd, struct stat *sb) final override;
|
|
|
|
~rocm_code_object_stream () override = 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 (bfd *, 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 (inferior *inf, int fd, ULONGEST offset,
|
|
ULONGEST size);
|
|
|
|
file_ptr read (bfd *abfd, void *buf, file_ptr size,
|
|
file_ptr offset) override;
|
|
|
|
LONGEST size () override;
|
|
|
|
~rocm_code_object_stream_file () override;
|
|
|
|
protected:
|
|
|
|
/* The inferior owning this code object stream. */
|
|
inferior *m_inf;
|
|
|
|
/* 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
|
|
(inferior *inf, int fd, ULONGEST offset, ULONGEST size)
|
|
: m_inf (inf), m_fd (fd), m_offset (offset), m_size (size)
|
|
{
|
|
}
|
|
|
|
file_ptr
|
|
rocm_code_object_stream_file::read (bfd *, 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 ()
|
|
{
|
|
auto info = get_solib_info (m_inf);
|
|
fileio_error target_errno;
|
|
if (info->fd_cache.close (m_fd, &target_errno) != 0)
|
|
warning (_("Failed to close solib: %s"),
|
|
strerror (fileio_error_to_host (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 (bfd *abfd, 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 (bfd *, 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 gdb_bfd_iovec_base *
|
|
rocm_bfd_iovec_open (bfd *abfd, inferior *inferior)
|
|
{
|
|
std::string_view uri (bfd_get_filename (abfd));
|
|
std::string_view protocol_delim = "://";
|
|
size_t protocol_end = uri.find (protocol_delim);
|
|
std::string protocol (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); });
|
|
|
|
std::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]))
|
|
{
|
|
std::string_view hex_digits = path.substr (i + 1, 2);
|
|
decoded_path += std::stoi (std::string (hex_digits), 0, 16);
|
|
i += 2;
|
|
}
|
|
else
|
|
decoded_path += path[i];
|
|
|
|
/* Tokenize the query/fragment. */
|
|
std::vector<std::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<std::string_view, std::string_view,
|
|
gdb::string_view_hash> params;
|
|
for (std::string_view token : tokens)
|
|
{
|
|
size_t delim = token.find ('=');
|
|
if (delim != std::string::npos)
|
|
{
|
|
std::string_view tag = token.substr (0, delim);
|
|
std::string_view val = token.substr (delim + 1);
|
|
params.emplace (tag, val);
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
ULONGEST offset = 0;
|
|
ULONGEST size = 0;
|
|
|
|
auto try_strtoulst = [] (std::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")
|
|
{
|
|
auto info = get_solib_info (inferior);
|
|
fileio_error target_errno;
|
|
int fd = info->fd_cache.open (decoded_path, &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 (inferior, fd, offset,
|
|
size);
|
|
}
|
|
|
|
if (protocol == "memory")
|
|
{
|
|
ULONGEST pid = try_strtoulst (path);
|
|
if (pid != inferior->pid)
|
|
{
|
|
warning (_("`%s': code object is from another inferior"),
|
|
std::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"),
|
|
std::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 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);
|
|
|
|
auto open = [] (bfd *nbfd) -> gdb_bfd_iovec_base *
|
|
{
|
|
return rocm_bfd_iovec_open (nbfd, current_inferior ());
|
|
};
|
|
|
|
gdb_bfd_ref_ptr abfd = gdb_bfd_openr_iovec (pathname, "elf64-amdgcn", open);
|
|
|
|
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);
|
|
|
|
/* For GDB to be able to use this solib, the exact AMDGPU processor type
|
|
must be supported by both BFD and the amd-dbgapi library. */
|
|
const unsigned char gfx_arch
|
|
= elf_elfheader (abfd)->e_flags & EF_AMDGPU_MACH ;
|
|
const bfd_arch_info_type *bfd_arch_info
|
|
= bfd_lookup_arch (bfd_arch_amdgcn, gfx_arch);
|
|
|
|
amd_dbgapi_architecture_id_t architecture_id;
|
|
amd_dbgapi_status_t dbgapi_query_arch
|
|
= amd_dbgapi_get_architecture (gfx_arch, &architecture_id);
|
|
|
|
if (dbgapi_query_arch != AMD_DBGAPI_STATUS_SUCCESS
|
|
|| bfd_arch_info == nullptr)
|
|
{
|
|
if (dbgapi_query_arch != AMD_DBGAPI_STATUS_SUCCESS
|
|
&& bfd_arch_info == nullptr)
|
|
{
|
|
/* Neither of the libraries knows about this arch, so we cannot
|
|
provide a human readable name for it. */
|
|
error (_("'%s': AMDGCN architecture %#02x is not supported."),
|
|
bfd_get_filename (abfd.get ()), gfx_arch);
|
|
}
|
|
else if (dbgapi_query_arch != AMD_DBGAPI_STATUS_SUCCESS)
|
|
{
|
|
gdb_assert (bfd_arch_info != nullptr);
|
|
error (_("'%s': AMDGCN architecture %s not supported by "
|
|
"amd-dbgapi."),
|
|
bfd_get_filename (abfd.get ()),
|
|
bfd_arch_info->printable_name);
|
|
}
|
|
else
|
|
{
|
|
gdb_assert (dbgapi_query_arch == AMD_DBGAPI_STATUS_SUCCESS);
|
|
char *arch_name;
|
|
if (amd_dbgapi_architecture_get_info
|
|
(architecture_id, AMD_DBGAPI_ARCHITECTURE_INFO_NAME,
|
|
sizeof (arch_name), &arch_name) != AMD_DBGAPI_STATUS_SUCCESS)
|
|
error ("amd_dbgapi_architecture_get_info call failed for arch "
|
|
"%#02x.", gfx_arch);
|
|
gdb::unique_xmalloc_ptr<char> arch_name_cleaner (arch_name);
|
|
|
|
error (_("'%s': AMDGCN architecture %s not supported."),
|
|
bfd_get_filename (abfd.get ()),
|
|
arch_name);
|
|
}
|
|
}
|
|
|
|
gdb_assert (gdbarch_from_bfd (abfd.get ()) != nullptr);
|
|
gdb_assert (is_amdgpu_arch (gdbarch_from_bfd (abfd.get ())));
|
|
|
|
return abfd;
|
|
}
|
|
|
|
static void
|
|
rocm_solib_create_inferior_hook (int from_tty)
|
|
{
|
|
get_solib_info (current_inferior ())->solib_list.clear ();
|
|
|
|
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);
|
|
|
|
info->solib_list.clear ();
|
|
std::vector<rocm_so> &sos = 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;
|
|
|
|
gdb::unique_xmalloc_ptr<char> uri_bytes_holder (uri_bytes);
|
|
|
|
lm_info_svr4_up li = std::make_unique<lm_info_svr4> ();
|
|
li->l_addr = l_addr;
|
|
|
|
/* Generate a unique name so that code objects with the same URI but
|
|
different load addresses are seen by gdb core as different shared
|
|
objects. */
|
|
std::string unique_name
|
|
= string_printf ("code_object_%ld", code_object_list[i].handle);
|
|
|
|
sos.emplace_back (uri_bytes, std::move (unique_name), std::move (li));
|
|
}
|
|
|
|
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 ()->arch (), &rocm_solib_ops);
|
|
}
|
|
}
|
|
|
|
static void
|
|
rocm_solib_target_inferior_created (inferior *inf)
|
|
{
|
|
get_solib_info (inf)->solib_list.clear ();
|
|
|
|
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 () });
|
|
}
|