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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"@(#)dt_program.c	1.12	08/05/05 SMI"
28
29#include <unistd.h>
30#include <strings.h>
31#include <stdlib.h>
32#include <errno.h>
33#include <assert.h>
34#include <ctype.h>
35#include <alloca.h>
36
37#include <dt_impl.h>
38#include <dt_program.h>
39#include <dt_printf.h>
40#include <dt_provider.h>
41#include <dt_ld.h>
42
43dtrace_prog_t *
44dt_program_create(dtrace_hdl_t *dtp)
45{
46	dtrace_prog_t *pgp = dt_zalloc(dtp, sizeof (dtrace_prog_t));
47
48	if (pgp != NULL)
49		dt_list_append(&dtp->dt_programs, pgp);
50	else
51		(void) dt_set_errno(dtp, EDT_NOMEM);
52
53	/*
54	 * By default, programs start with DOF version 1 so that output files
55	 * containing DOF are backward compatible. If a program requires new
56	 * DOF features, the version is increased as needed.
57	 */
58	/*
59	 * APPLE NOTE: The earliest DOF version Leopard will support is v3.
60	 * This includes relative offsets for probes, and is-enabled probes.
61	 */
62	pgp->dp_dofversion = DOF_VERSION_3;
63
64	return (pgp);
65}
66
67void
68dt_program_destroy(dtrace_hdl_t *dtp, dtrace_prog_t *pgp)
69{
70	dt_stmt_t *stp, *next;
71	uint_t i;
72
73	for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) {
74		next = dt_list_next(stp);
75		dtrace_stmt_destroy(dtp, stp->ds_desc);
76		dt_free(dtp, stp);
77	}
78
79	for (i = 0; i < pgp->dp_xrefslen; i++)
80		dt_free(dtp, pgp->dp_xrefs[i]);
81
82	dt_free(dtp, pgp->dp_xrefs);
83	dt_list_delete(&dtp->dt_programs, pgp);
84	dt_free(dtp, pgp);
85}
86
87/*ARGSUSED*/
88void
89dtrace_program_info(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
90    dtrace_proginfo_t *pip)
91{
92	dt_stmt_t *stp;
93	dtrace_actdesc_t *ap;
94	dtrace_ecbdesc_t *last = NULL;
95
96	if (pip == NULL)
97		return;
98
99	bzero(pip, sizeof (dtrace_proginfo_t));
100
101	if (dt_list_next(&pgp->dp_stmts) != NULL) {
102		pip->dpi_descattr = _dtrace_maxattr;
103		pip->dpi_stmtattr = _dtrace_maxattr;
104	} else {
105		pip->dpi_descattr = _dtrace_defattr;
106		pip->dpi_stmtattr = _dtrace_defattr;
107	}
108
109	for (stp = dt_list_next(&pgp->dp_stmts); stp; stp = dt_list_next(stp)) {
110		dtrace_ecbdesc_t *edp = stp->ds_desc->dtsd_ecbdesc;
111
112		if (edp == last)
113			continue;
114		last = edp;
115
116		pip->dpi_descattr =
117		    dt_attr_min(stp->ds_desc->dtsd_descattr, pip->dpi_descattr);
118
119		pip->dpi_stmtattr =
120		    dt_attr_min(stp->ds_desc->dtsd_stmtattr, pip->dpi_stmtattr);
121
122		/*
123		 * If there aren't any actions, account for the fact that
124		 * recording the epid will generate a record.
125		 */
126		if (edp->dted_action == NULL)
127			pip->dpi_recgens++;
128
129		for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) {
130			if (ap->dtad_kind == DTRACEACT_SPECULATE) {
131				pip->dpi_speculations++;
132				continue;
133			}
134
135			if (DTRACEACT_ISAGG(ap->dtad_kind)) {
136				pip->dpi_recgens -= ap->dtad_arg;
137				pip->dpi_aggregates++;
138				continue;
139			}
140
141			if (DTRACEACT_ISDESTRUCTIVE(ap->dtad_kind))
142				continue;
143
144			if (ap->dtad_kind == DTRACEACT_DIFEXPR &&
145			    ap->dtad_difo->dtdo_rtype.dtdt_kind ==
146			    DIF_TYPE_CTF &&
147			    ap->dtad_difo->dtdo_rtype.dtdt_size == 0)
148				continue;
149
150			pip->dpi_recgens++;
151		}
152	}
153}
154
155int
156dtrace_program_exec(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
157    dtrace_proginfo_t *pip)
158{
159	void *dof;
160	int n, err;
161
162	dtrace_program_info(dtp, pgp, pip);
163
164	if ((dof = dtrace_dof_create(dtp, pgp, DTRACE_D_STRIP)) == NULL)
165		return (-1);
166
167	n = dt_ioctl(dtp, DTRACEIOC_ENABLE, dof);
168        if (n == -1 && (errno & 0xfffff000))
169                n = (((unsigned int)errno) >> 12); /* Darwin's ioctls only return -1 or zero. Overload errno to mimic Solaris. */
170	dtrace_dof_destroy(dtp, dof);
171
172	if (n == -1) {
173		switch (errno) {
174		case EINVAL:
175			err = EDT_DIFINVAL;
176			break;
177		case EFAULT:
178			err = EDT_DIFFAULT;
179			break;
180		case E2BIG:
181			err = EDT_DIFSIZE;
182			break;
183		case EBUSY:
184			err = EDT_ENABLING_ERR;
185			break;
186		default:
187			err = errno;
188		}
189
190		return (dt_set_errno(dtp, err));
191	}
192
193	if (pip != NULL)
194		pip->dpi_matches += n;
195
196	return (0);
197}
198
199static void
200dt_ecbdesc_hold(dtrace_ecbdesc_t *edp)
201{
202	edp->dted_refcnt++;
203}
204
205void
206dt_ecbdesc_release(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp)
207{
208	if (--edp->dted_refcnt > 0)
209		return;
210
211	dt_difo_free(dtp, edp->dted_pred.dtpdd_difo);
212	assert(edp->dted_action == NULL);
213	dt_free(dtp, edp);
214}
215
216dtrace_ecbdesc_t *
217dt_ecbdesc_create(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
218{
219	dtrace_ecbdesc_t *edp;
220
221	if ((edp = dt_zalloc(dtp, sizeof (dtrace_ecbdesc_t))) == NULL) {
222		(void) dt_set_errno(dtp, EDT_NOMEM);
223		return (NULL);
224	}
225
226	edp->dted_probe = *pdp;
227	dt_ecbdesc_hold(edp);
228	return (edp);
229}
230
231dtrace_stmtdesc_t *
232dtrace_stmt_create(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp)
233{
234	dtrace_stmtdesc_t *sdp;
235
236	if ((sdp = dt_zalloc(dtp, sizeof (dtrace_stmtdesc_t))) == NULL)
237		return (NULL);
238
239	dt_ecbdesc_hold(edp);
240	sdp->dtsd_ecbdesc = edp;
241	sdp->dtsd_descattr = _dtrace_defattr;
242	sdp->dtsd_stmtattr = _dtrace_defattr;
243
244	return (sdp);
245}
246
247dtrace_actdesc_t *
248dtrace_stmt_action(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp)
249{
250	dtrace_actdesc_t *new;
251	dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc;
252
253	if ((new = dt_alloc(dtp, sizeof (dtrace_actdesc_t))) == NULL)
254		return (NULL);
255
256	if (sdp->dtsd_action_last != NULL) {
257		assert(sdp->dtsd_action != NULL);
258		assert(sdp->dtsd_action_last->dtad_next == NULL);
259		sdp->dtsd_action_last->dtad_next = new;
260	} else {
261		dtrace_actdesc_t *ap = edp->dted_action;
262
263		assert(sdp->dtsd_action == NULL);
264		sdp->dtsd_action = new;
265
266		while (ap != NULL && ap->dtad_next != NULL)
267			ap = ap->dtad_next;
268
269		if (ap == NULL)
270			edp->dted_action = new;
271		else
272			ap->dtad_next = new;
273	}
274
275	sdp->dtsd_action_last = new;
276	bzero(new, sizeof (dtrace_actdesc_t));
277	new->dtad_uarg = (uintptr_t)sdp;
278
279	return (new);
280}
281
282int
283dtrace_stmt_add(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, dtrace_stmtdesc_t *sdp)
284{
285	dt_stmt_t *stp = dt_alloc(dtp, sizeof (dt_stmt_t));
286
287	if (stp == NULL)
288		return (-1); /* errno is set for us */
289
290	dt_list_append(&pgp->dp_stmts, stp);
291	stp->ds_desc = sdp;
292
293	return (0);
294}
295
296int
297dtrace_stmt_iter(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
298    dtrace_stmt_f *func, void *data)
299{
300	dt_stmt_t *stp, *next;
301	int status = 0;
302
303	for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) {
304		next = dt_list_next(stp);
305		if ((status = func(dtp, pgp, stp->ds_desc, data)) != 0)
306			break;
307	}
308
309	return (status);
310}
311
312void
313dtrace_stmt_destroy(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp)
314{
315	dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc;
316
317	/*
318	 * We need to remove any actions that we have on this ECB, and
319	 * remove our hold on the ECB itself.
320	 */
321	if (sdp->dtsd_action != NULL) {
322		dtrace_actdesc_t *last = sdp->dtsd_action_last;
323		dtrace_actdesc_t *ap, *next;
324
325		assert(last != NULL);
326
327		for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) {
328			if (ap == sdp->dtsd_action)
329				break;
330
331			if (ap->dtad_next == sdp->dtsd_action)
332				break;
333		}
334
335		assert(ap != NULL);
336
337		if (ap == edp->dted_action)
338			edp->dted_action = last->dtad_next;
339		else
340			ap->dtad_next = last->dtad_next;
341
342		/*
343		 * We have now removed our action list from its ECB; we can
344		 * safely destroy the list.
345		 */
346		last->dtad_next = NULL;
347
348		for (ap = sdp->dtsd_action; ap != NULL; ap = next) {
349			assert(ap->dtad_uarg == (uintptr_t)sdp);
350			dt_difo_free(dtp, ap->dtad_difo);
351			next = ap->dtad_next;
352			dt_free(dtp, ap);
353		}
354	}
355
356	if (sdp->dtsd_fmtdata != NULL)
357		dt_printf_destroy(sdp->dtsd_fmtdata);
358
359	dt_ecbdesc_release(dtp, sdp->dtsd_ecbdesc);
360	dt_free(dtp, sdp);
361}
362
363typedef struct dt_header_info {
364	dtrace_hdl_t *dthi_dtp;	/* consumer handle */
365	FILE *dthi_out;		/* output file */
366	char *dthi_pmname;	/* provider macro name */
367	char *dthi_pfname;	/* provider function name */
368	int dthi_empty;		/* should we generate empty macros */
369} dt_header_info_t;
370
371static void
372dt_header_fmt_macro(char *buf, const char *str)
373{
374	for (;;) {
375		if (islower(*str)) {
376			*buf++ = *str++ + 'A' - 'a';
377		} else if (*str == '-') {
378			*buf++ = '_';
379			str++;
380		} else if (*str == '.') {
381			*buf++ = '_';
382			str++;
383		} else if ((*buf++ = *str++) == '\0') {
384			break;
385		}
386	}
387}
388
389static void
390dt_header_fmt_func(char *buf, const char *str)
391{
392	for (;;) {
393		if (*str == '-') {
394			*buf++ = '_';
395			*buf++ = '_';
396			str++;
397		} else if ((*buf++ = *str++) == '\0') {
398			break;
399		}
400	}
401}
402
403static bool dt_is_single_rank_pointer_type(ctf_file_t *file, ctf_id_t type) {
404	if (ctf_type_kind(file, type) == CTF_K_POINTER) {
405		ctf_id_t referenced_type = ctf_type_reference(file, type);
406		if (ctf_type_kind(file, referenced_type) != CTF_K_POINTER &&
407		    ctf_type_kind(file, referenced_type) != CTF_K_FUNCTION) {
408			return TRUE;
409		}
410	}
411
412	return FALSE;
413}
414
415/*ARGSUSED*/
416static int
417dt_header_decl(dt_idhash_t *dhp, dt_ident_t *idp, void *data)
418{
419	dt_header_info_t *infop = data;
420	dtrace_hdl_t *dtp = infop->dthi_dtp;
421	dt_probe_t *prp = idp->di_data;
422	dt_node_t *dnp;
423	char buf[DT_TYPE_NAMELEN];
424	char *fname;
425	const char *p;
426	int i;
427
428	p = prp->pr_name;
429	for (i = 0; (p = strchr(p, '-')) != NULL; i++)
430		p++;
431
432	fname = alloca(strlen(prp->pr_name) + 1 + i);
433	dt_header_fmt_func(fname, prp->pr_name);
434
435	char* probe;
436
437	if ((probe = dt_ld_encode_probe(infop->dthi_pfname, fname, prp)) == NULL)
438		return (dt_set_errno(dtp, errno));
439
440	if (fprintf(infop->dthi_out, "extern void %s(", probe) < 0)
441		return (dt_set_errno(dtp, errno));
442
443	for (dnp = prp->pr_nargs, i = 0; dnp != NULL; dnp = dnp->dn_list, i++) {
444		if (fprintf(infop->dthi_out, "%s%s",
445			    dt_is_single_rank_pointer_type(dnp->dn_ctfp, dnp->dn_type) ? "const " : "",
446			    ctf_type_name(dnp->dn_ctfp, dnp->dn_type,
447					  buf, sizeof (buf))) < 0)
448			return (dt_set_errno(dtp, errno));
449
450		if (i + 1 != prp->pr_nargc &&
451		    fprintf(infop->dthi_out, ", ") < 0)
452			return (dt_set_errno(dtp, errno));
453	}
454
455	if (i == 0 && fprintf(infop->dthi_out, "void") < 0)
456		return (dt_set_errno(dtp, errno));
457
458	if (fprintf(infop->dthi_out, ");\n") < 0)
459		return (dt_set_errno(dtp, errno));
460
461	char* isenabled;
462
463	if ((isenabled = dt_ld_encode_isenabled(infop->dthi_pfname, fname)) == NULL)
464		return (dt_set_errno(dtp, errno));
465
466	if (fprintf(infop->dthi_out, "extern int %s(void);\n", isenabled) < 0)
467		return (dt_set_errno(dtp, errno));
468
469	free(isenabled);
470	isenabled = NULL;
471
472	return (0);
473}
474
475/*ARGSUSED*/
476static int
477dt_header_probe(dt_idhash_t *dhp, dt_ident_t *idp, void *data)
478{
479	dt_header_info_t *infop = data;
480	dtrace_hdl_t *dtp = infop->dthi_dtp;
481	dt_probe_t *prp = idp->di_data;
482	char *mname, *fname;
483	const char *p;
484	int i;
485
486	p = prp->pr_name;
487	for (i = 0; (p = strchr(p, '-')) != NULL; i++)
488		p++;
489
490	mname = alloca(strlen(prp->pr_name) + 1);
491	dt_header_fmt_macro(mname, prp->pr_name);
492
493	fname = alloca(strlen(prp->pr_name) + 1 + i);
494	dt_header_fmt_func(fname, prp->pr_name);
495
496	if (fprintf(infop->dthi_out, "#define\t%s_%s(",
497	    infop->dthi_pmname, mname) < 0)
498		return (dt_set_errno(dtp, errno));
499
500	for (i = 0; i < prp->pr_nargc; i++) {
501		if (fprintf(infop->dthi_out, "arg%d", i) < 0)
502			return (dt_set_errno(dtp, errno));
503
504		if (i + 1 != prp->pr_nargc &&
505		    fprintf(infop->dthi_out, ", ") < 0)
506			return (dt_set_errno(dtp, errno));
507	}
508
509	if (fprintf(infop->dthi_out, ") \\\n") < 0)
510		return (dt_set_errno(dtp, errno));
511
512	if (fprintf(infop->dthi_out, "do { \\\n\t") < 0)
513		return (dt_set_errno(dtp, errno));
514
515	if (!infop->dthi_empty) {
516		if (fprintf(infop->dthi_out, "__asm__ volatile(\".reference \" %s_TYPEDEFS); \\\n\t", infop->dthi_pmname) < 0)
517			return (dt_set_errno(dtp, errno));
518
519		char* probe;
520
521		if ((probe = dt_ld_encode_probe(infop->dthi_pfname, fname, prp)) == NULL)
522			return (dt_set_errno(dtp, errno));
523
524		if (fprintf(infop->dthi_out, "%s(", probe) < 0)
525			return (dt_set_errno(dtp, errno));
526
527		free(probe);
528		probe = NULL;
529
530		for (i = 0; i < prp->pr_nargc; i++) {
531			if (fprintf(infop->dthi_out, "arg%d", i) < 0)
532				return (dt_set_errno(dtp, errno));
533
534			if (i + 1 != prp->pr_nargc &&
535			    fprintf(infop->dthi_out, ", ") < 0)
536				return (dt_set_errno(dtp, errno));
537		}
538		if (fprintf(infop->dthi_out, "); \\\n\t") < 0)
539			return (dt_set_errno(dtp, errno));
540
541		if (!infop->dthi_empty) {
542			if (fprintf(infop->dthi_out, "__asm__ volatile(\".reference \" %s_STABILITY); \\\n", infop->dthi_pmname) < 0)
543				return (dt_set_errno(dtp, errno));
544		}
545	}
546
547	if (fprintf(infop->dthi_out, "} while (0)\n") < 0)
548		return (dt_set_errno(dtp, errno));
549
550	if (!infop->dthi_empty) {
551		char* isenabled;
552
553		if (fprintf(infop->dthi_out, "#define\t%s_%s_ENABLED() \\\n",
554		    infop->dthi_pmname, mname) < 0)
555			return (dt_set_errno(dtp, errno));
556
557		if ((isenabled = dt_ld_encode_isenabled(infop->dthi_pfname, fname)) == NULL)
558			return (dt_set_errno(dtp, errno));
559
560		if (fprintf(infop->dthi_out, "\t({ int _r = %s(); \\\n", isenabled) < 0)
561			return (dt_set_errno(dtp, errno));
562
563		if (fprintf(infop->dthi_out, "\t\t__asm__ volatile(\"\"); \\\n") < 0)
564			return (dt_set_errno(dtp, errno));
565
566		if (fprintf(infop->dthi_out, "\t\t_r; })\n") < 0)
567			return (dt_set_errno(dtp, errno));
568
569		free(isenabled);
570		isenabled = NULL;
571	} else {
572		if (fprintf(infop->dthi_out, "#define\t%s_%s_ENABLED() (0)\n",
573		    infop->dthi_pmname, mname) < 0)
574			return (dt_set_errno(dtp, errno));
575	}
576
577	return (0);
578}
579
580static int
581dt_header_provider(dtrace_hdl_t *dtp, dt_provider_t *pvp, FILE *out)
582{
583	dt_header_info_t info;
584	const char *p;
585	int i;
586
587	if (pvp->pv_flags & DT_PROVIDER_IMPL)
588		return (0);
589
590	/*
591	 * Count the instances of the '-' character since we'll need to double
592	 * those up.
593	 */
594	p = pvp->pv_desc.dtvd_name;
595	for (i = 0; (p = strchr(p, '-')) != NULL; i++)
596		p++;
597
598	info.dthi_dtp = dtp;
599	info.dthi_out = out;
600	info.dthi_empty = 0;
601
602	info.dthi_pmname = alloca(strlen(pvp->pv_desc.dtvd_name) + 1);
603	dt_header_fmt_macro(info.dthi_pmname, pvp->pv_desc.dtvd_name);
604
605	info.dthi_pfname = alloca(strlen(pvp->pv_desc.dtvd_name) + 1 + i);
606	dt_header_fmt_func(info.dthi_pfname, pvp->pv_desc.dtvd_name);
607
608	char* stability;
609
610	if ((stability = dt_ld_encode_stability(info.dthi_pfname, pvp)) == NULL)
611		return (dt_set_errno(dtp, errno));
612
613	if (fprintf(out, "#define %s_STABILITY \"%s\"\n\n", info.dthi_pmname, stability) < 0)
614		return (dt_set_errno(dtp, errno));
615
616	free(stability);
617	stability = NULL;
618
619	char* typedefs;
620
621	if ((typedefs = dt_ld_encode_typedefs(info.dthi_pfname, pvp)) == NULL)
622		return (dt_set_errno(dtp, errno));
623
624	if (fprintf(out, "#define %s_TYPEDEFS \"%s\"\n\n", info.dthi_pmname, typedefs) < 0)
625		return (dt_set_errno(dtp, errno));
626
627	free(typedefs);
628	typedefs = NULL;
629
630	if (fprintf(out, "#if !defined(DTRACE_PROBES_DISABLED) || !DTRACE_PROBES_DISABLED\n\n") < 0)
631		return (dt_set_errno(dtp, errno));
632
633	if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0)
634		return (-1); /* dt_errno is set for us */
635	if (fprintf(out, "\n\n") < 0)
636		return (dt_set_errno(dtp, errno));
637	if (dt_idhash_iter(pvp->pv_probes, dt_header_decl, &info) != 0)
638		return (-1); /* dt_errno is set for us */
639
640	if (fprintf(out, "\n#else\n\n") < 0)
641		return (dt_set_errno(dtp, errno));
642
643	info.dthi_empty = 1;
644
645	if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0)
646		return (-1); /* dt_errno is set for us */
647
648	if (fprintf(out, "\n#endif /* !defined(DTRACE_PROBES_DISABLED) || !DTRACE_PROBES_DISABLED */\n\n") < 0)
649		return (dt_set_errno(dtp, errno));
650
651	return (0);
652}
653
654int
655dtrace_program_header(dtrace_hdl_t *dtp, FILE *out, const char *fname)
656{
657	dt_provider_t *pvp;
658	char *mfname, *p;
659
660	if (fname != NULL) {
661		if ((p = strrchr(fname, '/')) != NULL)
662			fname = p + 1;
663
664		mfname = alloca(strlen(fname) + 1);
665		dt_header_fmt_macro(mfname, fname);
666		if (fprintf(out, "#ifndef\t_%s\n#define\t_%s\n\n",
667		    mfname, mfname) < 0)
668			return (dt_set_errno(dtp, errno));
669	}
670
671	if (fprintf(out, "#include <unistd.h>\n\n") < 0)
672		return (-1);
673
674	if (fprintf(out, "#ifdef\t__cplusplus\nextern \"C\" {\n#endif\n\n") < 0)
675		return (-1);
676
677	for (pvp = dt_list_next(&dtp->dt_provlist);
678	    pvp != NULL; pvp = dt_list_next(pvp)) {
679		if (dt_header_provider(dtp, pvp, out) != 0)
680			return (-1); /* dt_errno is set for us */
681	}
682
683	if (fprintf(out, "\n#ifdef\t__cplusplus\n}\n#endif\n") < 0)
684		return (dt_set_errno(dtp, errno));
685
686	if (fname != NULL && fprintf(out, "\n#endif\t/* _%s */\n", mfname) < 0)
687		return (dt_set_errno(dtp, errno));
688
689	return (0);
690}
691