1dc0093f4Seschrock /* 2dc0093f4Seschrock * CDDL HEADER START 3dc0093f4Seschrock * 4dc0093f4Seschrock * The contents of this file are subject to the terms of the 5dc0093f4Seschrock * Common Development and Distribution License (the "License"). 6dc0093f4Seschrock * You may not use this file except in compliance with the License. 7dc0093f4Seschrock * 8dc0093f4Seschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9dc0093f4Seschrock * or http://www.opensolaris.org/os/licensing. 10dc0093f4Seschrock * See the License for the specific language governing permissions 11dc0093f4Seschrock * and limitations under the License. 12dc0093f4Seschrock * 13dc0093f4Seschrock * When distributing Covered Code, include this CDDL HEADER in each 14dc0093f4Seschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15dc0093f4Seschrock * If applicable, add the following below this CDDL HEADER, with the 16dc0093f4Seschrock * fields enclosed by brackets "[]" replaced with your own identifying 17dc0093f4Seschrock * information: Portions Copyright [yyyy] [name of copyright owner] 18dc0093f4Seschrock * 19dc0093f4Seschrock * CDDL HEADER END 20dc0093f4Seschrock */ 21dc0093f4Seschrock 22dc0093f4Seschrock /* 23e0070315Sdmick * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24dc0093f4Seschrock * Use is subject to license terms. 25f7184619SJoshua M. Clulow * Copyright 2012 Joshua M. Clulow <josh@sysmgr.org> 26f7184619SJoshua M. Clulow * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 27dc0093f4Seschrock */ 28dc0093f4Seschrock 29dc0093f4Seschrock #include <libdisasm.h> 30dc0093f4Seschrock 31dc0093f4Seschrock #include "dis_tables.h" 32dc0093f4Seschrock #include "libdisasm_impl.h" 33dc0093f4Seschrock 34f7184619SJoshua M. Clulow typedef struct dis_handle_i386 { 35f7184619SJoshua M. Clulow int dhx_mode; 36f7184619SJoshua M. Clulow dis86_t dhx_dis; 37f7184619SJoshua M. Clulow uint64_t dhx_end; 38f7184619SJoshua M. Clulow } dis_handle_i386_t; 39dc0093f4Seschrock 40dc0093f4Seschrock /* 41dc0093f4Seschrock * Returns true if we are near the end of a function. This is a cheap hack at 42dc0093f4Seschrock * detecting NULL padding between functions. If we're within a few bytes of the 43dc0093f4Seschrock * next function, or past the start, then return true. 44dc0093f4Seschrock */ 45dc0093f4Seschrock static int 46dc0093f4Seschrock check_func(void *data) 47dc0093f4Seschrock { 48dc0093f4Seschrock dis_handle_t *dhp = data; 49dc0093f4Seschrock uint64_t start; 50dc0093f4Seschrock size_t len; 51dc0093f4Seschrock 52dc0093f4Seschrock if (dhp->dh_lookup(dhp->dh_data, dhp->dh_addr, NULL, 0, &start, &len) 53dc0093f4Seschrock != 0) 54dc0093f4Seschrock return (0); 55dc0093f4Seschrock 56dc0093f4Seschrock if (start < dhp->dh_addr) 57dc0093f4Seschrock return (dhp->dh_addr > start + len - 0x10); 58dc0093f4Seschrock 59dc0093f4Seschrock return (1); 60dc0093f4Seschrock } 61dc0093f4Seschrock 62dc0093f4Seschrock static int 63dc0093f4Seschrock get_byte(void *data) 64dc0093f4Seschrock { 65dc0093f4Seschrock uchar_t byte; 66dc0093f4Seschrock dis_handle_t *dhp = data; 67dc0093f4Seschrock 68108ba071Seschrock if (dhp->dh_read(dhp->dh_data, dhp->dh_addr, &byte, sizeof (byte)) != 69108ba071Seschrock sizeof (byte)) 70dc0093f4Seschrock return (-1); 71dc0093f4Seschrock 72dc0093f4Seschrock dhp->dh_addr++; 73dc0093f4Seschrock 74dc0093f4Seschrock return ((int)byte); 75dc0093f4Seschrock } 76dc0093f4Seschrock 77dc0093f4Seschrock static int 78dc0093f4Seschrock do_lookup(void *data, uint64_t addr, char *buf, size_t buflen) 79dc0093f4Seschrock { 80dc0093f4Seschrock dis_handle_t *dhp = data; 81dc0093f4Seschrock 82dc0093f4Seschrock return (dhp->dh_lookup(dhp->dh_data, addr, buf, buflen, NULL, NULL)); 83dc0093f4Seschrock } 84dc0093f4Seschrock 85f7184619SJoshua M. Clulow static void 86f7184619SJoshua M. Clulow dis_i386_handle_detach(dis_handle_t *dhp) 87f7184619SJoshua M. Clulow { 88f7184619SJoshua M. Clulow dis_free(dhp->dh_arch_private, sizeof (dis_handle_i386_t)); 89f7184619SJoshua M. Clulow dhp->dh_arch_private = NULL; 90f7184619SJoshua M. Clulow } 91f7184619SJoshua M. Clulow 92f7184619SJoshua M. Clulow static int 93f7184619SJoshua M. Clulow dis_i386_handle_attach(dis_handle_t *dhp) 94dc0093f4Seschrock { 95f7184619SJoshua M. Clulow dis_handle_i386_t *dhx; 96dc0093f4Seschrock 97dc0093f4Seschrock /* 98dc0093f4Seschrock * Validate architecture flags 99dc0093f4Seschrock */ 100f7184619SJoshua M. Clulow if (dhp->dh_flags & ~(DIS_X86_SIZE16 | DIS_X86_SIZE32 | DIS_X86_SIZE64 | 101e0070315Sdmick DIS_OCTAL | DIS_NOIMMSYM)) { 102dc0093f4Seschrock (void) dis_seterrno(E_DIS_INVALFLAG); 103f7184619SJoshua M. Clulow return (-1); 104dc0093f4Seschrock } 105dc0093f4Seschrock 106dc0093f4Seschrock /* 107dc0093f4Seschrock * Create and initialize the internal structure 108dc0093f4Seschrock */ 109f7184619SJoshua M. Clulow if ((dhx = dis_zalloc(sizeof (dis_handle_i386_t))) == NULL) { 110dc0093f4Seschrock (void) dis_seterrno(E_DIS_NOMEM); 111f7184619SJoshua M. Clulow return (-1); 112dc0093f4Seschrock } 113f7184619SJoshua M. Clulow dhp->dh_arch_private = dhx; 114dc0093f4Seschrock 115dc0093f4Seschrock /* 116dc0093f4Seschrock * Initialize x86-specific architecture structure 117dc0093f4Seschrock */ 118f7184619SJoshua M. Clulow if (dhp->dh_flags & DIS_X86_SIZE16) 119f7184619SJoshua M. Clulow dhx->dhx_mode = SIZE16; 120f7184619SJoshua M. Clulow else if (dhp->dh_flags & DIS_X86_SIZE64) 121f7184619SJoshua M. Clulow dhx->dhx_mode = SIZE64; 122dc0093f4Seschrock else 123f7184619SJoshua M. Clulow dhx->dhx_mode = SIZE32; 124dc0093f4Seschrock 125f7184619SJoshua M. Clulow if (dhp->dh_flags & DIS_OCTAL) 126f7184619SJoshua M. Clulow dhx->dhx_dis.d86_flags = DIS_F_OCTAL; 127dc0093f4Seschrock 128f7184619SJoshua M. Clulow dhx->dhx_dis.d86_sprintf_func = dis_snprintf; 129f7184619SJoshua M. Clulow dhx->dhx_dis.d86_get_byte = get_byte; 130f7184619SJoshua M. Clulow dhx->dhx_dis.d86_sym_lookup = do_lookup; 131f7184619SJoshua M. Clulow dhx->dhx_dis.d86_check_func = check_func; 132dc0093f4Seschrock 133f7184619SJoshua M. Clulow dhx->dhx_dis.d86_data = dhp; 134dc0093f4Seschrock 135f7184619SJoshua M. Clulow return (0); 136dc0093f4Seschrock } 137dc0093f4Seschrock 138f7184619SJoshua M. Clulow static int 139f7184619SJoshua M. Clulow dis_i386_disassemble(dis_handle_t *dhp, uint64_t addr, char *buf, 140f7184619SJoshua M. Clulow size_t buflen) 141dc0093f4Seschrock { 142f7184619SJoshua M. Clulow dis_handle_i386_t *dhx = dhp->dh_arch_private; 143dc0093f4Seschrock dhp->dh_addr = addr; 144dc0093f4Seschrock 145e0070315Sdmick /* DIS_NOIMMSYM might not be set until now, so update */ 146e0070315Sdmick if (dhp->dh_flags & DIS_NOIMMSYM) 147f7184619SJoshua M. Clulow dhx->dhx_dis.d86_flags |= DIS_F_NOIMMSYM; 148e0070315Sdmick else 149f7184619SJoshua M. Clulow dhx->dhx_dis.d86_flags &= ~DIS_F_NOIMMSYM; 150e0070315Sdmick 151f7184619SJoshua M. Clulow if (dtrace_disx86(&dhx->dhx_dis, dhx->dhx_mode) != 0) 152dc0093f4Seschrock return (-1); 153dc0093f4Seschrock 154dc0093f4Seschrock if (buf != NULL) 155f7184619SJoshua M. Clulow dtrace_disx86_str(&dhx->dhx_dis, dhx->dhx_mode, addr, buf, 156dc0093f4Seschrock buflen); 157dc0093f4Seschrock 158dc0093f4Seschrock return (0); 159dc0093f4Seschrock } 160dc0093f4Seschrock 161f7184619SJoshua M. Clulow /* ARGSUSED */ 162f7184619SJoshua M. Clulow static int 163f7184619SJoshua M. Clulow dis_i386_max_instrlen(dis_handle_t *dhp) 164e0070315Sdmick { 165f7184619SJoshua M. Clulow return (15); 166e0070315Sdmick } 167e0070315Sdmick 168dc0093f4Seschrock /* ARGSUSED */ 169f7184619SJoshua M. Clulow static int 170f7184619SJoshua M. Clulow dis_i386_min_instrlen(dis_handle_t *dhp) 171dc0093f4Seschrock { 172f7184619SJoshua M. Clulow return (1); 173dc0093f4Seschrock } 174dc0093f4Seschrock 175dc0093f4Seschrock /* 176dc0093f4Seschrock * Return the previous instruction. On x86, we have no choice except to 177dc0093f4Seschrock * disassemble everything from the start of the symbol, and stop when we have 178dc0093f4Seschrock * reached our instruction address. If we're not in the middle of a known 179dc0093f4Seschrock * symbol, then we return the same address to indicate failure. 180dc0093f4Seschrock */ 181f7184619SJoshua M. Clulow static uint64_t 182f7184619SJoshua M. Clulow dis_i386_previnstr(dis_handle_t *dhp, uint64_t pc, int n) 183dc0093f4Seschrock { 184dc0093f4Seschrock uint64_t *hist, addr, start; 185dc0093f4Seschrock int cur, nseen; 186dc0093f4Seschrock uint64_t res = pc; 187dc0093f4Seschrock 188d79705c6Seschrock if (n <= 0) 189d79705c6Seschrock return (pc); 190d79705c6Seschrock 191dc0093f4Seschrock if (dhp->dh_lookup(dhp->dh_data, pc, NULL, 0, &start, NULL) != 0 || 192dc0093f4Seschrock start == pc) 193dc0093f4Seschrock return (res); 194dc0093f4Seschrock 195dc0093f4Seschrock hist = dis_zalloc(sizeof (uint64_t) * n); 196dc0093f4Seschrock 197dc0093f4Seschrock for (cur = 0, nseen = 0, addr = start; addr < pc; addr = dhp->dh_addr) { 198dc0093f4Seschrock hist[cur] = addr; 199dc0093f4Seschrock cur = (cur + 1) % n; 200dc0093f4Seschrock nseen++; 201dc0093f4Seschrock 202dc0093f4Seschrock /* if we cannot make forward progress, give up */ 203dc0093f4Seschrock if (dis_disassemble(dhp, addr, NULL, 0) != 0) 204dc0093f4Seschrock goto done; 205dc0093f4Seschrock } 206dc0093f4Seschrock 207dc0093f4Seschrock if (addr != pc) { 208dc0093f4Seschrock /* 209dc0093f4Seschrock * We scanned past %pc, but didn't find an instruction that 210dc0093f4Seschrock * started at %pc. This means that either the caller specified 211dc0093f4Seschrock * an invalid address, or we ran into something other than code 212dc0093f4Seschrock * during our scan. Virtually any combination of bytes can be 213dc0093f4Seschrock * construed as a valid Intel instruction, so any non-code bytes 214dc0093f4Seschrock * we encounter will have thrown off the scan. 215dc0093f4Seschrock */ 216dc0093f4Seschrock goto done; 217dc0093f4Seschrock } 218dc0093f4Seschrock 219dc0093f4Seschrock res = hist[(cur + n - MIN(n, nseen)) % n]; 220dc0093f4Seschrock 221dc0093f4Seschrock done: 222dc0093f4Seschrock dis_free(hist, sizeof (uint64_t) * n); 223dc0093f4Seschrock return (res); 224dc0093f4Seschrock } 225702941cdSRichard Lowe 226f7184619SJoshua M. Clulow static int 227f7184619SJoshua M. Clulow dis_i386_supports_flags(int flags) 228f7184619SJoshua M. Clulow { 229f7184619SJoshua M. Clulow int archflags = flags & DIS_ARCH_MASK; 230f7184619SJoshua M. Clulow 231f7184619SJoshua M. Clulow if (archflags == DIS_X86_SIZE16 || archflags == DIS_X86_SIZE32 || 232f7184619SJoshua M. Clulow archflags == DIS_X86_SIZE64) 233f7184619SJoshua M. Clulow return (1); 234f7184619SJoshua M. Clulow 235f7184619SJoshua M. Clulow return (0); 236f7184619SJoshua M. Clulow } 237f7184619SJoshua M. Clulow 238f7184619SJoshua M. Clulow static int 239f7184619SJoshua M. Clulow dis_i386_instrlen(dis_handle_t *dhp, uint64_t pc) 240702941cdSRichard Lowe { 241702941cdSRichard Lowe if (dis_disassemble(dhp, pc, NULL, 0) != 0) 242702941cdSRichard Lowe return (-1); 243702941cdSRichard Lowe 244702941cdSRichard Lowe return (dhp->dh_addr - pc); 245702941cdSRichard Lowe } 246f7184619SJoshua M. Clulow 247f7184619SJoshua M. Clulow dis_arch_t dis_arch_i386 = { 248*b3457a09SJosef 'Jeff' Sipek .da_supports_flags = dis_i386_supports_flags, 249*b3457a09SJosef 'Jeff' Sipek .da_handle_attach = dis_i386_handle_attach, 250*b3457a09SJosef 'Jeff' Sipek .da_handle_detach = dis_i386_handle_detach, 251*b3457a09SJosef 'Jeff' Sipek .da_disassemble = dis_i386_disassemble, 252*b3457a09SJosef 'Jeff' Sipek .da_previnstr = dis_i386_previnstr, 253*b3457a09SJosef 'Jeff' Sipek .da_min_instrlen = dis_i386_min_instrlen, 254*b3457a09SJosef 'Jeff' Sipek .da_max_instrlen = dis_i386_max_instrlen, 255*b3457a09SJosef 'Jeff' Sipek .da_instrlen = dis_i386_instrlen, 256f7184619SJoshua M. Clulow }; 257