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