Commit | Line | Data |
---|---|---|
5922befa LM |
1 | /* Handle ROCm Code Objects for GDB, the GNU Debugger. |
2 | ||
3 | Copyright (C) 2019 Free Software Foundation, Inc. | |
4 | Copyright (C) 2019 Advanced Micro Devices, Inc. All rights reserved. | |
5 | ||
6 | This file is part of GDB. | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 3 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
20 | ||
21 | #include "defs.h" | |
22 | ||
23 | #include "arch-utils.h" | |
24 | #include "elf-bfd.h" | |
25 | #include "gdbcore.h" | |
26 | #include "inferior.h" | |
27 | #include "objfiles.h" | |
28 | #include "observable.h" | |
29 | #include "rocm-tdep.h" | |
30 | #include "solib-svr4.h" | |
31 | #include "solib.h" | |
32 | #include "solist.h" | |
33 | #include "symfile.h" | |
34 | ||
35 | #include <functional> | |
36 | #include <string> | |
37 | ||
38 | #define ROCM_DSO_NAME_PREFIX "ROCm-supplied DSO [loaded from memory " | |
39 | ||
40 | #define ROCM_DSO_NAME_SUFFIX "]" | |
41 | ||
42 | /* ROCm-specific inferior data. */ | |
43 | ||
44 | struct solib_info | |
45 | { | |
46 | /* List of code objects loaded into the inferior. */ | |
47 | struct so_list *solib_list; | |
48 | }; | |
49 | ||
50 | /* Per-inferior data key. */ | |
51 | static const struct inferior_key<solib_info> rocm_solib_data; | |
52 | ||
53 | struct target_so_ops rocm_solib_ops; | |
54 | ||
55 | /* Free the solib linked list. */ | |
56 | ||
57 | static void | |
58 | rocm_free_solib_list (struct solib_info *info) | |
59 | { | |
60 | while (info->solib_list != NULL) | |
61 | { | |
62 | struct so_list *next = info->solib_list->next; | |
63 | ||
64 | free_so (info->solib_list); | |
65 | info->solib_list = next; | |
66 | } | |
67 | ||
68 | info->solib_list = NULL; | |
69 | } | |
70 | ||
71 | /* Fetch the solib_info data for the current inferior. */ | |
72 | ||
73 | static struct solib_info * | |
74 | get_solib_info (void) | |
75 | { | |
76 | struct inferior *inf = current_inferior (); | |
77 | ||
78 | struct solib_info *info = rocm_solib_data.get (inf); | |
79 | if (info == NULL) | |
80 | info = rocm_solib_data.emplace (inf); | |
81 | ||
82 | return info; | |
83 | } | |
84 | ||
85 | /* Relocate section addresses. */ | |
86 | ||
87 | static void | |
88 | rocm_solib_relocate_section_addresses (struct so_list *so, | |
89 | struct target_section *sec) | |
90 | { | |
91 | if (!rocm_is_amdgcn_gdbarch (gdbarch_from_bfd (so->abfd))) | |
92 | { | |
93 | svr4_so_ops.relocate_section_addresses (so, sec); | |
94 | return; | |
95 | } | |
96 | ||
97 | lm_info_svr4 *li = (lm_info_svr4 *)so->lm_info; | |
98 | sec->addr = sec->addr + li->l_addr; | |
99 | sec->endaddr = sec->endaddr + li->l_addr; | |
100 | } | |
101 | ||
102 | /* Make a deep copy of the solib linked list. */ | |
103 | ||
104 | static struct so_list * | |
105 | rocm_solib_copy_list (const struct so_list *src) | |
106 | { | |
107 | struct so_list *dst = NULL; | |
108 | struct so_list **link = &dst; | |
109 | ||
110 | while (src != NULL) | |
111 | { | |
112 | struct so_list *newobj; | |
113 | ||
114 | newobj = XNEW (struct so_list); | |
115 | memcpy (newobj, src, sizeof (struct so_list)); | |
116 | ||
117 | lm_info_svr4 *src_li = (lm_info_svr4 *)src->lm_info; | |
118 | newobj->lm_info = new lm_info_svr4 (*src_li); | |
119 | ||
120 | newobj->next = NULL; | |
121 | *link = newobj; | |
122 | link = &newobj->next; | |
123 | ||
124 | src = src->next; | |
125 | } | |
126 | ||
127 | return dst; | |
128 | } | |
129 | ||
130 | /* Build a list of `struct so_list' objects describing the shared | |
131 | objects currently loaded in the inferior. */ | |
132 | ||
133 | static struct so_list * | |
134 | rocm_solib_current_sos (void) | |
135 | { | |
136 | /* First, retrieve the host-side shared library list. */ | |
137 | struct so_list *head = svr4_so_ops.current_sos (); | |
138 | ||
139 | /* Then, the device-side shared library list. */ | |
140 | struct so_list *list = get_solib_info ()->solib_list; | |
141 | ||
142 | if (!list) | |
143 | return head; | |
144 | ||
145 | list = rocm_solib_copy_list (list); | |
146 | ||
147 | if (!head) | |
148 | return list; | |
149 | ||
150 | /* Append our libraries to the end of the list. */ | |
151 | struct so_list *tail; | |
152 | for (tail = head; tail->next; tail = tail->next) | |
153 | /* Nothing. */; | |
154 | tail->next = list; | |
155 | ||
156 | return head; | |
157 | } | |
158 | ||
159 | struct target_elf_image | |
160 | { | |
161 | /* The base address of the ELF file image in the inferior's memory. */ | |
162 | CORE_ADDR base_addr; | |
163 | ||
164 | /* The size of the ELF file image. */ | |
165 | ULONGEST size; | |
166 | }; | |
167 | ||
168 | static void * | |
169 | rocm_bfd_iovec_open (bfd *nbfd, void *open_closure) | |
170 | { | |
171 | return open_closure; | |
172 | } | |
173 | ||
174 | static int | |
175 | rocm_bfd_iovec_close (bfd *nbfd, void *stream) | |
176 | { | |
177 | xfree (stream); | |
178 | return 0; | |
179 | } | |
180 | ||
181 | static file_ptr | |
182 | rocm_bfd_iovec_pread (bfd *abfd, void *stream, void *buf, file_ptr nbytes, | |
183 | file_ptr offset) | |
184 | { | |
185 | CORE_ADDR addr = ((target_elf_image *)stream)->base_addr; | |
186 | ||
187 | if (target_read_memory (addr + offset, (gdb_byte *)buf, nbytes) != 0) | |
188 | { | |
189 | bfd_set_error (bfd_error_invalid_operation); | |
190 | return -1; | |
191 | } | |
192 | ||
193 | return nbytes; | |
194 | } | |
195 | ||
196 | static int | |
197 | rocm_bfd_iovec_stat (bfd *abfd, void *stream, struct stat *sb) | |
198 | { | |
199 | memset (sb, '\0', sizeof (struct stat)); | |
200 | sb->st_size = ((struct target_elf_image *)stream)->size; | |
201 | return 0; | |
202 | } | |
203 | ||
204 | static gdb_bfd_ref_ptr | |
205 | rocm_solib_bfd_open (const char *pathname) | |
206 | { | |
207 | struct target_elf_image *open_closure; | |
208 | CORE_ADDR addr, end; | |
209 | ||
210 | /* Handle regular SVR4 libraries. */ | |
211 | if (strstr (pathname, ROCM_DSO_NAME_PREFIX) != pathname) | |
212 | return svr4_so_ops.bfd_open (pathname); | |
213 | ||
214 | /* Decode the start and end addresses. */ | |
215 | if (sscanf (pathname + sizeof (ROCM_DSO_NAME_PREFIX) - 1, "0x%lx..0x%lx", | |
216 | &addr, &end) | |
217 | != 2) | |
218 | internal_error (__FILE__, __LINE__, "ROCm-GDB Error: bad DSO name: `%s'", | |
219 | pathname); | |
220 | ||
221 | open_closure = XNEW (struct target_elf_image); | |
222 | open_closure->base_addr = addr; | |
223 | open_closure->size = end - addr; | |
224 | ||
225 | gdb_bfd_ref_ptr abfd (gdb_bfd_openr_iovec ( | |
226 | pathname, "elf64-amdgcn", rocm_bfd_iovec_open, open_closure, | |
227 | rocm_bfd_iovec_pread, rocm_bfd_iovec_close, rocm_bfd_iovec_stat)); | |
228 | ||
229 | /* Check bfd format. */ | |
230 | if (!bfd_check_format (abfd.get (), bfd_object)) | |
231 | error (_ ("`%s': not in executable format: %s"), | |
232 | bfd_get_filename (abfd.get ()), bfd_errmsg (bfd_get_error ())); | |
233 | ||
234 | unsigned char osabi = elf_elfheader (abfd)->e_ident[EI_OSABI]; | |
235 | unsigned char osabiversion = elf_elfheader (abfd)->e_ident[EI_ABIVERSION]; | |
236 | ||
237 | /* Make a check if the code object in the elf is V3. ROCM-gdb has | |
238 | support only for V3. */ | |
239 | if (osabi != ELFOSABI_AMDGPU_HSA) | |
240 | error (_ ("`%s': ELF file OS ABI invalid (%d)."), | |
241 | bfd_get_filename (abfd.get ()), osabi); | |
242 | ||
243 | if (osabi == ELFOSABI_AMDGPU_HSA && osabiversion < 1) | |
244 | error (_ ("`%s': ELF file ABI version (%d) is not supported."), | |
245 | bfd_get_filename (abfd.get ()), osabiversion); | |
246 | ||
247 | return abfd; | |
248 | } | |
249 | ||
250 | static void | |
251 | rocm_solib_create_inferior_hook (int from_tty) | |
252 | { | |
253 | rocm_free_solib_list (get_solib_info ()); | |
254 | ||
255 | svr4_so_ops.solib_create_inferior_hook (from_tty); | |
256 | } | |
257 | ||
258 | static void | |
259 | rocm_update_solib_list () | |
260 | { | |
261 | amd_dbgapi_process_id_t process_id = get_amd_dbgapi_process_id (); | |
262 | solib_info *info = get_solib_info (); | |
263 | amd_dbgapi_status_t status; | |
264 | ||
265 | rocm_free_solib_list (info); | |
266 | struct so_list **link = &info->solib_list; | |
267 | ||
268 | amd_dbgapi_code_object_id_t *code_object_list; | |
269 | size_t count; | |
270 | ||
271 | if ((status = amd_dbgapi_code_object_list (process_id, &count, | |
272 | &code_object_list, nullptr)) | |
273 | != AMD_DBGAPI_STATUS_SUCCESS) | |
274 | { | |
275 | warning (_ ("amd_dbgapi_code_object_list failed (%d)"), status); | |
276 | return; | |
277 | } | |
278 | ||
279 | for (size_t i = 0; i < count; ++i) | |
280 | { | |
281 | struct so_list *so = XCNEW (struct so_list); | |
282 | lm_info_svr4 *li = new lm_info_svr4; | |
283 | so->lm_info = li; | |
284 | ||
285 | char *uri_bytes; | |
286 | ||
287 | if (amd_dbgapi_code_object_get_info ( | |
288 | process_id, code_object_list[i], | |
289 | AMD_DBGAPI_CODE_OBJECT_INFO_LOAD_ADDRESS, sizeof (li->l_addr), | |
290 | &li->l_addr) | |
291 | != AMD_DBGAPI_STATUS_SUCCESS | |
292 | || amd_dbgapi_code_object_get_info ( | |
293 | process_id, code_object_list[i], | |
294 | AMD_DBGAPI_CODE_OBJECT_INFO_URI_NAME, sizeof (uri_bytes), | |
295 | &uri_bytes) | |
296 | != AMD_DBGAPI_STATUS_SUCCESS) | |
297 | continue; | |
298 | ||
299 | /* FIXME: We need to properly decode the URI. */ | |
300 | ||
301 | std::string uri (uri_bytes); | |
302 | xfree (uri_bytes); | |
303 | ||
304 | size_t address_pos = uri.find ("://"); | |
305 | if (address_pos == std::string::npos) | |
306 | continue; | |
307 | address_pos += 3; | |
308 | ||
309 | size_t fragment_pos = uri.find ('#', address_pos); | |
310 | if (address_pos == std::string::npos) | |
311 | continue; | |
312 | ||
313 | std::string fragment = uri.substr (fragment_pos); | |
314 | ||
315 | /* Decode the offset and size. */ | |
316 | amd_dbgapi_global_address_t mem_addr; | |
317 | amd_dbgapi_size_t mem_size; | |
318 | ||
319 | if (sscanf (fragment.c_str (), "#offset=0x%lx&size=0x%lx", &mem_addr, | |
320 | &mem_size) | |
321 | != 2) | |
322 | internal_error (__FILE__, __LINE__, | |
323 | "ROCm-GDB Error: bad DSO name: `%s'", uri.c_str ()); | |
324 | ||
325 | xsnprintf (so->so_name, sizeof so->so_name, | |
326 | ROCM_DSO_NAME_PREFIX "%s..%s" ROCM_DSO_NAME_SUFFIX, | |
327 | hex_string (mem_addr), hex_string (mem_addr + mem_size)); | |
328 | strcpy (so->so_original_name, so->so_name); | |
329 | ||
330 | so->next = nullptr; | |
331 | *link = so; | |
332 | link = &so->next; | |
333 | } | |
334 | ||
335 | xfree (code_object_list); | |
336 | ||
337 | /* Force GDB to reload the solibs. */ | |
338 | clear_program_space_solib_cache (current_inferior ()->pspace); | |
339 | ||
340 | /* Switch terminal for any messages produced by | |
341 | breakpoint_re_set. */ | |
342 | target_terminal::ours_for_output (); | |
343 | ||
344 | solib_add (NULL, 0, auto_solib_add); | |
345 | ||
346 | /* Switch it back. */ | |
347 | target_terminal::inferior (); | |
348 | } | |
349 | ||
350 | static void | |
351 | rocm_solib_dbgapi_activated () | |
352 | { | |
353 | if (rocm_solib_ops.current_sos == NULL) | |
354 | { | |
355 | /* Override what we need to */ | |
356 | rocm_solib_ops = svr4_so_ops; | |
357 | rocm_solib_ops.current_sos = rocm_solib_current_sos; | |
358 | rocm_solib_ops.solib_create_inferior_hook | |
359 | = rocm_solib_create_inferior_hook; | |
360 | rocm_solib_ops.bfd_open = rocm_solib_bfd_open; | |
361 | rocm_solib_ops.relocate_section_addresses | |
362 | = rocm_solib_relocate_section_addresses; | |
363 | } | |
364 | ||
365 | /* Engage the ROCm so_ops. */ | |
366 | set_solib_ops (target_gdbarch (), &rocm_solib_ops); | |
367 | } | |
368 | ||
369 | static void | |
370 | rocm_solib_dbgapi_deactivated () | |
371 | { | |
372 | /* Disengage the ROCm so_ops. */ | |
373 | set_solib_ops (target_gdbarch (), &svr4_so_ops); | |
374 | } | |
375 | ||
376 | static void | |
377 | rocm_solib_target_inferior_created (struct target_ops *target, int from_tty) | |
378 | { | |
379 | rocm_free_solib_list (get_solib_info ()); | |
380 | rocm_update_solib_list (); | |
381 | } | |
382 | ||
383 | /* -Wmissing-prototypes */ | |
384 | extern initialize_file_ftype _initialize_rocm_solib; | |
385 | ||
386 | void | |
387 | _initialize_rocm_solib (void) | |
388 | { | |
389 | /* Install our observers. */ | |
390 | amd_dbgapi_activated.attach (rocm_solib_dbgapi_activated); | |
391 | amd_dbgapi_deactivated.attach (rocm_solib_dbgapi_deactivated); | |
392 | amd_dbgapi_code_object_list_updated.attach (rocm_update_solib_list); | |
393 | ||
394 | /* FIXME: remove this when we can clear the solist in | |
395 | rocm_solib_create_inferior_hook. */ | |
396 | gdb::observers::inferior_created.attach (rocm_solib_target_inferior_created); | |
397 | } |