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 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
29  */
30 
31 /*
32  * Used to dump structures and unions in forth mode.
33  *
34  * structures and unions are a bit more complicated than enums.  To make things
35  * just that much more interesting, we have to dump the members in reverse
36  * order, which is nice.  But wait!  It gets better!  For compatibility reasons,
37  * we need to dump the members in reverse-offset order, even if member-specific
38  * mode was used to request the members in something other than that order.
39  *
40  * The header op prints the macro header and saves the type being printed.
41  *
42  * In member-specific mode, the member op will be invoked for each structure
43  * or union member.  The member op adds the member name, format, type ID,
44  * and offset to a list, sorted in reverse order by offset.
45  *
46  * The trailer op is called when the structure or enum is complete.  If no
47  * members were specifically requested, then the trailer iterates through all
48  * of the members of the structure, pretending they were.  Each member is thus
49  * added, in reverse-offset order, to the list used in specific-member mode.
50  * Either way, we then proceed through the list, dumping each member out with
51  * fth_print_member.  Structure and union members are printed out differently,
52  * depending on member type, as follows:
53  *
54  *  Integer:
55  *	Normal integers: ' <format> <offset> <type>-field <name>
56  *	  <format> defaults to ".d" for enums, ".x" for others
57  *	  <offset> is the member offset, in bytes.
58  *	  <type> is "byte", "short", "long", or "ext" for 8-, 16-, 32-, and
59  *	    64-bit integers, respectively.
60  *	  <name> is the name of the member being printed
61  *
62  *	Bitfields:	 ' <format> <shift> <mask> <offset> bits-field <name>
63  *	  <format> defaults to ".x"
64  *	  <shift> is the number of times to right-shift the masked value
65  *	  <mask> use to extract the bit-field value from the read value
66  *	  <offset> is the member offset, in bytes
67  *	  <name> is the name of the member being printed
68  *
69  *  Float:		Ignored
70  *
71  *  Pointer:		 ' <format> <offset> ptr-field <name>
72  *	  <format> defaults to .x
73  *	  <offset> is in bytes
74  *	  <name> is the name of the member being printed
75  *
76  *  Array:
77  *	Arrays have a content-type-specific prefix, followed by an array
78  *	suffix.  The resulting line looks like this if the array contents
79  *	type is an integer, a pointer, or an enum:
80  *
81  *			 ' <fldc> ' <fmt> <sz> <elsz> <off> array-field <name>
82  *
83  *	The following is printed for array contents that are arrays:
84  *
85  *			 ' noop ' .x <sz> <elsz> <off> array-field <name>
86  *
87  *	The following is printed for array contents that are structs:
88  *
89  *			 ' noop ' <fmt> <sz> <elsz> <off> array-field <name>
90  *
91  *	  <fldc> is "c@", "w@", "l@", or "x@", depending on whether array
92  *	    elements are 8, 16, 32 or 64 bits wide.
93  *	  <fmt> defaults to ".x"
94  *	  <sz> is the size of the array, in bytes
95  *	  <elsz> is the size of the array elements
96  *	  <off> is the member offset, in bytes
97  *	  <name> is the nam eof the member being printed
98  *
99  *  Struct/Union:	 ' <format> <offset> struct-field <name>
100  *	  <format> defaults to ".x"
101  *	  <offset> is the member offset, in bytes
102  *	  <name> is the name of the member being printed
103  */
104 
105 #include <stdio.h>
106 #include <stdlib.h>
107 #include <string.h>
108 #include <sys/list.h>
109 
110 #include "ctf_headers.h"
111 #include "forth.h"
112 #include "memory.h"
113 
114 static ctf_id_t	fth_str_curtid;
115 static list_t	fth_str_curmems;
116 
117 /*
118  * Node type for the member-storage list (fth_str_curmems) built by
119  * fth_struct_members()
120  */
121 typedef struct fth_str_mem {
122 	list_node_t	fsm_node;
123 	char		*fsm_memname;
124 	char		*fsm_format;
125 	ctf_id_t	fsm_tid;
126 	ulong_t		fsm_off;
127 } fth_str_mem_t;
128 
129 typedef struct fth_struct_members_data {
130 	char		*fsmd_strname;
131 	char		*fsmd_memfilter;
132 	char		*fsmd_format;
133 	int		fsmd_matched;
134 } fth_struct_members_data_t;
135 
136 static int fth_print_member(fth_str_mem_t *, int);
137 
138 /* Comparison routined used to insert members into the fth_str_curmems list */
139 static int
fth_struct_memcmp(void * m1,void * m2)140 fth_struct_memcmp(void *m1, void *m2)
141 {
142 	fth_str_mem_t *mem1 = m1, *mem2 = m2;
143 
144 	if (mem1->fsm_off < mem2->fsm_off)
145 		return (1);
146 	else if (mem1->fsm_off > mem2->fsm_off)
147 		return (-1);
148 	else
149 		return (0);
150 }
151 
152 static void
fth_slist_add(fth_str_mem_t * mem)153 fth_slist_add(fth_str_mem_t *mem)
154 {
155 	fth_str_mem_t *l;
156 
157 	for (l = list_head(&fth_str_curmems); l != NULL;
158 	    l = list_next(&fth_str_curmems, l)) {
159 		if (fth_struct_memcmp(l, mem) > 0) {
160 			list_insert_before(&fth_str_curmems, l, mem);
161 			return;
162 		}
163 	}
164 	list_insert_tail(&fth_str_curmems, mem);
165 }
166 
167 static void
fth_free_str_mem(fth_str_mem_t * mem)168 fth_free_str_mem(fth_str_mem_t *mem)
169 {
170 	free(mem->fsm_memname);
171 	if (mem->fsm_format)
172 		free(mem->fsm_format);
173 	free(mem);
174 }
175 
176 static int
fth_struct_header(ctf_id_t tid)177 fth_struct_header(ctf_id_t tid)
178 {
179 	ssize_t sz;
180 
181 	fth_str_curtid = tid;
182 	list_create(&fth_str_curmems, sizeof (fth_str_mem_t),
183 	    offsetof(fth_str_mem_t, fsm_node));
184 
185 	if ((sz = ctf_type_size(ctf, fth_str_curtid)) == CTF_ERR)
186 		return (parse_warn("Can't get size for %s", fth_curtype));
187 
188 	(void) fprintf(out, "\n");
189 	(void) fprintf(out, "vocabulary %s-words\n", fth_curtype);
190 	(void) fprintf(out, "h# %x constant %s-sz\n", sz, fth_curtype);
191 	(void) fprintf(out, "%x ' %s-words c-struct .%s\n", sz, fth_curtype,
192 	    fth_curtype);
193 	(void) fprintf(out, "also %s-words definitions\n\n", fth_curtype);
194 
195 	return (0);
196 }
197 
198 /* Print the array prefix for integer and pointer members */
199 static int
fth_print_level(uint_t bits,char * format)200 fth_print_level(uint_t bits, char *format)
201 {
202 	if ((bits & (bits - 1)) != 0 ||(bits % 8) != 0 || bits > 64) {
203 		return (parse_warn("Unexpected bit size %d in %s",
204 		    bits, fth_curtype));
205 	}
206 
207 	(void) fprintf(out, "' %c@ ' %s", " cw l   x"[bits / 8], format);
208 
209 	return (0);
210 }
211 
212 /*
213  * Return the format to be used to print the member.  If one of the builtin
214  * formats "d" or "x" were specified, return ".d" or ".x", respectively.
215  * Otherwise, use the user-provided format as is, or use the default if none
216  * was provided.
217  */
218 static char *
fth_convert_format(char * format,char * def)219 fth_convert_format(char *format, char *def)
220 {
221 	static char dot[3] = ".";
222 
223 	if (format == NULL)
224 		return (def);
225 	else if (strlen(format) == 1) {
226 		dot[1] = *format;
227 		return (dot);
228 	} else
229 		return (format);
230 }
231 
232 static int
fth_print_integer(const char * memname,ulong_t off,uint_t bits,char * format,int level)233 fth_print_integer(const char *memname, ulong_t off, uint_t bits, char *format,
234     int level)
235 {
236 	format = fth_convert_format(format, ".x");
237 
238 	if (bits > 64) {
239 		return (parse_warn("%s.%s is too large (>8 bytes)",
240 		    fth_curtype, memname));
241 	}
242 
243 	if (level != 0)
244 		return (fth_print_level(bits, format));
245 
246 	if ((bits % NBBY) != 0 || (bits & (bits - 1)) != 0) {
247 		/* bit field */
248 		uint_t offset, shift, mask;
249 
250 		offset = (off / 32) * 4;
251 		shift = 32 - ((off % 32) + bits);
252 		mask = ((1 << bits) - 1) << shift;
253 
254 		(void) fprintf(out, "' %s %x %x %x bits-field %s\n",
255 		    format, shift, mask, offset, memname);
256 
257 	} else {
258 		char *type[] = {
259 			NULL, "byte", "short", NULL, "long",
260 			NULL, NULL, NULL, "ext"
261 		};
262 
263 		(void) fprintf(out, "' %s %lx %s-field %s\n", format, off / 8,
264 		    type[bits / 8], memname);
265 	}
266 
267 	return (0);
268 }
269 
270 static int
fth_print_pointer(const char * memname,ulong_t off,uint_t bits,char * format,int level)271 fth_print_pointer(const char *memname, ulong_t off, uint_t bits, char *format,
272     int level)
273 {
274 	format = fth_convert_format(format, ".x");
275 
276 	if (level != 0)
277 		return (fth_print_level(bits, format));
278 
279 	(void) fprintf(out, "' %s %lx ptr-field %s\n", format, off / 8,
280 	    memname);
281 
282 	return (0);
283 }
284 
285 static int
fth_print_struct(char * memname,ulong_t off,char * format,int level)286 fth_print_struct(char *memname, ulong_t off, char *format,
287     int level)
288 {
289 	format = fth_convert_format(format, ".x");
290 
291 	if (level != 0)
292 		(void) fprintf(out, "' noop ' %s", format);
293 	else {
294 		(void) fprintf(out, "' %s %lx struct-field %s\n", format,
295 		    off / 8, memname);
296 	}
297 
298 	return (0);
299 }
300 
301 static int
fth_print_enum(char * memname,ulong_t off,char * format,int level)302 fth_print_enum(char *memname, ulong_t off, char *format,
303     int level)
304 {
305 	format = fth_convert_format(format, ".d");
306 
307 	if (level != 0)
308 		(void) fprintf(out, "' l@ ' %s", format);
309 	else {
310 		(void) fprintf(out, "' %s %lx long-field %s\n", format, off / 8,
311 		    memname);
312 	}
313 
314 	return (0);
315 }
316 
317 static int
fth_print_array(char * memname,ctf_id_t tid,ulong_t off,ssize_t sz,char * format,int level)318 fth_print_array(char *memname, ctf_id_t tid, ulong_t off, ssize_t sz,
319     char *format, int level)
320 {
321 	if (level != 0)
322 		(void) fprintf(out, "' noop ' .x");
323 	else {
324 		fth_str_mem_t mem;
325 		ctf_arinfo_t ar;
326 
327 		/*
328 		 * print the prefix for the array contents type, then print
329 		 * the array macro
330 		 */
331 
332 		if (ctf_array_info(ctf, tid, &ar) == CTF_ERR) {
333 			return (parse_warn("Can't read array in %s.%s",
334 			    fth_curtype, memname));
335 		}
336 
337 		mem.fsm_memname = memname;
338 		mem.fsm_format = format;
339 		mem.fsm_tid = ar.ctr_contents;
340 		mem.fsm_off = off;
341 
342 		if (fth_print_member(&mem, level + 1) < 0)
343 			return (-1);
344 
345 		(void) fprintf(out, " %x %x %lx array-field %s\n", sz,
346 		    (sz / ar.ctr_nelems), off / 8, memname);
347 	}
348 
349 	return (0);
350 }
351 
352 /* dump a structure or union member */
353 static int
fth_print_member(fth_str_mem_t * mem,int level)354 fth_print_member(fth_str_mem_t *mem, int level)
355 {
356 	ctf_encoding_t e;
357 	ctf_id_t tid;
358 	int kind;
359 	ssize_t sz;
360 
361 	if ((tid = ctf_type_resolve(ctf, mem->fsm_tid)) == CTF_ERR) {
362 		return (parse_warn("Can't resolve %s.%s", fth_curtype,
363 		    mem->fsm_memname));
364 	}
365 
366 	if ((kind = ctf_type_kind(ctf, tid)) == CTF_ERR) {
367 		return (parse_warn("Can't get kind for %s.%s",
368 		    fth_curtype, mem->fsm_memname));
369 	}
370 
371 	if ((sz = ctf_type_size(ctf, tid)) == CTF_ERR) {
372 		return (parse_warn("Can't get size for %s.%s",
373 		    fth_curtype, mem->fsm_memname));
374 	}
375 
376 	switch (kind) {
377 	case CTF_K_INTEGER:
378 		if (ctf_type_encoding(ctf, tid, &e) == CTF_ERR)
379 			return (parse_warn("Can't get encoding for %ld", tid));
380 
381 		return (fth_print_integer(mem->fsm_memname, mem->fsm_off,
382 		    e.cte_bits, mem->fsm_format, level));
383 
384 	case CTF_K_FLOAT:
385 		(void) parse_warn("Ignoring floating point member %s.%s",
386 		    fth_curtype, mem->fsm_memname);
387 		return (0);
388 
389 	case CTF_K_POINTER:
390 		return (fth_print_pointer(mem->fsm_memname, mem->fsm_off,
391 		    sz * 8, mem->fsm_format, level));
392 
393 	case CTF_K_ARRAY:
394 		return (fth_print_array(mem->fsm_memname, tid, mem->fsm_off, sz,
395 		    mem->fsm_format, level));
396 
397 	case CTF_K_STRUCT:
398 	case CTF_K_UNION:
399 		return (fth_print_struct(mem->fsm_memname, mem->fsm_off,
400 		    mem->fsm_format, level));
401 
402 	case CTF_K_ENUM:
403 		return (fth_print_enum(mem->fsm_memname, mem->fsm_off,
404 		    mem->fsm_format, level));
405 
406 	case CTF_K_FORWARD:
407 		return (parse_warn("Type %ld in %s.%s is undefined", tid,
408 		    fth_curtype, mem->fsm_memname));
409 
410 	default:
411 		return (parse_warn("Unexpected kind %d for %s.%s", kind,
412 		    fth_curtype, mem->fsm_memname));
413 	}
414 }
415 
416 /*
417  * Add a member to list of members to be printed (fth_str_curmems).  If
418  * fsmd_memfilter is non-null, only add this member if its name matches that
419  * in the filter.
420  */
421 static int
fth_struct_members_cb(const char * memname,ctf_id_t tid,ulong_t off,void * arg)422 fth_struct_members_cb(const char *memname, ctf_id_t tid, ulong_t off, void *arg)
423 {
424 	fth_struct_members_data_t *fsmd = arg;
425 	fth_str_mem_t *mem;
426 
427 	if (fsmd->fsmd_memfilter != NULL && strcmp(fsmd->fsmd_memfilter,
428 	    memname) != 0)
429 		return (0);
430 
431 	fsmd->fsmd_matched = 1;
432 
433 	mem = xcalloc(sizeof (fth_str_mem_t));
434 	mem->fsm_memname = xstrdup(memname);
435 	if (fsmd->fsmd_format)
436 		mem->fsm_format = xstrdup(fsmd->fsmd_format);
437 	mem->fsm_tid = tid;
438 	mem->fsm_off = off;
439 
440 	fth_slist_add(mem);
441 
442 	return (0);
443 }
444 
445 /*
446  * If memfilter is non-null, iterate through the members of this type, causing
447  * every member to be added to the list.  Otherwise, use the iterator and
448  * the callback to add only the specified member.
449  */
450 static int
fth_struct_members(char * memfilter,char * format)451 fth_struct_members(char *memfilter, char *format)
452 {
453 	fth_struct_members_data_t fsmd;
454 
455 	fsmd.fsmd_strname = fth_curtype;
456 	fsmd.fsmd_memfilter = memfilter;
457 	fsmd.fsmd_format = format;
458 	fsmd.fsmd_matched = 0;
459 
460 	if (ctf_member_iter(ctf, fth_str_curtid, fth_struct_members_cb,
461 	    &fsmd) != 0)
462 		return (-1);
463 
464 	if (memfilter != NULL && fsmd.fsmd_matched == 0) {
465 		return (parse_warn("Invalid member %s.%s", fth_curtype,
466 		    memfilter));
467 	}
468 
469 	return (0);
470 }
471 
472 static int
fth_struct_trailer(void)473 fth_struct_trailer(void)
474 {
475 	fth_str_mem_t *mem;
476 
477 	if (list_is_empty(&fth_str_curmems)) {
478 		if (fth_struct_members(NULL, NULL) < 0)
479 			return (-1);
480 	}
481 
482 	while ((mem = list_remove_head(&fth_str_curmems)) != NULL) {
483 		if (fth_print_member(mem, 0) < 0)
484 			return (-1);
485 
486 		fth_free_str_mem(mem);
487 	}
488 	list_destroy(&fth_str_curmems);
489 
490 	(void) fprintf(out, "\n");
491 	(void) fprintf(out, "kdbg-words definitions\n");
492 	(void) fprintf(out, "previous\n");
493 	(void) fprintf(out, "\n");
494 	(void) fprintf(out, "\\ end %s section\n", fth_curtype);
495 	(void) fprintf(out, "\n");
496 
497 	return (0);
498 }
499 
500 fth_type_ops_t fth_struct_ops = {
501 	fth_struct_header,
502 	fth_struct_members,
503 	fth_struct_trailer
504 };
505