1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright 2018 Joyent, Inc.
26 * Copyright 2024 Oxide Computer Company
27 */
28
29 /*
30 * Raw File Target
31 *
32 * The raw file target is invoked whenever a file of unrecognizable type is
33 * specified on the command line, or when raw file examination is forced using
34 * the -f option. If one file is specified, that file will be opened as the
35 * "object" file. If two files are specified, the second one will be opened
36 * as the "core" file. Each file is opened using the fdio backend, which
37 * internally supports both byte-oriented i/o and block-oriented i/o as needed.
38 */
39
40 #include <mdb/mdb_modapi.h>
41 #include <mdb/mdb_target_impl.h>
42 #include <mdb/mdb_io_impl.h>
43 #include <mdb/mdb_conf.h>
44 #include <mdb/mdb_err.h>
45 #include <mdb/mdb.h>
46
47 #include <sys/dtrace.h>
48 #include <fcntl.h>
49
50 typedef struct rf_data {
51 mdb_io_t *r_object_fio;
52 mdb_io_t *r_core_fio;
53 } rf_data_t;
54
55 #define RF_OBJECT(p) (((rf_data_t *)(p))->r_object_fio)
56 #define RF_CORE(p) (((rf_data_t *)(p))->r_core_fio)
57
58 static void
rf_data_destroy(rf_data_t * rf)59 rf_data_destroy(rf_data_t *rf)
60 {
61 if (rf->r_object_fio != NULL)
62 mdb_io_destroy(rf->r_object_fio);
63
64 if (rf->r_core_fio != NULL)
65 mdb_io_destroy(rf->r_core_fio);
66
67 mdb_free(rf, sizeof (rf_data_t));
68 }
69
70 static int
rf_setflags(mdb_tgt_t * t,int flags)71 rf_setflags(mdb_tgt_t *t, int flags)
72 {
73 if ((flags ^ t->t_flags) & MDB_TGT_F_RDWR) {
74 uint_t otflags = t->t_flags;
75 rf_data_t *orf = t->t_data;
76 const char *argv[2];
77 int argc = 0;
78
79 if (orf->r_object_fio != NULL)
80 argv[argc++] = IOP_NAME(orf->r_object_fio);
81 if (orf->r_core_fio != NULL)
82 argv[argc++] = IOP_NAME(orf->r_core_fio);
83
84 t->t_flags = (t->t_flags & ~MDB_TGT_F_RDWR) |
85 (flags & MDB_TGT_F_RDWR);
86
87 if (mdb_rawfile_tgt_create(t, argc, argv) == -1) {
88 t->t_flags = otflags;
89 t->t_data = orf;
90 return (-1);
91 }
92
93 rf_data_destroy(orf);
94 }
95
96 return (0);
97 }
98
99 static void
rf_destroy(mdb_tgt_t * t)100 rf_destroy(mdb_tgt_t *t)
101 {
102 rf_data_destroy(t->t_data);
103 }
104
105 /*ARGSUSED*/
106 static const char *
rf_name(mdb_tgt_t * t)107 rf_name(mdb_tgt_t *t)
108 {
109 return ("raw");
110 }
111
112 static ssize_t
rf_read(mdb_io_t * io,void * buf,size_t nbytes,uint64_t addr)113 rf_read(mdb_io_t *io, void *buf, size_t nbytes, uint64_t addr)
114 {
115 ssize_t rbytes;
116
117 if (io == NULL)
118 return (set_errno(EMDB_NOMAP));
119
120 if (IOP_SEEK(io, addr, SEEK_SET) == -1)
121 return (-1); /* errno is set for us */
122
123 if ((rbytes = IOP_READ(io, buf, nbytes)) == 0)
124 (void) set_errno(EMDB_EOF);
125
126 return (rbytes);
127 }
128
129 static ssize_t
rf_write(mdb_io_t * io,const void * buf,size_t nbytes,uint64_t addr)130 rf_write(mdb_io_t *io, const void *buf, size_t nbytes, uint64_t addr)
131 {
132 if (io == NULL)
133 return (set_errno(EMDB_NOMAP));
134
135 if (IOP_SEEK(io, addr, SEEK_SET) == -1)
136 return (-1); /* errno is set for us */
137
138 return (IOP_WRITE(io, buf, nbytes));
139 }
140
141 static ssize_t
rf_aread(mdb_tgt_t * t,mdb_tgt_as_t as,void * buf,size_t len,mdb_tgt_addr_t addr)142 rf_aread(mdb_tgt_t *t, mdb_tgt_as_t as, void *buf,
143 size_t len, mdb_tgt_addr_t addr)
144 {
145 switch ((uintptr_t)as) {
146 case (uintptr_t)MDB_TGT_AS_VIRT:
147 case (uintptr_t)MDB_TGT_AS_VIRT_I:
148 case (uintptr_t)MDB_TGT_AS_VIRT_S:
149 case (uintptr_t)MDB_TGT_AS_PHYS:
150 if (RF_CORE(t->t_data) != NULL)
151 return (rf_read(RF_CORE(t->t_data), buf, len, addr));
152 /*FALLTHRU*/
153 case (uintptr_t)MDB_TGT_AS_FILE:
154 return (rf_read(RF_OBJECT(t->t_data), buf, len, addr));
155 default:
156 return (set_errno(EMDB_NOMAP));
157 }
158 }
159
160 static ssize_t
rf_awrite(mdb_tgt_t * t,mdb_tgt_as_t as,const void * buf,size_t len,mdb_tgt_addr_t addr)161 rf_awrite(mdb_tgt_t *t, mdb_tgt_as_t as, const void *buf,
162 size_t len, mdb_tgt_addr_t addr)
163 {
164 switch ((uintptr_t)as) {
165 case (uintptr_t)MDB_TGT_AS_VIRT:
166 case (uintptr_t)MDB_TGT_AS_VIRT_I:
167 case (uintptr_t)MDB_TGT_AS_VIRT_S:
168 case (uintptr_t)MDB_TGT_AS_PHYS:
169 if (RF_CORE(t->t_data) != NULL)
170 return (rf_write(RF_CORE(t->t_data), buf, len, addr));
171 /*FALLTHRU*/
172 case (uintptr_t)MDB_TGT_AS_FILE:
173 return (rf_write(RF_OBJECT(t->t_data), buf, len, addr));
174 default:
175 return (set_errno(EMDB_NOMAP));
176 }
177 }
178
179 static ssize_t
rf_vread(mdb_tgt_t * t,void * buf,size_t nbytes,uintptr_t addr)180 rf_vread(mdb_tgt_t *t, void *buf, size_t nbytes, uintptr_t addr)
181 {
182 if (RF_CORE(t->t_data) != NULL)
183 return (rf_read(RF_CORE(t->t_data), buf, nbytes, addr));
184
185 return (rf_read(RF_OBJECT(t->t_data), buf, nbytes, addr));
186 }
187
188 static ssize_t
rf_vwrite(mdb_tgt_t * t,const void * buf,size_t nbytes,uintptr_t addr)189 rf_vwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, uintptr_t addr)
190 {
191 if (RF_CORE(t->t_data) != NULL)
192 return (rf_write(RF_CORE(t->t_data), buf, nbytes, addr));
193
194 return (rf_write(RF_OBJECT(t->t_data), buf, nbytes, addr));
195 }
196
197 static ssize_t
rf_pread(mdb_tgt_t * t,void * buf,size_t nbytes,physaddr_t addr)198 rf_pread(mdb_tgt_t *t, void *buf, size_t nbytes, physaddr_t addr)
199 {
200 if (RF_CORE(t->t_data) != NULL)
201 return (rf_read(RF_CORE(t->t_data), buf, nbytes, addr));
202
203 return (rf_read(RF_OBJECT(t->t_data), buf, nbytes, addr));
204 }
205
206 static ssize_t
rf_pwrite(mdb_tgt_t * t,const void * buf,size_t nbytes,physaddr_t addr)207 rf_pwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, physaddr_t addr)
208 {
209 if (RF_CORE(t->t_data) != NULL)
210 return (rf_write(RF_CORE(t->t_data), buf, nbytes, addr));
211
212 return (rf_write(RF_OBJECT(t->t_data), buf, nbytes, addr));
213 }
214
215 static ssize_t
rf_fread(mdb_tgt_t * t,void * buf,size_t nbytes,uintptr_t addr)216 rf_fread(mdb_tgt_t *t, void *buf, size_t nbytes, uintptr_t addr)
217 {
218 return (rf_read(RF_OBJECT(t->t_data), buf, nbytes, addr));
219 }
220
221 static ssize_t
rf_fwrite(mdb_tgt_t * t,const void * buf,size_t nbytes,uintptr_t addr)222 rf_fwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, uintptr_t addr)
223 {
224 return (rf_write(RF_OBJECT(t->t_data), buf, nbytes, addr));
225 }
226
227
228 static int
rf_print_map(mdb_io_t * io,const char * type,int tflags,mdb_tgt_map_f * func,void * private)229 rf_print_map(mdb_io_t *io, const char *type, int tflags,
230 mdb_tgt_map_f *func, void *private)
231 {
232 mdb_map_t map;
233
234 (void) mdb_iob_snprintf(map.map_name, MDB_TGT_MAPSZ,
235 "%s (%s)", IOP_NAME(io), type);
236
237 map.map_base = 0;
238 map.map_size = IOP_SEEK(io, 0, SEEK_END);
239 map.map_flags = MDB_TGT_MAP_R;
240
241 if (tflags & MDB_TGT_F_RDWR)
242 map.map_flags |= MDB_TGT_MAP_W;
243
244 return (func(private, &map, map.map_name));
245 }
246
247 static int
rf_mapping_iter(mdb_tgt_t * t,mdb_tgt_map_f * func,void * private)248 rf_mapping_iter(mdb_tgt_t *t, mdb_tgt_map_f *func, void *private)
249 {
250 rf_data_t *rf = t->t_data;
251
252 if (rf->r_object_fio != NULL && rf_print_map(rf->r_object_fio,
253 "object file", t->t_flags, func, private) != 0)
254 return (0);
255
256 if (rf->r_core_fio != NULL && rf_print_map(rf->r_core_fio,
257 "core file", t->t_flags, func, private) != 0)
258 return (0);
259
260 return (0);
261 }
262
263 /*ARGSUSED*/
264 static int
rf_status(mdb_tgt_t * t,mdb_tgt_status_t * tsp)265 rf_status(mdb_tgt_t *t, mdb_tgt_status_t *tsp)
266 {
267 bzero(tsp, sizeof (mdb_tgt_status_t));
268
269 if (RF_CORE(t->t_data) != NULL)
270 tsp->st_state = MDB_TGT_DEAD;
271 else
272 tsp->st_state = MDB_TGT_IDLE;
273
274 return (0);
275 }
276
277 /*ARGSUSED*/
278 static int
rf_status_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)279 rf_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
280 {
281 rf_data_t *rf = mdb.m_target->t_data;
282
283 if (rf->r_object_fio != NULL) {
284 mdb_printf("debugging file '%s' (object file)",
285 IOP_NAME(rf->r_object_fio));
286
287 if (rf->r_core_fio != NULL) {
288 mdb_printf(" and file '%s' (core file)",
289 IOP_NAME(rf->r_core_fio));
290 }
291
292 mdb_printf("\n");
293 } else {
294 mdb_printf("debugging empty target\n");
295 }
296
297 return (DCMD_OK);
298 }
299
300 static const mdb_dcmd_t rf_dcmds[] = {
301 { "status", NULL, "print summary of current target", rf_status_dcmd },
302 { NULL }
303 };
304
305 static const struct rf_magic {
306 const char *rfm_str;
307 size_t rfm_len;
308 const char *rfm_mod;
309 } rf_magic[] = {
310 { DOF_MAG_STRING, DOF_MAG_STRLEN, "dof" },
311 { NULL, 0, NULL }
312 };
313
314 static void
rf_activate(mdb_tgt_t * t)315 rf_activate(mdb_tgt_t *t)
316 {
317 rf_data_t *rf = t->t_data;
318 const struct rf_magic *m;
319 mdb_var_t *v;
320 off64_t size;
321
322 (void) mdb_tgt_register_dcmds(t, &rf_dcmds[0], MDB_MOD_FORCE);
323
324 /*
325 * We set the legacy adb variable 'd' to be the size of the file (data
326 * segment). To get this value, we call seek() on the underlying fdio.
327 */
328 if (rf->r_object_fio != NULL) {
329 size = IOP_SEEK(rf->r_object_fio, 0, SEEK_END);
330 if ((v = mdb_nv_lookup(&mdb.m_nv, "d")) != NULL)
331 mdb_nv_set_value(v, size);
332 }
333
334 /*
335 * Load any debugging support modules that match the file type, as
336 * determined by our poor man's /etc/magic. If many clients need
337 * to use this feature, rf_magic[] should be computed dynamically.
338 */
339 for (m = rf_magic; m->rfm_str != NULL; m++) {
340 char *buf = mdb_alloc(m->rfm_len, UM_SLEEP);
341
342 if (mdb_tgt_vread(t, buf, m->rfm_len, 0) == m->rfm_len &&
343 bcmp(buf, m->rfm_str, m->rfm_len) == 0) {
344 (void) mdb_module_load(m->rfm_mod,
345 MDB_MOD_LOCAL | MDB_MOD_SILENT);
346 }
347
348 mdb_free(buf, m->rfm_len);
349 }
350 }
351
352 static void
rf_deactivate(mdb_tgt_t * t)353 rf_deactivate(mdb_tgt_t *t)
354 {
355 const mdb_dcmd_t *dcp;
356
357 for (dcp = &rf_dcmds[0]; dcp->dc_name != NULL; dcp++) {
358 if (mdb_module_remove_dcmd(t->t_module, dcp->dc_name) == -1)
359 warn("failed to remove dcmd %s", dcp->dc_name);
360 }
361 }
362
363 static const mdb_tgt_ops_t rawfile_ops = {
364 .t_setflags = rf_setflags,
365 .t_setcontext = (int (*)())(uintptr_t)mdb_tgt_notsup,
366 .t_activate = rf_activate,
367 .t_deactivate = rf_deactivate,
368 .t_periodic = (void (*)())(uintptr_t)mdb_tgt_nop,
369 .t_destroy = rf_destroy,
370 .t_name = rf_name,
371 .t_isa = (const char *(*)())mdb_conf_isa,
372 .t_platform = (const char *(*)())mdb_conf_platform,
373 .t_uname = (int (*)())(uintptr_t)mdb_tgt_notsup,
374 .t_dmodel = (int (*)())(uintptr_t)mdb_tgt_notsup,
375 .t_aread = rf_aread,
376 .t_awrite = rf_awrite,
377 .t_vread = rf_vread,
378 .t_vwrite = rf_vwrite,
379 .t_pread = rf_pread,
380 .t_pwrite = rf_pwrite,
381 .t_fread = rf_fread,
382 .t_fwrite = rf_fwrite,
383 .t_ioread = (ssize_t (*)())mdb_tgt_notsup,
384 .t_iowrite = (ssize_t (*)())mdb_tgt_notsup,
385 .t_vtop = (int (*)())(uintptr_t)mdb_tgt_notsup,
386 .t_lookup_by_name = (int (*)())(uintptr_t)mdb_tgt_notsup,
387 .t_lookup_by_addr = (int (*)())(uintptr_t)mdb_tgt_notsup,
388 .t_symbol_iter = (int (*)())(uintptr_t)mdb_tgt_notsup,
389 .t_mapping_iter = rf_mapping_iter,
390 .t_object_iter = rf_mapping_iter,
391 .t_addr_to_map = (const mdb_map_t *(*)())mdb_tgt_null,
392 .t_name_to_map = (const mdb_map_t *(*)())mdb_tgt_null,
393 .t_addr_to_ctf = (struct ctf_file *(*)())mdb_tgt_null,
394 .t_name_to_ctf = (struct ctf_file *(*)())mdb_tgt_null,
395 .t_status = rf_status,
396 .t_run = (int (*)())(uintptr_t)mdb_tgt_notsup,
397 .t_step = (int (*)())(uintptr_t)mdb_tgt_notsup,
398 .t_step_out = (int (*)())(uintptr_t)mdb_tgt_notsup,
399 .t_next = (int (*)())(uintptr_t)mdb_tgt_notsup,
400 .t_cont = (int (*)())(uintptr_t)mdb_tgt_notsup,
401 .t_signal = (int (*)())(uintptr_t)mdb_tgt_notsup,
402 .t_add_vbrkpt = (int (*)())(uintptr_t)mdb_tgt_null,
403 .t_add_sbrkpt = (int (*)())(uintptr_t)mdb_tgt_null,
404 .t_add_pwapt = (int (*)())(uintptr_t)mdb_tgt_null,
405 .t_add_vwapt = (int (*)())(uintptr_t)mdb_tgt_null,
406 .t_add_iowapt = (int (*)())(uintptr_t)mdb_tgt_null,
407 .t_add_sysenter = (int (*)())(uintptr_t)mdb_tgt_null,
408 .t_add_sysexit = (int (*)())(uintptr_t)mdb_tgt_null,
409 .t_add_signal = (int (*)())(uintptr_t)mdb_tgt_null,
410 .t_add_fault = (int (*)())(uintptr_t)mdb_tgt_null,
411 .t_getareg = (int (*)())(uintptr_t)mdb_tgt_notsup,
412 .t_putareg = (int (*)())(uintptr_t)mdb_tgt_notsup,
413 .t_stack_iter = (int (*)())(uintptr_t)mdb_tgt_notsup,
414 .t_auxv = (int (*)())(uintptr_t)mdb_tgt_notsup,
415 .t_thread_name = (int (*)())(uintptr_t)mdb_tgt_notsup,
416 };
417
418 int
mdb_rawfile_tgt_create(mdb_tgt_t * t,int argc,const char * argv[])419 mdb_rawfile_tgt_create(mdb_tgt_t *t, int argc, const char *argv[])
420 {
421 mdb_io_t *io[2] = { NULL, NULL };
422 rf_data_t *rf;
423 int oflags, i;
424
425 if (argc > 2)
426 return (set_errno(EINVAL));
427
428 rf = mdb_zalloc(sizeof (rf_data_t), UM_SLEEP);
429 t->t_ops = &rawfile_ops;
430 t->t_data = rf;
431
432 if (t->t_flags & MDB_TGT_F_RDWR)
433 oflags = O_RDWR;
434 else
435 oflags = O_RDONLY;
436
437 for (i = 0; i < argc; i++) {
438 io[i] = mdb_fdio_create_path(NULL, argv[i], oflags, 0);
439 if (io[i] == NULL) {
440 warn("failed to open %s", argv[i]);
441 goto err;
442 }
443 }
444
445 rf->r_object_fio = io[0]; /* first file is the "object" */
446 rf->r_core_fio = io[1]; /* second file is the "core" */
447 t->t_flags |= MDB_TGT_F_ASIO; /* do i/o using aread and awrite */
448
449 return (0);
450
451 err:
452 for (i = 0; i < argc; i++) {
453 if (io[i] != NULL)
454 mdb_io_destroy(io[i]);
455 }
456
457
458 mdb_free(rf, sizeof (rf_data_t));
459 return (set_errno(EMDB_TGT));
460 }
461