# Hammerhead: amd64-only - build directly, no arch subdirectories # This file combines the parent Makefile with amd64/Makefile functionality include ./Makefile.com # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # .KEEP_STATE: PROG = ctfstabs SRCS = \ ctfstabs.c \ forth.c \ fth_enum.c \ fth_struct.c \ genassym.c \ memory.c \ utils.c include ../Makefile.ctf # Hammerhead: Use illumos libctf from tools proto area, not GNU binutils libctf LDLIBS += -L$(ROOTONBLDLIBMACH) -lctf NATIVE_LIBS += libctf.so OBJS = $(SRCS:%.c=%.o) list.o CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-unused .NOTPARALLEL: .PARALLEL: $(OBJS) all: $(PROG) $(PROG): $(OBJS) $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) # Hammerhead: stabs-specific sources in common/ %.o: common/%.c $(COMPILE.c) $< # Shared CTF sources from ctf/common/ %.o: ../common/%.c $(COMPILE.c) $< %.o: $(SRC)/common/list/%.c $(COMPILE.c) $< $(ROOTONBLDMACHPROG): $(PROG) install: $(ROOTONBLDMACHPROG) clean: $(RM) $(OBJS) $(LINTFILES) include ../Makefile.ctf.targ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * This program has two modes. * * In the first, or genassym, mode, it generates a header file containing * #define'd values for offsets and other information about requested * structures and arrays. This header file can then be used by assembly * source files to access those structures without having to hard-code the * offsets. The offsets and values in the header file are derived from the * CTF data in a provided object file. * * The second mode creates forthdebug macros for specified structures and * members from an object file. The macros are created using the CTF data in * the object file. * * Forthdebug macros and offsets header files are generated using the same * tool for historical reasons. * * The input and output files, and their interaction with the tool are * shown below: * * --------------- ----------- cc -c -g ------------------ * |#includes | -----> |#includes| ------------> |object file with| * |mode-specific| ----------- ctfconvert | CTF data | * | directives| ------------------ * --------------- | * | | obj_file * | V * | ------------ ---------- * \-------------> |directives| ---------------> |ctfstabs| * ------------ input_template ---------- * | * V * --------------- * Mode-specific input and output formats are |mode-specific| * described in forth.c and genassym.c | output | * --------------- */ #include #include #include #include #include #include #include #include #include #include #include #include "ctf_headers.h" #include "utils.h" #include "memory.h" #include "ctfstabs.h" #define WORD_LEN 256 static int lineno; FILE *out; ctf_file_t *ctf; static void usage(void) { (void) fprintf(stderr, "Usage: %s -t genassym [-m model] " "[-i input_template] [-o output] obj_file\n", getpname()); (void) fprintf(stderr, " %s -t forth [-m model] " "[-i input_template] [-o output] obj_file\n", getpname()); exit(2); } /*PRINTFLIKE1*/ int parse_warn(char *format, ...) { va_list alist; (void) fprintf(stderr, "%s: Line %d: ", getpname(), lineno); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); (void) fprintf(stderr, "\n"); return (-1); } #define READLINE_BUF_INCR 2 /* * Read a line of input into a statically-allocated buffer. If the line * is larger than the buffer, the buffer will be dynamically resized. * Subsequent calls will overwrite the buffer. */ static char * readline(FILE *fp) { static char *buf, *bptr; static int buflen; if (buflen == 0) { buf = xmalloc(READLINE_BUF_INCR); buflen = READLINE_BUF_INCR; } bptr = buf; for (;;) { size_t len, off; if (fgets(bptr, buflen - (size_t)(bptr - buf), fp) == NULL) return (NULL); len = strlen(bptr); if (bptr[len - 1] == '\n') return (buf); off = (size_t)((bptr + len) - buf); buflen += READLINE_BUF_INCR; buf = xrealloc(buf, buflen); bptr = buf + off; } } /* * We're only given a type name. Even if it's a struct or a union, we * still only get the struct or union name. We therefore iterate through * the possible prefixes, trying to find the right type. */ ctf_id_t find_type(char *name) { char fullname[WORD_LEN]; ctf_id_t id; if ((id = ctf_lookup_by_name(ctf, name)) != CTF_ERR) return (id); (void) snprintf(fullname, WORD_LEN, "struct %s", name); if ((id = ctf_lookup_by_name(ctf, fullname)) != CTF_ERR) return (id); (void) snprintf(fullname, WORD_LEN, "union %s", name); if ((id = ctf_lookup_by_name(ctf, fullname)) != CTF_ERR) return (id); (void) snprintf(fullname, WORD_LEN, "enum %s", name); if ((id = ctf_lookup_by_name(ctf, fullname)) != CTF_ERR) return (id); return (CTF_ERR); } static int process_ifile(FILE *tmpl, proc_ops_t *ops) { char *line; int skipping; size_t len; int err = 0; for (lineno = skipping = 0; (line = readline(tmpl)) != NULL; lineno++) { len = strlen(line) - 1; line[len] = '\0'; if (len == 0) skipping = 0; if (skipping == 1) continue; if (ops->po_line(line) < 0) { (void) parse_warn("Error found: skipping to the next " "blank line"); err++; skipping = 1; continue; } } return (err > 0 ? -1 : 0); } static char * get_model(ctf_file_t *ctf) { ssize_t lsz; ctf_id_t lid; /* Neither of these should fail */ if ((lid = ctf_lookup_by_name(ctf, "long")) == CTF_ERR || (lsz = ctf_type_size(ctf, lid)) == CTF_ERR) die("Couldn't get size of long in object file"); if (lsz == 8) return ("lp64"); else if (lsz == 4) return ("ilp32"); else die("Unexpected size of long: %d bytes\n", lsz); return (NULL); } int main(int argc, char **argv) { char *model = NULL, *objfile = NULL, *outfile = NULL, *tmplfile = NULL; proc_ops_t *ops = &ga_ops; FILE *tmpl; int ctferr, c; while ((c = getopt(argc, argv, "i:m:o:t:")) != EOF) { switch (c) { case 'i': tmplfile = optarg; break; case 'm': model = optarg; break; case 't': if (strcmp(optarg, "genassym") == 0) ops = &ga_ops; else if (strcmp(optarg, "forth") == 0) ops = &fth_ops; else usage(); break; case 'o': outfile = optarg; break; default: usage(); } } if (argc - optind != 1) usage(); objfile = argv[optind]; if (tmplfile == NULL || strcmp(tmplfile, "-") == 0) tmpl = stdin; else if ((tmpl = fopen(tmplfile, "r")) == NULL) die("Couldn't open template file %s", tmplfile); /* * this can fail if ENOENT or if there's no CTF data in the file. */ if ((ctf = ctf_open(objfile, &ctferr)) == NULL) { die("Couldn't open object file %s: %s\n", objfile, ctf_errmsg(ctferr)); } if (model == NULL) model = get_model(ctf); else if (strcmp(model, get_model(ctf)) != 0) die("Model argument %s doesn't match the object file\n", model); if (outfile == NULL || strcmp(outfile, "-") == 0) out = stdout; else if ((out = fopen(outfile, "w")) == NULL) die("Couldn't open output file %s for writing", outfile); if ((ops->po_init != NULL && ops->po_init(model) < 0) || (process_ifile(tmpl, ops) < 0) || (ops->po_fini != NULL && ops->po_fini() < 0)) { (void) fclose(out); (void) unlink(outfile); return (1); } return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _CTFSTABS_H #define _CTFSTABS_H #include #ifdef __cplusplus extern "C" { #endif #include "ctf_headers.h" /* * The file-reading portion of ctfstabs communicates with the type-specific * backends (genassym and forth) via the proc_ops_t, one of which is supplied * by each backend. */ typedef struct proc_ops { /* * Called prior to reading the input template. A return of -1 signals * an error, and will halt processing. */ int (*po_init)(char *); /* * Called for each line in the input file. If an error is returned, * also signalled by a return of -1, input lines will be skipped, and * this method will not be called, until a blank line is encountered. */ int (*po_line)(char *); /* * Called after all input lines have been processed. */ int (*po_fini)(void); } proc_ops_t; extern proc_ops_t ga_ops; extern proc_ops_t fth_ops; extern FILE *out; /* the output file */ extern ctf_file_t *ctf; /* the input object file */ extern int parse_warn(char *, ...); extern ctf_id_t find_type(char *); #ifdef __cplusplus } #endif #endif /* _CTFSTABS_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * In this mode, we generate forthdebug macros as requested by the input * template, the format of which is given below. * * These templates have the following elements: * * 1. Macro creation * * Given the name of a structure, union, or enum type, a forthdebug macro * is created that will dump the members of the type. The type can be * specified as a standalone structure, union, or enum, or it can be * described relative to another structure or union as "type.member". * * By default, all struct members, union members, or enum values, as * appropriate, will be printed. An alternate form allows specific members * of struct or union types to be printed. Both forms must be followed by a * blank line. In the specific-member case, an optional format specifier can * be provided that will be used to dump the contents of the member. * Builtins `d' and `x' can be used to dump the member in decimal or * hexadecimal, respectively. Alternatively, a custom formatter can be * specified. * * 2. Model-specific sections * * `model_start' / `model_end' pairs function as an #ifdef for the ctfstabs * tool. They take, as an argument, either `ilp32' or `lp64'. If a 64-bit * macro is being generated (if a 64-bit object file is being used), lines * between `lp64' model pairs will be processed, but lines between `ilp32' * pairs will be omitted. The reverse is true for 32-bit macros. * * 3. Literal sections * * Portions of the input template file enclosed within `forth_start' / * `forth_end' pairs and between `verbatim_begin' / `verbatim_end' pairs * will be copied as-is to the output file. * * 4. Comments * * Lines beginning with backslashes are ignored. * * Example: * * \ dump the `foo' structure * foo * * \ dump the `a' and `b' members of the `bar' structure. dump member `b' * \ in hexadecimal * bar * a * b x * * \ dump the `big' member of the `baz' structure in 64-bit macros, and * \ the `small' member in 32-bit macros. * baz * model_start lp64 * big * model_end * model_start ilp32 * small * model_end * * \ copy `literal 1' and `literal 2' to the output file * verbatim_begin * literal 1 * verbatim_end * forth_start * literal 2 * forth_end * * For a more complex example, see common.fdbg. */ #include #include #include #include #include "ctf_headers.h" #include "ctfstabs.h" #include "forth.h" #include "utils.h" #include "memory.h" char *fth_curtype; /* name of the type being processed */ static fth_type_ops_t *fth_type_ops; /* see forth.h */ static char *fth_model; /* the current macro type - for model_start */ static int fth_ignoring; /* in a non-matching model_start/end pair */ static int fth_copying; /* in a verbatim_* or forth_* pair */ static int fth_init(char *model) { fth_model = model; return (0); } /*ARGSUSED*/ static int fth_null_header(ctf_id_t tid) { return (0); } /*ARGSUSED*/ static int fth_null_members(char *memfilter, char *format) { return (0); } static int fth_null_trailer(void) { return (0); } static fth_type_ops_t fth_null_ops = { fth_null_header, fth_null_members, fth_null_trailer }; /*ARGSUSED2*/ static int find_member_cb(const char *memname, ctf_id_t tid, ulong_t off, void *arg) { char *memtofind = arg; if (strcmp(memname, memtofind) == 0) return (tid); return (0); } /* find the tid of a specified member */ static ctf_id_t find_member(ctf_id_t tid, char *memname) { return (ctf_member_iter(ctf, tid, find_member_cb, memname)); } /* * Begin a macro. * * Once we figure out the type of the thing that we're supposed to dump (struct, * union, or enum), we select the proper type-specific ops-vector for dumping. */ static int fth_section_init(char *fullname) { ctf_id_t ltid = 0, tid; char *curtype, *lpart, *part, *npart; int lkind = 0, kind; curtype = xstrdup(fullname); lpart = NULL; part = strtok(fullname, "."); /* * First figure out what sort of type we're looking at. Life would be * simple if we were only going to get type names, but it's not - we * could also get `type.member'. In that case, we need to figure out * (and dump) the type of `member' instead. */ for (;;) { if (lpart == NULL) { /* First part - the struct name */ if ((tid = find_type(part)) == CTF_ERR || (tid = ctf_type_resolve(ctf, tid)) == CTF_ERR || (kind = ctf_type_kind(ctf, tid)) == CTF_ERR) { free(curtype); return (parse_warn("Couldn't find %s: %s", part, ctf_errmsg(ctf_errno(ctf)))); } } else { /* Second (or more) part - the member name */ if (lkind != CTF_K_STRUCT && lkind != CTF_K_UNION) { free(curtype); return (parse_warn("%s isn't a struct/union", lpart)); } if ((tid = find_member(ltid, part)) <= 0) { free(curtype); return (parse_warn("%s isn't a member of %s", part, lpart)); } if ((kind = ctf_type_kind(ctf, tid)) == CTF_ERR) { free(curtype); return (parse_warn("Can't get kind for %s", part)); } } /* * Stop if there aren't any more parts. We use `npart' here * because we don't want to clobber part - we need it later. */ if ((npart = strtok(NULL, ".")) == NULL) break; lpart = part; ltid = tid; lkind = kind; part = npart; } /* * Pick the right ops vector for dumping. */ switch (kind) { case CTF_K_STRUCT: case CTF_K_UNION: fth_type_ops = &fth_struct_ops; break; case CTF_K_ENUM: fth_type_ops = &fth_enum_ops; break; default: fth_type_ops = &fth_null_ops; free(curtype); return (parse_warn("%s isn't a struct, union, or enum", part)); } fth_curtype = curtype; return (fth_type_ops->fto_header(tid)); } static int fth_section_add_member(char *name, char *format) { if (fth_curtype == NULL) return (fth_section_init(name)); if (fth_type_ops->fto_members(name, format) < 0) return (-1); return (0); } static int fth_section_end(void) { if (fth_curtype == NULL) return (0); if (fth_type_ops->fto_trailer() < 0) return (-1); free(fth_curtype); fth_curtype = NULL; return (0); } static int fth_process_line(char *line) { char *format = NULL; char *word, *name, *c; int nblank = 0; int n; if (strlen(line) == 0) { if (fth_section_end() < 0) return (-1); if (fth_copying == 1 || nblank++ == 1) (void) fprintf(out, "\n"); return (0); } else nblank = 0; /* skip comments */ if (line[0] == '\\') return (0); if (strcmp(line, "model_end") == 0) { fth_ignoring = 0; return (0); } if (fth_ignoring == 1) return (0); word = "model_start "; if (strncmp(line, word, strlen(word)) == 0) { for (c = line + strlen(word); isspace(*c); c++); if (strlen(c) == strlen(fth_model) && strncmp(c, fth_model, strlen(fth_model)) == 0) /* EMPTY - match */; else fth_ignoring = 1; return (0); } if (strcmp(line, "verbatim_end") == 0 || strcmp(line, "forth_end") == 0) { char *start = (strcmp(line, "verbatim_end") == 0 ? "verbatim_begin" : "forth_start"); if (fth_copying == 0) { (void) parse_warn("Found %s without matching %s", line, start); if (fth_curtype != NULL) (void) fth_section_end(); return (-1); } fth_copying = 0; return (0); } if (fth_copying == 1) { (void) fprintf(out, "%s\n", line); return (0); } if (strcmp(line, "verbatim_begin") == 0 || strcmp(line, "forth_start") == 0) { if (fth_curtype != NULL) { (void) parse_warn("Expected blank line between %s " "macro and %s", fth_curtype, line); return (fth_section_end()); } fth_copying = 1; return (0); } for (n = 1, word = strtok(line, " \t"); word != NULL; word = strtok(NULL, " \t"), n++) { if (n == 1) name = word; else if (n == 2) format = word; else (void) parse_warn("Too many words"); } return (fth_section_add_member(name, format)); } static int fth_fini(void) { return (fth_section_end()); } proc_ops_t fth_ops = { fth_init, fth_process_line, fth_fini }; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FORTH_H #define _FORTH_H #ifdef __cplusplus extern "C" { #endif #include "ctfstabs.h" /* * The macros emitted for structs/unions and enums differ, but their * formatting in the input file and construction method is roughly the same. * If any of the ops return -1, the remainder of the type being processed is * skipped, and an error is signaled to the main portion of ctfstabs. */ typedef struct fth_type_ops { /* * Called to start a definition. */ int (*fto_header)(ctf_id_t); /* * When a specific-member request for a struct or union type is * encountered, this op will be invoked for each requested member. */ int (*fto_members)(char *, char *); /* * Invoked when the current open definition is to be finished. */ int (*fto_trailer)(void); } fth_type_ops_t; /* forth.c */ extern char *fth_curtype; /* fth_struct.c */ extern fth_type_ops_t fth_enum_ops; extern fth_type_ops_t fth_struct_ops; #ifdef __cplusplus } #endif #endif /* _FORTH_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Used to dump enums in forth mode. * * Enums are simple - there is no member-specific mode for them. We get * the header op at the start, and we save the type id for dumping later. * If we get the members op, we've been invoked in member-specific mode, * which is a syntax error for an enum. When we ge the trailer op, we * dump all of the members and the trailer thus concluding the enum. */ #include "forth.h" static ctf_id_t fth_enum_curtid; static int fth_enum_curnmems; static int fth_enum_header(ctf_id_t tid) { fth_enum_curtid = tid; (void) fprintf(out, "\n"); return (0); } /*ARGSUSED*/ static int fth_enum_members(char *memfilter, char *format) { return (parse_warn("Member-specific mode cannot be used for " " enums")); } /*ARGSUSED2*/ static int fth_enum_cb(const char *name, int value, void *arg) { (void) fprintf(out, "here ,\" %s\" %x\n", name, value); fth_enum_curnmems++; return (0); } static int fth_enum_trailer(void) { if (ctf_enum_iter(ctf, fth_enum_curtid, fth_enum_cb, NULL) != 0) return (-1); (void) fprintf(out, "%x c-enum .%s\n", fth_enum_curnmems, fth_curtype); fth_enum_curnmems = 0; return (0); } fth_type_ops_t fth_enum_ops = { fth_enum_header, fth_enum_members, fth_enum_trailer }; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. */ /* * Used to dump structures and unions in forth mode. * * structures and unions are a bit more complicated than enums. To make things * just that much more interesting, we have to dump the members in reverse * order, which is nice. But wait! It gets better! For compatibility reasons, * we need to dump the members in reverse-offset order, even if member-specific * mode was used to request the members in something other than that order. * * The header op prints the macro header and saves the type being printed. * * In member-specific mode, the member op will be invoked for each structure * or union member. The member op adds the member name, format, type ID, * and offset to a list, sorted in reverse order by offset. * * The trailer op is called when the structure or enum is complete. If no * members were specifically requested, then the trailer iterates through all * of the members of the structure, pretending they were. Each member is thus * added, in reverse-offset order, to the list used in specific-member mode. * Either way, we then proceed through the list, dumping each member out with * fth_print_member. Structure and union members are printed out differently, * depending on member type, as follows: * * Integer: * Normal integers: ' -field * defaults to ".d" for enums, ".x" for others * is the member offset, in bytes. * is "byte", "short", "long", or "ext" for 8-, 16-, 32-, and * 64-bit integers, respectively. * is the name of the member being printed * * Bitfields: ' bits-field * defaults to ".x" * is the number of times to right-shift the masked value * use to extract the bit-field value from the read value * is the member offset, in bytes * is the name of the member being printed * * Float: Ignored * * Pointer: ' ptr-field * defaults to .x * is in bytes * is the name of the member being printed * * Array: * Arrays have a content-type-specific prefix, followed by an array * suffix. The resulting line looks like this if the array contents * type is an integer, a pointer, or an enum: * * ' ' array-field * * The following is printed for array contents that are arrays: * * ' noop ' .x array-field * * The following is printed for array contents that are structs: * * ' noop ' array-field * * is "c@", "w@", "l@", or "x@", depending on whether array * elements are 8, 16, 32 or 64 bits wide. * defaults to ".x" * is the size of the array, in bytes * is the size of the array elements * is the member offset, in bytes * is the nam eof the member being printed * * Struct/Union: ' struct-field * defaults to ".x" * is the member offset, in bytes * is the name of the member being printed */ #include #include #include #include #include "ctf_headers.h" #include "forth.h" #include "memory.h" static ctf_id_t fth_str_curtid; static list_t fth_str_curmems; /* * Node type for the member-storage list (fth_str_curmems) built by * fth_struct_members() */ typedef struct fth_str_mem { list_node_t fsm_node; char *fsm_memname; char *fsm_format; ctf_id_t fsm_tid; ulong_t fsm_off; } fth_str_mem_t; typedef struct fth_struct_members_data { char *fsmd_strname; char *fsmd_memfilter; char *fsmd_format; int fsmd_matched; } fth_struct_members_data_t; static int fth_print_member(fth_str_mem_t *, int); /* Comparison routined used to insert members into the fth_str_curmems list */ static int fth_struct_memcmp(void *m1, void *m2) { fth_str_mem_t *mem1 = m1, *mem2 = m2; if (mem1->fsm_off < mem2->fsm_off) return (1); else if (mem1->fsm_off > mem2->fsm_off) return (-1); else return (0); } static void fth_slist_add(fth_str_mem_t *mem) { fth_str_mem_t *l; for (l = list_head(&fth_str_curmems); l != NULL; l = list_next(&fth_str_curmems, l)) { if (fth_struct_memcmp(l, mem) > 0) { list_insert_before(&fth_str_curmems, l, mem); return; } } list_insert_tail(&fth_str_curmems, mem); } static void fth_free_str_mem(fth_str_mem_t *mem) { free(mem->fsm_memname); if (mem->fsm_format) free(mem->fsm_format); free(mem); } static int fth_struct_header(ctf_id_t tid) { ssize_t sz; fth_str_curtid = tid; list_create(&fth_str_curmems, sizeof (fth_str_mem_t), offsetof(fth_str_mem_t, fsm_node)); if ((sz = ctf_type_size(ctf, fth_str_curtid)) == CTF_ERR) return (parse_warn("Can't get size for %s", fth_curtype)); (void) fprintf(out, "\n"); (void) fprintf(out, "vocabulary %s-words\n", fth_curtype); (void) fprintf(out, "h# %x constant %s-sz\n", sz, fth_curtype); (void) fprintf(out, "%x ' %s-words c-struct .%s\n", sz, fth_curtype, fth_curtype); (void) fprintf(out, "also %s-words definitions\n\n", fth_curtype); return (0); } /* Print the array prefix for integer and pointer members */ static int fth_print_level(uint_t bits, char *format) { if ((bits & (bits - 1)) != 0 ||(bits % 8) != 0 || bits > 64) { return (parse_warn("Unexpected bit size %d in %s", bits, fth_curtype)); } (void) fprintf(out, "' %c@ ' %s", " cw l x"[bits / 8], format); return (0); } /* * Return the format to be used to print the member. If one of the builtin * formats "d" or "x" were specified, return ".d" or ".x", respectively. * Otherwise, use the user-provided format as is, or use the default if none * was provided. */ static char * fth_convert_format(char *format, char *def) { static char dot[3] = "."; if (format == NULL) return (def); else if (strlen(format) == 1) { dot[1] = *format; return (dot); } else return (format); } static int fth_print_integer(const char *memname, ulong_t off, uint_t bits, char *format, int level) { format = fth_convert_format(format, ".x"); if (bits > 64) { return (parse_warn("%s.%s is too large (>8 bytes)", fth_curtype, memname)); } if (level != 0) return (fth_print_level(bits, format)); if ((bits % NBBY) != 0 || (bits & (bits - 1)) != 0) { /* bit field */ uint_t offset, shift, mask; offset = (off / 32) * 4; shift = 32 - ((off % 32) + bits); mask = ((1 << bits) - 1) << shift; (void) fprintf(out, "' %s %x %x %x bits-field %s\n", format, shift, mask, offset, memname); } else { char *type[] = { NULL, "byte", "short", NULL, "long", NULL, NULL, NULL, "ext" }; (void) fprintf(out, "' %s %lx %s-field %s\n", format, off / 8, type[bits / 8], memname); } return (0); } static int fth_print_pointer(const char *memname, ulong_t off, uint_t bits, char *format, int level) { format = fth_convert_format(format, ".x"); if (level != 0) return (fth_print_level(bits, format)); (void) fprintf(out, "' %s %lx ptr-field %s\n", format, off / 8, memname); return (0); } static int fth_print_struct(char *memname, ulong_t off, char *format, int level) { format = fth_convert_format(format, ".x"); if (level != 0) (void) fprintf(out, "' noop ' %s", format); else { (void) fprintf(out, "' %s %lx struct-field %s\n", format, off / 8, memname); } return (0); } static int fth_print_enum(char *memname, ulong_t off, char *format, int level) { format = fth_convert_format(format, ".d"); if (level != 0) (void) fprintf(out, "' l@ ' %s", format); else { (void) fprintf(out, "' %s %lx long-field %s\n", format, off / 8, memname); } return (0); } static int fth_print_array(char *memname, ctf_id_t tid, ulong_t off, ssize_t sz, char *format, int level) { if (level != 0) (void) fprintf(out, "' noop ' .x"); else { fth_str_mem_t mem; ctf_arinfo_t ar; /* * print the prefix for the array contents type, then print * the array macro */ if (ctf_array_info(ctf, tid, &ar) == CTF_ERR) { return (parse_warn("Can't read array in %s.%s", fth_curtype, memname)); } mem.fsm_memname = memname; mem.fsm_format = format; mem.fsm_tid = ar.ctr_contents; mem.fsm_off = off; if (fth_print_member(&mem, level + 1) < 0) return (-1); (void) fprintf(out, " %x %x %lx array-field %s\n", sz, (sz / ar.ctr_nelems), off / 8, memname); } return (0); } /* dump a structure or union member */ static int fth_print_member(fth_str_mem_t *mem, int level) { ctf_encoding_t e; ctf_id_t tid; int kind; ssize_t sz; if ((tid = ctf_type_resolve(ctf, mem->fsm_tid)) == CTF_ERR) { return (parse_warn("Can't resolve %s.%s", fth_curtype, mem->fsm_memname)); } if ((kind = ctf_type_kind(ctf, tid)) == CTF_ERR) { return (parse_warn("Can't get kind for %s.%s", fth_curtype, mem->fsm_memname)); } if ((sz = ctf_type_size(ctf, tid)) == CTF_ERR) { return (parse_warn("Can't get size for %s.%s", fth_curtype, mem->fsm_memname)); } switch (kind) { case CTF_K_INTEGER: if (ctf_type_encoding(ctf, tid, &e) == CTF_ERR) return (parse_warn("Can't get encoding for %ld", tid)); return (fth_print_integer(mem->fsm_memname, mem->fsm_off, e.cte_bits, mem->fsm_format, level)); case CTF_K_FLOAT: (void) parse_warn("Ignoring floating point member %s.%s", fth_curtype, mem->fsm_memname); return (0); case CTF_K_POINTER: return (fth_print_pointer(mem->fsm_memname, mem->fsm_off, sz * 8, mem->fsm_format, level)); case CTF_K_ARRAY: return (fth_print_array(mem->fsm_memname, tid, mem->fsm_off, sz, mem->fsm_format, level)); case CTF_K_STRUCT: case CTF_K_UNION: return (fth_print_struct(mem->fsm_memname, mem->fsm_off, mem->fsm_format, level)); case CTF_K_ENUM: return (fth_print_enum(mem->fsm_memname, mem->fsm_off, mem->fsm_format, level)); case CTF_K_FORWARD: return (parse_warn("Type %ld in %s.%s is undefined", tid, fth_curtype, mem->fsm_memname)); default: return (parse_warn("Unexpected kind %d for %s.%s", kind, fth_curtype, mem->fsm_memname)); } } /* * Add a member to list of members to be printed (fth_str_curmems). If * fsmd_memfilter is non-null, only add this member if its name matches that * in the filter. */ static int fth_struct_members_cb(const char *memname, ctf_id_t tid, ulong_t off, void *arg) { fth_struct_members_data_t *fsmd = arg; fth_str_mem_t *mem; if (fsmd->fsmd_memfilter != NULL && strcmp(fsmd->fsmd_memfilter, memname) != 0) return (0); fsmd->fsmd_matched = 1; mem = xcalloc(sizeof (fth_str_mem_t)); mem->fsm_memname = xstrdup(memname); if (fsmd->fsmd_format) mem->fsm_format = xstrdup(fsmd->fsmd_format); mem->fsm_tid = tid; mem->fsm_off = off; fth_slist_add(mem); return (0); } /* * If memfilter is non-null, iterate through the members of this type, causing * every member to be added to the list. Otherwise, use the iterator and * the callback to add only the specified member. */ static int fth_struct_members(char *memfilter, char *format) { fth_struct_members_data_t fsmd; fsmd.fsmd_strname = fth_curtype; fsmd.fsmd_memfilter = memfilter; fsmd.fsmd_format = format; fsmd.fsmd_matched = 0; if (ctf_member_iter(ctf, fth_str_curtid, fth_struct_members_cb, &fsmd) != 0) return (-1); if (memfilter != NULL && fsmd.fsmd_matched == 0) { return (parse_warn("Invalid member %s.%s", fth_curtype, memfilter)); } return (0); } static int fth_struct_trailer(void) { fth_str_mem_t *mem; if (list_is_empty(&fth_str_curmems)) { if (fth_struct_members(NULL, NULL) < 0) return (-1); } while ((mem = list_remove_head(&fth_str_curmems)) != NULL) { if (fth_print_member(mem, 0) < 0) return (-1); fth_free_str_mem(mem); } list_destroy(&fth_str_curmems); (void) fprintf(out, "\n"); (void) fprintf(out, "kdbg-words definitions\n"); (void) fprintf(out, "previous\n"); (void) fprintf(out, "\n"); (void) fprintf(out, "\\ end %s section\n", fth_curtype); (void) fprintf(out, "\n"); return (0); } fth_type_ops_t fth_struct_ops = { fth_struct_header, fth_struct_members, fth_struct_trailer }; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * In this mode, we generate header files containg various #defines which can * be used to access members of various structures, and to walk through arrays. * The input template specifies the structures and members for whom #defines * are to be generated. * * The template has the following elements * * 1. Given the name of a structure or union, #defines can be generated that * describe the type. If requested, #defines that give the size and the * log2 (shift) of the structure will be generated. The latter can only * be requested for structures whose size is a power of two. * * Per-member #defines are also generated. The value of these defines will * be the offsets necessary to access the members they describe. By * default, the name of the #define will be the name of the member, in upper * case, but a user-supplied version can be used instead. If the member is * an array, an extra #define will be generated that will give the increment * needed to access individual array elements. The name of the increment * #define will be identical to that of the member #define, but with an * "_INCR" suffix. * * 2. Literal cpp directives * * Lines beginning with "\#" are copied directly to the output file. * * 3. Comments * * Lines beginning with backslashes (excluding the literal cpp directives * described above) are ignored. * * Example input: * * \ Dump the `foo' structure, creating a size #define called FOO_SIZE, and a * \ shift #define called FOO_SHIFT. `foo' has one member called `mem'. * foo FOO_SIZE FOO_SHIFT * * \ Dump the `a' and `b' members of the `bar' structure. the offset * \ #defines for these members should be `FRED' and `BOB', respectively. * \ Both members are of type `char' * bar * a FRED * b BOB * * Example output: * * #define FOO_SIZE 0x4 * #define FOO_SHIFT 0x2 * #define FRED 0x0 * #define FRED_INCR 0x1 * #define BOB 0x4 */ #include #include #include #include #include #include "ctf_headers.h" #include "utils.h" #include "ctfstabs.h" static int ga_parse_tokens(char *line, int max, char ***wret) { char *c = line; char *word; int n; while (isspace(*c)) c++; for (n = 1, word = strtok(line, " \t"); word != NULL; word = strtok(NULL, " \t"), n++) { if (n > max) return (-1); *(wret[n - 1]) = word; } return (n - 1); } static int ga_parse_common(char *line, int min, int max, char **w1, char **w2, char **w3) { char **wret[3]; int nread; wret[0] = w1; wret[1] = w2; wret[2] = w3; if ((nread = ga_parse_tokens(line, max, wret)) < min) return (-1); if (nread < 3 && wret[2] != NULL) *wret[2] = (char *)NULL; if (nread < 2 && wret[1] != NULL) *wret[1] = (char *)NULL; if (nread < 1 && wret[0] != NULL) *wret[0] = (char *)NULL; return (nread); } /* * Valid format: typename [sizedefname [shiftdefname]] */ static int ga_parse_name(char *line, char **cnp, char **szdp, char **shdp) { return (ga_parse_common(line, 1, 3, cnp, szdp, shdp)); } /* * Valid format: memname [offdefname] */ static int ga_parse_member(char *line, char **mnp, char **offp) { return (ga_parse_common(line, 1, 2, mnp, offp, NULL)); } /* * Used to begin a new structure/union block, and to print the optional size * and optional shift constants. */ static int ga_process_name(char *line) { char *curname, *sizedef, *shdef; ctf_id_t curtype; ssize_t sz, shift; if (ga_parse_name(line, &curname, &sizedef, &shdef) < 0) return (parse_warn("Couldn't parse name")); if ((curtype = find_type(curname)) == CTF_ERR) return (parse_warn("Couldn't find type %s", curname)); if (sizedef != NULL) { if ((sz = ctf_type_size(ctf, curtype)) < 0) { return (parse_warn("Couldn't get size for type %s", curname)); } else if (sz == 0) { return (parse_warn("Invalid type size 0 for %s", curname)); } (void) fprintf(out, "#define\t%s\t0x%x\n", sizedef, sz); } if (shdef != NULL) { ssize_t tsz; for (shift = -1, tsz = sz; tsz > 0; tsz >>= 1, shift++) ; if (shift < 0 || 1 << shift != sz) { return (parse_warn("Can't make shift #define: %s size " "(%d) isn't a power of 2", curname, sz)); } (void) fprintf(out, "#define\t%s\t0x%x\n", shdef, shift); } return (curtype); } /* * ga_process_member() and ga_member_cb() are used to print the offset and * possibly array increment values for a given structure member. A specific * member is requested via ga_process_member(), and ga_member_cb() is used * to iterate through the members of the current structure type, looking for * that member. This is not the most efficient way to do things, but the * lists involved are generally short. */ typedef struct ga_member_cb_data { char *gmcb_memname; char *gmcb_submem; char *gmcb_offdef; size_t gmcb_off; } ga_member_cb_data_t; static int ga_member_find(ctf_id_t, ga_member_cb_data_t *); static int ga_member_cb(const char *name, ctf_id_t type, ulong_t off, void *arg) { ga_member_cb_data_t *md = arg; ctf_arinfo_t arinfo; char *label; if (strcmp(name, md->gmcb_memname) != 0) return (0); md->gmcb_off += off / 8; /* off is in bits */ if (md->gmcb_submem != NULL) { /* * The user requested foo.bar. We've found foo, and now need to * recurse down to bar. */ ga_member_cb_data_t smd; smd.gmcb_memname = md->gmcb_submem; smd.gmcb_submem = NULL; smd.gmcb_offdef = md->gmcb_offdef; smd.gmcb_off = md->gmcb_off; return (ga_member_find(type, &smd)); } if (md->gmcb_offdef == NULL) { int i; label = md->gmcb_memname; for (i = 0; i < strlen(label); i++) label[i] = toupper(label[i]); } else label = md->gmcb_offdef; /* offsets are in bits - we need bytes */ (void) fprintf(out, "#define\t%s\t0x%lx\n", label, (ulong_t)md->gmcb_off); if ((type = ctf_type_resolve(ctf, type)) == CTF_ERR) return (parse_warn("Couldn't resolve type %s", name)); if (ctf_array_info(ctf, type, &arinfo) == 0) { ssize_t sz; if ((sz = ctf_type_size(ctf, arinfo.ctr_contents)) < 0) return (parse_warn("Couldn't get array elem size")); (void) fprintf(out, "#define\t%s_INCR\t0x%x\n", label, sz); } return (1); } static int ga_member_find(ctf_id_t curtype, ga_member_cb_data_t *md) { char *c; int rc; if ((c = strchr(md->gmcb_memname, '.')) != NULL) *c++ = '\0'; md->gmcb_submem = c; if ((rc = ctf_member_iter(ctf, curtype, ga_member_cb, md)) == 0) { return (parse_warn("Couldn't find member named %s", md->gmcb_memname)); } else if (rc != 1) return (parse_warn("Can't parse")); return (1); } static int ga_process_member(ctf_id_t curtype, char *line) { ga_member_cb_data_t md = { 0 }; if (ga_parse_member(line, &md.gmcb_memname, &md.gmcb_offdef) < 0) return (parse_warn("Couldn't parse member")); return (ga_member_find(curtype, &md)); } static int ga_process_line(char *line) { static int curtype = -1; static int blanks = 0; if (strlen(line) == 0) { blanks++; return (1); } else if (blanks) { if (!isspace(line[0])) curtype = -1; blanks = 0; } if (line[0] == '\\') { if (line[1] == '#') { /* dump, verbatim, lines that begin with "\#" */ (void) fprintf(out, "%s\n", line + 1); } return (1); } else if (line[0] == '#') { /* * This is a comment of some sort; is it a line number * comment? Those look like '# 53 "filename.c"'. GCC * sometimes inserts them and removes all other vertical * whitespace, so they should be treated as a "type * terminator" like a blank line is. */ if (isdigit(line[2])) { /* line number, terminate type */ curtype = -1; } return (1); } if (curtype == -1) return ((curtype = ga_process_name(line))); else return (ga_process_member(curtype, line)); } proc_ops_t ga_ops = { NULL, ga_process_line, NULL };