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