xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_rawfile.c (revision d363b1b0cb9ef6d6f3febdd8d1cba46507e97098)
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
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
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
100 rf_destroy(mdb_tgt_t *t)
101 {
102 	rf_data_destroy(t->t_data);
103 }
104 
105 /*ARGSUSED*/
106 static const char *
107 rf_name(mdb_tgt_t *t)
108 {
109 	return ("raw");
110 }
111 
112 static ssize_t
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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