Home | History | Annotate | Line # | Download | only in gdb
solib-rocm.c revision 1.1
      1  1.1  christos /* Handle ROCm Code Objects for GDB, the GNU Debugger.
      2  1.1  christos 
      3  1.1  christos    Copyright (C) 2019-2024 Free Software Foundation, Inc.
      4  1.1  christos 
      5  1.1  christos    This file is part of GDB.
      6  1.1  christos 
      7  1.1  christos    This program is free software; you can redistribute it and/or modify
      8  1.1  christos    it under the terms of the GNU General Public License as published by
      9  1.1  christos    the Free Software Foundation; either version 3 of the License, or
     10  1.1  christos    (at your option) any later version.
     11  1.1  christos 
     12  1.1  christos    This program is distributed in the hope that it will be useful,
     13  1.1  christos    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  1.1  christos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15  1.1  christos    GNU General Public License for more details.
     16  1.1  christos 
     17  1.1  christos    You should have received a copy of the GNU General Public License
     18  1.1  christos    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     19  1.1  christos 
     20  1.1  christos 
     21  1.1  christos #include "amd-dbgapi-target.h"
     22  1.1  christos #include "amdgpu-tdep.h"
     23  1.1  christos #include "arch-utils.h"
     24  1.1  christos #include "elf-bfd.h"
     25  1.1  christos #include "elf/amdgpu.h"
     26  1.1  christos #include "event-top.h"
     27  1.1  christos #include "gdbsupport/fileio.h"
     28  1.1  christos #include "inferior.h"
     29  1.1  christos #include "observable.h"
     30  1.1  christos #include "solib.h"
     31  1.1  christos #include "solib-svr4.h"
     32  1.1  christos #include "solist.h"
     33  1.1  christos #include "symfile.h"
     34  1.1  christos 
     35  1.1  christos #include <unordered_map>
     36  1.1  christos 
     37  1.1  christos namespace {
     38  1.1  christos 
     39  1.1  christos /* Per inferior cache of opened file descriptors.  */
     40  1.1  christos struct rocm_solib_fd_cache
     41  1.1  christos {
     42  1.1  christos   explicit rocm_solib_fd_cache (inferior *inf) : m_inferior (inf) {}
     43  1.1  christos   DISABLE_COPY_AND_ASSIGN (rocm_solib_fd_cache);
     44  1.1  christos 
     45  1.1  christos   /* Return a read-only file descriptor to FILENAME and increment the
     46  1.1  christos      associated reference count.
     47  1.1  christos 
     48  1.1  christos      Open the file FILENAME if it is not already opened, reuse the existing file
     49  1.1  christos      descriptor otherwise.
     50  1.1  christos 
     51  1.1  christos      On error -1 is returned, and TARGET_ERRNO is set.  */
     52  1.1  christos   int open (const std::string &filename, fileio_error *target_errno);
     53  1.1  christos 
     54  1.1  christos   /* Decrement the reference count to FD and close FD if the reference count
     55  1.1  christos      reaches 0.
     56  1.1  christos 
     57  1.1  christos      On success, return 0.  On error, return -1 and set TARGET_ERRNO.  */
     58  1.1  christos   int close (int fd, fileio_error *target_errno);
     59  1.1  christos 
     60  1.1  christos private:
     61  1.1  christos   struct refcnt_fd
     62  1.1  christos   {
     63  1.1  christos     DISABLE_COPY_AND_ASSIGN (refcnt_fd);
     64  1.1  christos     refcnt_fd (int fd, int refcnt) : fd (fd), refcnt (refcnt) {}
     65  1.1  christos 
     66  1.1  christos     int fd = -1;
     67  1.1  christos     int refcnt = 0;
     68  1.1  christos   };
     69  1.1  christos 
     70  1.1  christos   inferior *m_inferior;
     71  1.1  christos   std::unordered_map<std::string, refcnt_fd> m_cache;
     72  1.1  christos };
     73  1.1  christos 
     74  1.1  christos int
     75  1.1  christos rocm_solib_fd_cache::open (const std::string &filename,
     76  1.1  christos 			   fileio_error *target_errno)
     77  1.1  christos {
     78  1.1  christos   auto it = m_cache.find (filename);
     79  1.1  christos   if (it == m_cache.end ())
     80  1.1  christos     {
     81  1.1  christos       /* The file is not yet opened on the target.  */
     82  1.1  christos       int fd
     83  1.1  christos 	= target_fileio_open (m_inferior, filename.c_str (), FILEIO_O_RDONLY,
     84  1.1  christos 			      false, 0, target_errno);
     85  1.1  christos       if (fd != -1)
     86  1.1  christos 	m_cache.emplace (std::piecewise_construct,
     87  1.1  christos 			 std::forward_as_tuple (filename),
     88  1.1  christos 			 std::forward_as_tuple (fd, 1));
     89  1.1  christos       return fd;
     90  1.1  christos     }
     91  1.1  christos   else
     92  1.1  christos     {
     93  1.1  christos       /* The file is already opened.  Increment the refcnt and return the
     94  1.1  christos 	 already opened FD.  */
     95  1.1  christos       it->second.refcnt++;
     96  1.1  christos       gdb_assert (it->second.fd != -1);
     97  1.1  christos       return it->second.fd;
     98  1.1  christos     }
     99  1.1  christos }
    100  1.1  christos 
    101  1.1  christos int
    102  1.1  christos rocm_solib_fd_cache::close (int fd, fileio_error *target_errno)
    103  1.1  christos {
    104  1.1  christos   using cache_val = std::unordered_map<std::string, refcnt_fd>::value_type;
    105  1.1  christos   auto it
    106  1.1  christos     = std::find_if (m_cache.begin (), m_cache.end (),
    107  1.1  christos 		    [fd](const cache_val &s) { return s.second.fd == fd; });
    108  1.1  christos 
    109  1.1  christos   gdb_assert (it != m_cache.end ());
    110  1.1  christos 
    111  1.1  christos   it->second.refcnt--;
    112  1.1  christos   if (it->second.refcnt == 0)
    113  1.1  christos     {
    114  1.1  christos       int ret = target_fileio_close (it->second.fd, target_errno);
    115  1.1  christos       m_cache.erase (it);
    116  1.1  christos       return ret;
    117  1.1  christos     }
    118  1.1  christos   else
    119  1.1  christos     {
    120  1.1  christos       /* Keep the FD open for the other users, return success.  */
    121  1.1  christos       return 0;
    122  1.1  christos     }
    123  1.1  christos }
    124  1.1  christos 
    125  1.1  christos } /* Anonymous namespace.  */
    126  1.1  christos 
    127  1.1  christos /* ROCm-specific inferior data.  */
    128  1.1  christos 
    129  1.1  christos struct rocm_so
    130  1.1  christos {
    131  1.1  christos   rocm_so (const char *name, std::string unique_name, lm_info_svr4_up lm_info)
    132  1.1  christos     : name (name),
    133  1.1  christos       unique_name (std::move (unique_name)),
    134  1.1  christos       lm_info (std::move (lm_info))
    135  1.1  christos   {}
    136  1.1  christos 
    137  1.1  christos   std::string name, unique_name;
    138  1.1  christos   lm_info_svr4_up lm_info;
    139  1.1  christos };
    140  1.1  christos 
    141  1.1  christos struct solib_info
    142  1.1  christos {
    143  1.1  christos   explicit solib_info (inferior *inf)
    144  1.1  christos     : fd_cache (inf)
    145  1.1  christos   {};
    146  1.1  christos 
    147  1.1  christos   /* List of code objects loaded into the inferior.  */
    148  1.1  christos   std::vector<rocm_so> solib_list;
    149  1.1  christos 
    150  1.1  christos   /* Cache of opened FD in the inferior.  */
    151  1.1  christos   rocm_solib_fd_cache fd_cache;
    152  1.1  christos };
    153  1.1  christos 
    154  1.1  christos /* Per-inferior data key.  */
    155  1.1  christos static const registry<inferior>::key<solib_info> rocm_solib_data;
    156  1.1  christos 
    157  1.1  christos static solib_ops rocm_solib_ops;
    158  1.1  christos 
    159  1.1  christos /* Fetch the solib_info data for INF.  */
    160  1.1  christos 
    161  1.1  christos static struct solib_info *
    162  1.1  christos get_solib_info (inferior *inf)
    163  1.1  christos {
    164  1.1  christos   solib_info *info = rocm_solib_data.get (inf);
    165  1.1  christos 
    166  1.1  christos   if (info == nullptr)
    167  1.1  christos     info = rocm_solib_data.emplace (inf, inf);
    168  1.1  christos 
    169  1.1  christos   return info;
    170  1.1  christos }
    171  1.1  christos 
    172  1.1  christos /* Relocate section addresses.  */
    173  1.1  christos 
    174  1.1  christos static void
    175  1.1  christos rocm_solib_relocate_section_addresses (solib &so,
    176  1.1  christos 				       struct target_section *sec)
    177  1.1  christos {
    178  1.1  christos   if (!is_amdgpu_arch (gdbarch_from_bfd (so.abfd.get ())))
    179  1.1  christos     {
    180  1.1  christos       svr4_so_ops.relocate_section_addresses (so, sec);
    181  1.1  christos       return;
    182  1.1  christos     }
    183  1.1  christos 
    184  1.1  christos   auto *li = gdb::checked_static_cast<lm_info_svr4 *> (so.lm_info.get ());
    185  1.1  christos   sec->addr = sec->addr + li->l_addr;
    186  1.1  christos   sec->endaddr = sec->endaddr + li->l_addr;
    187  1.1  christos }
    188  1.1  christos 
    189  1.1  christos static void rocm_update_solib_list ();
    190  1.1  christos 
    191  1.1  christos static void
    192  1.1  christos rocm_solib_handle_event ()
    193  1.1  christos {
    194  1.1  christos   /* Since we sit on top of svr4_so_ops, we might get called following an event
    195  1.1  christos      concerning host libraries.  We must therefore forward the call.  If the
    196  1.1  christos      event was for a ROCm code object, it will be a no-op.  On the other hand,
    197  1.1  christos      if the event was for host libraries, rocm_update_solib_list will be
    198  1.1  christos      essentially be a no-op (it will reload the same code object list as was
    199  1.1  christos      previously loaded).  */
    200  1.1  christos   svr4_so_ops.handle_event ();
    201  1.1  christos 
    202  1.1  christos   rocm_update_solib_list ();
    203  1.1  christos }
    204  1.1  christos 
    205  1.1  christos /* Create so_list objects from rocm_so objects in SOS.  */
    206  1.1  christos 
    207  1.1  christos static intrusive_list<solib>
    208  1.1  christos so_list_from_rocm_sos (const std::vector<rocm_so> &sos)
    209  1.1  christos {
    210  1.1  christos   intrusive_list<solib> dst;
    211  1.1  christos 
    212  1.1  christos   for (const rocm_so &so : sos)
    213  1.1  christos     {
    214  1.1  christos       solib *newobj = new solib;
    215  1.1  christos       newobj->lm_info = std::make_unique<lm_info_svr4> (*so.lm_info);
    216  1.1  christos 
    217  1.1  christos       newobj->so_name = so.name;
    218  1.1  christos       newobj->so_original_name = so.unique_name;
    219  1.1  christos 
    220  1.1  christos       dst.push_back (*newobj);
    221  1.1  christos     }
    222  1.1  christos 
    223  1.1  christos   return dst;
    224  1.1  christos }
    225  1.1  christos 
    226  1.1  christos /* Build a list of `struct solib' objects describing the shared
    227  1.1  christos    objects currently loaded in the inferior.  */
    228  1.1  christos 
    229  1.1  christos static intrusive_list<solib>
    230  1.1  christos rocm_solib_current_sos ()
    231  1.1  christos {
    232  1.1  christos   /* First, retrieve the host-side shared library list.  */
    233  1.1  christos   intrusive_list<solib> sos = svr4_so_ops.current_sos ();
    234  1.1  christos 
    235  1.1  christos   /* Then, the device-side shared library list.  */
    236  1.1  christos   std::vector<rocm_so> &dev_sos = get_solib_info (current_inferior ())->solib_list;
    237  1.1  christos 
    238  1.1  christos   if (dev_sos.empty ())
    239  1.1  christos     return sos;
    240  1.1  christos 
    241  1.1  christos   intrusive_list<solib> dev_so_list = so_list_from_rocm_sos (dev_sos);
    242  1.1  christos 
    243  1.1  christos   if (sos.empty ())
    244  1.1  christos     return dev_so_list;
    245  1.1  christos 
    246  1.1  christos   /* Append our libraries to the end of the list.  */
    247  1.1  christos   sos.splice (std::move (dev_so_list));
    248  1.1  christos 
    249  1.1  christos   return sos;
    250  1.1  christos }
    251  1.1  christos 
    252  1.1  christos namespace {
    253  1.1  christos 
    254  1.1  christos /* Interface to interact with a ROCm code object stream.  */
    255  1.1  christos 
    256  1.1  christos struct rocm_code_object_stream : public gdb_bfd_iovec_base
    257  1.1  christos {
    258  1.1  christos   DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream);
    259  1.1  christos 
    260  1.1  christos   int stat (bfd *abfd, struct stat *sb) final override;
    261  1.1  christos 
    262  1.1  christos   ~rocm_code_object_stream () override = default;
    263  1.1  christos 
    264  1.1  christos protected:
    265  1.1  christos   rocm_code_object_stream () = default;
    266  1.1  christos 
    267  1.1  christos   /* Return the size of the object file, or -1 if the size cannot be
    268  1.1  christos      determined.
    269  1.1  christos 
    270  1.1  christos      This is a helper function for stat.  */
    271  1.1  christos   virtual LONGEST size () = 0;
    272  1.1  christos };
    273  1.1  christos 
    274  1.1  christos int
    275  1.1  christos rocm_code_object_stream::stat (bfd *, struct stat *sb)
    276  1.1  christos {
    277  1.1  christos   const LONGEST size = this->size ();
    278  1.1  christos   if (size == -1)
    279  1.1  christos     return -1;
    280  1.1  christos 
    281  1.1  christos   memset (sb, '\0', sizeof (struct stat));
    282  1.1  christos   sb->st_size = size;
    283  1.1  christos   return 0;
    284  1.1  christos }
    285  1.1  christos 
    286  1.1  christos /* Interface to a ROCm object stream which is embedded in an ELF file
    287  1.1  christos    accessible to the debugger.  */
    288  1.1  christos 
    289  1.1  christos struct rocm_code_object_stream_file final : rocm_code_object_stream
    290  1.1  christos {
    291  1.1  christos   DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_file);
    292  1.1  christos 
    293  1.1  christos   rocm_code_object_stream_file (inferior *inf, int fd, ULONGEST offset,
    294  1.1  christos 				ULONGEST size);
    295  1.1  christos 
    296  1.1  christos   file_ptr read (bfd *abfd, void *buf, file_ptr size,
    297  1.1  christos 		 file_ptr offset) override;
    298  1.1  christos 
    299  1.1  christos   LONGEST size () override;
    300  1.1  christos 
    301  1.1  christos   ~rocm_code_object_stream_file () override;
    302  1.1  christos 
    303  1.1  christos protected:
    304  1.1  christos 
    305  1.1  christos   /* The inferior owning this code object stream.  */
    306  1.1  christos   inferior *m_inf;
    307  1.1  christos 
    308  1.1  christos   /* The target file descriptor for this stream.  */
    309  1.1  christos   int m_fd;
    310  1.1  christos 
    311  1.1  christos   /* The offset of the ELF file image in the target file.  */
    312  1.1  christos   ULONGEST m_offset;
    313  1.1  christos 
    314  1.1  christos   /* The size of the ELF file image.  The value 0 means that it was
    315  1.1  christos      unspecified in the URI descriptor.  */
    316  1.1  christos   ULONGEST m_size;
    317  1.1  christos };
    318  1.1  christos 
    319  1.1  christos rocm_code_object_stream_file::rocm_code_object_stream_file
    320  1.1  christos   (inferior *inf, int fd, ULONGEST offset, ULONGEST size)
    321  1.1  christos   : m_inf (inf), m_fd (fd), m_offset (offset), m_size (size)
    322  1.1  christos {
    323  1.1  christos }
    324  1.1  christos 
    325  1.1  christos file_ptr
    326  1.1  christos rocm_code_object_stream_file::read (bfd *, void *buf, file_ptr size,
    327  1.1  christos 				    file_ptr offset)
    328  1.1  christos {
    329  1.1  christos   fileio_error target_errno;
    330  1.1  christos   file_ptr nbytes = 0;
    331  1.1  christos   while (size > 0)
    332  1.1  christos     {
    333  1.1  christos       QUIT;
    334  1.1  christos 
    335  1.1  christos       file_ptr bytes_read
    336  1.1  christos 	= target_fileio_pread (m_fd, static_cast<gdb_byte *> (buf) + nbytes,
    337  1.1  christos 			       size, m_offset + offset + nbytes,
    338  1.1  christos 			       &target_errno);
    339  1.1  christos 
    340  1.1  christos       if (bytes_read == 0)
    341  1.1  christos 	break;
    342  1.1  christos 
    343  1.1  christos       if (bytes_read < 0)
    344  1.1  christos 	{
    345  1.1  christos 	  errno = fileio_error_to_host (target_errno);
    346  1.1  christos 	  bfd_set_error (bfd_error_system_call);
    347  1.1  christos 	  return -1;
    348  1.1  christos 	}
    349  1.1  christos 
    350  1.1  christos       nbytes += bytes_read;
    351  1.1  christos       size -= bytes_read;
    352  1.1  christos     }
    353  1.1  christos 
    354  1.1  christos   return nbytes;
    355  1.1  christos }
    356  1.1  christos 
    357  1.1  christos LONGEST
    358  1.1  christos rocm_code_object_stream_file::size ()
    359  1.1  christos {
    360  1.1  christos   if (m_size == 0)
    361  1.1  christos     {
    362  1.1  christos       fileio_error target_errno;
    363  1.1  christos       struct stat stat;
    364  1.1  christos       if (target_fileio_fstat (m_fd, &stat, &target_errno) < 0)
    365  1.1  christos 	{
    366  1.1  christos 	  errno = fileio_error_to_host (target_errno);
    367  1.1  christos 	  bfd_set_error (bfd_error_system_call);
    368  1.1  christos 	  return -1;
    369  1.1  christos 	}
    370  1.1  christos 
    371  1.1  christos       /* Check that the offset is valid.  */
    372  1.1  christos       if (m_offset >= stat.st_size)
    373  1.1  christos 	{
    374  1.1  christos 	  bfd_set_error (bfd_error_bad_value);
    375  1.1  christos 	  return -1;
    376  1.1  christos 	}
    377  1.1  christos 
    378  1.1  christos       m_size = stat.st_size - m_offset;
    379  1.1  christos     }
    380  1.1  christos 
    381  1.1  christos   return m_size;
    382  1.1  christos }
    383  1.1  christos 
    384  1.1  christos rocm_code_object_stream_file::~rocm_code_object_stream_file ()
    385  1.1  christos {
    386  1.1  christos   auto info = get_solib_info (m_inf);
    387  1.1  christos   fileio_error target_errno;
    388  1.1  christos   if (info->fd_cache.close (m_fd, &target_errno) != 0)
    389  1.1  christos     warning (_("Failed to close solib: %s"),
    390  1.1  christos 	     strerror (fileio_error_to_host (target_errno)));
    391  1.1  christos }
    392  1.1  christos 
    393  1.1  christos /* Interface to a code object which lives in the inferior's memory.  */
    394  1.1  christos 
    395  1.1  christos struct rocm_code_object_stream_memory final : public rocm_code_object_stream
    396  1.1  christos {
    397  1.1  christos   DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_memory);
    398  1.1  christos 
    399  1.1  christos   rocm_code_object_stream_memory (gdb::byte_vector buffer);
    400  1.1  christos 
    401  1.1  christos   file_ptr read (bfd *abfd, void *buf, file_ptr size,
    402  1.1  christos 		 file_ptr offset) override;
    403  1.1  christos 
    404  1.1  christos protected:
    405  1.1  christos 
    406  1.1  christos   /* Snapshot of the original ELF image taken during load.  This is done to
    407  1.1  christos      support the situation where an inferior uses an in-memory image, and
    408  1.1  christos      releases or re-uses this memory before GDB is done using it.  */
    409  1.1  christos   gdb::byte_vector m_objfile_image;
    410  1.1  christos 
    411  1.1  christos   LONGEST size () override
    412  1.1  christos   {
    413  1.1  christos     return m_objfile_image.size ();
    414  1.1  christos   }
    415  1.1  christos };
    416  1.1  christos 
    417  1.1  christos rocm_code_object_stream_memory::rocm_code_object_stream_memory
    418  1.1  christos   (gdb::byte_vector buffer)
    419  1.1  christos   : m_objfile_image (std::move (buffer))
    420  1.1  christos {
    421  1.1  christos }
    422  1.1  christos 
    423  1.1  christos file_ptr
    424  1.1  christos rocm_code_object_stream_memory::read (bfd *, void *buf, file_ptr size,
    425  1.1  christos 				      file_ptr offset)
    426  1.1  christos {
    427  1.1  christos   if (size > m_objfile_image.size () - offset)
    428  1.1  christos     size = m_objfile_image.size () - offset;
    429  1.1  christos 
    430  1.1  christos   memcpy (buf, m_objfile_image.data () + offset, size);
    431  1.1  christos   return size;
    432  1.1  christos }
    433  1.1  christos 
    434  1.1  christos } /* anonymous namespace */
    435  1.1  christos 
    436  1.1  christos static gdb_bfd_iovec_base *
    437  1.1  christos rocm_bfd_iovec_open (bfd *abfd, inferior *inferior)
    438  1.1  christos {
    439  1.1  christos   std::string_view uri (bfd_get_filename (abfd));
    440  1.1  christos   std::string_view protocol_delim = "://";
    441  1.1  christos   size_t protocol_end = uri.find (protocol_delim);
    442  1.1  christos   std::string protocol (uri.substr (0, protocol_end));
    443  1.1  christos   protocol_end += protocol_delim.length ();
    444  1.1  christos 
    445  1.1  christos   std::transform (protocol.begin (), protocol.end (), protocol.begin (),
    446  1.1  christos 		  [] (unsigned char c) { return std::tolower (c); });
    447  1.1  christos 
    448  1.1  christos   std::string_view path;
    449  1.1  christos   size_t path_end = uri.find_first_of ("#?", protocol_end);
    450  1.1  christos   if (path_end != std::string::npos)
    451  1.1  christos     path = uri.substr (protocol_end, path_end++ - protocol_end);
    452  1.1  christos   else
    453  1.1  christos     path = uri.substr (protocol_end);
    454  1.1  christos 
    455  1.1  christos   /* %-decode the string.  */
    456  1.1  christos   std::string decoded_path;
    457  1.1  christos   decoded_path.reserve (path.length ());
    458  1.1  christos   for (size_t i = 0; i < path.length (); ++i)
    459  1.1  christos     if (path[i] == '%'
    460  1.1  christos 	&& i < path.length () - 2
    461  1.1  christos 	&& std::isxdigit ((unsigned char)path[i + 1])
    462  1.1  christos 	&& std::isxdigit ((unsigned char)path[i + 2]))
    463  1.1  christos       {
    464  1.1  christos 	std::string_view hex_digits = path.substr (i + 1, 2);
    465  1.1  christos 	decoded_path += std::stoi (std::string (hex_digits), 0, 16);
    466  1.1  christos 	i += 2;
    467  1.1  christos       }
    468  1.1  christos     else
    469  1.1  christos       decoded_path += path[i];
    470  1.1  christos 
    471  1.1  christos   /* Tokenize the query/fragment.  */
    472  1.1  christos   std::vector<std::string_view> tokens;
    473  1.1  christos   size_t pos, last = path_end;
    474  1.1  christos   while ((pos = uri.find ('&', last)) != std::string::npos)
    475  1.1  christos     {
    476  1.1  christos       tokens.emplace_back (uri.substr (last, pos - last));
    477  1.1  christos       last = pos + 1;
    478  1.1  christos     }
    479  1.1  christos 
    480  1.1  christos   if (last != std::string::npos)
    481  1.1  christos     tokens.emplace_back (uri.substr (last));
    482  1.1  christos 
    483  1.1  christos   /* Create a tag-value map from the tokenized query/fragment.  */
    484  1.1  christos   std::unordered_map<std::string_view, std::string_view,
    485  1.1  christos 		     gdb::string_view_hash> params;
    486  1.1  christos   for (std::string_view token : tokens)
    487  1.1  christos     {
    488  1.1  christos       size_t delim = token.find ('=');
    489  1.1  christos       if (delim != std::string::npos)
    490  1.1  christos 	{
    491  1.1  christos 	  std::string_view tag = token.substr (0, delim);
    492  1.1  christos 	  std::string_view val = token.substr (delim + 1);
    493  1.1  christos 	  params.emplace (tag, val);
    494  1.1  christos 	}
    495  1.1  christos     }
    496  1.1  christos 
    497  1.1  christos   try
    498  1.1  christos     {
    499  1.1  christos       ULONGEST offset = 0;
    500  1.1  christos       ULONGEST size = 0;
    501  1.1  christos 
    502  1.1  christos       auto try_strtoulst = [] (std::string_view v)
    503  1.1  christos 	{
    504  1.1  christos 	  errno = 0;
    505  1.1  christos 	  ULONGEST value = strtoulst (v.data (), nullptr, 0);
    506  1.1  christos 	  if (errno != 0)
    507  1.1  christos 	    {
    508  1.1  christos 	      /* The actual message doesn't matter, the exception is caught
    509  1.1  christos 		 below, transformed in a BFD error, and the message is lost.  */
    510  1.1  christos 	      error (_("Failed to parse integer."));
    511  1.1  christos 	    }
    512  1.1  christos 
    513  1.1  christos 	  return value;
    514  1.1  christos 	};
    515  1.1  christos 
    516  1.1  christos       auto offset_it = params.find ("offset");
    517  1.1  christos       if (offset_it != params.end ())
    518  1.1  christos 	offset = try_strtoulst (offset_it->second);
    519  1.1  christos 
    520  1.1  christos       auto size_it = params.find ("size");
    521  1.1  christos       if (size_it != params.end ())
    522  1.1  christos 	{
    523  1.1  christos 	  size = try_strtoulst (size_it->second);
    524  1.1  christos 	  if (size == 0)
    525  1.1  christos 	    error (_("Invalid size value"));
    526  1.1  christos 	}
    527  1.1  christos 
    528  1.1  christos       if (protocol == "file")
    529  1.1  christos 	{
    530  1.1  christos 	  auto info = get_solib_info (inferior);
    531  1.1  christos 	  fileio_error target_errno;
    532  1.1  christos 	  int fd = info->fd_cache.open (decoded_path, &target_errno);
    533  1.1  christos 
    534  1.1  christos 	  if (fd == -1)
    535  1.1  christos 	    {
    536  1.1  christos 	      errno = fileio_error_to_host (target_errno);
    537  1.1  christos 	      bfd_set_error (bfd_error_system_call);
    538  1.1  christos 	      return nullptr;
    539  1.1  christos 	    }
    540  1.1  christos 
    541  1.1  christos 	  return new rocm_code_object_stream_file (inferior, fd, offset,
    542  1.1  christos 						   size);
    543  1.1  christos 	}
    544  1.1  christos 
    545  1.1  christos       if (protocol == "memory")
    546  1.1  christos 	{
    547  1.1  christos 	  ULONGEST pid = try_strtoulst (path);
    548  1.1  christos 	  if (pid != inferior->pid)
    549  1.1  christos 	    {
    550  1.1  christos 	      warning (_("`%s': code object is from another inferior"),
    551  1.1  christos 		       std::string (uri).c_str ());
    552  1.1  christos 	      bfd_set_error (bfd_error_bad_value);
    553  1.1  christos 	      return nullptr;
    554  1.1  christos 	    }
    555  1.1  christos 
    556  1.1  christos 	  gdb::byte_vector buffer (size);
    557  1.1  christos 	  if (target_read_memory (offset, buffer.data (), size) != 0)
    558  1.1  christos 	    {
    559  1.1  christos 	      warning (_("Failed to copy the code object from the inferior"));
    560  1.1  christos 	      bfd_set_error (bfd_error_bad_value);
    561  1.1  christos 	      return nullptr;
    562  1.1  christos 	    }
    563  1.1  christos 
    564  1.1  christos 	  return new rocm_code_object_stream_memory (std::move (buffer));
    565  1.1  christos 	}
    566  1.1  christos 
    567  1.1  christos       warning (_("`%s': protocol not supported: %s"),
    568  1.1  christos 	       std::string (uri).c_str (), protocol.c_str ());
    569  1.1  christos       bfd_set_error (bfd_error_bad_value);
    570  1.1  christos       return nullptr;
    571  1.1  christos     }
    572  1.1  christos   catch (const gdb_exception_quit &ex)
    573  1.1  christos     {
    574  1.1  christos       set_quit_flag ();
    575  1.1  christos       bfd_set_error (bfd_error_bad_value);
    576  1.1  christos       return nullptr;
    577  1.1  christos     }
    578  1.1  christos   catch (const gdb_exception &ex)
    579  1.1  christos     {
    580  1.1  christos       bfd_set_error (bfd_error_bad_value);
    581  1.1  christos       return nullptr;
    582  1.1  christos     }
    583  1.1  christos }
    584  1.1  christos 
    585  1.1  christos static gdb_bfd_ref_ptr
    586  1.1  christos rocm_solib_bfd_open (const char *pathname)
    587  1.1  christos {
    588  1.1  christos   /* Handle regular files with SVR4 open.  */
    589  1.1  christos   if (strstr (pathname, "://") == nullptr)
    590  1.1  christos     return svr4_so_ops.bfd_open (pathname);
    591  1.1  christos 
    592  1.1  christos   auto open = [] (bfd *nbfd) -> gdb_bfd_iovec_base *
    593  1.1  christos   {
    594  1.1  christos     return rocm_bfd_iovec_open (nbfd, current_inferior ());
    595  1.1  christos   };
    596  1.1  christos 
    597  1.1  christos   gdb_bfd_ref_ptr abfd = gdb_bfd_openr_iovec (pathname, "elf64-amdgcn", open);
    598  1.1  christos 
    599  1.1  christos   if (abfd == nullptr)
    600  1.1  christos     error (_("Could not open `%s' as an executable file: %s"), pathname,
    601  1.1  christos 	   bfd_errmsg (bfd_get_error ()));
    602  1.1  christos 
    603  1.1  christos   /* Check bfd format.  */
    604  1.1  christos   if (!bfd_check_format (abfd.get (), bfd_object))
    605  1.1  christos     error (_("`%s': not in executable format: %s"),
    606  1.1  christos 	   bfd_get_filename (abfd.get ()), bfd_errmsg (bfd_get_error ()));
    607  1.1  christos 
    608  1.1  christos   unsigned char osabi = elf_elfheader (abfd)->e_ident[EI_OSABI];
    609  1.1  christos   unsigned char osabiversion = elf_elfheader (abfd)->e_ident[EI_ABIVERSION];
    610  1.1  christos 
    611  1.1  christos   /* Check that the code object is using the HSA OS ABI.  */
    612  1.1  christos   if (osabi != ELFOSABI_AMDGPU_HSA)
    613  1.1  christos     error (_("`%s': ELF file OS ABI is not supported (%d)."),
    614  1.1  christos 	   bfd_get_filename (abfd.get ()), osabi);
    615  1.1  christos 
    616  1.1  christos   /* We support HSA code objects V3 and greater.  */
    617  1.1  christos   if (osabiversion < ELFABIVERSION_AMDGPU_HSA_V3)
    618  1.1  christos     error (_("`%s': ELF file HSA OS ABI version is not supported (%d)."),
    619  1.1  christos 	   bfd_get_filename (abfd.get ()), osabiversion);
    620  1.1  christos 
    621  1.1  christos   /* For GDB to be able to use this solib, the exact AMDGPU processor type
    622  1.1  christos      must be supported by both BFD and the amd-dbgapi library.  */
    623  1.1  christos   const unsigned char gfx_arch
    624  1.1  christos     = elf_elfheader (abfd)->e_flags & EF_AMDGPU_MACH ;
    625  1.1  christos   const bfd_arch_info_type *bfd_arch_info
    626  1.1  christos     = bfd_lookup_arch (bfd_arch_amdgcn, gfx_arch);
    627  1.1  christos 
    628  1.1  christos   amd_dbgapi_architecture_id_t architecture_id;
    629  1.1  christos   amd_dbgapi_status_t dbgapi_query_arch
    630  1.1  christos     = amd_dbgapi_get_architecture (gfx_arch, &architecture_id);
    631  1.1  christos 
    632  1.1  christos   if (dbgapi_query_arch != AMD_DBGAPI_STATUS_SUCCESS
    633  1.1  christos       || bfd_arch_info ==  nullptr)
    634  1.1  christos     {
    635  1.1  christos       if (dbgapi_query_arch != AMD_DBGAPI_STATUS_SUCCESS
    636  1.1  christos 	  && bfd_arch_info ==  nullptr)
    637  1.1  christos 	{
    638  1.1  christos 	  /* Neither of the libraries knows about this arch, so we cannot
    639  1.1  christos 	     provide a human readable name for it.  */
    640  1.1  christos 	  error (_("'%s': AMDGCN architecture %#02x is not supported."),
    641  1.1  christos 		 bfd_get_filename (abfd.get ()), gfx_arch);
    642  1.1  christos 	}
    643  1.1  christos       else if (dbgapi_query_arch != AMD_DBGAPI_STATUS_SUCCESS)
    644  1.1  christos 	{
    645  1.1  christos 	  gdb_assert (bfd_arch_info != nullptr);
    646  1.1  christos 	  error (_("'%s': AMDGCN architecture %s not supported by "
    647  1.1  christos 		   "amd-dbgapi."),
    648  1.1  christos 		 bfd_get_filename (abfd.get ()),
    649  1.1  christos 		 bfd_arch_info->printable_name);
    650  1.1  christos 	}
    651  1.1  christos       else
    652  1.1  christos 	{
    653  1.1  christos 	  gdb_assert (dbgapi_query_arch == AMD_DBGAPI_STATUS_SUCCESS);
    654  1.1  christos 	  char *arch_name;
    655  1.1  christos 	  if (amd_dbgapi_architecture_get_info
    656  1.1  christos 	      (architecture_id, AMD_DBGAPI_ARCHITECTURE_INFO_NAME,
    657  1.1  christos 	       sizeof (arch_name), &arch_name) != AMD_DBGAPI_STATUS_SUCCESS)
    658  1.1  christos 	    error ("amd_dbgapi_architecture_get_info call failed for arch "
    659  1.1  christos 		   "%#02x.", gfx_arch);
    660  1.1  christos 	  gdb::unique_xmalloc_ptr<char> arch_name_cleaner (arch_name);
    661  1.1  christos 
    662  1.1  christos 	  error (_("'%s': AMDGCN architecture %s not supported."),
    663  1.1  christos 		 bfd_get_filename (abfd.get ()),
    664  1.1  christos 		 arch_name);
    665  1.1  christos 	}
    666  1.1  christos     }
    667  1.1  christos 
    668  1.1  christos   gdb_assert (gdbarch_from_bfd (abfd.get ()) != nullptr);
    669  1.1  christos   gdb_assert (is_amdgpu_arch (gdbarch_from_bfd (abfd.get ())));
    670  1.1  christos 
    671  1.1  christos   return abfd;
    672  1.1  christos }
    673  1.1  christos 
    674  1.1  christos static void
    675  1.1  christos rocm_solib_create_inferior_hook (int from_tty)
    676  1.1  christos {
    677  1.1  christos   get_solib_info (current_inferior ())->solib_list.clear ();
    678  1.1  christos 
    679  1.1  christos   svr4_so_ops.solib_create_inferior_hook (from_tty);
    680  1.1  christos }
    681  1.1  christos 
    682  1.1  christos static void
    683  1.1  christos rocm_update_solib_list ()
    684  1.1  christos {
    685  1.1  christos   inferior *inf = current_inferior ();
    686  1.1  christos 
    687  1.1  christos   amd_dbgapi_process_id_t process_id = get_amd_dbgapi_process_id (inf);
    688  1.1  christos   if (process_id.handle == AMD_DBGAPI_PROCESS_NONE.handle)
    689  1.1  christos     return;
    690  1.1  christos 
    691  1.1  christos   solib_info *info = get_solib_info (inf);
    692  1.1  christos 
    693  1.1  christos   info->solib_list.clear ();
    694  1.1  christos   std::vector<rocm_so> &sos = info->solib_list;
    695  1.1  christos 
    696  1.1  christos   amd_dbgapi_code_object_id_t *code_object_list;
    697  1.1  christos   size_t count;
    698  1.1  christos 
    699  1.1  christos   amd_dbgapi_status_t status
    700  1.1  christos     = amd_dbgapi_process_code_object_list (process_id, &count,
    701  1.1  christos 					   &code_object_list, nullptr);
    702  1.1  christos   if (status != AMD_DBGAPI_STATUS_SUCCESS)
    703  1.1  christos     {
    704  1.1  christos       warning (_("amd_dbgapi_process_code_object_list failed (%s)"),
    705  1.1  christos 	       get_status_string (status));
    706  1.1  christos       return;
    707  1.1  christos     }
    708  1.1  christos 
    709  1.1  christos   for (size_t i = 0; i < count; ++i)
    710  1.1  christos     {
    711  1.1  christos       CORE_ADDR l_addr;
    712  1.1  christos       char *uri_bytes;
    713  1.1  christos 
    714  1.1  christos       status = amd_dbgapi_code_object_get_info
    715  1.1  christos 	(code_object_list[i], AMD_DBGAPI_CODE_OBJECT_INFO_LOAD_ADDRESS,
    716  1.1  christos 	 sizeof (l_addr), &l_addr);
    717  1.1  christos       if (status != AMD_DBGAPI_STATUS_SUCCESS)
    718  1.1  christos 	continue;
    719  1.1  christos 
    720  1.1  christos       status = amd_dbgapi_code_object_get_info
    721  1.1  christos 	(code_object_list[i], AMD_DBGAPI_CODE_OBJECT_INFO_URI_NAME,
    722  1.1  christos 	 sizeof (uri_bytes), &uri_bytes);
    723  1.1  christos       if (status != AMD_DBGAPI_STATUS_SUCCESS)
    724  1.1  christos 	continue;
    725  1.1  christos 
    726  1.1  christos       gdb::unique_xmalloc_ptr<char> uri_bytes_holder (uri_bytes);
    727  1.1  christos 
    728  1.1  christos       lm_info_svr4_up li = std::make_unique<lm_info_svr4> ();
    729  1.1  christos       li->l_addr = l_addr;
    730  1.1  christos 
    731  1.1  christos       /* Generate a unique name so that code objects with the same URI but
    732  1.1  christos 	 different load addresses are seen by gdb core as different shared
    733  1.1  christos 	 objects.  */
    734  1.1  christos       std::string unique_name
    735  1.1  christos 	= string_printf ("code_object_%ld", code_object_list[i].handle);
    736  1.1  christos 
    737  1.1  christos       sos.emplace_back (uri_bytes, std::move (unique_name), std::move (li));
    738  1.1  christos     }
    739  1.1  christos 
    740  1.1  christos   xfree (code_object_list);
    741  1.1  christos 
    742  1.1  christos   if (rocm_solib_ops.current_sos == NULL)
    743  1.1  christos     {
    744  1.1  christos       /* Override what we need to.  */
    745  1.1  christos       rocm_solib_ops = svr4_so_ops;
    746  1.1  christos       rocm_solib_ops.current_sos = rocm_solib_current_sos;
    747  1.1  christos       rocm_solib_ops.solib_create_inferior_hook
    748  1.1  christos 	= rocm_solib_create_inferior_hook;
    749  1.1  christos       rocm_solib_ops.bfd_open = rocm_solib_bfd_open;
    750  1.1  christos       rocm_solib_ops.relocate_section_addresses
    751  1.1  christos 	= rocm_solib_relocate_section_addresses;
    752  1.1  christos       rocm_solib_ops.handle_event = rocm_solib_handle_event;
    753  1.1  christos 
    754  1.1  christos       /* Engage the ROCm so_ops.  */
    755  1.1  christos       set_gdbarch_so_ops (current_inferior ()->arch (), &rocm_solib_ops);
    756  1.1  christos     }
    757  1.1  christos }
    758  1.1  christos 
    759  1.1  christos static void
    760  1.1  christos rocm_solib_target_inferior_created (inferior *inf)
    761  1.1  christos {
    762  1.1  christos   get_solib_info (inf)->solib_list.clear ();
    763  1.1  christos 
    764  1.1  christos   rocm_update_solib_list ();
    765  1.1  christos 
    766  1.1  christos   /* Force GDB to reload the solibs.  */
    767  1.1  christos   current_inferior ()->pspace->clear_solib_cache ();
    768  1.1  christos   solib_add (nullptr, 0, auto_solib_add);
    769  1.1  christos }
    770  1.1  christos 
    771  1.1  christos /* -Wmissing-prototypes */
    772  1.1  christos extern initialize_file_ftype _initialize_rocm_solib;
    773  1.1  christos 
    774  1.1  christos void
    775  1.1  christos _initialize_rocm_solib ()
    776  1.1  christos {
    777  1.1  christos   /* The dependency on the amd-dbgapi exists because solib-rocm's
    778  1.1  christos      inferior_created observer needs amd-dbgapi to have attached the process,
    779  1.1  christos      which happens in amd_dbgapi_target's inferior_created observer.  */
    780  1.1  christos   gdb::observers::inferior_created.attach
    781  1.1  christos     (rocm_solib_target_inferior_created,
    782  1.1  christos      "solib-rocm",
    783  1.1  christos      { &get_amd_dbgapi_target_inferior_created_observer_token () });
    784  1.1  christos }
    785