/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2018 Joyent, Inc. * Copyright 2024 Oxide Computer Company */ /* * Raw File Target * * The raw file target is invoked whenever a file of unrecognizable type is * specified on the command line, or when raw file examination is forced using * the -f option. If one file is specified, that file will be opened as the * "object" file. If two files are specified, the second one will be opened * as the "core" file. Each file is opened using the fdio backend, which * internally supports both byte-oriented i/o and block-oriented i/o as needed. */ #include #include #include #include #include #include #include #include typedef struct rf_data { mdb_io_t *r_object_fio; mdb_io_t *r_core_fio; } rf_data_t; #define RF_OBJECT(p) (((rf_data_t *)(p))->r_object_fio) #define RF_CORE(p) (((rf_data_t *)(p))->r_core_fio) static void rf_data_destroy(rf_data_t *rf) { if (rf->r_object_fio != NULL) mdb_io_destroy(rf->r_object_fio); if (rf->r_core_fio != NULL) mdb_io_destroy(rf->r_core_fio); mdb_free(rf, sizeof (rf_data_t)); } static int rf_setflags(mdb_tgt_t *t, int flags) { if ((flags ^ t->t_flags) & MDB_TGT_F_RDWR) { uint_t otflags = t->t_flags; rf_data_t *orf = t->t_data; const char *argv[2]; int argc = 0; if (orf->r_object_fio != NULL) argv[argc++] = IOP_NAME(orf->r_object_fio); if (orf->r_core_fio != NULL) argv[argc++] = IOP_NAME(orf->r_core_fio); t->t_flags = (t->t_flags & ~MDB_TGT_F_RDWR) | (flags & MDB_TGT_F_RDWR); if (mdb_rawfile_tgt_create(t, argc, argv) == -1) { t->t_flags = otflags; t->t_data = orf; return (-1); } rf_data_destroy(orf); } return (0); } static void rf_destroy(mdb_tgt_t *t) { rf_data_destroy(t->t_data); } /*ARGSUSED*/ static const char * rf_name(mdb_tgt_t *t) { return ("raw"); } static ssize_t rf_read(mdb_io_t *io, void *buf, size_t nbytes, uint64_t addr) { ssize_t rbytes; if (io == NULL) return (set_errno(EMDB_NOMAP)); if (IOP_SEEK(io, addr, SEEK_SET) == -1) return (-1); /* errno is set for us */ if ((rbytes = IOP_READ(io, buf, nbytes)) == 0) (void) set_errno(EMDB_EOF); return (rbytes); } static ssize_t rf_write(mdb_io_t *io, const void *buf, size_t nbytes, uint64_t addr) { if (io == NULL) return (set_errno(EMDB_NOMAP)); if (IOP_SEEK(io, addr, SEEK_SET) == -1) return (-1); /* errno is set for us */ return (IOP_WRITE(io, buf, nbytes)); } static ssize_t rf_aread(mdb_tgt_t *t, mdb_tgt_as_t as, void *buf, size_t len, mdb_tgt_addr_t addr) { switch ((uintptr_t)as) { case (uintptr_t)MDB_TGT_AS_VIRT: case (uintptr_t)MDB_TGT_AS_VIRT_I: case (uintptr_t)MDB_TGT_AS_VIRT_S: case (uintptr_t)MDB_TGT_AS_PHYS: if (RF_CORE(t->t_data) != NULL) return (rf_read(RF_CORE(t->t_data), buf, len, addr)); /*FALLTHRU*/ case (uintptr_t)MDB_TGT_AS_FILE: return (rf_read(RF_OBJECT(t->t_data), buf, len, addr)); default: return (set_errno(EMDB_NOMAP)); } } 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) { switch ((uintptr_t)as) { case (uintptr_t)MDB_TGT_AS_VIRT: case (uintptr_t)MDB_TGT_AS_VIRT_I: case (uintptr_t)MDB_TGT_AS_VIRT_S: case (uintptr_t)MDB_TGT_AS_PHYS: if (RF_CORE(t->t_data) != NULL) return (rf_write(RF_CORE(t->t_data), buf, len, addr)); /*FALLTHRU*/ case (uintptr_t)MDB_TGT_AS_FILE: return (rf_write(RF_OBJECT(t->t_data), buf, len, addr)); default: return (set_errno(EMDB_NOMAP)); } } static ssize_t rf_vread(mdb_tgt_t *t, void *buf, size_t nbytes, uintptr_t addr) { if (RF_CORE(t->t_data) != NULL) return (rf_read(RF_CORE(t->t_data), buf, nbytes, addr)); return (rf_read(RF_OBJECT(t->t_data), buf, nbytes, addr)); } static ssize_t rf_vwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, uintptr_t addr) { if (RF_CORE(t->t_data) != NULL) return (rf_write(RF_CORE(t->t_data), buf, nbytes, addr)); return (rf_write(RF_OBJECT(t->t_data), buf, nbytes, addr)); } static ssize_t rf_pread(mdb_tgt_t *t, void *buf, size_t nbytes, physaddr_t addr) { if (RF_CORE(t->t_data) != NULL) return (rf_read(RF_CORE(t->t_data), buf, nbytes, addr)); return (rf_read(RF_OBJECT(t->t_data), buf, nbytes, addr)); } static ssize_t rf_pwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, physaddr_t addr) { if (RF_CORE(t->t_data) != NULL) return (rf_write(RF_CORE(t->t_data), buf, nbytes, addr)); return (rf_write(RF_OBJECT(t->t_data), buf, nbytes, addr)); } static ssize_t rf_fread(mdb_tgt_t *t, void *buf, size_t nbytes, uintptr_t addr) { return (rf_read(RF_OBJECT(t->t_data), buf, nbytes, addr)); } static ssize_t rf_fwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, uintptr_t addr) { return (rf_write(RF_OBJECT(t->t_data), buf, nbytes, addr)); } static int rf_print_map(mdb_io_t *io, const char *type, int tflags, mdb_tgt_map_f *func, void *private) { mdb_map_t map; (void) mdb_iob_snprintf(map.map_name, MDB_TGT_MAPSZ, "%s (%s)", IOP_NAME(io), type); map.map_base = 0; map.map_size = IOP_SEEK(io, 0, SEEK_END); map.map_flags = MDB_TGT_MAP_R; if (tflags & MDB_TGT_F_RDWR) map.map_flags |= MDB_TGT_MAP_W; return (func(private, &map, map.map_name)); } static int rf_mapping_iter(mdb_tgt_t *t, mdb_tgt_map_f *func, void *private) { rf_data_t *rf = t->t_data; if (rf->r_object_fio != NULL && rf_print_map(rf->r_object_fio, "object file", t->t_flags, func, private) != 0) return (0); if (rf->r_core_fio != NULL && rf_print_map(rf->r_core_fio, "core file", t->t_flags, func, private) != 0) return (0); return (0); } /*ARGSUSED*/ static int rf_status(mdb_tgt_t *t, mdb_tgt_status_t *tsp) { bzero(tsp, sizeof (mdb_tgt_status_t)); if (RF_CORE(t->t_data) != NULL) tsp->st_state = MDB_TGT_DEAD; else tsp->st_state = MDB_TGT_IDLE; return (0); } /*ARGSUSED*/ static int rf_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { rf_data_t *rf = mdb.m_target->t_data; if (rf->r_object_fio != NULL) { mdb_printf("debugging file '%s' (object file)", IOP_NAME(rf->r_object_fio)); if (rf->r_core_fio != NULL) { mdb_printf(" and file '%s' (core file)", IOP_NAME(rf->r_core_fio)); } mdb_printf("\n"); } else { mdb_printf("debugging empty target\n"); } return (DCMD_OK); } static const mdb_dcmd_t rf_dcmds[] = { { "status", NULL, "print summary of current target", rf_status_dcmd }, { NULL } }; static const struct rf_magic { const char *rfm_str; size_t rfm_len; const char *rfm_mod; } rf_magic[] = { { DOF_MAG_STRING, DOF_MAG_STRLEN, "dof" }, { NULL, 0, NULL } }; static void rf_activate(mdb_tgt_t *t) { rf_data_t *rf = t->t_data; const struct rf_magic *m; mdb_var_t *v; off64_t size; (void) mdb_tgt_register_dcmds(t, &rf_dcmds[0], MDB_MOD_FORCE); /* * We set the legacy adb variable 'd' to be the size of the file (data * segment). To get this value, we call seek() on the underlying fdio. */ if (rf->r_object_fio != NULL) { size = IOP_SEEK(rf->r_object_fio, 0, SEEK_END); if ((v = mdb_nv_lookup(&mdb.m_nv, "d")) != NULL) mdb_nv_set_value(v, size); } /* * Load any debugging support modules that match the file type, as * determined by our poor man's /etc/magic. If many clients need * to use this feature, rf_magic[] should be computed dynamically. */ for (m = rf_magic; m->rfm_str != NULL; m++) { char *buf = mdb_alloc(m->rfm_len, UM_SLEEP); if (mdb_tgt_vread(t, buf, m->rfm_len, 0) == m->rfm_len && bcmp(buf, m->rfm_str, m->rfm_len) == 0) { (void) mdb_module_load(m->rfm_mod, MDB_MOD_LOCAL | MDB_MOD_SILENT); } mdb_free(buf, m->rfm_len); } } static void rf_deactivate(mdb_tgt_t *t) { const mdb_dcmd_t *dcp; for (dcp = &rf_dcmds[0]; dcp->dc_name != NULL; dcp++) { if (mdb_module_remove_dcmd(t->t_module, dcp->dc_name) == -1) warn("failed to remove dcmd %s", dcp->dc_name); } } static const mdb_tgt_ops_t rawfile_ops = { .t_setflags = rf_setflags, .t_setcontext = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_activate = rf_activate, .t_deactivate = rf_deactivate, .t_periodic = (void (*)())(uintptr_t)mdb_tgt_nop, .t_destroy = rf_destroy, .t_name = rf_name, .t_isa = (const char *(*)())mdb_conf_isa, .t_platform = (const char *(*)())mdb_conf_platform, .t_uname = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_dmodel = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_aread = rf_aread, .t_awrite = rf_awrite, .t_vread = rf_vread, .t_vwrite = rf_vwrite, .t_pread = rf_pread, .t_pwrite = rf_pwrite, .t_fread = rf_fread, .t_fwrite = rf_fwrite, .t_ioread = (ssize_t (*)())mdb_tgt_notsup, .t_iowrite = (ssize_t (*)())mdb_tgt_notsup, .t_vtop = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_lookup_by_name = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_lookup_by_addr = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_symbol_iter = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_mapping_iter = rf_mapping_iter, .t_object_iter = rf_mapping_iter, .t_addr_to_map = (const mdb_map_t *(*)())mdb_tgt_null, .t_name_to_map = (const mdb_map_t *(*)())mdb_tgt_null, .t_addr_to_ctf = (struct ctf_file *(*)())mdb_tgt_null, .t_name_to_ctf = (struct ctf_file *(*)())mdb_tgt_null, .t_status = rf_status, .t_run = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_step = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_step_out = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_next = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_cont = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_signal = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_add_vbrkpt = (int (*)())(uintptr_t)mdb_tgt_null, .t_add_sbrkpt = (int (*)())(uintptr_t)mdb_tgt_null, .t_add_pwapt = (int (*)())(uintptr_t)mdb_tgt_null, .t_add_vwapt = (int (*)())(uintptr_t)mdb_tgt_null, .t_add_iowapt = (int (*)())(uintptr_t)mdb_tgt_null, .t_add_sysenter = (int (*)())(uintptr_t)mdb_tgt_null, .t_add_sysexit = (int (*)())(uintptr_t)mdb_tgt_null, .t_add_signal = (int (*)())(uintptr_t)mdb_tgt_null, .t_add_fault = (int (*)())(uintptr_t)mdb_tgt_null, .t_getareg = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_putareg = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_stack_iter = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_auxv = (int (*)())(uintptr_t)mdb_tgt_notsup, .t_thread_name = (int (*)())(uintptr_t)mdb_tgt_notsup, }; int mdb_rawfile_tgt_create(mdb_tgt_t *t, int argc, const char *argv[]) { mdb_io_t *io[2] = { NULL, NULL }; rf_data_t *rf; int oflags, i; if (argc > 2) return (set_errno(EINVAL)); rf = mdb_zalloc(sizeof (rf_data_t), UM_SLEEP); t->t_ops = &rawfile_ops; t->t_data = rf; if (t->t_flags & MDB_TGT_F_RDWR) oflags = O_RDWR; else oflags = O_RDONLY; for (i = 0; i < argc; i++) { io[i] = mdb_fdio_create_path(NULL, argv[i], oflags, 0); if (io[i] == NULL) { warn("failed to open %s", argv[i]); goto err; } } rf->r_object_fio = io[0]; /* first file is the "object" */ rf->r_core_fio = io[1]; /* second file is the "core" */ t->t_flags |= MDB_TGT_F_ASIO; /* do i/o using aread and awrite */ return (0); err: for (i = 0; i < argc; i++) { if (io[i] != NULL) mdb_io_destroy(io[i]); } mdb_free(rf, sizeof (rf_data_t)); return (set_errno(EMDB_TGT)); }