Path: blob/main/cddl/contrib/opensolaris/lib/libdtrace/common/dt_pragma.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 (the "License").5* You may not use this file except in compliance with the License.6*7* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE8* or http://www.opensolaris.org/os/licensing.9* See the License for the specific language governing permissions10* and limitations under the License.11*12* When distributing Covered Code, include this CDDL HEADER in each13* file and include the License file at usr/src/OPENSOLARIS.LICENSE.14* If applicable, add the following below this CDDL HEADER, with the15* fields enclosed by brackets "[]" replaced with your own identifying16* information: Portions Copyright [yyyy] [name of copyright owner]17*18* CDDL HEADER END19*/2021/*22* Copyright 2008 Sun Microsystems, Inc. All rights reserved.23* Copyright (c) 2011, Joyent Inc. All rights reserved.24*/2526#pragma ident "%Z%%M% %I% %E% SMI"2728#include <assert.h>29#include <strings.h>30#ifdef illumos31#include <alloca.h>32#endif33#include <fcntl.h>34#include <stdlib.h>35#include <stdio.h>3637#include <sys/types.h>38#include <sys/sysctl.h>39#include <sys/stat.h>4041#include <dt_parser.h>42#include <dt_impl.h>43#include <dt_provider.h>44#include <dt_module.h>4546/*47* This callback function is installed in a given identifier hash to search for48* and apply deferred pragmas that are pending for a given new identifier name.49* Multiple pragmas may be pending for a given name; we processs all of them.50*/51/*ARGSUSED*/52static void53dt_pragma_apply(dt_idhash_t *dhp, dt_ident_t *idp)54{55dt_idhash_t *php;56dt_ident_t *pdp;5758if ((php = yypcb->pcb_pragmas) == NULL)59return; /* no pragmas pending for current compilation pass */6061while ((pdp = dt_idhash_lookup(php, idp->di_name)) != NULL) {62switch (pdp->di_kind) {63case DT_IDENT_PRAGAT:64idp->di_attr = pdp->di_attr;65break;66case DT_IDENT_PRAGBN:67idp->di_vers = pdp->di_vers;68break;69}70dt_idhash_delete(php, pdp);71}72}7374/*75* The #pragma attributes directive can be used to reset stability attributes76* on a global identifier or inline definition. If the identifier is already77* defined, we can just change di_attr. If not, we insert the pragma into a78* hash table of the current pcb's deferred pragmas for later processing.79*/80static void81dt_pragma_attributes(const char *prname, dt_node_t *dnp)82{83dtrace_hdl_t *dtp = yypcb->pcb_hdl;84dtrace_attribute_t attr, *a;85dt_provider_t *pvp;86const char *name, *part;87dt_ident_t *idp;8889if (dnp == NULL || dnp->dn_kind != DT_NODE_IDENT ||90dnp->dn_list == NULL || dnp->dn_list->dn_kind != DT_NODE_IDENT) {91xyerror(D_PRAGMA_MALFORM, "malformed #pragma %s "92"<attributes> <ident>\n", prname);93}9495if (dtrace_str2attr(dnp->dn_string, &attr) == -1) {96xyerror(D_PRAGMA_INVAL, "invalid attributes "97"specified by #pragma %s\n", prname);98}99100dnp = dnp->dn_list;101name = dnp->dn_string;102103if (strcmp(name, "provider") == 0) {104dnp = dnp->dn_list;105name = dnp->dn_string;106107dnp = dnp->dn_list;108part = dnp->dn_string;109110if ((pvp = dt_provider_lookup(dtp, name)) != NULL) {111if (strcmp(part, "provider") == 0) {112a = &pvp->pv_desc.dtvd_attr.dtpa_provider;113} else if (strcmp(part, "module") == 0) {114a = &pvp->pv_desc.dtvd_attr.dtpa_mod;115} else if (strcmp(part, "function") == 0) {116a = &pvp->pv_desc.dtvd_attr.dtpa_func;117} else if (strcmp(part, "name") == 0) {118a = &pvp->pv_desc.dtvd_attr.dtpa_name;119} else if (strcmp(part, "args") == 0) {120a = &pvp->pv_desc.dtvd_attr.dtpa_args;121} else {122xyerror(D_PRAGMA_INVAL, "invalid component "123"\"%s\" in attribute #pragma "124"for provider %s\n", name, part);125}126127*a = attr;128return;129}130131} else if ((idp = dt_idstack_lookup(132&yypcb->pcb_globals, name)) != NULL) {133134if (idp->di_gen != dtp->dt_gen) {135xyerror(D_PRAGMA_SCOPE, "#pragma %s cannot modify "136"entity defined outside program scope\n", prname);137}138139idp->di_attr = attr;140return;141}142143if (yypcb->pcb_pragmas == NULL && (yypcb->pcb_pragmas =144dt_idhash_create("pragma", NULL, 0, 0)) == NULL)145longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM);146147idp = dt_idhash_insert(yypcb->pcb_pragmas, name, DT_IDENT_PRAGAT, 0, 0,148attr, 0, &dt_idops_thaw, (void *)prname, dtp->dt_gen);149150if (idp == NULL)151longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM);152153if (dtp->dt_globals->dh_defer == NULL)154dtp->dt_globals->dh_defer = &dt_pragma_apply;155}156157/*158* The #pragma binding directive can be used to reset the version binding159* on a global identifier or inline definition. If the identifier is already160* defined, we can just change di_vers. If not, we insert the pragma into a161* hash table of the current pcb's deferred pragmas for later processing.162*/163static void164dt_pragma_binding(const char *prname, dt_node_t *dnp)165{166dtrace_hdl_t *dtp = yypcb->pcb_hdl;167dt_version_t vers;168const char *name;169dt_ident_t *idp;170171if (dnp == NULL || dnp->dn_kind != DT_NODE_STRING ||172dnp->dn_list == NULL || dnp->dn_list->dn_kind != DT_NODE_IDENT) {173xyerror(D_PRAGMA_MALFORM, "malformed #pragma %s "174"\"version\" <ident>\n", prname);175}176177if (dt_version_str2num(dnp->dn_string, &vers) == -1) {178xyerror(D_PRAGMA_INVAL, "invalid version string "179"specified by #pragma %s\n", prname);180}181182name = dnp->dn_list->dn_string;183idp = dt_idstack_lookup(&yypcb->pcb_globals, name);184185if (idp != NULL) {186if (idp->di_gen != dtp->dt_gen) {187xyerror(D_PRAGMA_SCOPE, "#pragma %s cannot modify "188"entity defined outside program scope\n", prname);189}190idp->di_vers = vers;191return;192}193194if (yypcb->pcb_pragmas == NULL && (yypcb->pcb_pragmas =195dt_idhash_create("pragma", NULL, 0, 0)) == NULL)196longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM);197198idp = dt_idhash_insert(yypcb->pcb_pragmas, name, DT_IDENT_PRAGBN, 0, 0,199_dtrace_defattr, vers, &dt_idops_thaw, (void *)prname, dtp->dt_gen);200201if (idp == NULL)202longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM);203204if (dtp->dt_globals->dh_defer == NULL)205dtp->dt_globals->dh_defer = &dt_pragma_apply;206}207208static void209dt_pragma_depends_finddep(dtrace_hdl_t *dtp, const char *lname, char *lib,210size_t len)211{212dt_dirpath_t *dirp;213struct stat sbuf;214int found = 0;215216for (dirp = dt_list_next(&dtp->dt_lib_path); dirp != NULL;217dirp = dt_list_next(dirp)) {218(void) snprintf(lib, len, "%s/%s", dirp->dir_path, lname);219220if (stat(lib, &sbuf) == 0) {221found = 1;222break;223}224}225226if (!found)227xyerror(D_PRAGMA_DEPEND,228"failed to find dependency in libpath: %s", lname);229}230231/*232* The #pragma depends_on directive can be used to express a dependency on a233* module, provider or library which if not present will cause processing to234* abort.235*/236static void237dt_pragma_depends(const char *prname, dt_node_t *cnp)238{239dtrace_hdl_t *dtp = yypcb->pcb_hdl;240dt_node_t *nnp = cnp ? cnp->dn_list : NULL;241int found;242dt_lib_depend_t *dld;243char lib[MAXPATHLEN];244size_t plen;245char *provs, *cpy, *tok;246247if (cnp == NULL || nnp == NULL ||248cnp->dn_kind != DT_NODE_IDENT || nnp->dn_kind != DT_NODE_IDENT) {249xyerror(D_PRAGMA_MALFORM, "malformed #pragma %s "250"<class> <name>\n", prname);251}252253if (strcmp(cnp->dn_string, "provider") == 0) {254/*255* First try to get the provider list using the256* debug.dtrace.providers sysctl, since that'll work even if257* we're not running as root.258*/259provs = NULL;260if (sysctlbyname("debug.dtrace.providers", NULL, &plen, NULL, 0) ||261((provs = dt_alloc(dtp, plen)) == NULL) ||262sysctlbyname("debug.dtrace.providers", provs, &plen, NULL, 0))263found = dt_provider_lookup(dtp, nnp->dn_string) != NULL;264else {265found = B_FALSE;266for (cpy = provs; (tok = strsep(&cpy, " ")) != NULL; )267if (strcmp(tok, nnp->dn_string) == 0) {268found = B_TRUE;269break;270}271if (found == B_FALSE)272found = dt_provider_lookup(dtp,273nnp->dn_string) != NULL;274}275if (provs != NULL)276dt_free(dtp, provs);277} else if (strcmp(cnp->dn_string, "module") == 0) {278dt_module_t *mp = dt_module_lookup_by_name(dtp, nnp->dn_string);279found = mp != NULL && dt_module_getctf(dtp, mp) != NULL;280#ifdef __FreeBSD__281if (!found) {282dt_kmodule_t *dkmp = dt_kmodule_lookup(dtp,283nnp->dn_string);284found = dkmp != NULL &&285dt_module_getctf(dtp, dkmp->dkm_module) != NULL;286}287#endif288} else if (strcmp(cnp->dn_string, "library") == 0) {289if (yypcb->pcb_cflags & DTRACE_C_CTL) {290assert(dtp->dt_filetag != NULL);291292dt_pragma_depends_finddep(dtp, nnp->dn_string, lib,293sizeof (lib));294295dld = dt_lib_depend_lookup(&dtp->dt_lib_dep,296dtp->dt_filetag);297assert(dld != NULL);298299if ((dt_lib_depend_add(dtp, &dld->dtld_dependencies,300lib)) != 0) {301xyerror(D_PRAGMA_DEPEND,302"failed to add dependency %s:%s\n", lib,303dtrace_errmsg(dtp, dtrace_errno(dtp)));304}305} else {306/*307* By this point we have already performed a topological308* sort of the dependencies; we process this directive309* as satisfied as long as the dependency was properly310* loaded.311*/312if (dtp->dt_filetag == NULL)313xyerror(D_PRAGMA_DEPEND, "main program may "314"not explicitly depend on a library");315316dld = dt_lib_depend_lookup(&dtp->dt_lib_dep,317dtp->dt_filetag);318assert(dld != NULL);319320dt_pragma_depends_finddep(dtp, nnp->dn_string, lib,321sizeof (lib));322dld = dt_lib_depend_lookup(&dtp->dt_lib_dep_sorted,323lib);324assert(dld != NULL);325326if (!dld->dtld_loaded)327xyerror(D_PRAGMA_DEPEND, "program requires "328"library \"%s\" which failed to load",329lib);330}331332found = B_TRUE;333} else {334xyerror(D_PRAGMA_INVAL, "invalid class %s "335"specified by #pragma %s\n", cnp->dn_string, prname);336}337338if (!found) {339xyerror(D_PRAGMA_DEPEND, "program requires %s %s\n",340cnp->dn_string, nnp->dn_string);341}342}343344/*345* The #pragma error directive can be followed by any list of tokens, which we346* just concatenate and print as part of our error message.347*/348static void349dt_pragma_error(const char *prname, dt_node_t *dnp)350{351dt_node_t *enp;352size_t n = 0;353char *s;354355for (enp = dnp; enp != NULL; enp = enp->dn_list) {356if (enp->dn_kind == DT_NODE_IDENT ||357enp->dn_kind == DT_NODE_STRING)358n += strlen(enp->dn_string) + 1;359}360361s = alloca(n + 1);362s[0] = '\0';363364for (enp = dnp; enp != NULL; enp = enp->dn_list) {365if (enp->dn_kind == DT_NODE_IDENT ||366enp->dn_kind == DT_NODE_STRING) {367(void) strcat(s, enp->dn_string);368(void) strcat(s, " ");369}370}371372xyerror(D_PRAGERR, "#%s: %s\n", prname, s);373}374375/*ARGSUSED*/376static void377dt_pragma_ident(const char *prname, dt_node_t *dnp)378{379/* ignore any #ident or #pragma ident lines */380}381382static void383dt_pragma_option(const char *prname, dt_node_t *dnp)384{385dtrace_hdl_t *dtp = yypcb->pcb_hdl;386char *opt, *val;387388if (dnp == NULL || dnp->dn_kind != DT_NODE_IDENT) {389xyerror(D_PRAGMA_MALFORM,390"malformed #pragma %s <option>=<val>\n", prname);391}392393if (dnp->dn_list != NULL) {394xyerror(D_PRAGMA_MALFORM,395"superfluous arguments specified for #pragma %s\n", prname);396}397398opt = alloca(strlen(dnp->dn_string) + 1);399(void) strcpy(opt, dnp->dn_string);400401if ((val = strchr(opt, '=')) != NULL)402*val++ = '\0';403404if (dtrace_setopt(dtp, opt, val) == -1) {405if (val == NULL) {406xyerror(D_PRAGMA_OPTSET,407"failed to set option '%s': %s\n", opt,408dtrace_errmsg(dtp, dtrace_errno(dtp)));409} else {410xyerror(D_PRAGMA_OPTSET,411"failed to set option '%s' to '%s': %s\n",412opt, val, dtrace_errmsg(dtp, dtrace_errno(dtp)));413}414}415}416417/*418* The #line directive is used to reset the input line number and to optionally419* note the file name for use in error messages. Sun cpp(1) also produces a420* third integer token after the filename which is one of the following:421*422* 0 - line change has nothing to do with an #include file423* 1 - line change because we just entered a #include file424* 2 - line change because we just exited a #include file425*426* We use these state tokens to adjust pcb_idepth, which in turn controls427* whether type lookups access the global type space or not.428*/429static void430dt_pragma_line(const char *prname, dt_node_t *dnp)431{432dt_node_t *fnp = dnp ? dnp->dn_list : NULL;433dt_node_t *inp = fnp ? fnp->dn_list : NULL;434435if ((dnp == NULL || dnp->dn_kind != DT_NODE_INT) ||436(fnp != NULL && fnp->dn_kind != DT_NODE_STRING) ||437(inp != NULL && inp->dn_kind != DT_NODE_INT)) {438xyerror(D_PRAGMA_MALFORM, "malformed #%s "439"<line> [ [\"file\"] state ]\n", prname);440}441442/*443* If a file is specified, free any old pcb_filetag and swap fnp's444* dn_string into pcb_filetag as the new filename for error messages.445*/446if (fnp != NULL) {447if (yypcb->pcb_filetag != NULL)448free(yypcb->pcb_filetag);449450/*451* This is not pretty, but is a necessary evil until we either452* write "dpp" or get a useful standalone cpp from DevPro. If453* the filename begins with /dev/fd, we know it's the master454* input file (see dt_preproc() in dt_cc.c), so just clear the455* dt_filetag pointer so error messages refer to the main file.456*/457if (strncmp(fnp->dn_string, "/dev/fd/", 8) != 0) {458yypcb->pcb_filetag = fnp->dn_string;459fnp->dn_string = NULL;460} else461yypcb->pcb_filetag = NULL;462}463464if (inp != NULL) {465if (inp->dn_value == 1)466yypcb->pcb_idepth++;467else if (inp->dn_value == 2 && yypcb->pcb_idepth != 0)468yypcb->pcb_idepth--;469}470471yylineno = dnp->dn_value;472}473474/*475* D compiler pragma types range from control directives to common pragmas to476* D custom pragmas, in order of specificity. Similar to gcc, we use #pragma D477* as a special prefix for our pragmas so they can be used in mixed headers.478*/479#define DT_PRAGMA_DIR 0 /* pragma directive may be used after naked # */480#define DT_PRAGMA_SUB 1 /* pragma directive may be used after #pragma */481#define DT_PRAGMA_DCP 2 /* pragma may only be used after #pragma D */482483static const struct dt_pragmadesc {484const char *dpd_name;485void (*dpd_func)(const char *, dt_node_t *);486int dpd_kind;487} dt_pragmas[] = {488{ "attributes", dt_pragma_attributes, DT_PRAGMA_DCP },489{ "binding", dt_pragma_binding, DT_PRAGMA_DCP },490{ "depends_on", dt_pragma_depends, DT_PRAGMA_DCP },491{ "error", dt_pragma_error, DT_PRAGMA_DIR },492{ "ident", dt_pragma_ident, DT_PRAGMA_DIR },493{ "line", dt_pragma_line, DT_PRAGMA_DIR },494{ "option", dt_pragma_option, DT_PRAGMA_DCP },495{ NULL, NULL }496};497498/*499* Process a control line #directive by looking up the directive name in our500* lookup table and invoking the corresponding function with the token list.501* According to K&R[A12.9], we silently ignore null directive lines.502*/503void504dt_pragma(dt_node_t *pnp)505{506const struct dt_pragmadesc *dpd;507dt_node_t *dnp;508int kind = DT_PRAGMA_DIR;509510for (dnp = pnp; dnp != NULL; dnp = dnp->dn_list) {511if (dnp->dn_kind == DT_NODE_INT) {512dt_pragma_line("line", dnp);513break;514}515516if (dnp->dn_kind != DT_NODE_IDENT)517xyerror(D_PRAGCTL_INVAL, "invalid control directive\n");518519if (kind == DT_PRAGMA_DIR &&520strcmp(dnp->dn_string, "pragma") == 0) {521kind = DT_PRAGMA_SUB;522continue;523}524525if (kind == DT_PRAGMA_SUB &&526strcmp(dnp->dn_string, "D") == 0) {527kind = DT_PRAGMA_DCP;528continue;529}530531for (dpd = dt_pragmas; dpd->dpd_name != NULL; dpd++) {532if (dpd->dpd_kind <= kind &&533strcmp(dpd->dpd_name, dnp->dn_string) == 0)534break;535}536537yylineno--; /* since we've already seen \n */538539if (dpd->dpd_name != NULL) {540dpd->dpd_func(dpd->dpd_name, dnp->dn_list);541yylineno++;542break;543}544545switch (kind) {546case DT_PRAGMA_DIR:547xyerror(D_PRAGCTL_INVAL, "invalid control directive: "548"#%s\n", dnp->dn_string);549/*NOTREACHED*/550case DT_PRAGMA_SUB:551break; /* K&R[A12.8] says to ignore unknown pragmas */552case DT_PRAGMA_DCP:553default:554xyerror(D_PRAGMA_INVAL, "invalid D pragma: %s\n",555dnp->dn_string);556}557558yylineno++;559break;560}561562dt_node_list_free(&pnp);563}564565566