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 (c) 1997-2000 by Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * interceptor.c -- a functional decomposition of generate.c,
31 *	the code generator for apptrace
32 */
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <unistd.h>
38#include <sys/types.h>
39#include "parser.h"
40#include "trace.h"
41#include "util.h"
42#include "db.h"
43#include "symtab.h"
44#include "io.h"
45#include "bindings.h"
46#include "printfuncs.h"
47#include "errlog.h"
48#include "parseproto.h"
49
50static void generate_i_declarations(char *, int, char *);
51static void generate_i_preamble(ENTRY *);
52static void generate_i_call();
53static int  generate_i_bindings(int);
54static void generate_i_postamble(ENTRY *, int, char *, char *);
55static void generate_i_evaluations(ENTRY *);
56static void generate_i_prints(ENTRY *, char *, char *);
57static void generate_i_closedown(char *, int);
58static void generate_i_live_vars(ENTRY *);
59static void generate_return_printf(int);
60static char *variables_get_errorname(void);
61
62/*
63 * generate_interceptor -- make code for an individual interceptor, written
64 *	as an output grammar
65 */
66void
67generate_interceptor(ENTRY *function)
68{
69	char	*prototype = symtab_get_prototype(),
70		*library_name = db_get_current_library(),
71		*function_name,
72		*error_name;
73	int	void_func;
74
75	errlog(BEGIN, "generate_interceptor() {");
76
77	/* Check for required information. */
78	if (validity_of(function) == NO) {
79		symtab_set_skip(YES);
80		errlog(WARNING|INPUT, "No prototype for interface, "
81			"it will be skipped");
82		errlog(END, "}");
83		return;
84	}
85
86	/* Collect things we'll use more than once. */
87	function_name = name_of(function);
88
89	error_name = variables_get_errorname();
90
91	void_func = is_void(function);
92
93	/*
94	 * Emit "artificial" prototype here so that if there's a
95	 * disagreement between it and the prototype contained in the
96	 * declaring header, the compiler will flag it.
97	 * First #undef the function to make sure the prototype in the header
98	 * is exposed and to avoid breaking the artificial prototype if it's
99	 * not.
100	 */
101	{
102		decl_t *dp;
103		char *buf;
104		char const *err;
105		size_t s;
106
107		s = strlen(prototype) + 2;
108		buf = malloc(s);
109		if (buf == NULL)
110			abort();
111		(void) strcpy(buf, prototype);
112		buf[s - 2] = ';';
113		buf[s - 1] = '\0';
114
115		err = decl_Parse(buf, &dp);
116		if (err != NULL)
117			errlog(FATAL, "\"%s\", line %d: %s: %s",
118			    symtab_get_filename(), line_of(function),
119			    err, prototype);
120
121		/* generate the mapfile entry */
122		(void) fprintf(Mapfp, "\t__abi_%s;\n", decl_GetName(dp));
123
124		(void) decl_ToString(buf, DTS_DECL, dp, function_name);
125		(void) fprintf(Bodyfp, "#line %d \"%s\"\n",
126		    line_of(function), symtab_get_filename());
127		(void) fprintf(Bodyfp, "#undef %s\n", function_name);
128		(void) fprintf(Bodyfp, "extern %s;\n", buf);
129
130		(void) fprintf(Bodyfp, "static %s\n{\n", prototype);
131
132		(void) decl_ToString(buf, DTS_RET, dp, "_return");
133		generate_i_declarations(error_name, void_func, buf);
134		decl_Destroy(dp);
135		free(buf);
136	}
137
138	generate_i_preamble(function);
139	generate_i_call(function, void_func, library_name, error_name);
140	generate_i_postamble(function, void_func, error_name, library_name);
141
142	errlog(END, "}");
143}
144
145/*
146 * print_function_signature -- print the line defining the function, without
147 *      an ``extern'' prefix or either a ``;'' or ''{'' suffix.
148 */
149void
150print_function_signature(char *xtype, char *name, char *formals)
151{
152	char	buffer[MAXLINE];
153
154	(void) snprintf(buffer, sizeof (buffer), "%s", name);
155	(void) fprintf(Bodyfp,  xtype, buffer);
156	if (strstr(xtype, "(*") == NULL) {
157		(void) fprintf(Bodyfp,  "(%s)", formals);
158	}
159}
160
161
162/*
163 * generate_i_declarations -- generate the declarations which
164 *      are local to the interceptor function itself.
165 */
166static void
167generate_i_declarations(char *errname, int voidfunc, char *ret_str)
168{
169
170	errlog(BEGIN, "generate_i_declarations() {");
171	if (*errname != NULL) {
172		/* Create locals for errno-type variable, */
173		(void) fprintf(Bodyfp,
174		    "    int saved_errvar = %s;\n", errname);
175		(void) fprintf(Bodyfp,  "    int functions_errvar;\n");
176	}
177
178	if (need_exception_binding()) {
179		/* Create a local for that. */
180		(void) fprintf(Bodyfp,  "    int exception = 0;\n");
181	}
182	if (! voidfunc) {
183		/* Create a return value. */
184		(void) fprintf(Bodyfp,  "    %s;\n", ret_str);
185	}
186	(void) fprintf(Bodyfp, "    sigset_t omask;\n");
187	(void) putc('\n', Bodyfp);
188	errlog(END, "}");
189}
190
191
192/*
193 * generate_i_preamble -- do the actions which must occur
194 *      before the call.
195 */
196static void
197generate_i_preamble(ENTRY *function)
198{
199	errlog(BEGIN, "generate_i_preamble() {");
200	generate_i_live_vars(function); /* Deferred. */
201
202	if (symtab_get_nonreturn() == YES) {
203		/* Make things safe for printing */
204		(void) fprintf(Bodyfp,
205		    "    abilock(&omask);\n");
206		/* Print all the args in terse format. */
207		generate_printf(function);
208		(void) fputs("    putc('\\n', ABISTREAM);\n\n", Bodyfp);
209		/* unlock stdio */
210		(void) fprintf(Bodyfp,
211		    "    abiunlock(&omask);\n");
212	}
213
214	errlog(END, "}");
215}
216
217/*
218 * generate_i_call -- implement the save/call/restore cycle
219 */
220static void
221generate_i_call(
222	ENTRY	*function,
223	int	void_func,
224	char	*library_name,
225	char	*error_name)
226{
227	char	*function_name = name_of(function),
228		*function_cast = symtab_get_cast(),
229		*actual_args = symtab_get_actuals();
230
231	errlog(BEGIN, "generate_i_call() {");
232	/* Zero the error variable. */
233	if (*error_name != NULL) {
234		(void) fprintf(Bodyfp,  "    %s = 0;\n", error_name);
235	}
236
237	/* Then print the call itself. */
238	if (void_func) {
239		(void) fprintf(Bodyfp,
240		    "    (void) ABI_CALL_REAL(%s, %s, %s)(%s);\n",
241		    library_name, function_name, function_cast, actual_args);
242	} else {
243		(void) fprintf(Bodyfp,
244		    "    _return = ABI_CALL_REAL(%s, %s, %s)(%s);\n",
245		    library_name, function_name, function_cast, actual_args);
246	}
247
248	/* Then set the local copy of the error variable. */
249	if (*error_name != NULL) {
250		(void) fprintf(Bodyfp,
251		    "    functions_errvar = %s;\n", error_name);
252	}
253	(void) putc('\n', Bodyfp);
254
255	/* Make things safe for printing */
256	(void) fprintf(Bodyfp,
257	    "    abilock(&omask);\n");
258
259	errlog(END, "}");
260}
261
262/*
263 * generate_i_postamble -- do all the things which come
264 *      after the call.  In the case of apptrace, this is most of the work.
265 */
266static void
267generate_i_postamble(ENTRY *function, int void_func,
268    char *error_name, char *library_name)
269{
270	errlog(BEGIN, "generate_i_postamble() {");
271	if (symtab_get_nonreturn() == NO) {
272		/* Print all the args in terse format. */
273		generate_printf(function);
274	}
275
276	/* If it isn't supposed to return, and actually ends up here, */
277	/* we'd better be prepared to print all sorts of diagnostic stuff */
278	(void) putc('\n', Bodyfp);
279	if (generate_i_bindings(void_func) == YES) {
280		generate_return_printf(void_func);
281	}
282
283	generate_i_prints(function, library_name, name_of(function));
284	generate_i_evaluations(function); /* Deferred */
285	generate_i_closedown(error_name, void_func);
286	errlog(END, "}");
287}
288
289/*
290 * generate_i_bindings -- see about success and failure, so we can decide
291 *      what to do next.
292 */
293static int
294generate_i_bindings(int void_func)
295{
296	ENTRY   *e;
297	char *exception;
298
299	exception  = ((e = symtab_get_exception()) != NULL)?
300	    (name_of(e)? name_of(e): ""): "";
301
302	errlog(BEGIN, "generate_i_bindings() {");
303	if (void_func && bindings_exist()) {
304		/* To become a warning, as there are spec errors! TBD */
305		errlog(FATAL, "exception bindings found in a "
306			"void function");
307	} else if (void_func || need_bindings(exception) == NO) {
308		(void) fprintf(Bodyfp,
309		    "    (void) putc('\\n', ABISTREAM);\n");
310		(void) putc('\n', Bodyfp);
311		errlog(END, "}");
312		return (NO);
313	} else {
314		/*
315		 * Then there is a return value, so we try to
316		 * generate exception bindings
317		 * and code to print errno on exception.
318		 */
319		if ((generate_bindings(exception)) != ANTONYMS) {
320			/* Generate code to cross-evaluate them. */
321			(void) fprintf(Bodyfp,
322			    "    if (!exception) {\n");
323			errlog(END, "}");
324			return (YES);
325		}
326	}
327
328	/* should not get here */
329	errlog(END, "}");
330	return (NO);
331}
332
333/*
334 * generate_return_printf -- print the return value and end the line
335 */
336static void
337generate_return_printf(int void_func)
338{
339	errlog(BEGIN, "generate_return_printf() {");
340	if (void_func) {
341		(void) fprintf(Bodyfp,  "    putc('\\n', ABISTREAM);\n");
342		errlog(END, "}");
343		return;
344	}
345	/* If its a non-void function there are bindings. */
346	(void) fprintf(Bodyfp,
347	    "\t/* Just end the line */\n"
348	    "\tputc('\\n', ABISTREAM);\n"
349	    "    }\n"
350	    "    else {\n"
351	    "        fprintf(ABISTREAM, \"%%s%%d (%%s)\\n\", errnostr, "
352	    "functions_errvar, strerror((int)functions_errvar));\n"
353	    "    }\n\n");
354	errlog(END, "}");
355}
356
357/*
358 * generate_i_prints -- if we're doing the verbose stuff,
359 *      generate verbose printouts of the variables.
360 */
361static void
362generate_i_prints(ENTRY *function, char *lib, char *func)
363{
364	ENTRY   *e;
365
366	errlog(BEGIN, "generate_i_prints() {");
367	if ((e = symtab_get_first_arg()) != NULL || !is_void(e)) {
368		/* Then we have to generate code for verbose reports. */
369		(void) fprintf(Bodyfp,  "    if (ABI_VFLAG(%s, %s) != 0) {\n",
370			lib, func);
371		generate_printfunc_calls(function);
372		(void) fprintf(Bodyfp,  "    }\n");
373	}
374	(void) putc('\n', Bodyfp);
375	errlog(END, "}");
376}
377
378/*
379 * generate_i_closedown -- restore error variables and return.
380 */
381static void
382generate_i_closedown(char *error_name, int void_func)
383{
384	errlog(BEGIN, "generate_i_closedown() {");
385
386	/* unlock stdio */
387	(void) fprintf(Bodyfp,
388	    "    abiunlock(&omask);\n");
389
390	if (*error_name != NULL) {
391		/* Restore error variables. */
392		(void) fprintf(Bodyfp,
393		    "    %s = (functions_errvar == 0)? "
394		    "            saved_errvar: functions_errvar;\n",
395		    error_name);
396	}
397
398	/* And return. */
399	(void) fprintf(Bodyfp,
400	    "    return%s;\n",
401	    (void_func)? "": " _return");
402	(void) fprintf(Bodyfp,  "}\n");
403	(void) putc('\n', Bodyfp);
404	errlog(END, "}");
405}
406
407
408/*
409 * generate_i_live_vars -- generate temps for any ``out''
410 *	or ``inout'' variables in the function.  Deferred.
411 */
412/*ARGSUSED*/
413static void
414generate_i_live_vars(ENTRY *function)
415{
416	errlog(BEGIN, "generate_i_live_vars() {");
417	errlog(END, "}");
418}
419
420/*
421 * generate_i_evaluations -- generate evaluations for
422 *	all the expressions. Deferred.
423 */
424/*ARGSUSED*/
425static void
426generate_i_evaluations(ENTRY *function)
427{
428	errlog(BEGIN, "generate_i_evaluations() {");
429	errlog(END, "}");
430}
431
432
433static char *
434variables_get_errorname(void)
435{
436	return ("ABI_ERRNO");
437}
438