Path: blob/main/cddl/contrib/opensolaris/lib/libdtrace/common/dt_as.c
39562 views
/*1* CDDL HEADER START2*3* The contents of this file are subject to the terms of the4* Common Development and Distribution License, Version 1.0 only5* (the "License"). You may not use this file except in compliance6* with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or http://www.opensolaris.org/os/licensing.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/21/*22* Copyright 2005 Sun Microsystems, Inc. All rights reserved.23* Use is subject to license terms.24*/25/*26* Copyright (c) 2013 by Delphix. All rights reserved.27* Copyright (c) 2013 Joyent, Inc. All rights reserved.28*/2930#include <sys/types.h>31#include <strings.h>32#include <stdlib.h>33#include <assert.h>3435#include <dt_impl.h>36#include <dt_parser.h>37#include <dt_as.h>3839void40dt_irlist_create(dt_irlist_t *dlp)41{42bzero(dlp, sizeof (dt_irlist_t));43dlp->dl_label = 1;44}4546void47dt_irlist_destroy(dt_irlist_t *dlp)48{49dt_irnode_t *dip, *nip;5051for (dip = dlp->dl_list; dip != NULL; dip = nip) {52nip = dip->di_next;53free(dip);54}55}5657void58dt_irlist_append(dt_irlist_t *dlp, dt_irnode_t *dip)59{60if (dlp->dl_last != NULL)61dlp->dl_last->di_next = dip;62else63dlp->dl_list = dip;6465dlp->dl_last = dip;6667if (dip->di_label == DT_LBL_NONE || dip->di_instr != DIF_INSTR_NOP)68dlp->dl_len++; /* don't count forward refs in instr count */69}7071uint_t72dt_irlist_label(dt_irlist_t *dlp)73{74return (dlp->dl_label++);75}7677/*ARGSUSED*/78static int79dt_countvar(dt_idhash_t *dhp, dt_ident_t *idp, void *data)80{81size_t *np = data;8283if (idp->di_flags & (DT_IDFLG_DIFR | DT_IDFLG_DIFW))84(*np)++; /* include variable in vartab */8586return (0);87}8889/*ARGSUSED*/90static int91dt_copyvar(dt_idhash_t *dhp, dt_ident_t *idp, void *data)92{93dt_pcb_t *pcb = data;94dtrace_difv_t *dvp;95ssize_t stroff;96dt_node_t dn;9798if (!(idp->di_flags & (DT_IDFLG_DIFR | DT_IDFLG_DIFW)))99return (0); /* omit variable from vartab */100101dvp = &pcb->pcb_difo->dtdo_vartab[pcb->pcb_asvidx++];102stroff = dt_strtab_insert(pcb->pcb_strtab, idp->di_name);103104if (stroff == -1L)105longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);106if (stroff > DIF_STROFF_MAX)107longjmp(pcb->pcb_jmpbuf, EDT_STR2BIG);108109dvp->dtdv_name = (uint_t)stroff;110dvp->dtdv_id = idp->di_id;111dvp->dtdv_flags = 0;112113dvp->dtdv_kind = (idp->di_kind == DT_IDENT_ARRAY) ?114DIFV_KIND_ARRAY : DIFV_KIND_SCALAR;115116if (idp->di_flags & DT_IDFLG_LOCAL)117dvp->dtdv_scope = DIFV_SCOPE_LOCAL;118else if (idp->di_flags & DT_IDFLG_TLS)119dvp->dtdv_scope = DIFV_SCOPE_THREAD;120else121dvp->dtdv_scope = DIFV_SCOPE_GLOBAL;122123if (idp->di_flags & DT_IDFLG_DIFR)124dvp->dtdv_flags |= DIFV_F_REF;125if (idp->di_flags & DT_IDFLG_DIFW)126dvp->dtdv_flags |= DIFV_F_MOD;127128bzero(&dn, sizeof (dn));129dt_node_type_assign(&dn, idp->di_ctfp, idp->di_type, B_FALSE);130dt_node_diftype(pcb->pcb_hdl, &dn, &dvp->dtdv_type);131132idp->di_flags &= ~(DT_IDFLG_DIFR | DT_IDFLG_DIFW);133return (0);134}135136static ssize_t137dt_copystr(const char *s, size_t n, size_t off, dt_pcb_t *pcb)138{139bcopy(s, pcb->pcb_difo->dtdo_strtab + off, n);140return (n);141}142143/*144* Rewrite the xlate/xlarg instruction at dtdo_buf[i] so that the instruction's145* xltab index reflects the offset 'xi' of the assigned dtdo_xlmtab[] location.146* We track the cumulative references to translators and members in the pcb's147* pcb_asxrefs[] array, a two-dimensional array of bitmaps indexed by the148* global translator id and then by the corresponding translator member id.149*/150static void151dt_as_xlate(dt_pcb_t *pcb, dtrace_difo_t *dp,152uint_t i, uint_t xi, dt_node_t *dnp)153{154dtrace_hdl_t *dtp = pcb->pcb_hdl;155dt_xlator_t *dxp = dnp->dn_membexpr->dn_xlator;156157assert(i < dp->dtdo_len);158assert(xi < dp->dtdo_xlmlen);159160assert(dnp->dn_kind == DT_NODE_MEMBER);161assert(dnp->dn_membexpr->dn_kind == DT_NODE_XLATOR);162163assert(dxp->dx_id < dtp->dt_xlatorid);164assert(dnp->dn_membid < dxp->dx_nmembers);165166if (pcb->pcb_asxrefs == NULL) {167pcb->pcb_asxreflen = dtp->dt_xlatorid;168pcb->pcb_asxrefs =169dt_zalloc(dtp, sizeof (ulong_t *) * pcb->pcb_asxreflen);170if (pcb->pcb_asxrefs == NULL)171longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);172}173174if (pcb->pcb_asxrefs[dxp->dx_id] == NULL) {175pcb->pcb_asxrefs[dxp->dx_id] =176dt_zalloc(dtp, BT_SIZEOFMAP(dxp->dx_nmembers));177if (pcb->pcb_asxrefs[dxp->dx_id] == NULL)178longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);179}180181dp->dtdo_buf[i] = DIF_INSTR_XLATE(182DIF_INSTR_OP(dp->dtdo_buf[i]), xi, DIF_INSTR_RD(dp->dtdo_buf[i]));183184BT_SET(pcb->pcb_asxrefs[dxp->dx_id], dnp->dn_membid);185dp->dtdo_xlmtab[xi] = dnp;186}187188static void189dt_as_undef(const dt_ident_t *idp, uint_t offset)190{191const char *kind, *mark = (idp->di_flags & DT_IDFLG_USER) ? "``" : "`";192const dtrace_syminfo_t *dts = idp->di_data;193194if (idp->di_flags & DT_IDFLG_USER)195kind = "user";196else if (idp->di_flags & DT_IDFLG_PRIM)197kind = "primary kernel";198else199kind = "loadable kernel";200201yylineno = idp->di_lineno;202203xyerror(D_ASRELO, "relocation remains against %s symbol %s%s%s (offset "204"0x%x)\n", kind, dts->dts_object, mark, dts->dts_name, offset);205}206207dtrace_difo_t *208dt_as(dt_pcb_t *pcb)209{210dtrace_hdl_t *dtp = pcb->pcb_hdl;211dt_irlist_t *dlp = &pcb->pcb_ir;212uint_t *labels = NULL;213dt_irnode_t *dip;214dtrace_difo_t *dp;215dt_ident_t *idp;216217size_t n = 0;218uint_t i;219220uint_t kmask, kbits, umask, ubits;221uint_t krel = 0, urel = 0, xlrefs = 0;222223/*224* Select bitmasks based upon the desired symbol linking policy. We225* test (di_extern->di_flags & xmask) == xbits to determine if the226* symbol should have a relocation entry generated in the loop below.227*228* DT_LINK_KERNEL = kernel symbols static, user symbols dynamic229* DT_LINK_PRIMARY = primary kernel symbols static, others dynamic230* DT_LINK_DYNAMIC = all symbols dynamic231* DT_LINK_STATIC = all symbols static232*233* By 'static' we mean that we use the symbol's value at compile-time234* in the final DIF. By 'dynamic' we mean that we create a relocation235* table entry for the symbol's value so it can be relocated later.236*/237switch (dtp->dt_linkmode) {238case DT_LINK_KERNEL:239kmask = 0;240kbits = -1u;241umask = DT_IDFLG_USER;242ubits = DT_IDFLG_USER;243break;244case DT_LINK_PRIMARY:245kmask = DT_IDFLG_USER | DT_IDFLG_PRIM;246kbits = 0;247umask = DT_IDFLG_USER;248ubits = DT_IDFLG_USER;249break;250case DT_LINK_DYNAMIC:251kmask = DT_IDFLG_USER;252kbits = 0;253umask = DT_IDFLG_USER;254ubits = DT_IDFLG_USER;255break;256case DT_LINK_STATIC:257kmask = umask = 0;258kbits = ubits = -1u;259break;260default:261xyerror(D_UNKNOWN, "internal error -- invalid link mode %u\n",262dtp->dt_linkmode);263}264265assert(pcb->pcb_difo == NULL);266pcb->pcb_difo = dt_zalloc(dtp, sizeof (dtrace_difo_t));267268if ((dp = pcb->pcb_difo) == NULL)269longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);270271dp->dtdo_buf = dt_alloc(dtp, sizeof (dif_instr_t) * dlp->dl_len);272273if (dp->dtdo_buf == NULL)274longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);275276if ((labels = dt_alloc(dtp, sizeof (uint_t) * dlp->dl_label)) == NULL)277longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);278279/*280* Make an initial pass through the instruction list, filling in the281* instruction buffer with valid instructions and skipping labeled nops.282* While doing this, we also fill in our labels[] translation table283* and we count up the number of relocation table entries we will need.284*/285for (i = 0, dip = dlp->dl_list; dip != NULL; dip = dip->di_next) {286if (dip->di_label != DT_LBL_NONE)287labels[dip->di_label] = i;288289if (dip->di_label == DT_LBL_NONE ||290dip->di_instr != DIF_INSTR_NOP)291dp->dtdo_buf[i++] = dip->di_instr;292293if (dip->di_extern == NULL)294continue; /* no external references needed */295296switch (DIF_INSTR_OP(dip->di_instr)) {297case DIF_OP_SETX:298idp = dip->di_extern;299if ((idp->di_flags & kmask) == kbits)300krel++;301else if ((idp->di_flags & umask) == ubits)302urel++;303break;304case DIF_OP_XLATE:305case DIF_OP_XLARG:306xlrefs++;307break;308default:309xyerror(D_UNKNOWN, "unexpected assembler relocation "310"for opcode 0x%x\n", DIF_INSTR_OP(dip->di_instr));311}312}313314assert(i == dlp->dl_len);315dp->dtdo_len = dlp->dl_len;316317/*318* Make a second pass through the instructions, relocating each branch319* label to the index of the final instruction in the buffer and noting320* any other instruction-specific DIFO flags such as dtdo_destructive.321*/322for (i = 0; i < dp->dtdo_len; i++) {323dif_instr_t instr = dp->dtdo_buf[i];324uint_t op = DIF_INSTR_OP(instr);325326if (op == DIF_OP_CALL) {327if (DIF_INSTR_SUBR(instr) == DIF_SUBR_COPYOUT ||328DIF_INSTR_SUBR(instr) == DIF_SUBR_COPYOUTSTR)329dp->dtdo_destructive = 1;330continue;331}332333if (op >= DIF_OP_BA && op <= DIF_OP_BLEU) {334assert(DIF_INSTR_LABEL(instr) < dlp->dl_label);335dp->dtdo_buf[i] = DIF_INSTR_BRANCH(op,336labels[DIF_INSTR_LABEL(instr)]);337}338}339340dt_free(dtp, labels);341pcb->pcb_asvidx = 0;342343/*344* Allocate memory for the appropriate number of variable records and345* then fill in each variable record. As we populate the variable346* table we insert the corresponding variable names into the strtab.347*/348(void) dt_idhash_iter(dtp->dt_tls, dt_countvar, &n);349(void) dt_idhash_iter(dtp->dt_globals, dt_countvar, &n);350(void) dt_idhash_iter(pcb->pcb_locals, dt_countvar, &n);351352if (n != 0) {353dp->dtdo_vartab = dt_alloc(dtp, n * sizeof (dtrace_difv_t));354dp->dtdo_varlen = (uint32_t)n;355356if (dp->dtdo_vartab == NULL)357longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);358359(void) dt_idhash_iter(dtp->dt_tls, dt_copyvar, pcb);360(void) dt_idhash_iter(dtp->dt_globals, dt_copyvar, pcb);361(void) dt_idhash_iter(pcb->pcb_locals, dt_copyvar, pcb);362}363364/*365* Allocate memory for the appropriate number of relocation table366* entries based upon our kernel and user counts from the first pass.367*/368if (krel != 0) {369dp->dtdo_kreltab = dt_alloc(dtp,370krel * sizeof (dof_relodesc_t));371dp->dtdo_krelen = krel;372373if (dp->dtdo_kreltab == NULL)374longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);375}376377if (urel != 0) {378dp->dtdo_ureltab = dt_alloc(dtp,379urel * sizeof (dof_relodesc_t));380dp->dtdo_urelen = urel;381382if (dp->dtdo_ureltab == NULL)383longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);384}385386if (xlrefs != 0) {387dp->dtdo_xlmtab = dt_zalloc(dtp, sizeof (dt_node_t *) * xlrefs);388dp->dtdo_xlmlen = xlrefs;389390if (dp->dtdo_xlmtab == NULL)391longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);392}393394/*395* If any relocations are needed, make another pass through the396* instruction list and fill in the relocation table entries.397*/398if (krel + urel + xlrefs != 0) {399uint_t knodef = pcb->pcb_cflags & DTRACE_C_KNODEF;400uint_t unodef = pcb->pcb_cflags & DTRACE_C_UNODEF;401402dof_relodesc_t *krp = dp->dtdo_kreltab;403dof_relodesc_t *urp = dp->dtdo_ureltab;404dt_node_t **xlp = dp->dtdo_xlmtab;405406i = 0; /* dtdo_buf[] index */407408for (dip = dlp->dl_list; dip != NULL; dip = dip->di_next) {409dof_relodesc_t *rp;410ssize_t soff;411uint_t nodef;412413if (dip->di_label != DT_LBL_NONE &&414dip->di_instr == DIF_INSTR_NOP)415continue; /* skip label declarations */416417i++; /* advance dtdo_buf[] index */418419if (DIF_INSTR_OP(dip->di_instr) == DIF_OP_XLATE ||420DIF_INSTR_OP(dip->di_instr) == DIF_OP_XLARG) {421assert(dp->dtdo_buf[i - 1] == dip->di_instr);422dt_as_xlate(pcb, dp, i - 1, (uint_t)423(xlp++ - dp->dtdo_xlmtab), dip->di_extern);424continue;425}426427if ((idp = dip->di_extern) == NULL)428continue; /* no relocation entry needed */429430if ((idp->di_flags & kmask) == kbits) {431nodef = knodef;432rp = krp++;433} else if ((idp->di_flags & umask) == ubits) {434nodef = unodef;435rp = urp++;436} else437continue;438439if (!nodef)440dt_as_undef(idp, i);441442assert(DIF_INSTR_OP(dip->di_instr) == DIF_OP_SETX);443soff = dt_strtab_insert(pcb->pcb_strtab, idp->di_name);444445if (soff == -1L)446longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);447if (soff > DIF_STROFF_MAX)448longjmp(pcb->pcb_jmpbuf, EDT_STR2BIG);449450rp->dofr_name = (dof_stridx_t)soff;451rp->dofr_type = DOF_RELO_SETX;452rp->dofr_offset = DIF_INSTR_INTEGER(dip->di_instr) *453sizeof (uint64_t);454rp->dofr_data = 0;455}456457assert(krp == dp->dtdo_kreltab + dp->dtdo_krelen);458assert(urp == dp->dtdo_ureltab + dp->dtdo_urelen);459assert(xlp == dp->dtdo_xlmtab + dp->dtdo_xlmlen);460assert(i == dp->dtdo_len);461}462463/*464* Allocate memory for the compiled string table and then copy the465* chunks from the string table into the final string buffer.466*/467if ((n = dt_strtab_size(pcb->pcb_strtab)) != 0) {468if ((dp->dtdo_strtab = dt_alloc(dtp, n)) == NULL)469longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);470471(void) dt_strtab_write(pcb->pcb_strtab,472(dt_strtab_write_f *)dt_copystr, pcb);473dp->dtdo_strlen = (uint32_t)n;474}475476/*477* Allocate memory for the compiled integer table and then copy the478* integer constants from the table into the final integer buffer.479*/480if ((n = dt_inttab_size(pcb->pcb_inttab)) != 0) {481if ((dp->dtdo_inttab = dt_alloc(dtp,482n * sizeof (uint64_t))) == NULL)483longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);484485dt_inttab_write(pcb->pcb_inttab, dp->dtdo_inttab);486dp->dtdo_intlen = (uint32_t)n;487}488489/*490* Fill in the DIFO return type from the type associated with the491* node saved in pcb_dret, and then clear pcb_difo and pcb_dret492* now that the assembler has completed successfully.493*/494dt_node_diftype(dtp, pcb->pcb_dret, &dp->dtdo_rtype);495pcb->pcb_difo = NULL;496pcb->pcb_dret = NULL;497498if (pcb->pcb_cflags & DTRACE_C_DIFV)499dt_dis(dp, stderr);500501return (dp);502}503504505