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 2006 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)) { 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_OP_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 if (dtrace_disx86(&dhp->dh_dis, dhp->dh_mode) != 0) 149 return (-1); 150 151 if (buf != NULL) 152 dtrace_disx86_str(&dhp->dh_dis, dhp->dh_mode, addr, buf, 153 buflen); 154 155 return (0); 156 } 157 158 void 159 dis_handle_destroy(dis_handle_t *dhp) 160 { 161 dis_free(dhp, sizeof (dis_handle_t)); 162 } 163 164 void 165 dis_set_data(dis_handle_t *dhp, void *data) 166 { 167 dhp->dh_data = data; 168 } 169 170 /* ARGSUSED */ 171 int 172 dis_max_instrlen(dis_handle_t *dhp) 173 { 174 return (15); 175 } 176 177 #define MIN(a, b) ((a) < (b) ? (a) : (b)) 178 179 /* 180 * Return the previous instruction. On x86, we have no choice except to 181 * disassemble everything from the start of the symbol, and stop when we have 182 * reached our instruction address. If we're not in the middle of a known 183 * symbol, then we return the same address to indicate failure. 184 */ 185 uint64_t 186 dis_previnstr(dis_handle_t *dhp, uint64_t pc, int n) 187 { 188 uint64_t *hist, addr, start; 189 int cur, nseen; 190 uint64_t res = pc; 191 192 if (n <= 0) 193 return (pc); 194 195 if (dhp->dh_lookup(dhp->dh_data, pc, NULL, 0, &start, NULL) != 0 || 196 start == pc) 197 return (res); 198 199 hist = dis_zalloc(sizeof (uint64_t) * n); 200 201 for (cur = 0, nseen = 0, addr = start; addr < pc; addr = dhp->dh_addr) { 202 hist[cur] = addr; 203 cur = (cur + 1) % n; 204 nseen++; 205 206 /* if we cannot make forward progress, give up */ 207 if (dis_disassemble(dhp, addr, NULL, 0) != 0) 208 goto done; 209 } 210 211 if (addr != pc) { 212 /* 213 * We scanned past %pc, but didn't find an instruction that 214 * started at %pc. This means that either the caller specified 215 * an invalid address, or we ran into something other than code 216 * during our scan. Virtually any combination of bytes can be 217 * construed as a valid Intel instruction, so any non-code bytes 218 * we encounter will have thrown off the scan. 219 */ 220 goto done; 221 } 222 223 res = hist[(cur + n - MIN(n, nseen)) % n]; 224 225 done: 226 dis_free(hist, sizeof (uint64_t) * n); 227 return (res); 228 } 229