xref: /illumos-gate/usr/src/cmd/sgs/libelf/misc/demangle.c (revision e2f4f3da)
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) 1988 AT&T
24  *	  All Rights Reserved
25  *
26  *
27  *	Copyright (c) 1998 by Sun Microsystems, Inc.
28  *	All rights reserved.
29  */
30 
31 #include <ctype.h>
32 #include <setjmp.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <thread.h>
36 #include "elf_dem.h"
37 #include "String.h"
38 #include "msg.h"
39 
40 /*
41  * The variable "hold" contains the pointer to the array initially
42  * handed to demangle.  It is returned if it is not possible to
43  * demangle the string.  NULL is returned if a memory allocation
44  * problem is encountered.  Thus one can do the following:
45  *
46  * char *mn = "Some mangled name";
47  * char *dm = mangle(mn);
48  * if (dm == NULL)
49  *	printf("allocation error\n");
50  * else if (dm == mn)
51  * 	printf("name could not be demangled\n");
52  * else
53  *	printf("demangled name is: %s\n",dm);
54  */
55 static char *hold;
56 
57 /*
58  * this String is the working buffer for the demangle
59  * routine.  A pointer into this String is returned
60  * from demangle when it is possible to demangle the
61  * String.  For this reason, the pointer should not
62  * be saved between calls of demangle(), nor freed.
63  */
64 static String *s = 0;
65 
66 static int
67 getint(char **c)
68 {
69 	return (strtol(*c, c, 10));
70 }
71 
72 /*
73  * If a mangled name has a __
74  * that is not at the very beginning
75  * of the string, then this routine
76  * is called to demangle that part
77  * of the name.  All overloaded functions,
78  * and class members fall into this category.
79  *
80  * c should start with two underscores followed by a non-zero digit or an F.
81  */
82 static char *
83 second(char *c)
84 {
85 	int n;
86 	if (strncmp(c, MSG_ORIG(MSG_STR_DBLUNDBAR), 2))
87 		return (hold);
88 	c += 2;
89 
90 	if (!(isdigit(*c) || *c == 'F'))
91 		return (hold);
92 
93 	if (isdigit(*c)) {
94 		/* a member */
95 		n = getint(&c);
96 		if (n == 0 || (int)strlen(c) < n)
97 			return (hold);
98 		s = prep_String(MSG_ORIG(MSG_STR_DBLCOL), s);
99 		s = nprep_String(c, s, n);
100 		c += n;
101 	}
102 	if (*c == 'F') {
103 		/* an overloaded function */
104 		switch (*++c) {
105 		case '\0':
106 			return (hold);
107 		case 'v':
108 			s = app_String(s, MSG_ORIG(MSG_STR_OPENCLOSEPAR));
109 			break;
110 		default:
111 			if (demangle_doargs(&s, c) < 0)
112 				return (hold);
113 		}
114 	}
115 	return (PTR(s));
116 }
117 
118 char *
119 demangle(char *c)
120 {
121 	volatile int i = 0;
122 	extern jmp_buf jbuf;
123 	static mutex_t	mlock = DEFAULTMUTEX;
124 
125 	(void) mutex_lock(&mlock);
126 
127 	if (setjmp(jbuf)) {
128 		(void) mutex_unlock(&mlock);
129 		return (0);
130 	}
131 
132 	hold = c;
133 	s = mk_String(s);
134 	s = set_String(s, MSG_ORIG(MSG_STR_EMPTY));
135 
136 	if (c == 0 || *c == 0) {
137 		c = hold;
138 		(void) mutex_unlock(&mlock);
139 		return (c);
140 	}
141 
142 	if (strncmp(c, MSG_ORIG(MSG_STR_DBLUNDBAR), 2) != 0) {
143 		/*
144 		 * If a name does not begin with a __
145 		 * but it does contain one, it is either
146 		 * a member or an overloaded function.
147 		 */
148 		while (c[i] && strncmp(c+i, MSG_ORIG(MSG_STR_DBLUNDBAR), 2))
149 			i++;
150 		if (c[i]) {
151 			/* Advance to first non-underscore */
152 			while (c[i+2] == '_')
153 				i++;
154 		}
155 		if (strncmp(c+i, MSG_ORIG(MSG_STR_DBLUNDBAR), 2) == 0) {
156 			/* Copy the simple name */
157 			s = napp_String(s, c, i);
158 			/* Process the signature */
159 			c = second(c+i);
160 			(void) mutex_unlock(&mlock);
161 			return (c);
162 		} else {
163 			c = hold;
164 			(void) mutex_unlock(&mlock);
165 			return (c);
166 		}
167 	} else {
168 		const char	*x;
169 		int		oplen;
170 
171 		c += 2;
172 
173 		/*
174 		 * For automatic variables, or internal static
175 		 * variables, a __(number) is prepended to the
176 		 * name.  If this is encountered, strip this off
177 		 * and return.
178 		 */
179 		if (isdigit(*c)) {
180 			while (isdigit(*c))
181 				c++;
182 			(void) mutex_unlock(&mlock);
183 			return (c);
184 		}
185 
186 		/*
187 		 * Handle operator functions -- this
188 		 * automatically calls second, since
189 		 * all operator functions are overloaded.
190 		 */
191 		if (x = findop(c, &oplen)) {
192 			s = app_String(s, MSG_ORIG(MSG_STR_OPERATOR_1));
193 			s = app_String(s, x);
194 			c += oplen;
195 			c = second(c);
196 			(void) mutex_unlock(&mlock);
197 			return (c);
198 		}
199 
200 		/*
201 		 * Operator cast does not fit the mould
202 		 * of the other operators.  Its type name
203 		 * is encoded.  The cast function must
204 		 * take a void as an argument.
205 		 */
206 		if (strncmp(c, MSG_ORIG(MSG_STR_OP), 2) == 0) {
207 			int r;
208 			s = app_String(s, MSG_ORIG(MSG_STR_OPERATOR_2));
209 			c += 2;
210 			r = demangle_doarg(&s, c);
211 			if (r < 0) {
212 				c = hold;
213 				(void) mutex_unlock(&mlock);
214 				return (c);
215 			}
216 			c += r;
217 			c = second(c);
218 			(void) mutex_unlock(&mlock);
219 			return (c);
220 		}
221 
222 		/*
223 		 * Constructors and Destructors are also
224 		 * a special case of operator name.  Note
225 		 * that the destructor, while overloaded,
226 		 * must always take the same arguments --
227 		 * none.
228 		 */
229 		if ((*c == 'c' || *c == 'd') &&
230 		    strncmp(c+1, MSG_ORIG(MSG_STR_TDBLUNDBAR), 3) == 0) {
231 			int n;
232 			char *c2 = c+2;
233 			char cx = c[0];
234 			c += 4;
235 			n = getint(&c);
236 			if (n == 0) {
237 				c = hold;
238 				(void) mutex_unlock(&mlock);
239 				return (c);
240 			}
241 			s = napp_String(s, c, n);
242 			if (cx == 'd')
243 				s = prep_String(MSG_ORIG(MSG_STR_TILDE), s);
244 			c = second(c2);
245 			(void) mutex_unlock(&mlock);
246 			return (c);
247 		}
248 		c = hold;
249 		(void) mutex_unlock(&mlock);
250 		return (c);
251 	}
252 }
253