xref: /titanic_52/usr/src/lib/abi/apptrace/common/abienv.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
30*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
31*7c478bd9Sstevel@tonic-gate #include <unistd.h>
32*7c478bd9Sstevel@tonic-gate #include <string.h>
33*7c478bd9Sstevel@tonic-gate #include <stdio.h>
34*7c478bd9Sstevel@tonic-gate #include <dlfcn.h>
35*7c478bd9Sstevel@tonic-gate #include <errno.h>
36*7c478bd9Sstevel@tonic-gate #include <fnmatch.h>
37*7c478bd9Sstevel@tonic-gate #include <apptrace.h>
38*7c478bd9Sstevel@tonic-gate #include <libintl.h>
39*7c478bd9Sstevel@tonic-gate #include "abienv.h"
40*7c478bd9Sstevel@tonic-gate 
41*7c478bd9Sstevel@tonic-gate static char const *strdup_sym = "strdup";
42*7c478bd9Sstevel@tonic-gate static char const *malloc_sym = "malloc";
43*7c478bd9Sstevel@tonic-gate static char const *comma = ",";
44*7c478bd9Sstevel@tonic-gate 
45*7c478bd9Sstevel@tonic-gate static void
46*7c478bd9Sstevel@tonic-gate bugout(char const *call)
47*7c478bd9Sstevel@tonic-gate {
48*7c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr,
49*7c478bd9Sstevel@tonic-gate 			dgettext(TEXT_DOMAIN, "apptrace: %s failed\n"),
50*7c478bd9Sstevel@tonic-gate 			call);
51*7c478bd9Sstevel@tonic-gate 	exit(EXIT_FAILURE);
52*7c478bd9Sstevel@tonic-gate }
53*7c478bd9Sstevel@tonic-gate 
54*7c478bd9Sstevel@tonic-gate void
55*7c478bd9Sstevel@tonic-gate build_env_list(Liblist **list, char const *env)
56*7c478bd9Sstevel@tonic-gate {
57*7c478bd9Sstevel@tonic-gate 	char *envstr;
58*7c478bd9Sstevel@tonic-gate 	char *tok;
59*7c478bd9Sstevel@tonic-gate 
60*7c478bd9Sstevel@tonic-gate 	if ((envstr = getenv(env)) == NULL)
61*7c478bd9Sstevel@tonic-gate 		return;
62*7c478bd9Sstevel@tonic-gate 
63*7c478bd9Sstevel@tonic-gate 	if ((envstr = strdup(envstr)) == NULL)
64*7c478bd9Sstevel@tonic-gate 		bugout(strdup_sym);
65*7c478bd9Sstevel@tonic-gate 
66*7c478bd9Sstevel@tonic-gate 	tok = strtok(envstr, comma);
67*7c478bd9Sstevel@tonic-gate 	while (tok != NULL) {
68*7c478bd9Sstevel@tonic-gate 		Liblist *lp;
69*7c478bd9Sstevel@tonic-gate 
70*7c478bd9Sstevel@tonic-gate 		if ((lp = malloc(sizeof (Liblist))) == NULL)
71*7c478bd9Sstevel@tonic-gate 			bugout(malloc_sym);
72*7c478bd9Sstevel@tonic-gate 
73*7c478bd9Sstevel@tonic-gate 		lp->l_libname = tok;
74*7c478bd9Sstevel@tonic-gate 		lp->l_next = *list;
75*7c478bd9Sstevel@tonic-gate 		*list = lp;
76*7c478bd9Sstevel@tonic-gate 		tok = strtok(NULL, comma);
77*7c478bd9Sstevel@tonic-gate 	}
78*7c478bd9Sstevel@tonic-gate }
79*7c478bd9Sstevel@tonic-gate 
80*7c478bd9Sstevel@tonic-gate void
81*7c478bd9Sstevel@tonic-gate build_env_list1(Liblist **list, Liblist **listend, const char *env)
82*7c478bd9Sstevel@tonic-gate {
83*7c478bd9Sstevel@tonic-gate 	char *envstr;
84*7c478bd9Sstevel@tonic-gate 	char *tok;
85*7c478bd9Sstevel@tonic-gate 
86*7c478bd9Sstevel@tonic-gate 	if ((envstr = getenv(env)) == NULL)
87*7c478bd9Sstevel@tonic-gate 		return;
88*7c478bd9Sstevel@tonic-gate 
89*7c478bd9Sstevel@tonic-gate 	/*
90*7c478bd9Sstevel@tonic-gate 	 * It is possible that we have a single file name,
91*7c478bd9Sstevel@tonic-gate 	 * in which case the subseqent loop will do nothing
92*7c478bd9Sstevel@tonic-gate 	 */
93*7c478bd9Sstevel@tonic-gate 	if (strchr(envstr, ',') == NULL) {
94*7c478bd9Sstevel@tonic-gate 		appendlist(list, listend, envstr, 1);
95*7c478bd9Sstevel@tonic-gate 		return;
96*7c478bd9Sstevel@tonic-gate 	}
97*7c478bd9Sstevel@tonic-gate 
98*7c478bd9Sstevel@tonic-gate 	if ((envstr = strdup(envstr)) == NULL)
99*7c478bd9Sstevel@tonic-gate 		bugout(strdup_sym);
100*7c478bd9Sstevel@tonic-gate 
101*7c478bd9Sstevel@tonic-gate 	tok = strtok(envstr, comma);
102*7c478bd9Sstevel@tonic-gate 	while (tok != NULL) {
103*7c478bd9Sstevel@tonic-gate 		appendlist(list, listend, tok, 1);
104*7c478bd9Sstevel@tonic-gate 		tok = strtok(NULL, comma);
105*7c478bd9Sstevel@tonic-gate 	}
106*7c478bd9Sstevel@tonic-gate 	free(envstr);
107*7c478bd9Sstevel@tonic-gate }
108*7c478bd9Sstevel@tonic-gate 
109*7c478bd9Sstevel@tonic-gate void
110*7c478bd9Sstevel@tonic-gate env_to_intlist(Intlist **list, char const *env)
111*7c478bd9Sstevel@tonic-gate {
112*7c478bd9Sstevel@tonic-gate 	char *envstr;
113*7c478bd9Sstevel@tonic-gate 	char *tok;
114*7c478bd9Sstevel@tonic-gate 
115*7c478bd9Sstevel@tonic-gate 	if ((envstr = getenv(env)) == NULL)
116*7c478bd9Sstevel@tonic-gate 		return;
117*7c478bd9Sstevel@tonic-gate 
118*7c478bd9Sstevel@tonic-gate 	if ((envstr = strdup(envstr)) == NULL)
119*7c478bd9Sstevel@tonic-gate 		bugout(strdup_sym);
120*7c478bd9Sstevel@tonic-gate 
121*7c478bd9Sstevel@tonic-gate 	for (tok = strtok(envstr, comma);
122*7c478bd9Sstevel@tonic-gate 	    tok != NULL;
123*7c478bd9Sstevel@tonic-gate 	    tok = strtok(NULL, comma)) {
124*7c478bd9Sstevel@tonic-gate 
125*7c478bd9Sstevel@tonic-gate 		Intlist *ip;
126*7c478bd9Sstevel@tonic-gate 
127*7c478bd9Sstevel@tonic-gate 		if ((ip = malloc(sizeof (Intlist))) == NULL)
128*7c478bd9Sstevel@tonic-gate 			bugout(malloc_sym);
129*7c478bd9Sstevel@tonic-gate 
130*7c478bd9Sstevel@tonic-gate 		if ((ip->i_name = strdup(tok)) == NULL)
131*7c478bd9Sstevel@tonic-gate 			bugout(strdup_sym);
132*7c478bd9Sstevel@tonic-gate 
133*7c478bd9Sstevel@tonic-gate 		ip->i_next = *list;
134*7c478bd9Sstevel@tonic-gate 		*list = ip;
135*7c478bd9Sstevel@tonic-gate 	}
136*7c478bd9Sstevel@tonic-gate 	free(envstr);
137*7c478bd9Sstevel@tonic-gate }
138*7c478bd9Sstevel@tonic-gate 
139*7c478bd9Sstevel@tonic-gate void
140*7c478bd9Sstevel@tonic-gate appendlist(Liblist **list, Liblist **listend, const char *name, int fatal)
141*7c478bd9Sstevel@tonic-gate {
142*7c478bd9Sstevel@tonic-gate 	Liblist	*lp;
143*7c478bd9Sstevel@tonic-gate 	void	*handle;
144*7c478bd9Sstevel@tonic-gate 
145*7c478bd9Sstevel@tonic-gate 	if (access(name, R_OK)) {
146*7c478bd9Sstevel@tonic-gate 		if (fatal) {
147*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
148*7c478bd9Sstevel@tonic-gate 					dgettext(TEXT_DOMAIN,
149*7c478bd9Sstevel@tonic-gate 						"apptrace: %s: %s\n"),
150*7c478bd9Sstevel@tonic-gate 					name,
151*7c478bd9Sstevel@tonic-gate 					strerror(errno));
152*7c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
153*7c478bd9Sstevel@tonic-gate 		}
154*7c478bd9Sstevel@tonic-gate 		return;
155*7c478bd9Sstevel@tonic-gate 	}
156*7c478bd9Sstevel@tonic-gate 
157*7c478bd9Sstevel@tonic-gate 	if ((handle = dlopen(name, RTLD_LAZY)) == NULL) {
158*7c478bd9Sstevel@tonic-gate 		if (fatal) {
159*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
160*7c478bd9Sstevel@tonic-gate 					dgettext(TEXT_DOMAIN,
161*7c478bd9Sstevel@tonic-gate 					"apptrace: dlopen on %s failed: %s\n"),
162*7c478bd9Sstevel@tonic-gate 					name,
163*7c478bd9Sstevel@tonic-gate 					dlerror());
164*7c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
165*7c478bd9Sstevel@tonic-gate 		}
166*7c478bd9Sstevel@tonic-gate 		return;
167*7c478bd9Sstevel@tonic-gate 	}
168*7c478bd9Sstevel@tonic-gate 
169*7c478bd9Sstevel@tonic-gate 	/* OK, so now add it to the end of the list */
170*7c478bd9Sstevel@tonic-gate 	if ((lp = malloc(sizeof (Liblist))) == NULL)
171*7c478bd9Sstevel@tonic-gate 		bugout(malloc_sym);
172*7c478bd9Sstevel@tonic-gate 
173*7c478bd9Sstevel@tonic-gate 	if ((lp->l_libname = strdup(name)) == NULL)
174*7c478bd9Sstevel@tonic-gate 		bugout(strdup_sym);
175*7c478bd9Sstevel@tonic-gate 	lp->l_handle = handle;
176*7c478bd9Sstevel@tonic-gate 	lp->l_next = NULL;
177*7c478bd9Sstevel@tonic-gate 	if (*listend)
178*7c478bd9Sstevel@tonic-gate 		(*listend)->l_next = lp;
179*7c478bd9Sstevel@tonic-gate 	if (*list == NULL)
180*7c478bd9Sstevel@tonic-gate 		*list = lp;
181*7c478bd9Sstevel@tonic-gate 	*listend = lp;
182*7c478bd9Sstevel@tonic-gate }
183*7c478bd9Sstevel@tonic-gate 
184*7c478bd9Sstevel@tonic-gate /*
185*7c478bd9Sstevel@tonic-gate  * Called abibasename() to avoid clash with basename(3C)
186*7c478bd9Sstevel@tonic-gate  * Incidentally, basename(3C) is destructive which is why
187*7c478bd9Sstevel@tonic-gate  * we are not using it instead.
188*7c478bd9Sstevel@tonic-gate  */
189*7c478bd9Sstevel@tonic-gate char *
190*7c478bd9Sstevel@tonic-gate abibasename(const char *str)
191*7c478bd9Sstevel@tonic-gate {
192*7c478bd9Sstevel@tonic-gate 	char *p;
193*7c478bd9Sstevel@tonic-gate 
194*7c478bd9Sstevel@tonic-gate 	if ((p = strrchr(str, '/')) != NULL)
195*7c478bd9Sstevel@tonic-gate 		return (p + 1);
196*7c478bd9Sstevel@tonic-gate 	else
197*7c478bd9Sstevel@tonic-gate 		return ((char *)str);
198*7c478bd9Sstevel@tonic-gate }
199*7c478bd9Sstevel@tonic-gate 
200*7c478bd9Sstevel@tonic-gate Liblist *
201*7c478bd9Sstevel@tonic-gate check_list(Liblist *list, char const *str)
202*7c478bd9Sstevel@tonic-gate {
203*7c478bd9Sstevel@tonic-gate 	char *basename1, *basename2, *p1, *p2;
204*7c478bd9Sstevel@tonic-gate 	Liblist *ret = NULL;
205*7c478bd9Sstevel@tonic-gate 
206*7c478bd9Sstevel@tonic-gate 	if (list == NULL)
207*7c478bd9Sstevel@tonic-gate 		return (NULL);
208*7c478bd9Sstevel@tonic-gate 
209*7c478bd9Sstevel@tonic-gate 	if ((basename2 = strdup(abibasename(str))) == NULL)
210*7c478bd9Sstevel@tonic-gate 		bugout(strdup_sym);
211*7c478bd9Sstevel@tonic-gate 	if ((p2 = strchr(basename2, '.')) != NULL)
212*7c478bd9Sstevel@tonic-gate 		*p2 = '\0';
213*7c478bd9Sstevel@tonic-gate 
214*7c478bd9Sstevel@tonic-gate 	for (; list; list = list->l_next) {
215*7c478bd9Sstevel@tonic-gate 		/* Lose the dirname */
216*7c478bd9Sstevel@tonic-gate 		if ((basename1 = strdup(abibasename(list->l_libname))) == NULL)
217*7c478bd9Sstevel@tonic-gate 			bugout(strdup_sym);
218*7c478bd9Sstevel@tonic-gate 		/* Lose the suffix */
219*7c478bd9Sstevel@tonic-gate 		if ((p1 = strchr(basename1, '.')) != NULL)
220*7c478bd9Sstevel@tonic-gate 			*p1 = '\0';
221*7c478bd9Sstevel@tonic-gate 		if (fnmatch(basename1, basename2, 0) == 0) {
222*7c478bd9Sstevel@tonic-gate 			ret = list;
223*7c478bd9Sstevel@tonic-gate 			free(basename1);
224*7c478bd9Sstevel@tonic-gate 			break;
225*7c478bd9Sstevel@tonic-gate 		}
226*7c478bd9Sstevel@tonic-gate 		free(basename1);
227*7c478bd9Sstevel@tonic-gate 	}
228*7c478bd9Sstevel@tonic-gate 
229*7c478bd9Sstevel@tonic-gate 	free(basename2);
230*7c478bd9Sstevel@tonic-gate 	return (ret);
231*7c478bd9Sstevel@tonic-gate }
232*7c478bd9Sstevel@tonic-gate 
233*7c478bd9Sstevel@tonic-gate int
234*7c478bd9Sstevel@tonic-gate check_intlist(Intlist *list, char const *iface)
235*7c478bd9Sstevel@tonic-gate {
236*7c478bd9Sstevel@tonic-gate 	if (list == NULL)
237*7c478bd9Sstevel@tonic-gate 		return (0);
238*7c478bd9Sstevel@tonic-gate 
239*7c478bd9Sstevel@tonic-gate 	for (; list != NULL; list = list->i_next) {
240*7c478bd9Sstevel@tonic-gate 		if (fnmatch(list->i_name, iface, 0) == 0)
241*7c478bd9Sstevel@tonic-gate 			return (1);
242*7c478bd9Sstevel@tonic-gate 	}
243*7c478bd9Sstevel@tonic-gate 
244*7c478bd9Sstevel@tonic-gate 	return (0);
245*7c478bd9Sstevel@tonic-gate }
246*7c478bd9Sstevel@tonic-gate 
247*7c478bd9Sstevel@tonic-gate char *
248*7c478bd9Sstevel@tonic-gate checkenv(char const *env)
249*7c478bd9Sstevel@tonic-gate {
250*7c478bd9Sstevel@tonic-gate 	char *envstr;
251*7c478bd9Sstevel@tonic-gate 
252*7c478bd9Sstevel@tonic-gate 	if ((envstr = getenv(env)) == NULL)
253*7c478bd9Sstevel@tonic-gate 		return (NULL);
254*7c478bd9Sstevel@tonic-gate 	while (*envstr == ' ')
255*7c478bd9Sstevel@tonic-gate 		envstr++;
256*7c478bd9Sstevel@tonic-gate 	if (*envstr == '\0')
257*7c478bd9Sstevel@tonic-gate 		return (NULL);
258*7c478bd9Sstevel@tonic-gate 	return (envstr);
259*7c478bd9Sstevel@tonic-gate }
260*7c478bd9Sstevel@tonic-gate 
261*7c478bd9Sstevel@tonic-gate int
262*7c478bd9Sstevel@tonic-gate build_interceptor_path(char *buf, size_t l, char const *path)
263*7c478bd9Sstevel@tonic-gate {
264*7c478bd9Sstevel@tonic-gate 	char *p, *t, *f;
265*7c478bd9Sstevel@tonic-gate #if defined(_LP64)
266*7c478bd9Sstevel@tonic-gate 	char *m;
267*7c478bd9Sstevel@tonic-gate #endif
268*7c478bd9Sstevel@tonic-gate 	int ret;
269*7c478bd9Sstevel@tonic-gate 
270*7c478bd9Sstevel@tonic-gate 	/* Duplicate the path */
271*7c478bd9Sstevel@tonic-gate 	if ((p = strdup(path)) == NULL)
272*7c478bd9Sstevel@tonic-gate 		bugout(strdup_sym);
273*7c478bd9Sstevel@tonic-gate 
274*7c478bd9Sstevel@tonic-gate 	/* Find the last slash, if there ain't one bug out */
275*7c478bd9Sstevel@tonic-gate 	if ((t = strrchr(p, '/')) == NULL) {
276*7c478bd9Sstevel@tonic-gate 		ret = 0;
277*7c478bd9Sstevel@tonic-gate 		goto done;
278*7c478bd9Sstevel@tonic-gate 	}
279*7c478bd9Sstevel@tonic-gate 
280*7c478bd9Sstevel@tonic-gate 	/*
281*7c478bd9Sstevel@tonic-gate 	 * Wack the slash to a null byte.
282*7c478bd9Sstevel@tonic-gate 	 * Thus if we got:
283*7c478bd9Sstevel@tonic-gate 	 * 	/A/B/C/D.so.1
284*7c478bd9Sstevel@tonic-gate 	 * p now points to /A/B/C
285*7c478bd9Sstevel@tonic-gate 	 * f is set to point to D.so.1
286*7c478bd9Sstevel@tonic-gate 	 */
287*7c478bd9Sstevel@tonic-gate 	*t = '\0';
288*7c478bd9Sstevel@tonic-gate 	f = ++t;
289*7c478bd9Sstevel@tonic-gate 
290*7c478bd9Sstevel@tonic-gate #if defined(_LP64)
291*7c478bd9Sstevel@tonic-gate 	/*
292*7c478bd9Sstevel@tonic-gate 	 * As above except that in LP64 (for sparc) we'll get:
293*7c478bd9Sstevel@tonic-gate 	 *	/A/B/C/sparcv9/D.so.1
294*7c478bd9Sstevel@tonic-gate 	 * thus p now points to:
295*7c478bd9Sstevel@tonic-gate 	 *	/A/B/C/sparcv9
296*7c478bd9Sstevel@tonic-gate 	 * so we repeat the wack so that we get:
297*7c478bd9Sstevel@tonic-gate 	 *	/A/B/C
298*7c478bd9Sstevel@tonic-gate 	 * and retain a pointer, m, to the machine dependent portion.
299*7c478bd9Sstevel@tonic-gate 	 */
300*7c478bd9Sstevel@tonic-gate 	if ((t = strrchr(p, '/')) == NULL) {
301*7c478bd9Sstevel@tonic-gate 		ret = 0;
302*7c478bd9Sstevel@tonic-gate 		goto done;
303*7c478bd9Sstevel@tonic-gate 	}
304*7c478bd9Sstevel@tonic-gate 	*t = '\0';
305*7c478bd9Sstevel@tonic-gate 	m = ++t;
306*7c478bd9Sstevel@tonic-gate 
307*7c478bd9Sstevel@tonic-gate 	/*
308*7c478bd9Sstevel@tonic-gate 	 * Now we can build a path name.
309*7c478bd9Sstevel@tonic-gate 	 * This path is only a guess that'll be checked later in appendlist().
310*7c478bd9Sstevel@tonic-gate 	 * Some system libraries, like libc.so.1, reside in /lib while their
311*7c478bd9Sstevel@tonic-gate 	 * corresponding abi_* counterparts reside in /usr/lib.  The same is
312*7c478bd9Sstevel@tonic-gate 	 * true for libraries like libc_psr.so.1 that reside in /platform
313*7c478bd9Sstevel@tonic-gate 	 * rather than /usr/platform.  To deal with this, we check whether
314*7c478bd9Sstevel@tonic-gate 	 * the file in the direct path name we generate exists, and if not,
315*7c478bd9Sstevel@tonic-gate 	 * we prepend "/usr" to it.  This handles all existing cases.
316*7c478bd9Sstevel@tonic-gate 	 */
317*7c478bd9Sstevel@tonic-gate 	ret = snprintf(buf, l, "%s/abi/%s/abi_%s", p, m, f);
318*7c478bd9Sstevel@tonic-gate 	if (access(buf, R_OK) != 0 && strncmp(buf, "/usr/", 5) != 0)
319*7c478bd9Sstevel@tonic-gate 		ret = snprintf(buf, l, "/usr%s/abi/%s/abi_%s", p, m, f);
320*7c478bd9Sstevel@tonic-gate #else
321*7c478bd9Sstevel@tonic-gate 	ret = snprintf(buf, l, "%s/abi/abi_%s", p, f);
322*7c478bd9Sstevel@tonic-gate 	if (access(buf, R_OK) != 0 && strncmp(buf, "/usr/", 5) != 0)
323*7c478bd9Sstevel@tonic-gate 		ret = snprintf(buf, l, "/usr%s/abi/abi_%s", p, f);
324*7c478bd9Sstevel@tonic-gate #endif
325*7c478bd9Sstevel@tonic-gate 
326*7c478bd9Sstevel@tonic-gate done:
327*7c478bd9Sstevel@tonic-gate 	free(p);
328*7c478bd9Sstevel@tonic-gate 	return (ret);
329*7c478bd9Sstevel@tonic-gate }
330