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