Home | History | Annotate | Line # | Download | only in gdb
      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.1.2  christos static owning_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.1.2  christos   owning_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.1.2  christos       auto &newobj = dst.emplace_back ();
    215      1.1  christos 
    216  1.1.1.2  christos       newobj.lm_info = std::make_unique<lm_info_svr4> (*so.lm_info);
    217  1.1.1.2  christos       newobj.so_name = so.name;
    218  1.1.1.2  christos       newobj.so_original_name = so.unique_name;
    219      1.1  christos     }
    220      1.1  christos 
    221      1.1  christos   return dst;
    222      1.1  christos }
    223      1.1  christos 
    224      1.1  christos /* Build a list of `struct solib' objects describing the shared
    225      1.1  christos    objects currently loaded in the inferior.  */
    226      1.1  christos 
    227  1.1.1.2  christos static owning_intrusive_list<solib>
    228      1.1  christos rocm_solib_current_sos ()
    229      1.1  christos {
    230      1.1  christos   /* First, retrieve the host-side shared library list.  */
    231  1.1.1.2  christos   owning_intrusive_list<solib> sos = svr4_so_ops.current_sos ();
    232      1.1  christos 
    233      1.1  christos   /* Then, the device-side shared library list.  */
    234      1.1  christos   std::vector<rocm_so> &dev_sos = get_solib_info (current_inferior ())->solib_list;
    235      1.1  christos 
    236      1.1  christos   if (dev_sos.empty ())
    237      1.1  christos     return sos;
    238      1.1  christos 
    239  1.1.1.2  christos   owning_intrusive_list<solib> dev_so_list = so_list_from_rocm_sos (dev_sos);
    240      1.1  christos 
    241      1.1  christos   if (sos.empty ())
    242      1.1  christos     return dev_so_list;
    243      1.1  christos 
    244      1.1  christos   /* Append our libraries to the end of the list.  */
    245      1.1  christos   sos.splice (std::move (dev_so_list));
    246      1.1  christos 
    247      1.1  christos   return sos;
    248      1.1  christos }
    249      1.1  christos 
    250      1.1  christos namespace {
    251      1.1  christos 
    252      1.1  christos /* Interface to interact with a ROCm code object stream.  */
    253      1.1  christos 
    254      1.1  christos struct rocm_code_object_stream : public gdb_bfd_iovec_base
    255      1.1  christos {
    256      1.1  christos   DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream);
    257      1.1  christos 
    258      1.1  christos   int stat (bfd *abfd, struct stat *sb) final override;
    259      1.1  christos 
    260      1.1  christos   ~rocm_code_object_stream () override = default;
    261      1.1  christos 
    262      1.1  christos protected:
    263      1.1  christos   rocm_code_object_stream () = default;
    264      1.1  christos 
    265      1.1  christos   /* Return the size of the object file, or -1 if the size cannot be
    266      1.1  christos      determined.
    267      1.1  christos 
    268      1.1  christos      This is a helper function for stat.  */
    269      1.1  christos   virtual LONGEST size () = 0;
    270      1.1  christos };
    271      1.1  christos 
    272      1.1  christos int
    273      1.1  christos rocm_code_object_stream::stat (bfd *, struct stat *sb)
    274      1.1  christos {
    275      1.1  christos   const LONGEST size = this->size ();
    276      1.1  christos   if (size == -1)
    277      1.1  christos     return -1;
    278      1.1  christos 
    279      1.1  christos   memset (sb, '\0', sizeof (struct stat));
    280      1.1  christos   sb->st_size = size;
    281      1.1  christos   return 0;
    282      1.1  christos }
    283      1.1  christos 
    284      1.1  christos /* Interface to a ROCm object stream which is embedded in an ELF file
    285      1.1  christos    accessible to the debugger.  */
    286      1.1  christos 
    287      1.1  christos struct rocm_code_object_stream_file final : rocm_code_object_stream
    288      1.1  christos {
    289      1.1  christos   DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_file);
    290      1.1  christos 
    291      1.1  christos   rocm_code_object_stream_file (inferior *inf, int fd, ULONGEST offset,
    292      1.1  christos 				ULONGEST size);
    293      1.1  christos 
    294      1.1  christos   file_ptr read (bfd *abfd, void *buf, file_ptr size,
    295      1.1  christos 		 file_ptr offset) override;
    296      1.1  christos 
    297      1.1  christos   LONGEST size () override;
    298      1.1  christos 
    299      1.1  christos   ~rocm_code_object_stream_file () override;
    300      1.1  christos 
    301      1.1  christos protected:
    302      1.1  christos 
    303      1.1  christos   /* The inferior owning this code object stream.  */
    304      1.1  christos   inferior *m_inf;
    305      1.1  christos 
    306      1.1  christos   /* The target file descriptor for this stream.  */
    307      1.1  christos   int m_fd;
    308      1.1  christos 
    309      1.1  christos   /* The offset of the ELF file image in the target file.  */
    310      1.1  christos   ULONGEST m_offset;
    311      1.1  christos 
    312      1.1  christos   /* The size of the ELF file image.  The value 0 means that it was
    313      1.1  christos      unspecified in the URI descriptor.  */
    314      1.1  christos   ULONGEST m_size;
    315      1.1  christos };
    316      1.1  christos 
    317      1.1  christos rocm_code_object_stream_file::rocm_code_object_stream_file
    318      1.1  christos   (inferior *inf, int fd, ULONGEST offset, ULONGEST size)
    319      1.1  christos   : m_inf (inf), m_fd (fd), m_offset (offset), m_size (size)
    320      1.1  christos {
    321      1.1  christos }
    322      1.1  christos 
    323      1.1  christos file_ptr
    324      1.1  christos rocm_code_object_stream_file::read (bfd *, void *buf, file_ptr size,
    325      1.1  christos 				    file_ptr offset)
    326      1.1  christos {
    327      1.1  christos   fileio_error target_errno;
    328      1.1  christos   file_ptr nbytes = 0;
    329      1.1  christos   while (size > 0)
    330      1.1  christos     {
    331      1.1  christos       QUIT;
    332      1.1  christos 
    333      1.1  christos       file_ptr bytes_read
    334      1.1  christos 	= target_fileio_pread (m_fd, static_cast<gdb_byte *> (buf) + nbytes,
    335      1.1  christos 			       size, m_offset + offset + nbytes,
    336      1.1  christos 			       &target_errno);
    337      1.1  christos 
    338      1.1  christos       if (bytes_read == 0)
    339      1.1  christos 	break;
    340      1.1  christos 
    341      1.1  christos       if (bytes_read < 0)
    342      1.1  christos 	{
    343      1.1  christos 	  errno = fileio_error_to_host (target_errno);
    344      1.1  christos 	  bfd_set_error (bfd_error_system_call);
    345      1.1  christos 	  return -1;
    346      1.1  christos 	}
    347      1.1  christos 
    348      1.1  christos       nbytes += bytes_read;
    349      1.1  christos       size -= bytes_read;
    350      1.1  christos     }
    351      1.1  christos 
    352      1.1  christos   return nbytes;
    353      1.1  christos }
    354      1.1  christos 
    355      1.1  christos LONGEST
    356      1.1  christos rocm_code_object_stream_file::size ()
    357      1.1  christos {
    358      1.1  christos   if (m_size == 0)
    359      1.1  christos     {
    360      1.1  christos       fileio_error target_errno;
    361      1.1  christos       struct stat stat;
    362      1.1  christos       if (target_fileio_fstat (m_fd, &stat, &target_errno) < 0)
    363      1.1  christos 	{
    364      1.1  christos 	  errno = fileio_error_to_host (target_errno);
    365      1.1  christos 	  bfd_set_error (bfd_error_system_call);
    366      1.1  christos 	  return -1;
    367      1.1  christos 	}
    368      1.1  christos 
    369      1.1  christos       /* Check that the offset is valid.  */
    370      1.1  christos       if (m_offset >= stat.st_size)
    371      1.1  christos 	{
    372      1.1  christos 	  bfd_set_error (bfd_error_bad_value);
    373      1.1  christos 	  return -1;
    374      1.1  christos 	}
    375      1.1  christos 
    376      1.1  christos       m_size = stat.st_size - m_offset;
    377      1.1  christos     }
    378      1.1  christos 
    379      1.1  christos   return m_size;
    380      1.1  christos }
    381      1.1  christos 
    382      1.1  christos rocm_code_object_stream_file::~rocm_code_object_stream_file ()
    383      1.1  christos {
    384      1.1  christos   auto info = get_solib_info (m_inf);
    385      1.1  christos   fileio_error target_errno;
    386      1.1  christos   if (info->fd_cache.close (m_fd, &target_errno) != 0)
    387      1.1  christos     warning (_("Failed to close solib: %s"),
    388      1.1  christos 	     strerror (fileio_error_to_host (target_errno)));
    389      1.1  christos }
    390      1.1  christos 
    391      1.1  christos /* Interface to a code object which lives in the inferior's memory.  */
    392      1.1  christos 
    393      1.1  christos struct rocm_code_object_stream_memory final : public rocm_code_object_stream
    394      1.1  christos {
    395      1.1  christos   DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_memory);
    396      1.1  christos 
    397      1.1  christos   rocm_code_object_stream_memory (gdb::byte_vector buffer);
    398      1.1  christos 
    399      1.1  christos   file_ptr read (bfd *abfd, void *buf, file_ptr size,
    400      1.1  christos 		 file_ptr offset) override;
    401      1.1  christos 
    402      1.1  christos protected:
    403      1.1  christos 
    404      1.1  christos   /* Snapshot of the original ELF image taken during load.  This is done to
    405      1.1  christos      support the situation where an inferior uses an in-memory image, and
    406      1.1  christos      releases or re-uses this memory before GDB is done using it.  */
    407      1.1  christos   gdb::byte_vector m_objfile_image;
    408      1.1  christos 
    409      1.1  christos   LONGEST size () override
    410      1.1  christos   {
    411      1.1  christos     return m_objfile_image.size ();
    412      1.1  christos   }
    413      1.1  christos };
    414      1.1  christos 
    415      1.1  christos rocm_code_object_stream_memory::rocm_code_object_stream_memory
    416      1.1  christos   (gdb::byte_vector buffer)
    417      1.1  christos   : m_objfile_image (std::move (buffer))
    418      1.1  christos {
    419      1.1  christos }
    420      1.1  christos 
    421      1.1  christos file_ptr
    422      1.1  christos rocm_code_object_stream_memory::read (bfd *, void *buf, file_ptr size,
    423      1.1  christos 				      file_ptr offset)
    424      1.1  christos {
    425      1.1  christos   if (size > m_objfile_image.size () - offset)
    426      1.1  christos     size = m_objfile_image.size () - offset;
    427      1.1  christos 
    428      1.1  christos   memcpy (buf, m_objfile_image.data () + offset, size);
    429      1.1  christos   return size;
    430      1.1  christos }
    431      1.1  christos 
    432      1.1  christos } /* anonymous namespace */
    433      1.1  christos 
    434      1.1  christos static gdb_bfd_iovec_base *
    435      1.1  christos rocm_bfd_iovec_open (bfd *abfd, inferior *inferior)
    436      1.1  christos {
    437      1.1  christos   std::string_view uri (bfd_get_filename (abfd));
    438      1.1  christos   std::string_view protocol_delim = "://";
    439      1.1  christos   size_t protocol_end = uri.find (protocol_delim);
    440      1.1  christos   std::string protocol (uri.substr (0, protocol_end));
    441      1.1  christos   protocol_end += protocol_delim.length ();
    442      1.1  christos 
    443      1.1  christos   std::transform (protocol.begin (), protocol.end (), protocol.begin (),
    444      1.1  christos 		  [] (unsigned char c) { return std::tolower (c); });
    445      1.1  christos 
    446      1.1  christos   std::string_view path;
    447      1.1  christos   size_t path_end = uri.find_first_of ("#?", protocol_end);
    448      1.1  christos   if (path_end != std::string::npos)
    449      1.1  christos     path = uri.substr (protocol_end, path_end++ - protocol_end);
    450      1.1  christos   else
    451      1.1  christos     path = uri.substr (protocol_end);
    452      1.1  christos 
    453      1.1  christos   /* %-decode the string.  */
    454      1.1  christos   std::string decoded_path;
    455      1.1  christos   decoded_path.reserve (path.length ());
    456      1.1  christos   for (size_t i = 0; i < path.length (); ++i)
    457      1.1  christos     if (path[i] == '%'
    458      1.1  christos 	&& i < path.length () - 2
    459      1.1  christos 	&& std::isxdigit ((unsigned char)path[i + 1])
    460      1.1  christos 	&& std::isxdigit ((unsigned char)path[i + 2]))
    461      1.1  christos       {
    462      1.1  christos 	std::string_view hex_digits = path.substr (i + 1, 2);
    463      1.1  christos 	decoded_path += std::stoi (std::string (hex_digits), 0, 16);
    464      1.1  christos 	i += 2;
    465      1.1  christos       }
    466      1.1  christos     else
    467      1.1  christos       decoded_path += path[i];
    468      1.1  christos 
    469      1.1  christos   /* Tokenize the query/fragment.  */
    470      1.1  christos   std::vector<std::string_view> tokens;
    471      1.1  christos   size_t pos, last = path_end;
    472      1.1  christos   while ((pos = uri.find ('&', last)) != std::string::npos)
    473      1.1  christos     {
    474      1.1  christos       tokens.emplace_back (uri.substr (last, pos - last));
    475      1.1  christos       last = pos + 1;
    476      1.1  christos     }
    477      1.1  christos 
    478      1.1  christos   if (last != std::string::npos)
    479      1.1  christos     tokens.emplace_back (uri.substr (last));
    480      1.1  christos 
    481      1.1  christos   /* Create a tag-value map from the tokenized query/fragment.  */
    482      1.1  christos   std::unordered_map<std::string_view, std::string_view,
    483      1.1  christos 		     gdb::string_view_hash> params;
    484      1.1  christos   for (std::string_view token : tokens)
    485      1.1  christos     {
    486      1.1  christos       size_t delim = token.find ('=');
    487      1.1  christos       if (delim != std::string::npos)
    488      1.1  christos 	{
    489      1.1  christos 	  std::string_view tag = token.substr (0, delim);
    490      1.1  christos 	  std::string_view val = token.substr (delim + 1);
    491      1.1  christos 	  params.emplace (tag, val);
    492      1.1  christos 	}
    493      1.1  christos     }
    494      1.1  christos 
    495      1.1  christos   try
    496      1.1  christos     {
    497      1.1  christos       ULONGEST offset = 0;
    498      1.1  christos       ULONGEST size = 0;
    499      1.1  christos 
    500      1.1  christos       auto try_strtoulst = [] (std::string_view v)
    501      1.1  christos 	{
    502      1.1  christos 	  errno = 0;
    503      1.1  christos 	  ULONGEST value = strtoulst (v.data (), nullptr, 0);
    504      1.1  christos 	  if (errno != 0)
    505      1.1  christos 	    {
    506      1.1  christos 	      /* The actual message doesn't matter, the exception is caught
    507      1.1  christos 		 below, transformed in a BFD error, and the message is lost.  */
    508      1.1  christos 	      error (_("Failed to parse integer."));
    509      1.1  christos 	    }
    510      1.1  christos 
    511      1.1  christos 	  return value;
    512      1.1  christos 	};
    513      1.1  christos 
    514      1.1  christos       auto offset_it = params.find ("offset");
    515      1.1  christos       if (offset_it != params.end ())
    516      1.1  christos 	offset = try_strtoulst (offset_it->second);
    517      1.1  christos 
    518      1.1  christos       auto size_it = params.find ("size");
    519      1.1  christos       if (size_it != params.end ())
    520      1.1  christos 	{
    521      1.1  christos 	  size = try_strtoulst (size_it->second);
    522      1.1  christos 	  if (size == 0)
    523      1.1  christos 	    error (_("Invalid size value"));
    524      1.1  christos 	}
    525      1.1  christos 
    526      1.1  christos       if (protocol == "file")
    527      1.1  christos 	{
    528      1.1  christos 	  auto info = get_solib_info (inferior);
    529      1.1  christos 	  fileio_error target_errno;
    530      1.1  christos 	  int fd = info->fd_cache.open (decoded_path, &target_errno);
    531      1.1  christos 
    532      1.1  christos 	  if (fd == -1)
    533      1.1  christos 	    {
    534      1.1  christos 	      errno = fileio_error_to_host (target_errno);
    535      1.1  christos 	      bfd_set_error (bfd_error_system_call);
    536      1.1  christos 	      return nullptr;
    537      1.1  christos 	    }
    538      1.1  christos 
    539      1.1  christos 	  return new rocm_code_object_stream_file (inferior, fd, offset,
    540      1.1  christos 						   size);
    541      1.1  christos 	}
    542      1.1  christos 
    543      1.1  christos       if (protocol == "memory")
    544      1.1  christos 	{
    545      1.1  christos 	  ULONGEST pid = try_strtoulst (path);
    546      1.1  christos 	  if (pid != inferior->pid)
    547      1.1  christos 	    {
    548      1.1  christos 	      warning (_("`%s': code object is from another inferior"),
    549      1.1  christos 		       std::string (uri).c_str ());
    550      1.1  christos 	      bfd_set_error (bfd_error_bad_value);
    551      1.1  christos 	      return nullptr;
    552      1.1  christos 	    }
    553      1.1  christos 
    554      1.1  christos 	  gdb::byte_vector buffer (size);
    555      1.1  christos 	  if (target_read_memory (offset, buffer.data (), size) != 0)
    556      1.1  christos 	    {
    557      1.1  christos 	      warning (_("Failed to copy the code object from the inferior"));
    558      1.1  christos 	      bfd_set_error (bfd_error_bad_value);
    559      1.1  christos 	      return nullptr;
    560      1.1  christos 	    }
    561      1.1  christos 
    562      1.1  christos 	  return new rocm_code_object_stream_memory (std::move (buffer));
    563      1.1  christos 	}
    564      1.1  christos 
    565      1.1  christos       warning (_("`%s': protocol not supported: %s"),
    566      1.1  christos 	       std::string (uri).c_str (), protocol.c_str ());
    567      1.1  christos       bfd_set_error (bfd_error_bad_value);
    568      1.1  christos       return nullptr;
    569      1.1  christos     }
    570      1.1  christos   catch (const gdb_exception_quit &ex)
    571      1.1  christos     {
    572      1.1  christos       set_quit_flag ();
    573      1.1  christos       bfd_set_error (bfd_error_bad_value);
    574      1.1  christos       return nullptr;
    575      1.1  christos     }
    576      1.1  christos   catch (const gdb_exception &ex)
    577      1.1  christos     {
    578      1.1  christos       bfd_set_error (bfd_error_bad_value);
    579      1.1  christos       return nullptr;
    580      1.1  christos     }
    581      1.1  christos }
    582      1.1  christos 
    583      1.1  christos static gdb_bfd_ref_ptr
    584      1.1  christos rocm_solib_bfd_open (const char *pathname)
    585      1.1  christos {
    586      1.1  christos   /* Handle regular files with SVR4 open.  */
    587      1.1  christos   if (strstr (pathname, "://") == nullptr)
    588      1.1  christos     return svr4_so_ops.bfd_open (pathname);
    589      1.1  christos 
    590      1.1  christos   auto open = [] (bfd *nbfd) -> gdb_bfd_iovec_base *
    591      1.1  christos   {
    592      1.1  christos     return rocm_bfd_iovec_open (nbfd, current_inferior ());
    593      1.1  christos   };
    594      1.1  christos 
    595      1.1  christos   gdb_bfd_ref_ptr abfd = gdb_bfd_openr_iovec (pathname, "elf64-amdgcn", open);
    596      1.1  christos 
    597      1.1  christos   if (abfd == nullptr)
    598      1.1  christos     error (_("Could not open `%s' as an executable file: %s"), pathname,
    599      1.1  christos 	   bfd_errmsg (bfd_get_error ()));
    600      1.1  christos 
    601      1.1  christos   /* Check bfd format.  */
    602      1.1  christos   if (!bfd_check_format (abfd.get (), bfd_object))
    603      1.1  christos     error (_("`%s': not in executable format: %s"),
    604      1.1  christos 	   bfd_get_filename (abfd.get ()), bfd_errmsg (bfd_get_error ()));
    605      1.1  christos 
    606      1.1  christos   unsigned char osabi = elf_elfheader (abfd)->e_ident[EI_OSABI];
    607      1.1  christos   unsigned char osabiversion = elf_elfheader (abfd)->e_ident[EI_ABIVERSION];
    608      1.1  christos 
    609      1.1  christos   /* Check that the code object is using the HSA OS ABI.  */
    610      1.1  christos   if (osabi != ELFOSABI_AMDGPU_HSA)
    611      1.1  christos     error (_("`%s': ELF file OS ABI is not supported (%d)."),
    612      1.1  christos 	   bfd_get_filename (abfd.get ()), osabi);
    613      1.1  christos 
    614      1.1  christos   /* We support HSA code objects V3 and greater.  */
    615      1.1  christos   if (osabiversion < ELFABIVERSION_AMDGPU_HSA_V3)
    616      1.1  christos     error (_("`%s': ELF file HSA OS ABI version is not supported (%d)."),
    617      1.1  christos 	   bfd_get_filename (abfd.get ()), osabiversion);
    618      1.1  christos 
    619      1.1  christos   /* For GDB to be able to use this solib, the exact AMDGPU processor type
    620      1.1  christos      must be supported by both BFD and the amd-dbgapi library.  */
    621      1.1  christos   const unsigned char gfx_arch
    622      1.1  christos     = elf_elfheader (abfd)->e_flags & EF_AMDGPU_MACH ;
    623      1.1  christos   const bfd_arch_info_type *bfd_arch_info
    624      1.1  christos     = bfd_lookup_arch (bfd_arch_amdgcn, gfx_arch);
    625      1.1  christos 
    626      1.1  christos   amd_dbgapi_architecture_id_t architecture_id;
    627      1.1  christos   amd_dbgapi_status_t dbgapi_query_arch
    628      1.1  christos     = amd_dbgapi_get_architecture (gfx_arch, &architecture_id);
    629      1.1  christos 
    630      1.1  christos   if (dbgapi_query_arch != AMD_DBGAPI_STATUS_SUCCESS
    631      1.1  christos       || bfd_arch_info ==  nullptr)
    632      1.1  christos     {
    633      1.1  christos       if (dbgapi_query_arch != AMD_DBGAPI_STATUS_SUCCESS
    634      1.1  christos 	  && bfd_arch_info ==  nullptr)
    635      1.1  christos 	{
    636      1.1  christos 	  /* Neither of the libraries knows about this arch, so we cannot
    637      1.1  christos 	     provide a human readable name for it.  */
    638      1.1  christos 	  error (_("'%s': AMDGCN architecture %#02x is not supported."),
    639      1.1  christos 		 bfd_get_filename (abfd.get ()), gfx_arch);
    640      1.1  christos 	}
    641      1.1  christos       else if (dbgapi_query_arch != AMD_DBGAPI_STATUS_SUCCESS)
    642      1.1  christos 	{
    643      1.1  christos 	  gdb_assert (bfd_arch_info != nullptr);
    644      1.1  christos 	  error (_("'%s': AMDGCN architecture %s not supported by "
    645      1.1  christos 		   "amd-dbgapi."),
    646      1.1  christos 		 bfd_get_filename (abfd.get ()),
    647      1.1  christos 		 bfd_arch_info->printable_name);
    648      1.1  christos 	}
    649      1.1  christos       else
    650      1.1  christos 	{
    651      1.1  christos 	  gdb_assert (dbgapi_query_arch == AMD_DBGAPI_STATUS_SUCCESS);
    652      1.1  christos 	  char *arch_name;
    653      1.1  christos 	  if (amd_dbgapi_architecture_get_info
    654      1.1  christos 	      (architecture_id, AMD_DBGAPI_ARCHITECTURE_INFO_NAME,
    655      1.1  christos 	       sizeof (arch_name), &arch_name) != AMD_DBGAPI_STATUS_SUCCESS)
    656      1.1  christos 	    error ("amd_dbgapi_architecture_get_info call failed for arch "
    657      1.1  christos 		   "%#02x.", gfx_arch);
    658      1.1  christos 	  gdb::unique_xmalloc_ptr<char> arch_name_cleaner (arch_name);
    659      1.1  christos 
    660      1.1  christos 	  error (_("'%s': AMDGCN architecture %s not supported."),
    661      1.1  christos 		 bfd_get_filename (abfd.get ()),
    662      1.1  christos 		 arch_name);
    663      1.1  christos 	}
    664      1.1  christos     }
    665      1.1  christos 
    666      1.1  christos   gdb_assert (gdbarch_from_bfd (abfd.get ()) != nullptr);
    667      1.1  christos   gdb_assert (is_amdgpu_arch (gdbarch_from_bfd (abfd.get ())));
    668      1.1  christos 
    669      1.1  christos   return abfd;
    670      1.1  christos }
    671      1.1  christos 
    672      1.1  christos static void
    673      1.1  christos rocm_solib_create_inferior_hook (int from_tty)
    674      1.1  christos {
    675      1.1  christos   get_solib_info (current_inferior ())->solib_list.clear ();
    676      1.1  christos 
    677      1.1  christos   svr4_so_ops.solib_create_inferior_hook (from_tty);
    678      1.1  christos }
    679      1.1  christos 
    680      1.1  christos static void
    681      1.1  christos rocm_update_solib_list ()
    682      1.1  christos {
    683      1.1  christos   inferior *inf = current_inferior ();
    684      1.1  christos 
    685      1.1  christos   amd_dbgapi_process_id_t process_id = get_amd_dbgapi_process_id (inf);
    686      1.1  christos   if (process_id.handle == AMD_DBGAPI_PROCESS_NONE.handle)
    687      1.1  christos     return;
    688      1.1  christos 
    689      1.1  christos   solib_info *info = get_solib_info (inf);
    690      1.1  christos 
    691      1.1  christos   info->solib_list.clear ();
    692      1.1  christos   std::vector<rocm_so> &sos = info->solib_list;
    693      1.1  christos 
    694      1.1  christos   amd_dbgapi_code_object_id_t *code_object_list;
    695      1.1  christos   size_t count;
    696      1.1  christos 
    697      1.1  christos   amd_dbgapi_status_t status
    698      1.1  christos     = amd_dbgapi_process_code_object_list (process_id, &count,
    699      1.1  christos 					   &code_object_list, nullptr);
    700      1.1  christos   if (status != AMD_DBGAPI_STATUS_SUCCESS)
    701      1.1  christos     {
    702      1.1  christos       warning (_("amd_dbgapi_process_code_object_list failed (%s)"),
    703      1.1  christos 	       get_status_string (status));
    704      1.1  christos       return;
    705      1.1  christos     }
    706      1.1  christos 
    707      1.1  christos   for (size_t i = 0; i < count; ++i)
    708      1.1  christos     {
    709      1.1  christos       CORE_ADDR l_addr;
    710      1.1  christos       char *uri_bytes;
    711      1.1  christos 
    712      1.1  christos       status = amd_dbgapi_code_object_get_info
    713      1.1  christos 	(code_object_list[i], AMD_DBGAPI_CODE_OBJECT_INFO_LOAD_ADDRESS,
    714      1.1  christos 	 sizeof (l_addr), &l_addr);
    715      1.1  christos       if (status != AMD_DBGAPI_STATUS_SUCCESS)
    716      1.1  christos 	continue;
    717      1.1  christos 
    718      1.1  christos       status = amd_dbgapi_code_object_get_info
    719      1.1  christos 	(code_object_list[i], AMD_DBGAPI_CODE_OBJECT_INFO_URI_NAME,
    720      1.1  christos 	 sizeof (uri_bytes), &uri_bytes);
    721      1.1  christos       if (status != AMD_DBGAPI_STATUS_SUCCESS)
    722      1.1  christos 	continue;
    723      1.1  christos 
    724      1.1  christos       gdb::unique_xmalloc_ptr<char> uri_bytes_holder (uri_bytes);
    725      1.1  christos 
    726      1.1  christos       lm_info_svr4_up li = std::make_unique<lm_info_svr4> ();
    727      1.1  christos       li->l_addr = l_addr;
    728      1.1  christos 
    729      1.1  christos       /* Generate a unique name so that code objects with the same URI but
    730      1.1  christos 	 different load addresses are seen by gdb core as different shared
    731      1.1  christos 	 objects.  */
    732      1.1  christos       std::string unique_name
    733      1.1  christos 	= string_printf ("code_object_%ld", code_object_list[i].handle);
    734      1.1  christos 
    735      1.1  christos       sos.emplace_back (uri_bytes, std::move (unique_name), std::move (li));
    736      1.1  christos     }
    737      1.1  christos 
    738      1.1  christos   xfree (code_object_list);
    739      1.1  christos 
    740      1.1  christos   if (rocm_solib_ops.current_sos == NULL)
    741      1.1  christos     {
    742      1.1  christos       /* Override what we need to.  */
    743      1.1  christos       rocm_solib_ops = svr4_so_ops;
    744      1.1  christos       rocm_solib_ops.current_sos = rocm_solib_current_sos;
    745      1.1  christos       rocm_solib_ops.solib_create_inferior_hook
    746      1.1  christos 	= rocm_solib_create_inferior_hook;
    747      1.1  christos       rocm_solib_ops.bfd_open = rocm_solib_bfd_open;
    748      1.1  christos       rocm_solib_ops.relocate_section_addresses
    749      1.1  christos 	= rocm_solib_relocate_section_addresses;
    750      1.1  christos       rocm_solib_ops.handle_event = rocm_solib_handle_event;
    751      1.1  christos 
    752      1.1  christos       /* Engage the ROCm so_ops.  */
    753      1.1  christos       set_gdbarch_so_ops (current_inferior ()->arch (), &rocm_solib_ops);
    754      1.1  christos     }
    755      1.1  christos }
    756      1.1  christos 
    757      1.1  christos static void
    758      1.1  christos rocm_solib_target_inferior_created (inferior *inf)
    759      1.1  christos {
    760      1.1  christos   get_solib_info (inf)->solib_list.clear ();
    761      1.1  christos 
    762      1.1  christos   rocm_update_solib_list ();
    763      1.1  christos 
    764      1.1  christos   /* Force GDB to reload the solibs.  */
    765      1.1  christos   current_inferior ()->pspace->clear_solib_cache ();
    766      1.1  christos   solib_add (nullptr, 0, auto_solib_add);
    767      1.1  christos }
    768      1.1  christos 
    769      1.1  christos /* -Wmissing-prototypes */
    770      1.1  christos extern initialize_file_ftype _initialize_rocm_solib;
    771      1.1  christos 
    772      1.1  christos void
    773      1.1  christos _initialize_rocm_solib ()
    774      1.1  christos {
    775      1.1  christos   /* The dependency on the amd-dbgapi exists because solib-rocm's
    776      1.1  christos      inferior_created observer needs amd-dbgapi to have attached the process,
    777      1.1  christos      which happens in amd_dbgapi_target's inferior_created observer.  */
    778      1.1  christos   gdb::observers::inferior_created.attach
    779      1.1  christos     (rocm_solib_target_inferior_created,
    780      1.1  christos      "solib-rocm",
    781      1.1  christos      { &get_amd_dbgapi_target_inferior_created_observer_token () });
    782      1.1  christos }
    783