xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_rawfile.c (revision b69c34dad3717624ff6b4f32b71014ee05b6a678)
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
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
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
99 rf_destroy(mdb_tgt_t *t)
100 {
101 	rf_data_destroy(t->t_data);
102 }
103 
104 /*ARGSUSED*/
105 static const char *
106 rf_name(mdb_tgt_t *t)
107 {
108 	return ("raw");
109 }
110 
111 static ssize_t
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
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
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_PHYS:
147 		if (RF_CORE(t->t_data) != NULL)
148 			return (rf_read(RF_CORE(t->t_data), buf, len, addr));
149 		/*FALLTHRU*/
150 	case (uintptr_t)MDB_TGT_AS_FILE:
151 		return (rf_read(RF_OBJECT(t->t_data), buf, len, addr));
152 	default:
153 		return (set_errno(EMDB_NOMAP));
154 	}
155 }
156 
157 static ssize_t
158 rf_awrite(mdb_tgt_t *t, mdb_tgt_as_t as, const void *buf,
159     size_t len, mdb_tgt_addr_t addr)
160 {
161 	switch ((uintptr_t)as) {
162 	case (uintptr_t)MDB_TGT_AS_VIRT:
163 	case (uintptr_t)MDB_TGT_AS_PHYS:
164 		if (RF_CORE(t->t_data) != NULL)
165 			return (rf_write(RF_CORE(t->t_data), buf, len, addr));
166 		/*FALLTHRU*/
167 	case (uintptr_t)MDB_TGT_AS_FILE:
168 		return (rf_write(RF_OBJECT(t->t_data), buf, len, addr));
169 	default:
170 		return (set_errno(EMDB_NOMAP));
171 	}
172 }
173 
174 static ssize_t
175 rf_vread(mdb_tgt_t *t, void *buf, size_t nbytes, uintptr_t addr)
176 {
177 	if (RF_CORE(t->t_data) != NULL)
178 		return (rf_read(RF_CORE(t->t_data), buf, nbytes, addr));
179 
180 	return (rf_read(RF_OBJECT(t->t_data), buf, nbytes, addr));
181 }
182 
183 static ssize_t
184 rf_vwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, uintptr_t addr)
185 {
186 	if (RF_CORE(t->t_data) != NULL)
187 		return (rf_write(RF_CORE(t->t_data), buf, nbytes, addr));
188 
189 	return (rf_write(RF_OBJECT(t->t_data), buf, nbytes, addr));
190 }
191 
192 static ssize_t
193 rf_pread(mdb_tgt_t *t, void *buf, size_t nbytes, physaddr_t addr)
194 {
195 	if (RF_CORE(t->t_data) != NULL)
196 		return (rf_read(RF_CORE(t->t_data), buf, nbytes, addr));
197 
198 	return (rf_read(RF_OBJECT(t->t_data), buf, nbytes, addr));
199 }
200 
201 static ssize_t
202 rf_pwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, physaddr_t addr)
203 {
204 	if (RF_CORE(t->t_data) != NULL)
205 		return (rf_write(RF_CORE(t->t_data), buf, nbytes, addr));
206 
207 	return (rf_write(RF_OBJECT(t->t_data), buf, nbytes, addr));
208 }
209 
210 static ssize_t
211 rf_fread(mdb_tgt_t *t, void *buf, size_t nbytes, uintptr_t addr)
212 {
213 	return (rf_read(RF_OBJECT(t->t_data), buf, nbytes, addr));
214 }
215 
216 static ssize_t
217 rf_fwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, uintptr_t addr)
218 {
219 	return (rf_write(RF_OBJECT(t->t_data), buf, nbytes, addr));
220 }
221 
222 
223 static int
224 rf_print_map(mdb_io_t *io, const char *type, int tflags,
225     mdb_tgt_map_f *func, void *private)
226 {
227 	mdb_map_t map;
228 
229 	(void) mdb_iob_snprintf(map.map_name, MDB_TGT_MAPSZ,
230 	    "%s (%s)", IOP_NAME(io), type);
231 
232 	map.map_base = 0;
233 	map.map_size = IOP_SEEK(io, 0, SEEK_END);
234 	map.map_flags = MDB_TGT_MAP_R;
235 
236 	if (tflags & MDB_TGT_F_RDWR)
237 		map.map_flags |= MDB_TGT_MAP_W;
238 
239 	return (func(private, &map, map.map_name));
240 }
241 
242 static int
243 rf_mapping_iter(mdb_tgt_t *t, mdb_tgt_map_f *func, void *private)
244 {
245 	rf_data_t *rf = t->t_data;
246 
247 	if (rf->r_object_fio != NULL && rf_print_map(rf->r_object_fio,
248 	    "object file", t->t_flags, func, private) != 0)
249 		return (0);
250 
251 	if (rf->r_core_fio != NULL && rf_print_map(rf->r_core_fio,
252 	    "core file", t->t_flags, func, private) != 0)
253 		return (0);
254 
255 	return (0);
256 }
257 
258 /*ARGSUSED*/
259 static int
260 rf_status(mdb_tgt_t *t, mdb_tgt_status_t *tsp)
261 {
262 	bzero(tsp, sizeof (mdb_tgt_status_t));
263 
264 	if (RF_CORE(t->t_data) != NULL)
265 		tsp->st_state = MDB_TGT_DEAD;
266 	else
267 		tsp->st_state = MDB_TGT_IDLE;
268 
269 	return (0);
270 }
271 
272 /*ARGSUSED*/
273 static int
274 rf_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
275 {
276 	rf_data_t *rf = mdb.m_target->t_data;
277 
278 	if (rf->r_object_fio != NULL) {
279 		mdb_printf("debugging file '%s' (object file)",
280 		    IOP_NAME(rf->r_object_fio));
281 
282 		if (rf->r_core_fio != NULL) {
283 			mdb_printf(" and file '%s' (core file)",
284 			    IOP_NAME(rf->r_core_fio));
285 		}
286 
287 		mdb_printf("\n");
288 	} else {
289 		mdb_printf("debugging empty target\n");
290 	}
291 
292 	return (DCMD_OK);
293 }
294 
295 static const mdb_dcmd_t rf_dcmds[] = {
296 	{ "status", NULL, "print summary of current target", rf_status_dcmd },
297 	{ NULL }
298 };
299 
300 static const struct rf_magic {
301 	const char *rfm_str;
302 	size_t rfm_len;
303 	const char *rfm_mod;
304 } rf_magic[] = {
305 	{ DOF_MAG_STRING, DOF_MAG_STRLEN, "dof" },
306 	{ NULL, 0, NULL }
307 };
308 
309 static void
310 rf_activate(mdb_tgt_t *t)
311 {
312 	rf_data_t *rf = t->t_data;
313 	const struct rf_magic *m;
314 	mdb_var_t *v;
315 	off64_t size;
316 
317 	(void) mdb_tgt_register_dcmds(t, &rf_dcmds[0], MDB_MOD_FORCE);
318 
319 	/*
320 	 * We set the legacy adb variable 'd' to be the size of the file (data
321 	 * segment).  To get this value, we call seek() on the underlying fdio.
322 	 */
323 	if (rf->r_object_fio != NULL) {
324 		size = IOP_SEEK(rf->r_object_fio, 0, SEEK_END);
325 		if ((v = mdb_nv_lookup(&mdb.m_nv, "d")) != NULL)
326 			mdb_nv_set_value(v, size);
327 	}
328 
329 	/*
330 	 * Load any debugging support modules that match the file type, as
331 	 * determined by our poor man's /etc/magic.  If many clients need
332 	 * to use this feature, rf_magic[] should be computed dynamically.
333 	 */
334 	for (m = rf_magic; m->rfm_str != NULL; m++) {
335 		char *buf = mdb_alloc(m->rfm_len, UM_SLEEP);
336 
337 		if (mdb_tgt_vread(t, buf, m->rfm_len, 0) == m->rfm_len &&
338 		    bcmp(buf, m->rfm_str, m->rfm_len) == 0) {
339 			(void) mdb_module_load(m->rfm_mod,
340 			    MDB_MOD_LOCAL | MDB_MOD_SILENT);
341 		}
342 
343 		mdb_free(buf, m->rfm_len);
344 	}
345 }
346 
347 static void
348 rf_deactivate(mdb_tgt_t *t)
349 {
350 	const mdb_dcmd_t *dcp;
351 
352 	for (dcp = &rf_dcmds[0]; dcp->dc_name != NULL; dcp++) {
353 		if (mdb_module_remove_dcmd(t->t_module, dcp->dc_name) == -1)
354 			warn("failed to remove dcmd %s", dcp->dc_name);
355 	}
356 }
357 
358 static const mdb_tgt_ops_t rawfile_ops = {
359 	rf_setflags,				/* t_setflags */
360 	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_setcontext */
361 	rf_activate,				/* t_activate */
362 	rf_deactivate,				/* t_deactivate */
363 	(void (*)())(uintptr_t) mdb_tgt_nop,	/* t_periodic */
364 	rf_destroy,				/* t_destroy */
365 	rf_name,				/* t_name */
366 	(const char *(*)()) mdb_conf_isa,	/* t_isa */
367 	(const char *(*)()) mdb_conf_platform,	/* t_platform */
368 	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_uname */
369 	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_dmodel */
370 	rf_aread,				/* t_aread */
371 	rf_awrite,				/* t_awrite */
372 	rf_vread,				/* t_vread */
373 	rf_vwrite,				/* t_vwrite */
374 	rf_pread,				/* t_pread */
375 	rf_pwrite,				/* t_pwrite */
376 	rf_fread,				/* t_fread */
377 	rf_fwrite,				/* t_fwrite */
378 	(ssize_t (*)()) mdb_tgt_notsup,		/* t_ioread */
379 	(ssize_t (*)()) mdb_tgt_notsup,		/* t_iowrite */
380 	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_vtop */
381 	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_lookup_by_name */
382 	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_lookup_by_addr */
383 	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_symbol_iter */
384 	rf_mapping_iter,			/* t_mapping_iter */
385 	rf_mapping_iter,			/* t_object_iter */
386 	(const mdb_map_t *(*)()) mdb_tgt_null,	/* t_addr_to_map */
387 	(const mdb_map_t *(*)()) mdb_tgt_null,	/* t_name_to_map */
388 	(struct ctf_file *(*)()) mdb_tgt_null,	/* t_addr_to_ctf */
389 	(struct ctf_file *(*)()) mdb_tgt_null,	/* t_name_to_ctf */
390 	rf_status,				/* t_status */
391 	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_run */
392 	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_step */
393 	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_step_out */
394 	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_next */
395 	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_cont */
396 	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_signal */
397 	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_vbrkpt */
398 	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_sbrkpt */
399 	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_pwapt */
400 	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_vwapt */
401 	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_iowapt */
402 	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_sysenter */
403 	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_sysexit */
404 	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_signal */
405 	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_fault */
406 	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_getareg */
407 	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_putareg */
408 	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_stack_iter */
409 	(int (*)())(uintptr_t) mdb_tgt_notsup	/* t_auxv */
410 };
411 
412 int
413 mdb_rawfile_tgt_create(mdb_tgt_t *t, int argc, const char *argv[])
414 {
415 	mdb_io_t *io[2] = { NULL, NULL };
416 	rf_data_t *rf;
417 	int oflags, i;
418 
419 	if (argc > 2)
420 		return (set_errno(EINVAL));
421 
422 	rf = mdb_zalloc(sizeof (rf_data_t), UM_SLEEP);
423 	t->t_ops = &rawfile_ops;
424 	t->t_data = rf;
425 
426 	if (t->t_flags & MDB_TGT_F_RDWR)
427 		oflags = O_RDWR;
428 	else
429 		oflags = O_RDONLY;
430 
431 	for (i = 0; i < argc; i++) {
432 		io[i] = mdb_fdio_create_path(NULL, argv[i], oflags, 0);
433 		if (io[i] == NULL) {
434 			warn("failed to open %s", argv[i]);
435 			goto err;
436 		}
437 	}
438 
439 	rf->r_object_fio = io[0];	/* first file is the "object" */
440 	rf->r_core_fio = io[1];		/* second file is the "core" */
441 	t->t_flags |= MDB_TGT_F_ASIO;	/* do i/o using aread and awrite */
442 
443 	return (0);
444 
445 err:
446 	for (i = 0; i < argc; i++) {
447 		if (io[i] != NULL)
448 			mdb_io_destroy(io[i]);
449 	}
450 
451 
452 	mdb_free(rf, sizeof (rf_data_t));
453 	return (set_errno(EMDB_TGT));
454 }
455