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