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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* 27 * Copyright (c) 2013 by Delphix. All rights reserved. 28 * Copyright (c) 2013 Joyent, Inc. All rights reserved. 29 */ 30 31 #include <sys/types.h> 32 #include <strings.h> 33 #include <stdlib.h> 34 #include <assert.h> 35 36 #include <dt_impl.h> 37 #include <dt_parser.h> 38 #include <dt_as.h> 39 40 void 41 dt_irlist_create(dt_irlist_t *dlp) 42 { 43 bzero(dlp, sizeof (dt_irlist_t)); 44 dlp->dl_label = 1; 45 } 46 47 void 48 dt_irlist_destroy(dt_irlist_t *dlp) 49 { 50 dt_irnode_t *dip, *nip; 51 52 for (dip = dlp->dl_list; dip != NULL; dip = nip) { 53 nip = dip->di_next; 54 free(dip); 55 } 56 } 57 58 void 59 dt_irlist_append(dt_irlist_t *dlp, dt_irnode_t *dip) 60 { 61 if (dlp->dl_last != NULL) 62 dlp->dl_last->di_next = dip; 63 else 64 dlp->dl_list = dip; 65 66 dlp->dl_last = dip; 67 68 if (dip->di_label == DT_LBL_NONE || dip->di_instr != DIF_INSTR_NOP) 69 dlp->dl_len++; /* don't count forward refs in instr count */ 70 } 71 72 uint_t 73 dt_irlist_label(dt_irlist_t *dlp) 74 { 75 return (dlp->dl_label++); 76 } 77 78 /*ARGSUSED*/ 79 static int 80 dt_countvar(dt_idhash_t *dhp, dt_ident_t *idp, void *data) 81 { 82 size_t *np = data; 83 84 if (idp->di_flags & (DT_IDFLG_DIFR | DT_IDFLG_DIFW)) 85 (*np)++; /* include variable in vartab */ 86 87 return (0); 88 } 89 90 /*ARGSUSED*/ 91 static int 92 dt_copyvar(dt_idhash_t *dhp, dt_ident_t *idp, void *data) 93 { 94 dt_pcb_t *pcb = data; 95 dtrace_difv_t *dvp; 96 ssize_t stroff; 97 dt_node_t dn; 98 99 if (!(idp->di_flags & (DT_IDFLG_DIFR | DT_IDFLG_DIFW))) 100 return (0); /* omit variable from vartab */ 101 102 dvp = &pcb->pcb_difo->dtdo_vartab[pcb->pcb_asvidx++]; 103 stroff = dt_strtab_insert(pcb->pcb_strtab, idp->di_name); 104 105 if (stroff == -1L) 106 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 107 if (stroff > DIF_STROFF_MAX) 108 longjmp(pcb->pcb_jmpbuf, EDT_STR2BIG); 109 110 dvp->dtdv_name = (uint_t)stroff; 111 dvp->dtdv_id = idp->di_id; 112 dvp->dtdv_flags = 0; 113 114 dvp->dtdv_kind = (idp->di_kind == DT_IDENT_ARRAY) ? 115 DIFV_KIND_ARRAY : DIFV_KIND_SCALAR; 116 117 if (idp->di_flags & DT_IDFLG_LOCAL) 118 dvp->dtdv_scope = DIFV_SCOPE_LOCAL; 119 else if (idp->di_flags & DT_IDFLG_TLS) 120 dvp->dtdv_scope = DIFV_SCOPE_THREAD; 121 else 122 dvp->dtdv_scope = DIFV_SCOPE_GLOBAL; 123 124 if (idp->di_flags & DT_IDFLG_DIFR) 125 dvp->dtdv_flags |= DIFV_F_REF; 126 if (idp->di_flags & DT_IDFLG_DIFW) 127 dvp->dtdv_flags |= DIFV_F_MOD; 128 129 bzero(&dn, sizeof (dn)); 130 dt_node_type_assign(&dn, idp->di_ctfp, idp->di_type, B_FALSE); 131 dt_node_diftype(pcb->pcb_hdl, &dn, &dvp->dtdv_type); 132 133 idp->di_flags &= ~(DT_IDFLG_DIFR | DT_IDFLG_DIFW); 134 return (0); 135 } 136 137 static ssize_t 138 dt_copystr(const char *s, size_t n, size_t off, dt_pcb_t *pcb) 139 { 140 bcopy(s, pcb->pcb_difo->dtdo_strtab + off, n); 141 return (n); 142 } 143 144 /* 145 * Rewrite the xlate/xlarg instruction at dtdo_buf[i] so that the instruction's 146 * xltab index reflects the offset 'xi' of the assigned dtdo_xlmtab[] location. 147 * We track the cumulative references to translators and members in the pcb's 148 * pcb_asxrefs[] array, a two-dimensional array of bitmaps indexed by the 149 * global translator id and then by the corresponding translator member id. 150 */ 151 static void 152 dt_as_xlate(dt_pcb_t *pcb, dtrace_difo_t *dp, 153 uint_t i, uint_t xi, dt_node_t *dnp) 154 { 155 dtrace_hdl_t *dtp = pcb->pcb_hdl; 156 dt_xlator_t *dxp = dnp->dn_membexpr->dn_xlator; 157 158 assert(i < dp->dtdo_len); 159 assert(xi < dp->dtdo_xlmlen); 160 161 assert(dnp->dn_kind == DT_NODE_MEMBER); 162 assert(dnp->dn_membexpr->dn_kind == DT_NODE_XLATOR); 163 164 assert(dxp->dx_id < dtp->dt_xlatorid); 165 assert(dnp->dn_membid < dxp->dx_nmembers); 166 167 if (pcb->pcb_asxrefs == NULL) { 168 pcb->pcb_asxreflen = dtp->dt_xlatorid; 169 pcb->pcb_asxrefs = 170 dt_zalloc(dtp, sizeof (ulong_t *) * pcb->pcb_asxreflen); 171 if (pcb->pcb_asxrefs == NULL) 172 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 173 } 174 175 if (pcb->pcb_asxrefs[dxp->dx_id] == NULL) { 176 pcb->pcb_asxrefs[dxp->dx_id] = 177 dt_zalloc(dtp, BT_SIZEOFMAP(dxp->dx_nmembers)); 178 if (pcb->pcb_asxrefs[dxp->dx_id] == NULL) 179 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 180 } 181 182 dp->dtdo_buf[i] = DIF_INSTR_XLATE( 183 DIF_INSTR_OP(dp->dtdo_buf[i]), xi, DIF_INSTR_RD(dp->dtdo_buf[i])); 184 185 BT_SET(pcb->pcb_asxrefs[dxp->dx_id], dnp->dn_membid); 186 dp->dtdo_xlmtab[xi] = dnp; 187 } 188 189 static void 190 dt_as_undef(const dt_ident_t *idp, uint_t offset) 191 { 192 const char *kind, *mark = (idp->di_flags & DT_IDFLG_USER) ? "``" : "`"; 193 const dtrace_syminfo_t *dts = idp->di_data; 194 195 if (idp->di_flags & DT_IDFLG_USER) 196 kind = "user"; 197 else if (idp->di_flags & DT_IDFLG_PRIM) 198 kind = "primary kernel"; 199 else 200 kind = "loadable kernel"; 201 202 yylineno = idp->di_lineno; 203 204 xyerror(D_ASRELO, "relocation remains against %s symbol %s%s%s (offset " 205 "0x%x)\n", kind, dts->dts_object, mark, dts->dts_name, offset); 206 } 207 208 dtrace_difo_t * 209 dt_as(dt_pcb_t *pcb) 210 { 211 dtrace_hdl_t *dtp = pcb->pcb_hdl; 212 dt_irlist_t *dlp = &pcb->pcb_ir; 213 uint_t *labels = NULL; 214 dt_irnode_t *dip; 215 dtrace_difo_t *dp; 216 dt_ident_t *idp; 217 218 size_t n = 0; 219 uint_t i; 220 221 uint_t kmask, kbits, umask, ubits; 222 uint_t krel = 0, urel = 0, xlrefs = 0; 223 224 /* 225 * Select bitmasks based upon the desired symbol linking policy. We 226 * test (di_extern->di_flags & xmask) == xbits to determine if the 227 * symbol should have a relocation entry generated in the loop below. 228 * 229 * DT_LINK_KERNEL = kernel symbols static, user symbols dynamic 230 * DT_LINK_PRIMARY = primary kernel symbols static, others dynamic 231 * DT_LINK_DYNAMIC = all symbols dynamic 232 * DT_LINK_STATIC = all symbols static 233 * 234 * By 'static' we mean that we use the symbol's value at compile-time 235 * in the final DIF. By 'dynamic' we mean that we create a relocation 236 * table entry for the symbol's value so it can be relocated later. 237 */ 238 switch (dtp->dt_linkmode) { 239 case DT_LINK_KERNEL: 240 kmask = 0; 241 kbits = -1u; 242 umask = DT_IDFLG_USER; 243 ubits = DT_IDFLG_USER; 244 break; 245 case DT_LINK_PRIMARY: 246 kmask = DT_IDFLG_USER | DT_IDFLG_PRIM; 247 kbits = 0; 248 umask = DT_IDFLG_USER; 249 ubits = DT_IDFLG_USER; 250 break; 251 case DT_LINK_DYNAMIC: 252 kmask = DT_IDFLG_USER; 253 kbits = 0; 254 umask = DT_IDFLG_USER; 255 ubits = DT_IDFLG_USER; 256 break; 257 case DT_LINK_STATIC: 258 kmask = umask = 0; 259 kbits = ubits = -1u; 260 break; 261 default: 262 kmask = umask = 0; 263 kbits = ubits = -1u; 264 xyerror(D_UNKNOWN, "internal error -- invalid link mode %u\n", 265 dtp->dt_linkmode); 266 } 267 268 assert(pcb->pcb_difo == NULL); 269 pcb->pcb_difo = dt_zalloc(dtp, sizeof (dtrace_difo_t)); 270 271 if ((dp = pcb->pcb_difo) == NULL) 272 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 273 274 dp->dtdo_buf = dt_alloc(dtp, sizeof (dif_instr_t) * dlp->dl_len); 275 276 if (dp->dtdo_buf == NULL) 277 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 278 279 if ((labels = dt_alloc(dtp, sizeof (uint_t) * dlp->dl_label)) == NULL) 280 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 281 282 /* 283 * Make an initial pass through the instruction list, filling in the 284 * instruction buffer with valid instructions and skipping labeled nops. 285 * While doing this, we also fill in our labels[] translation table 286 * and we count up the number of relocation table entries we will need. 287 */ 288 for (i = 0, dip = dlp->dl_list; dip != NULL; dip = dip->di_next) { 289 if (dip->di_label != DT_LBL_NONE) 290 labels[dip->di_label] = i; 291 292 if (dip->di_label == DT_LBL_NONE || 293 dip->di_instr != DIF_INSTR_NOP) 294 dp->dtdo_buf[i++] = dip->di_instr; 295 296 if (dip->di_extern == NULL) 297 continue; /* no external references needed */ 298 299 switch (DIF_INSTR_OP(dip->di_instr)) { 300 case DIF_OP_SETX: 301 idp = dip->di_extern; 302 if ((idp->di_flags & kmask) == kbits) 303 krel++; 304 else if ((idp->di_flags & umask) == ubits) 305 urel++; 306 break; 307 case DIF_OP_XLATE: 308 case DIF_OP_XLARG: 309 xlrefs++; 310 break; 311 default: 312 xyerror(D_UNKNOWN, "unexpected assembler relocation " 313 "for opcode 0x%x\n", DIF_INSTR_OP(dip->di_instr)); 314 } 315 } 316 317 assert(i == dlp->dl_len); 318 dp->dtdo_len = dlp->dl_len; 319 320 /* 321 * Make a second pass through the instructions, relocating each branch 322 * label to the index of the final instruction in the buffer and noting 323 * any other instruction-specific DIFO flags such as dtdo_destructive. 324 */ 325 for (i = 0; i < dp->dtdo_len; i++) { 326 dif_instr_t instr = dp->dtdo_buf[i]; 327 uint_t op = DIF_INSTR_OP(instr); 328 329 if (op == DIF_OP_CALL) { 330 if (DIF_INSTR_SUBR(instr) == DIF_SUBR_COPYOUT || 331 DIF_INSTR_SUBR(instr) == DIF_SUBR_COPYOUTSTR) 332 dp->dtdo_destructive = 1; 333 continue; 334 } 335 336 if (op >= DIF_OP_BA && op <= DIF_OP_BLEU) { 337 assert(DIF_INSTR_LABEL(instr) < dlp->dl_label); 338 dp->dtdo_buf[i] = DIF_INSTR_BRANCH(op, 339 labels[DIF_INSTR_LABEL(instr)]); 340 } 341 } 342 343 dt_free(dtp, labels); 344 pcb->pcb_asvidx = 0; 345 346 /* 347 * Allocate memory for the appropriate number of variable records and 348 * then fill in each variable record. As we populate the variable 349 * table we insert the corresponding variable names into the strtab. 350 */ 351 (void) dt_idhash_iter(dtp->dt_tls, dt_countvar, &n); 352 (void) dt_idhash_iter(dtp->dt_globals, dt_countvar, &n); 353 (void) dt_idhash_iter(pcb->pcb_locals, dt_countvar, &n); 354 355 if (n != 0) { 356 dp->dtdo_vartab = dt_alloc(dtp, n * sizeof (dtrace_difv_t)); 357 dp->dtdo_varlen = (uint32_t)n; 358 359 if (dp->dtdo_vartab == NULL) 360 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 361 362 (void) dt_idhash_iter(dtp->dt_tls, dt_copyvar, pcb); 363 (void) dt_idhash_iter(dtp->dt_globals, dt_copyvar, pcb); 364 (void) dt_idhash_iter(pcb->pcb_locals, dt_copyvar, pcb); 365 } 366 367 /* 368 * Allocate memory for the appropriate number of relocation table 369 * entries based upon our kernel and user counts from the first pass. 370 */ 371 if (krel != 0) { 372 dp->dtdo_kreltab = dt_alloc(dtp, 373 krel * sizeof (dof_relodesc_t)); 374 dp->dtdo_krelen = krel; 375 376 if (dp->dtdo_kreltab == NULL) 377 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 378 } 379 380 if (urel != 0) { 381 dp->dtdo_ureltab = dt_alloc(dtp, 382 urel * sizeof (dof_relodesc_t)); 383 dp->dtdo_urelen = urel; 384 385 if (dp->dtdo_ureltab == NULL) 386 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 387 } 388 389 if (xlrefs != 0) { 390 dp->dtdo_xlmtab = dt_zalloc(dtp, sizeof (dt_node_t *) * xlrefs); 391 dp->dtdo_xlmlen = xlrefs; 392 393 if (dp->dtdo_xlmtab == NULL) 394 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 395 } 396 397 /* 398 * If any relocations are needed, make another pass through the 399 * instruction list and fill in the relocation table entries. 400 */ 401 if (krel + urel + xlrefs != 0) { 402 uint_t knodef = pcb->pcb_cflags & DTRACE_C_KNODEF; 403 uint_t unodef = pcb->pcb_cflags & DTRACE_C_UNODEF; 404 405 dof_relodesc_t *krp = dp->dtdo_kreltab; 406 dof_relodesc_t *urp = dp->dtdo_ureltab; 407 dt_node_t **xlp = dp->dtdo_xlmtab; 408 409 i = 0; /* dtdo_buf[] index */ 410 411 for (dip = dlp->dl_list; dip != NULL; dip = dip->di_next) { 412 dof_relodesc_t *rp; 413 ssize_t soff; 414 uint_t nodef; 415 416 if (dip->di_label != DT_LBL_NONE && 417 dip->di_instr == DIF_INSTR_NOP) 418 continue; /* skip label declarations */ 419 420 i++; /* advance dtdo_buf[] index */ 421 422 if (DIF_INSTR_OP(dip->di_instr) == DIF_OP_XLATE || 423 DIF_INSTR_OP(dip->di_instr) == DIF_OP_XLARG) { 424 assert(dp->dtdo_buf[i - 1] == dip->di_instr); 425 dt_as_xlate(pcb, dp, i - 1, (uint_t) 426 (xlp++ - dp->dtdo_xlmtab), dip->di_extern); 427 continue; 428 } 429 430 if ((idp = dip->di_extern) == NULL) 431 continue; /* no relocation entry needed */ 432 433 if ((idp->di_flags & kmask) == kbits) { 434 nodef = knodef; 435 rp = krp++; 436 } else if ((idp->di_flags & umask) == ubits) { 437 nodef = unodef; 438 rp = urp++; 439 } else 440 continue; 441 442 if (!nodef) 443 dt_as_undef(idp, i); 444 445 assert(DIF_INSTR_OP(dip->di_instr) == DIF_OP_SETX); 446 soff = dt_strtab_insert(pcb->pcb_strtab, idp->di_name); 447 448 if (soff == -1L) 449 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 450 if (soff > DIF_STROFF_MAX) 451 longjmp(pcb->pcb_jmpbuf, EDT_STR2BIG); 452 453 rp->dofr_name = (dof_stridx_t)soff; 454 rp->dofr_type = DOF_RELO_SETX; 455 rp->dofr_offset = DIF_INSTR_INTEGER(dip->di_instr) * 456 sizeof (uint64_t); 457 rp->dofr_data = 0; 458 } 459 460 assert(krp == dp->dtdo_kreltab + dp->dtdo_krelen); 461 assert(urp == dp->dtdo_ureltab + dp->dtdo_urelen); 462 assert(xlp == dp->dtdo_xlmtab + dp->dtdo_xlmlen); 463 assert(i == dp->dtdo_len); 464 } 465 466 /* 467 * Allocate memory for the compiled string table and then copy the 468 * chunks from the string table into the final string buffer. 469 */ 470 if ((n = dt_strtab_size(pcb->pcb_strtab)) != 0) { 471 if ((dp->dtdo_strtab = dt_alloc(dtp, n)) == NULL) 472 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 473 474 (void) dt_strtab_write(pcb->pcb_strtab, 475 (dt_strtab_write_f *)dt_copystr, pcb); 476 dp->dtdo_strlen = (uint32_t)n; 477 } 478 479 /* 480 * Allocate memory for the compiled integer table and then copy the 481 * integer constants from the table into the final integer buffer. 482 */ 483 if ((n = dt_inttab_size(pcb->pcb_inttab)) != 0) { 484 if ((dp->dtdo_inttab = dt_alloc(dtp, 485 n * sizeof (uint64_t))) == NULL) 486 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 487 488 dt_inttab_write(pcb->pcb_inttab, dp->dtdo_inttab); 489 dp->dtdo_intlen = (uint32_t)n; 490 } 491 492 /* 493 * Fill in the DIFO return type from the type associated with the 494 * node saved in pcb_dret, and then clear pcb_difo and pcb_dret 495 * now that the assembler has completed successfully. 496 */ 497 dt_node_diftype(dtp, pcb->pcb_dret, &dp->dtdo_rtype); 498 pcb->pcb_difo = NULL; 499 pcb->pcb_dret = NULL; 500 501 if (pcb->pcb_cflags & DTRACE_C_DIFV) 502 dt_dis(dp, stderr); 503 504 return (dp); 505 } 506