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 "@(#)dt_isadep.c 1.14 06/02/22 SMI" 28 29#if defined(__arm__) || defined(__armv6__) || defined(__arm64__) 30 31#include <stdlib.h> 32#include <assert.h> 33#include <errno.h> 34#include <string.h> 35#include <libgen.h> 36 37#include <dt_impl.h> 38#include <dt_pid.h> 39 40#define SIGNEXTEND(x,v) ((((int) (x)) << (32-v)) >> (32-v)) 41#define ALIGNADDR(x,v) (((x) >> v) << v) 42 43#define TYPE_SAME 0 44#define TYPE_TEXT 1 45#define TYPE_DATA 2 46 47int 48dt_pid_create_entry_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, 49 fasttrap_probe_spec_t *ftp, const GElf_Sym *symp) 50{ 51 char dmodel = Pstatus(P)->pr_dmodel; 52 53 ftp->ftps_probe_type = DTFTP_ENTRY; 54 ftp->ftps_pc = symp->st_value; // Keep st_value as uint64_t 55 56 if (dmodel == PR_MODEL_LP64) { 57 // 64 bit doesn't have arm vs thumb mode 58 59 ftp->ftps_arch_subinfo = 0; 60 } else { 61 if (symp->st_arch_subinfo == 0) { 62 dt_dprintf("No arm/thumb information for %s:%s, not instrumenting\n",ftp->ftps_mod,ftp->ftps_func); 63 return (1); 64 } else { 65 ftp->ftps_arch_subinfo = symp->st_arch_subinfo; 66 } 67 } 68 69 ftp->ftps_size = (size_t)symp->st_size; 70 ftp->ftps_noffs = 1; 71 ftp->ftps_offs[0] = 0; 72 73 if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 74 dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 75 strerror(errno)); 76 return (dt_set_errno(dtp, errno)); 77 } 78 79 return (1); 80} 81 82/*ARGSUSED*/ 83static int 84dt_pid_create_return_probe32(struct ps_prochandle *P, dtrace_hdl_t *dtp, 85 fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, uint64_t *stret) 86{ 87 /* 88 * This function best read with the ARM architecture reference handy. 89 */ 90 91 uint8_t *text; 92 93 /* 94 * When all the pc relative data is at the end of a function, there are no problems. But 95 * after inlining, it's possible to have constants mixed in with the code. Create a table 96 * to deal with this. It will be the same size as the instructions. When we instrument 97 * instructions, we will have two modes: text and data. For our table, we will have three 98 * values: TYPE_SAME - continue in previous mode; TYPE_TEXT - switch to text; TYPE_DATA - 99 * switch to data. 100 * 101 * When we see a pc-relative load, we assume that is the beginning of a data section. When 102 * we see a branch, we assume that is the beginning of more text. 103 */ 104 uint8_t *constants; 105 106 ftp->ftps_probe_type = DTFTP_RETURN; 107 ftp->ftps_pc = symp->st_value; 108 109 if (symp->st_arch_subinfo == 0) { 110 dt_dprintf("No arm/thumb information for %s:%s, not instrumenting\n",ftp->ftps_mod,ftp->ftps_func); 111 return (1); 112 } else { 113 ftp->ftps_arch_subinfo = symp->st_arch_subinfo; 114 } 115 116 ftp->ftps_size = (size_t)symp->st_size; 117 ftp->ftps_noffs = 0; 118 119 /* 120 * We allocate a few extra bytes at the end so we don't have to check 121 * for overrunning the buffer. 122 */ 123 if ((text = calloc(1, symp->st_size + 4)) == NULL) { 124 dt_dprintf("mr sparkle: malloc() failed\n"); 125 return (DT_PROC_ERR); 126 } 127 128 if ((constants = calloc(1, symp->st_size + 4)) == NULL) { 129 dt_dprintf("mr sparkle: malloc() failed\n"); 130 free(text); 131 return (DT_PROC_ERR); 132 } 133 134 /* 135 * We run into all sorts of problems with the ldr instruction offset calculating, if 136 * the original function starts on a halfword. The easiest workaround is to start our 137 * base so that we start on a halfword too. The base address will need to be changed 138 * back before we free the buffer. This problem only occurs in Thumb mode. 139 */ 140 if ((ftp->ftps_arch_subinfo == 2) && (symp->st_value & 2)) 141 text = text + 2; 142 143 if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) { 144 dt_dprintf("mr sparkle: Pread() failed\n"); 145 free(text); 146 free(constants); 147 return (DT_PROC_ERR); 148 } 149 150 /* Approved methods of returning from a function and their valid encodings: 151 * (A = ARM, T = Thumb 16 bit, T2 = Thumb 32 bit) 152 * ldr pc, [...] (A, T2) 153 * mov pc, reg (A, T) 154 * mov pc, immed (A) 155 * bx reg (A, T) 156 * b address (A, T, T2) 157 * ldmfd sp!, [ ... pc ] (A, T2) 158 * pop { ... pc } (T) 159 * 160 * We also first check for ldr reg, [pc ...] since that is a pc relative load, and a pc relative 161 * load will tell us where any extra data is stored (after the end of a function). 162 */ 163 164 if (ftp->ftps_arch_subinfo == 1) { 165 /* Arm mode */ 166 uint32_t* limit = (uint32_t*) (text + ftp->ftps_size); 167 uint32_t* inst = (uint32_t*) text; 168 ulong_t offset; 169 int mode = TYPE_TEXT; 170 171 while (inst < limit) { 172 offset = (ulong_t) inst - (ulong_t) text; 173 if ((mode == TYPE_DATA && constants[offset] == TYPE_SAME) || constants[offset] == TYPE_DATA) { 174 inst++; 175 mode = TYPE_DATA; 176 continue; 177 } 178 if (constants[offset] == TYPE_TEXT) 179 mode = TYPE_TEXT; 180 181 /* 182 * All of the instructions we instrument are conditional, so if the instruction is 183 * unconditional, skip immediately to the next one. 184 */ 185 if ((*inst & 0xF0000000) == 0xF0000000) { 186 inst++; 187 continue; 188 } 189 190 if ((*inst & 0x0F7F0000) == 0x051F0000) { 191 /* ldr reg, [pc, ...] */ 192 uint32_t displacement = *inst & 0xFFF; 193 ulong_t target = ((uint32_t) inst) + 8; 194 if (*inst & (1 << 23)) { 195 target += displacement; 196 } else { 197 target -= displacement; 198 } 199 if (((ulong_t) inst) < target && target < ((ulong_t) limit)) 200 constants[(ulong_t) target - (ulong_t) text] = TYPE_DATA; 201 } 202 203 if ((*inst & 0x0E50F000) == 0x0410F000) { 204 /* ldr pc, [...] */ 205 /* We are using a jump table to do a jump. This could be equivalent to 206 * either a branch or a branch and link. We only want to instrument 207 * the first case. To identify a link, we will check the previous 208 * instruction to see if it's a mov lr, pc. This is a little fragile, 209 * but it will catch most cases. 210 */ 211 if (inst > text) { 212 /* It's not the first instruction in the function */ 213 uint32_t prevInst = *(inst-1); 214 215 /* Also check to see if the condition codes are different */ 216 if ((prevInst & 0xF0000000) != (*inst & 0xF0000000) || 217 (prevInst & 0x0FFFFFFF) != 0x01A0E00F) 218 goto is_ret_arm; 219 } else { 220 goto is_ret_arm; 221 } 222 } else if ((*inst & 0x0FFFFFF0) == 0x01A0F000) { 223 /* mov pc, reg */ 224 goto is_ret_arm; 225 } else if ((*inst & 0x0FFFF000) == 0x03A0F000) { 226 /* mov pc, immed */ 227 goto is_ret_arm; 228 } else if ((*inst & 0x0FFFFFF0) == 0x012FFF10) { 229 /* bx reg */ 230 goto is_ret_arm; 231 } else if ((*inst & 0x0F000000) == 0x0A000000) { 232 /* branch */ 233 ulong_t target = ((ulong_t) inst) + 8 + (SIGNEXTEND(*inst & 0x00FFFFFF,24) << 2); 234 if (((ulong_t) inst) < target && target < ((ulong_t) limit)) 235 constants[(ulong_t) target - (ulong_t) text] = TYPE_TEXT; 236 if (target < text || limit <= target) 237 goto is_ret_arm; 238 } else if ((*inst & 0x0FFF8000) == 0x08BD8000) { 239 /* ldmfd sp!, { pc ... } */ 240 goto is_ret_arm; 241 } 242 243 inst++; 244 continue; 245 246is_ret_arm: 247 offset = (ulong_t) inst - (ulong_t) text; 248 dt_dprintf("return at offset %lx Arm\n", offset); 249 ftp->ftps_offs[ftp->ftps_noffs++] = offset; 250 inst++; 251 } 252 } else if (ftp->ftps_arch_subinfo == 2) { 253 /* Thumb mode */ 254 uint16_t* limit = (uint16_t*) (text + ftp->ftps_size); 255 uint16_t* inst = (uint16_t*) text; 256 ulong_t offset; 257 int mode = TYPE_TEXT; 258 259 while (inst < limit) { 260 offset = (ulong_t) inst - (ulong_t) text; 261 262 if ((mode == TYPE_DATA && constants[offset] == TYPE_SAME) || constants[offset] == TYPE_DATA) { 263 /* Data is always one full word */ 264 inst += 2; 265 mode = TYPE_DATA; 266 continue; 267 } 268 if (constants[offset] == TYPE_TEXT) 269 mode = TYPE_TEXT; 270 271 if (((*inst >> 11) & 0x1F) > 0x1C) { 272 /* Four byte thumb instruction */ 273 uint16_t* inst2 = inst+1; 274 275 if ((*inst & 0xFF7F) == 0xF85F) { 276 /* PC relative load */ 277 uint32_t displacement = *inst2 & 0xFFF; 278 ulong_t target = ALIGNADDR(((uint32_t) inst)+4, 2); 279 if (*inst & (1 << 7)) { 280 target += displacement; 281 } else { 282 target -= displacement; 283 } 284 if (((ulong_t) inst) < target && target < ((ulong_t) limit)) 285 constants[(ulong_t) target - (ulong_t) text] = TYPE_DATA; 286 } else if ((*inst & 0xFF3F) == 0xED1F && (*inst2 & 0x0E00) == 0x0A00) { 287 /* PC relative vload */ 288 uint32_t displacement = (*inst2 * 0xFF) << 2; 289 ulong_t target = ALIGNADDR(((uint32_t) inst)+4, 2); 290 if (*inst & (1 << 7)) { 291 target += displacement; 292 } else { 293 target -= displacement; 294 } 295 if (((ulong_t) inst) < target && target < ((ulong_t) limit)) 296 constants[(ulong_t) target - (ulong_t) text] = TYPE_DATA; 297 } 298 299 if ((*inst & 0xF800) == 0xF000 && (*inst2 & 0xD000) == 0x8000) { 300 int cond = (*inst >> 6) & 0xF; 301 302 if (cond != 0xE && cond != 0xF) { 303 /* Conditional branch */ 304 int S = (*inst >> 10) & 1, J1 = (*inst2 >> 13) & 1, J2 = (*inst2 >> 11) & 1; 305 ulong_t target = ((ulong_t) inst) + 4 + SIGNEXTEND( 306 (S << 20) | (J2 << 19) | (J1 << 18) | 307 ((*inst & 0x003F) << 12) | ((*inst2 & 0x07FF) << 1), 308 21); 309 if (((ulong_t) inst) < target && target < ((ulong_t) limit)) 310 constants[(ulong_t) target - (ulong_t) text] = TYPE_TEXT; 311 if (target < text || limit <= target) 312 goto is_ret_thumb2; 313 } 314 } else if ((*inst & 0xF800) == 0xF000 && (*inst2 & 0xD000) == 0x9000) { 315 /* Unconditional branch */ 316 int S = (*inst >> 10) & 1, J1 = (*inst2 >> 13) & 1, J2 = (*inst2 >> 11) & 1; 317 int I1 = (J1 != S) ? 0 : 1, I2 = (J2 != S) ? 0 : 1; 318 ulong_t target = ((ulong_t) inst) + 4 + SIGNEXTEND( 319 (S << 24) | (I1 << 23) | (I2 << 22) | 320 ((*inst & 0x3FF) << 12) | ((*inst2 & 0x7FF) << 1), 321 25); 322 if (((ulong_t) inst) < target && target < ((ulong_t) limit)) 323 constants[(ulong_t) target - (ulong_t) text] = TYPE_TEXT; 324 if (target < text || limit <= target) 325 goto is_ret_thumb2; 326 } else if (*inst == 0xE8BD && (*inst2 & 0x8000) == 0x8000) { 327 /* ldm sp!, { pc ... } */ 328 goto is_ret_thumb2; 329 } else if ((*inst & 0xFF70) == 0xF850 && (*inst2 & 0xF000) == 0xF000) { 330 /* ldr pc, [ ... ] */ 331 goto is_ret_thumb2; 332 } 333 334 inst += 2; 335 continue; 336 337is_ret_thumb2: 338 offset = (ulong_t) inst - (ulong_t) text; 339 dt_dprintf("return at offset %lx Thumb32\n", offset); 340 ftp->ftps_offs[ftp->ftps_noffs++] = offset; 341 inst += 2; 342 } else { 343 /* Two byte thumb instruction */ 344 if ((*inst & 0xF800) == 0x4800) { 345 /* PC relative load */ 346 ulong_t target = ALIGNADDR(((uint32_t) inst) + ((*inst & 0xFF) * 4) + 4, 2); 347 if (((ulong_t) inst) < target && target < ((ulong_t) limit)) 348 constants[(ulong_t) target - (ulong_t) text] = TYPE_DATA; 349 } 350 351 if ((*inst & 0xFF87) == 0x4687) { 352 /* mov pc, reg */ 353 goto is_ret_thumb; 354 } else if ((*inst & 0xFF87) == 0x4700) { 355 /* bx reg */ 356 goto is_ret_thumb; 357 } else if ((*inst & 0xF000) == 0xD000) { 358 int cond = (*inst >> 8) & 0xF; 359 if (cond != 0xE && cond != 0xF) { 360 /* Conditional branch */ 361 ulong_t target = ((ulong_t) inst) + 4 + (SIGNEXTEND(*inst & 0xFF,8) << 1); 362 if (((ulong_t) inst) < target && target < ((ulong_t) limit)) 363 constants[(ulong_t) target - (ulong_t) text] = TYPE_TEXT; 364 if (target < text || limit <= target) 365 goto is_ret_thumb; 366 } 367 } else if ((*inst & 0xF800) == 0xE000) { 368 /* Unconditional branch */ 369 ulong_t target = ((ulong_t) inst) + 4 + (SIGNEXTEND(*inst & 0x7FF,11) << 1); 370 if (((ulong_t) inst) < target && target < ((ulong_t) limit)) 371 constants[(ulong_t) target - (ulong_t) text] = TYPE_TEXT; 372 if (target < text || limit <= target) 373 goto is_ret_thumb; 374 } else if ((*inst & 0xFF00) == 0xBD00) { 375 /* pop { ..., pc} */ 376 goto is_ret_thumb; 377 } 378 379 inst++; 380 continue; 381 382is_ret_thumb: 383 offset = (ulong_t) inst - (ulong_t) text; 384 dt_dprintf("return at offset %lx Thumb16\n", offset); 385 ftp->ftps_offs[ftp->ftps_noffs++] = offset; 386 inst++; 387 } 388 } 389 } 390 391 /* Reset the buffer base (see above comments) */ 392 if ((ftp->ftps_arch_subinfo == 2) && (symp->st_value & 2)) 393 text = text - 2; 394 free(text); 395 free(constants); 396 if (ftp->ftps_noffs > 0) { 397 if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 398 dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 399 strerror(errno)); 400 return (dt_set_errno(dtp, errno)); 401 } 402 } 403 404 return (ftp->ftps_noffs); 405} 406 407/*ARGSUSED*/ 408static int 409dt_pid_create_return_probe64(struct ps_prochandle *P, dtrace_hdl_t *dtp, 410 fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, uint64_t *stret) 411{ 412 uint8_t *text; 413 414 ftp->ftps_probe_type = DTFTP_RETURN; 415 ftp->ftps_pc = symp->st_value; 416 417 ftp->ftps_arch_subinfo = 0; 418 419 ftp->ftps_size = (size_t)symp->st_size; 420 ftp->ftps_noffs = 0; 421 422 /* 423 * We allocate a few extra bytes at the end so we don't have to check 424 * for overrunning the buffer. 425 */ 426 if ((text = calloc(1, symp->st_size + 4)) == NULL) { 427 dt_dprintf("mr sparkle: malloc() failed\n"); 428 return (DT_PROC_ERR); 429 } 430 431 if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) { 432 dt_dprintf("mr sparkle: Pread() failed\n"); 433 free(text); 434 return (DT_PROC_ERR); 435 } 436 437 uint32_t* limit = (uint32_t*) (text + ftp->ftps_size); 438 uint32_t* inst = (uint32_t*) text; 439 440 while (inst < limit) { 441 /* b imm26 */ 442 if (((*inst & 0xfc000000) == 0x14000000)) { 443 /* 444 * The offset is from the address of this instruction in the 445 * range +/-128MB, and is encoded as "imm26" times 4. 446 */ 447 uint32_t target = ((uint32_t) inst) + 4 * SIGNEXTEND(*inst & ((1 << 26) - 1), 26); 448 if (target < (uint32_t) text || (uint32_t) limit <= target) { 449 goto is_ret; 450 } 451 } 452 453 /* ret x30 */ 454 if (*inst == 0xd65f03c0) { 455 goto is_ret; 456 } 457 458 inst++; 459 continue; 460 461is_ret: 462 dt_dprintf("return at offset %lx Arm64\n", (ulong_t) inst - (ulong_t) text); 463 ftp->ftps_offs[ftp->ftps_noffs++] = (ulong_t) inst - (ulong_t) text; 464 inst++; 465 } 466 467 free(text); 468 if (ftp->ftps_noffs > 0) { 469 if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 470 dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 471 strerror(errno)); 472 return (dt_set_errno(dtp, errno)); 473 } 474 } 475 476 return (ftp->ftps_noffs); 477} 478 479/*ARGSUSED*/ 480int 481dt_pid_create_return_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, 482 fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, uint64_t *stret) 483{ 484 char dmodel = Pstatus(P)->pr_dmodel; 485 486 if (dmodel == PR_MODEL_LP64) { 487 return dt_pid_create_return_probe64(P, dtp, ftp, symp, stret); 488 } else { 489 return dt_pid_create_return_probe32(P, dtp, ftp, symp, stret); 490 } 491} 492 493/*ARGSUSED*/ 494int 495dt_pid_create_offset_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, 496 fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, ulong_t off) 497{ 498 /* Not implemented */ 499 return (1); 500} 501 502/*ARGSUSED*/ 503int 504dt_pid_create_glob_offset_probes(struct ps_prochandle *P, dtrace_hdl_t *dtp, 505 fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, const char *pattern) 506{ 507 /* Not implemented */ 508 return (-1); 509} 510 511#endif // __arm__ || __armv6__ || __arm64__ 512