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.
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.
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]
23 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2011, Joyent Inc. All rights reserved.
34 #include <sys/types.h>
37 #include <dt_parser.h>
39 #include <dt_provider.h>
40 #include <dt_module.h>
43 * This callback function is installed in a given identifier hash to search for
44 * and apply deferred pragmas that are pending for a given new identifier name.
45 * Multiple pragmas may be pending for a given name; we processs all of them.
49 dt_pragma_apply(dt_idhash_t
*dhp
, dt_ident_t
*idp
)
54 if ((php
= yypcb
->pcb_pragmas
) == NULL
)
55 return; /* no pragmas pending for current compilation pass */
57 while ((pdp
= dt_idhash_lookup(php
, idp
->di_name
)) != NULL
) {
58 switch (pdp
->di_kind
) {
60 idp
->di_attr
= pdp
->di_attr
;
63 idp
->di_vers
= pdp
->di_vers
;
66 dt_idhash_delete(php
, pdp
);
71 * The #pragma attributes directive can be used to reset stability attributes
72 * on a global identifier or inline definition. If the identifier is already
73 * defined, we can just change di_attr. If not, we insert the pragma into a
74 * hash table of the current pcb's deferred pragmas for later processing.
77 dt_pragma_attributes(const char *prname
, dt_node_t
*dnp
)
79 dtrace_hdl_t
*dtp
= yypcb
->pcb_hdl
;
80 dtrace_attribute_t attr
, *a
;
82 const char *name
, *part
;
85 if (dnp
== NULL
|| dnp
->dn_kind
!= DT_NODE_IDENT
||
86 dnp
->dn_list
== NULL
|| dnp
->dn_list
->dn_kind
!= DT_NODE_IDENT
) {
87 xyerror(D_PRAGMA_MALFORM
, "malformed #pragma %s "
88 "<attributes> <ident>\n", prname
);
91 if (dtrace_str2attr(dnp
->dn_string
, &attr
) == -1) {
92 xyerror(D_PRAGMA_INVAL
, "invalid attributes "
93 "specified by #pragma %s\n", prname
);
97 name
= dnp
->dn_string
;
99 if (strcmp(name
, "provider") == 0) {
101 name
= dnp
->dn_string
;
104 part
= dnp
->dn_string
;
106 if ((pvp
= dt_provider_lookup(dtp
, name
)) != NULL
) {
107 if (strcmp(part
, "provider") == 0) {
108 a
= &pvp
->pv_desc
.dtvd_attr
.dtpa_provider
;
109 } else if (strcmp(part
, "module") == 0) {
110 a
= &pvp
->pv_desc
.dtvd_attr
.dtpa_mod
;
111 } else if (strcmp(part
, "function") == 0) {
112 a
= &pvp
->pv_desc
.dtvd_attr
.dtpa_func
;
113 } else if (strcmp(part
, "name") == 0) {
114 a
= &pvp
->pv_desc
.dtvd_attr
.dtpa_name
;
115 } else if (strcmp(part
, "args") == 0) {
116 a
= &pvp
->pv_desc
.dtvd_attr
.dtpa_args
;
118 xyerror(D_PRAGMA_INVAL
, "invalid component "
119 "\"%s\" in attribute #pragma "
120 "for provider %s\n", name
, part
);
127 } else if ((idp
= dt_idstack_lookup(
128 &yypcb
->pcb_globals
, name
)) != NULL
) {
130 if (idp
->di_gen
!= dtp
->dt_gen
) {
131 xyerror(D_PRAGMA_SCOPE
, "#pragma %s cannot modify "
132 "entity defined outside program scope\n", prname
);
139 if (yypcb
->pcb_pragmas
== NULL
&& (yypcb
->pcb_pragmas
=
140 dt_idhash_create("pragma", NULL
, 0, 0)) == NULL
)
141 longjmp(yypcb
->pcb_jmpbuf
, EDT_NOMEM
);
143 idp
= dt_idhash_insert(yypcb
->pcb_pragmas
, name
, DT_IDENT_PRAGAT
, 0, 0,
144 attr
, 0, &dt_idops_thaw
, (void *)prname
, dtp
->dt_gen
);
147 longjmp(yypcb
->pcb_jmpbuf
, EDT_NOMEM
);
149 if (dtp
->dt_globals
->dh_defer
== NULL
)
150 dtp
->dt_globals
->dh_defer
= &dt_pragma_apply
;
154 * The #pragma binding directive can be used to reset the version binding
155 * on a global identifier or inline definition. If the identifier is already
156 * defined, we can just change di_vers. If not, we insert the pragma into a
157 * hash table of the current pcb's deferred pragmas for later processing.
160 dt_pragma_binding(const char *prname
, dt_node_t
*dnp
)
162 dtrace_hdl_t
*dtp
= yypcb
->pcb_hdl
;
167 if (dnp
== NULL
|| dnp
->dn_kind
!= DT_NODE_STRING
||
168 dnp
->dn_list
== NULL
|| dnp
->dn_list
->dn_kind
!= DT_NODE_IDENT
) {
169 xyerror(D_PRAGMA_MALFORM
, "malformed #pragma %s "
170 "\"version\" <ident>\n", prname
);
173 if (dt_version_str2num(dnp
->dn_string
, &vers
) == -1) {
174 xyerror(D_PRAGMA_INVAL
, "invalid version string "
175 "specified by #pragma %s\n", prname
);
178 name
= dnp
->dn_list
->dn_string
;
179 idp
= dt_idstack_lookup(&yypcb
->pcb_globals
, name
);
182 if (idp
->di_gen
!= dtp
->dt_gen
) {
183 xyerror(D_PRAGMA_SCOPE
, "#pragma %s cannot modify "
184 "entity defined outside program scope\n", prname
);
190 if (yypcb
->pcb_pragmas
== NULL
&& (yypcb
->pcb_pragmas
=
191 dt_idhash_create("pragma", NULL
, 0, 0)) == NULL
)
192 longjmp(yypcb
->pcb_jmpbuf
, EDT_NOMEM
);
194 idp
= dt_idhash_insert(yypcb
->pcb_pragmas
, name
, DT_IDENT_PRAGBN
, 0, 0,
195 _dtrace_defattr
, vers
, &dt_idops_thaw
, (void *)prname
, dtp
->dt_gen
);
198 longjmp(yypcb
->pcb_jmpbuf
, EDT_NOMEM
);
200 if (dtp
->dt_globals
->dh_defer
== NULL
)
201 dtp
->dt_globals
->dh_defer
= &dt_pragma_apply
;
205 dt_pragma_depends_finddep(dtrace_hdl_t
*dtp
, const char *lname
, char *lib
,
212 for (dirp
= dt_list_next(&dtp
->dt_lib_path
); dirp
!= NULL
;
213 dirp
= dt_list_next(dirp
)) {
214 (void) snprintf(lib
, len
, "%s/%s", dirp
->dir_path
, lname
);
216 if (stat(lib
, &sbuf
) == 0) {
223 xyerror(D_PRAGMA_DEPEND
,
224 "failed to find dependency in libpath: %s", lname
);
228 * The #pragma depends_on directive can be used to express a dependency on a
229 * module, provider or library which if not present will cause processing to
233 dt_pragma_depends(const char *prname
, dt_node_t
*cnp
)
235 dtrace_hdl_t
*dtp
= yypcb
->pcb_hdl
;
236 dt_node_t
*nnp
= cnp
? cnp
->dn_list
: NULL
;
238 dt_lib_depend_t
*dld
;
239 char lib
[MAXPATHLEN
];
241 if (cnp
== NULL
|| nnp
== NULL
||
242 cnp
->dn_kind
!= DT_NODE_IDENT
|| nnp
->dn_kind
!= DT_NODE_IDENT
) {
243 xyerror(D_PRAGMA_MALFORM
, "malformed #pragma %s "
244 "<class> <name>\n", prname
);
247 if (strcmp(cnp
->dn_string
, "provider") == 0)
248 found
= dt_provider_lookup(dtp
, nnp
->dn_string
) != NULL
;
249 else if (strcmp(cnp
->dn_string
, "module") == 0) {
250 dt_module_t
*mp
= dt_module_lookup_by_name(dtp
, nnp
->dn_string
);
251 found
= mp
!= NULL
&& dt_module_getctf(dtp
, mp
) != NULL
;
252 } else if (strcmp(cnp
->dn_string
, "library") == 0) {
253 if (yypcb
->pcb_cflags
& DTRACE_C_CTL
) {
254 assert(dtp
->dt_filetag
!= NULL
);
256 dt_pragma_depends_finddep(dtp
, nnp
->dn_string
, lib
,
259 dld
= dt_lib_depend_lookup(&dtp
->dt_lib_dep
,
263 if ((dt_lib_depend_add(dtp
, &dld
->dtld_dependencies
,
265 xyerror(D_PRAGMA_DEPEND
,
266 "failed to add dependency %s:%s\n", lib
,
267 dtrace_errmsg(dtp
, dtrace_errno(dtp
)));
271 * By this point we have already performed a topological
272 * sort of the dependencies; we process this directive
273 * as satisfied as long as the dependency was properly
276 if (dtp
->dt_filetag
== NULL
)
277 xyerror(D_PRAGMA_DEPEND
, "main program may "
278 "not explicitly depend on a library");
280 dld
= dt_lib_depend_lookup(&dtp
->dt_lib_dep
,
284 dt_pragma_depends_finddep(dtp
, nnp
->dn_string
, lib
,
286 dld
= dt_lib_depend_lookup(&dtp
->dt_lib_dep_sorted
,
290 if (!dld
->dtld_loaded
)
291 xyerror(D_PRAGMA_DEPEND
, "program requires "
292 "library \"%s\" which failed to load",
298 xyerror(D_PRAGMA_INVAL
, "invalid class %s "
299 "specified by #pragma %s\n", cnp
->dn_string
, prname
);
303 xyerror(D_PRAGMA_DEPEND
, "program requires %s %s\n",
304 cnp
->dn_string
, nnp
->dn_string
);
309 * The #pragma error directive can be followed by any list of tokens, which we
310 * just concatenate and print as part of our error message.
313 dt_pragma_error(const char *prname
, dt_node_t
*dnp
)
319 for (enp
= dnp
; enp
!= NULL
; enp
= enp
->dn_list
) {
320 if (enp
->dn_kind
== DT_NODE_IDENT
||
321 enp
->dn_kind
== DT_NODE_STRING
)
322 n
+= strlen(enp
->dn_string
) + 1;
328 for (enp
= dnp
; enp
!= NULL
; enp
= enp
->dn_list
) {
329 if (enp
->dn_kind
== DT_NODE_IDENT
||
330 enp
->dn_kind
== DT_NODE_STRING
) {
331 (void) strcat(s
, enp
->dn_string
);
332 (void) strcat(s
, " ");
336 xyerror(D_PRAGERR
, "#%s: %s\n", prname
, s
);
341 dt_pragma_ident(const char *prname
, dt_node_t
*dnp
)
343 /* ignore any #ident or #pragma ident lines */
347 dt_pragma_option(const char *prname
, dt_node_t
*dnp
)
349 dtrace_hdl_t
*dtp
= yypcb
->pcb_hdl
;
352 if (dnp
== NULL
|| dnp
->dn_kind
!= DT_NODE_IDENT
) {
353 xyerror(D_PRAGMA_MALFORM
,
354 "malformed #pragma %s <option>=<val>\n", prname
);
357 if (dnp
->dn_list
!= NULL
) {
358 xyerror(D_PRAGMA_MALFORM
,
359 "superfluous arguments specified for #pragma %s\n", prname
);
362 opt
= strdupa(dnp
->dn_string
);
364 if ((val
= strchr(opt
, '=')) != NULL
)
367 if (dtrace_setopt(dtp
, opt
, val
) == -1) {
369 xyerror(D_PRAGMA_OPTSET
,
370 "failed to set option '%s': %s\n", opt
,
371 dtrace_errmsg(dtp
, dtrace_errno(dtp
)));
373 xyerror(D_PRAGMA_OPTSET
,
374 "failed to set option '%s' to '%s': %s\n",
375 opt
, val
, dtrace_errmsg(dtp
, dtrace_errno(dtp
)));
381 * The #line directive is used to reset the input line number and to optionally
382 * note the file name for use in error messages. Sun cpp(1) also produces a
383 * third integer token after the filename which is one of the following:
385 * 0 - line change has nothing to do with an #include file
386 * 1 - line change because we just entered a #include file
387 * 2 - line change because we just exited a #include file
389 * We use these state tokens to adjust pcb_idepth, which in turn controls
390 * whether type lookups access the global type space or not.
393 dt_pragma_line(const char *prname
, dt_node_t
*dnp
)
395 dt_node_t
*fnp
= dnp
? dnp
->dn_list
: NULL
;
396 dt_node_t
*inp
= fnp
? fnp
->dn_list
: NULL
;
398 if ((dnp
== NULL
|| dnp
->dn_kind
!= DT_NODE_INT
) ||
399 (fnp
!= NULL
&& fnp
->dn_kind
!= DT_NODE_STRING
) ||
400 (inp
!= NULL
&& inp
->dn_kind
!= DT_NODE_INT
)) {
401 xyerror(D_PRAGMA_MALFORM
, "malformed #%s "
402 "<line> [ [\"file\"] state ]\n", prname
);
406 * If a file is specified, free any old pcb_filetag and swap fnp's
407 * dn_string into pcb_filetag as the new filename for error messages.
410 free(yypcb
->pcb_filetag
);
413 * This is not pretty, but is a necessary evil until we either
414 * write "dpp" or get a useful standalone cpp from DevPro. If
415 * the filename begins with /dev/fd, we know it's the master
416 * input file (see dt_preproc() in dt_cc.c), so just clear the
417 * dt_filetag pointer so error messages refer to the main file.
419 if (strncmp(fnp
->dn_string
, "/dev/fd/", 8) != 0) {
420 yypcb
->pcb_filetag
= fnp
->dn_string
;
421 fnp
->dn_string
= NULL
;
423 yypcb
->pcb_filetag
= NULL
;
427 if (inp
->dn_value
== 1)
429 else if (inp
->dn_value
== 2 && yypcb
->pcb_idepth
!= 0)
433 yylineno
= dnp
->dn_value
;
437 * D compiler pragma types range from control directives to common pragmas to
438 * D custom pragmas, in order of specificity. Similar to gcc, we use #pragma D
439 * as a special prefix for our pragmas so they can be used in mixed headers.
441 #define DT_PRAGMA_DIR 0 /* pragma directive may be used after naked # */
442 #define DT_PRAGMA_SUB 1 /* pragma directive may be used after #pragma */
443 #define DT_PRAGMA_DCP 2 /* pragma may only be used after #pragma D */
445 static const struct dt_pragmadesc
{
446 const char *dpd_name
;
447 void (*dpd_func
)(const char *, dt_node_t
*);
450 { "attributes", dt_pragma_attributes
, DT_PRAGMA_DCP
},
451 { "binding", dt_pragma_binding
, DT_PRAGMA_DCP
},
452 { "depends_on", dt_pragma_depends
, DT_PRAGMA_DCP
},
453 { "error", dt_pragma_error
, DT_PRAGMA_DIR
},
454 { "ident", dt_pragma_ident
, DT_PRAGMA_DIR
},
455 { "line", dt_pragma_line
, DT_PRAGMA_DIR
},
456 { "option", dt_pragma_option
, DT_PRAGMA_DCP
},
461 * Process a control line #directive by looking up the directive name in our
462 * lookup table and invoking the corresponding function with the token list.
463 * According to K&R[A12.9], we silently ignore null directive lines.
466 dt_pragma(dt_node_t
*pnp
)
468 const struct dt_pragmadesc
*dpd
;
470 int kind
= DT_PRAGMA_DIR
;
472 for (dnp
= pnp
; dnp
!= NULL
; dnp
= dnp
->dn_list
) {
473 if (dnp
->dn_kind
== DT_NODE_INT
) {
474 dt_pragma_line("line", dnp
);
478 if (dnp
->dn_kind
!= DT_NODE_IDENT
)
479 xyerror(D_PRAGCTL_INVAL
, "invalid control directive\n");
481 if (kind
== DT_PRAGMA_DIR
&&
482 strcmp(dnp
->dn_string
, "pragma") == 0) {
483 kind
= DT_PRAGMA_SUB
;
487 if (kind
== DT_PRAGMA_SUB
&&
488 strcmp(dnp
->dn_string
, "D") == 0) {
489 kind
= DT_PRAGMA_DCP
;
493 for (dpd
= dt_pragmas
; dpd
->dpd_name
!= NULL
; dpd
++) {
494 if (dpd
->dpd_kind
<= kind
&&
495 strcmp(dpd
->dpd_name
, dnp
->dn_string
) == 0)
499 yylineno
--; /* since we've already seen \n */
501 if (dpd
->dpd_name
!= NULL
) {
502 dpd
->dpd_func(dpd
->dpd_name
, dnp
->dn_list
);
509 xyerror(D_PRAGCTL_INVAL
, "invalid control directive: "
510 "#%s\n", dnp
->dn_string
);
513 break; /* K&R[A12.8] says to ignore unknown pragmas */
516 xyerror(D_PRAGMA_INVAL
, "invalid D pragma: %s\n",
524 dt_node_list_free(&pnp
);