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