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