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 xyerror(D_UNKNOWN, "internal error -- invalid link mode %u\n", 263 dtp->dt_linkmode); 264 } 265 266 assert(pcb->pcb_difo == NULL); 267 pcb->pcb_difo = dt_zalloc(dtp, sizeof (dtrace_difo_t)); 268 269 if ((dp = pcb->pcb_difo) == NULL) 270 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 271 272 dp->dtdo_buf = dt_alloc(dtp, sizeof (dif_instr_t) * dlp->dl_len); 273 274 if (dp->dtdo_buf == NULL) 275 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 276 277 if ((labels = dt_alloc(dtp, sizeof (uint_t) * dlp->dl_label)) == NULL) 278 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 279 280 /* 281 * Make an initial pass through the instruction list, filling in the 282 * instruction buffer with valid instructions and skipping labeled nops. 283 * While doing this, we also fill in our labels[] translation table 284 * and we count up the number of relocation table entries we will need. 285 */ 286 for (i = 0, dip = dlp->dl_list; dip != NULL; dip = dip->di_next) { 287 if (dip->di_label != DT_LBL_NONE) 288 labels[dip->di_label] = i; 289 290 if (dip->di_label == DT_LBL_NONE || 291 dip->di_instr != DIF_INSTR_NOP) 292 dp->dtdo_buf[i++] = dip->di_instr; 293 294 if (dip->di_extern == NULL) 295 continue; /* no external references needed */ 296 297 switch (DIF_INSTR_OP(dip->di_instr)) { 298 case DIF_OP_SETX: 299 idp = dip->di_extern; 300 if ((idp->di_flags & kmask) == kbits) 301 krel++; 302 else if ((idp->di_flags & umask) == ubits) 303 urel++; 304 break; 305 case DIF_OP_XLATE: 306 case DIF_OP_XLARG: 307 xlrefs++; 308 break; 309 default: 310 xyerror(D_UNKNOWN, "unexpected assembler relocation " 311 "for opcode 0x%x\n", DIF_INSTR_OP(dip->di_instr)); 312 } 313 } 314 315 assert(i == dlp->dl_len); 316 dp->dtdo_len = dlp->dl_len; 317 318 /* 319 * Make a second pass through the instructions, relocating each branch 320 * label to the index of the final instruction in the buffer and noting 321 * any other instruction-specific DIFO flags such as dtdo_destructive. 322 */ 323 for (i = 0; i < dp->dtdo_len; i++) { 324 dif_instr_t instr = dp->dtdo_buf[i]; 325 uint_t op = DIF_INSTR_OP(instr); 326 327 if (op == DIF_OP_CALL) { 328 if (DIF_INSTR_SUBR(instr) == DIF_SUBR_COPYOUT || 329 DIF_INSTR_SUBR(instr) == DIF_SUBR_COPYOUTSTR) 330 dp->dtdo_destructive = 1; 331 continue; 332 } 333 334 if (op >= DIF_OP_BA && op <= DIF_OP_BLEU) { 335 assert(DIF_INSTR_LABEL(instr) < dlp->dl_label); 336 dp->dtdo_buf[i] = DIF_INSTR_BRANCH(op, 337 labels[DIF_INSTR_LABEL(instr)]); 338 } 339 } 340 341 dt_free(dtp, labels); 342 pcb->pcb_asvidx = 0; 343 344 /* 345 * Allocate memory for the appropriate number of variable records and 346 * then fill in each variable record. As we populate the variable 347 * table we insert the corresponding variable names into the strtab. 348 */ 349 (void) dt_idhash_iter(dtp->dt_tls, dt_countvar, &n); 350 (void) dt_idhash_iter(dtp->dt_globals, dt_countvar, &n); 351 (void) dt_idhash_iter(pcb->pcb_locals, dt_countvar, &n); 352 353 if (n != 0) { 354 dp->dtdo_vartab = dt_alloc(dtp, n * sizeof (dtrace_difv_t)); 355 dp->dtdo_varlen = (uint32_t)n; 356 357 if (dp->dtdo_vartab == NULL) 358 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 359 360 (void) dt_idhash_iter(dtp->dt_tls, dt_copyvar, pcb); 361 (void) dt_idhash_iter(dtp->dt_globals, dt_copyvar, pcb); 362 (void) dt_idhash_iter(pcb->pcb_locals, dt_copyvar, pcb); 363 } 364 365 /* 366 * Allocate memory for the appropriate number of relocation table 367 * entries based upon our kernel and user counts from the first pass. 368 */ 369 if (krel != 0) { 370 dp->dtdo_kreltab = dt_alloc(dtp, 371 krel * sizeof (dof_relodesc_t)); 372 dp->dtdo_krelen = krel; 373 374 if (dp->dtdo_kreltab == NULL) 375 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 376 } 377 378 if (urel != 0) { 379 dp->dtdo_ureltab = dt_alloc(dtp, 380 urel * sizeof (dof_relodesc_t)); 381 dp->dtdo_urelen = urel; 382 383 if (dp->dtdo_ureltab == NULL) 384 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 385 } 386 387 if (xlrefs != 0) { 388 dp->dtdo_xlmtab = dt_zalloc(dtp, sizeof (dt_node_t *) * xlrefs); 389 dp->dtdo_xlmlen = xlrefs; 390 391 if (dp->dtdo_xlmtab == NULL) 392 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 393 } 394 395 /* 396 * If any relocations are needed, make another pass through the 397 * instruction list and fill in the relocation table entries. 398 */ 399 if (krel + urel + xlrefs != 0) { 400 uint_t knodef = pcb->pcb_cflags & DTRACE_C_KNODEF; 401 uint_t unodef = pcb->pcb_cflags & DTRACE_C_UNODEF; 402 403 dof_relodesc_t *krp = dp->dtdo_kreltab; 404 dof_relodesc_t *urp = dp->dtdo_ureltab; 405 dt_node_t **xlp = dp->dtdo_xlmtab; 406 407 i = 0; /* dtdo_buf[] index */ 408 409 for (dip = dlp->dl_list; dip != NULL; dip = dip->di_next) { 410 dof_relodesc_t *rp; 411 ssize_t soff; 412 uint_t nodef; 413 414 if (dip->di_label != DT_LBL_NONE && 415 dip->di_instr == DIF_INSTR_NOP) 416 continue; /* skip label declarations */ 417 418 i++; /* advance dtdo_buf[] index */ 419 420 if (DIF_INSTR_OP(dip->di_instr) == DIF_OP_XLATE || 421 DIF_INSTR_OP(dip->di_instr) == DIF_OP_XLARG) { 422 assert(dp->dtdo_buf[i - 1] == dip->di_instr); 423 dt_as_xlate(pcb, dp, i - 1, (uint_t) 424 (xlp++ - dp->dtdo_xlmtab), dip->di_extern); 425 continue; 426 } 427 428 if ((idp = dip->di_extern) == NULL) 429 continue; /* no relocation entry needed */ 430 431 if ((idp->di_flags & kmask) == kbits) { 432 nodef = knodef; 433 rp = krp++; 434 } else if ((idp->di_flags & umask) == ubits) { 435 nodef = unodef; 436 rp = urp++; 437 } else 438 continue; 439 440 if (!nodef) 441 dt_as_undef(idp, i); 442 443 assert(DIF_INSTR_OP(dip->di_instr) == DIF_OP_SETX); 444 soff = dt_strtab_insert(pcb->pcb_strtab, idp->di_name); 445 446 if (soff == -1L) 447 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 448 if (soff > DIF_STROFF_MAX) 449 longjmp(pcb->pcb_jmpbuf, EDT_STR2BIG); 450 451 rp->dofr_name = (dof_stridx_t)soff; 452 rp->dofr_type = DOF_RELO_SETX; 453 rp->dofr_offset = DIF_INSTR_INTEGER(dip->di_instr) * 454 sizeof (uint64_t); 455 rp->dofr_data = 0; 456 } 457 458 assert(krp == dp->dtdo_kreltab + dp->dtdo_krelen); 459 assert(urp == dp->dtdo_ureltab + dp->dtdo_urelen); 460 assert(xlp == dp->dtdo_xlmtab + dp->dtdo_xlmlen); 461 assert(i == dp->dtdo_len); 462 } 463 464 /* 465 * Allocate memory for the compiled string table and then copy the 466 * chunks from the string table into the final string buffer. 467 */ 468 if ((n = dt_strtab_size(pcb->pcb_strtab)) != 0) { 469 if ((dp->dtdo_strtab = dt_alloc(dtp, n)) == NULL) 470 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 471 472 (void) dt_strtab_write(pcb->pcb_strtab, 473 (dt_strtab_write_f *)dt_copystr, pcb); 474 dp->dtdo_strlen = (uint32_t)n; 475 } 476 477 /* 478 * Allocate memory for the compiled integer table and then copy the 479 * integer constants from the table into the final integer buffer. 480 */ 481 if ((n = dt_inttab_size(pcb->pcb_inttab)) != 0) { 482 if ((dp->dtdo_inttab = dt_alloc(dtp, 483 n * sizeof (uint64_t))) == NULL) 484 longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); 485 486 dt_inttab_write(pcb->pcb_inttab, dp->dtdo_inttab); 487 dp->dtdo_intlen = (uint32_t)n; 488 } 489 490 /* 491 * Fill in the DIFO return type from the type associated with the 492 * node saved in pcb_dret, and then clear pcb_difo and pcb_dret 493 * now that the assembler has completed successfully. 494 */ 495 dt_node_diftype(dtp, pcb->pcb_dret, &dp->dtdo_rtype); 496 pcb->pcb_difo = NULL; 497 pcb->pcb_dret = NULL; 498 499 if (pcb->pcb_cflags & DTRACE_C_DIFV) 500 dt_dis(dp, stderr); 501 502 return (dp); 503 } 504