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