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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * In this mode, we generate header files containg various #defines which can 30 * be used to access members of various structures, and to walk through arrays. 31 * The input template specifies the structures and members for whom #defines 32 * are to be generated. 33 * 34 * The template has the following elements 35 * 36 * 1. Given the name of a structure or union, #defines can be generated that 37 * describe the type. If requested, #defines that give the size and the 38 * log2 (shift) of the structure will be generated. The latter can only 39 * be requested for structures whose size is a power of two. 40 * 41 * Per-member #defines are also generated. The value of these defines will 42 * be the offsets necessary to access the members they describe. By 43 * default, the name of the #define will be the name of the member, in upper 44 * case, but a user-supplied version can be used instead. If the member is 45 * an array, an extra #define will be generated that will give the increment 46 * needed to access individual array elements. The name of the increment 47 * #define will be identical to that of the member #define, but with an 48 * "_INCR" suffix. 49 * 50 * 2. Literal cpp directives 51 * 52 * Lines beginning with "\#" are copied directly to the output file. 53 * 54 * 3. Comments 55 * 56 * Lines beginning with backslashes (excluding the literal cpp directives 57 * described above) are ignored. 58 * 59 * Example input: 60 * 61 * \ Dump the `foo' structure, creating a size #define called FOO_SIZE, and a 62 * \ shift #define called FOO_SHIFT. `foo' has one member called `mem'. 63 * foo FOO_SIZE FOO_SHIFT 64 * 65 * \ Dump the `a' and `b' members of the `bar' structure. the offset 66 * \ #defines for these members should be `FRED' and `BOB', respectively. 67 * \ Both members are of type `char' 68 * bar 69 * a FRED 70 * b BOB 71 * 72 * Example output: 73 * 74 * #define FOO_SIZE 0x4 75 * #define FOO_SHIFT 0x2 76 * #define FRED 0x0 77 * #define FRED_INCR 0x1 78 * #define BOB 0x4 79 */ 80 81 #include <string.h> 82 #include <stdio.h> 83 #include <stdlib.h> 84 #include <ctype.h> 85 #include <sys/types.h> 86 87 #include "ctf_headers.h" 88 #include "utils.h" 89 #include "ctfstabs.h" 90 91 static int 92 ga_parse_tokens(char *line, int max, char ***wret) 93 { 94 char *c = line; 95 char *word; 96 int n; 97 98 while (isspace(*c)) 99 c++; 100 101 for (n = 1, word = strtok(line, " \t"); word != NULL; 102 word = strtok(NULL, " \t"), n++) { 103 if (n > max) 104 return (-1); 105 106 *(wret[n - 1]) = word; 107 } 108 109 return (n - 1); 110 } 111 112 static int 113 ga_parse_common(char *line, int min, int max, char **w1, char **w2, char **w3) 114 { 115 char **wret[3]; 116 int nread; 117 118 wret[0] = w1; 119 wret[1] = w2; 120 wret[2] = w3; 121 122 if ((nread = ga_parse_tokens(line, max, wret)) < min) 123 return (-1); 124 125 if (nread < 3 && wret[2] != NULL) 126 *wret[2] = (char *)NULL; 127 if (nread < 2 && wret[1] != NULL) 128 *wret[1] = (char *)NULL; 129 if (nread < 1 && wret[0] != NULL) 130 *wret[0] = (char *)NULL; 131 132 return (nread); 133 } 134 135 /* 136 * Valid format: typename [sizedefname [shiftdefname]] 137 */ 138 static int 139 ga_parse_name(char *line, char **cnp, char **szdp, char **shdp) 140 { 141 return (ga_parse_common(line, 1, 3, cnp, szdp, shdp)); 142 } 143 144 /* 145 * Valid format: memname [offdefname] 146 */ 147 static int 148 ga_parse_member(char *line, char **mnp, char **offp) 149 { 150 return (ga_parse_common(line, 1, 2, mnp, offp, NULL)); 151 } 152 153 /* 154 * Used to begin a new structure/union block, and to print the optional size 155 * and optional shift constants. 156 */ 157 static int 158 ga_process_name(char *line) 159 { 160 char *curname, *sizedef, *shdef; 161 ctf_id_t curtype; 162 ssize_t sz, shift; 163 164 if (ga_parse_name(line, &curname, &sizedef, &shdef) < 0) 165 return (parse_warn("Couldn't parse name")); 166 167 if ((curtype = find_type(curname)) == CTF_ERR) 168 return (parse_warn("Couldn't find type %s", curname)); 169 170 if (sizedef != NULL) { 171 if ((sz = ctf_type_size(ctf, curtype)) < 0) { 172 return (parse_warn("Couldn't get size for type %s", 173 curname)); 174 } else if (sz == 0) { 175 return (parse_warn("Invalid type size 0 for %s", 176 curname)); 177 } 178 179 (void) fprintf(out, "#define\t%s\t0x%x\n", sizedef, sz); 180 } 181 182 if (shdef != NULL) { 183 ssize_t tsz; 184 185 for (shift = -1, tsz = sz; tsz > 0; tsz >>= 1, shift++); 186 if (shift < 0 || 1 << shift != sz) { 187 return (parse_warn("Can't make shift #define: %s size " 188 "(%d) isn't a power of 2", curname, sz)); 189 } 190 191 (void) fprintf(out, "#define\t%s\t0x%x\n", shdef, shift); 192 } 193 194 return (curtype); 195 } 196 197 /* 198 * ga_process_member() and ga_member_cb() are used to print the offset and 199 * possibly array increment values for a given structure member. A specific 200 * member is requested via ga_process_member(), and ga_member_cb() is used 201 * to iterate through the members of the current structure type, looking for 202 * that member. This is not the most efficient way to do things, but the 203 * lists involved are generally short. 204 */ 205 typedef struct ga_member_cb_data { 206 char *gmcb_memname; 207 char *gmcb_submem; 208 char *gmcb_offdef; 209 size_t gmcb_off; 210 } ga_member_cb_data_t; 211 212 static int ga_member_find(ctf_id_t, ga_member_cb_data_t *); 213 214 static int 215 ga_member_cb(const char *name, ctf_id_t type, ulong_t off, void *arg) 216 { 217 ga_member_cb_data_t *md = arg; 218 ctf_arinfo_t arinfo; 219 char *label; 220 221 if (strcmp(name, md->gmcb_memname) != 0) 222 return (0); 223 224 md->gmcb_off += off / 8; /* off is in bits */ 225 226 if (md->gmcb_submem != NULL) { 227 /* 228 * The user requested foo.bar. We've found foo, and now need to 229 * recurse down to bar. 230 */ 231 ga_member_cb_data_t smd; 232 233 smd.gmcb_memname = md->gmcb_submem; 234 smd.gmcb_submem = NULL; 235 smd.gmcb_offdef = md->gmcb_offdef; 236 smd.gmcb_off = md->gmcb_off; 237 238 return (ga_member_find(type, &smd)); 239 } 240 241 if (md->gmcb_offdef == NULL) { 242 int i; 243 244 label = md->gmcb_memname; 245 for (i = 0; i < strlen(label); i++) 246 label[i] = toupper(label[i]); 247 } else 248 label = md->gmcb_offdef; 249 250 /* offsets are in bits - we need bytes */ 251 (void) fprintf(out, "#define\t%s\t0x%lx\n", label, 252 (ulong_t)md->gmcb_off); 253 254 if ((type = ctf_type_resolve(ctf, type)) == CTF_ERR) 255 return (parse_warn("Couldn't resolve type %s", name)); 256 257 if (ctf_array_info(ctf, type, &arinfo) == 0) { 258 ssize_t sz; 259 260 if ((sz = ctf_type_size(ctf, arinfo.ctr_contents)) < 0) 261 return (parse_warn("Couldn't get array elem size")); 262 263 (void) fprintf(out, "#define\t%s_INCR\t0x%x\n", label, sz); 264 } 265 266 return (1); 267 } 268 269 static int 270 ga_member_find(ctf_id_t curtype, ga_member_cb_data_t *md) 271 { 272 char *c; 273 int rc; 274 275 if ((c = strchr(md->gmcb_memname, '.')) != NULL) 276 *c++ = NULL; 277 md->gmcb_submem = c; 278 279 if ((rc = ctf_member_iter(ctf, curtype, ga_member_cb, md)) == 0) { 280 return (parse_warn("Couldn't find member named %s", 281 md->gmcb_memname)); 282 } else if (rc != 1) 283 return (parse_warn("Can't parse")); 284 285 return (1); 286 } 287 288 static int 289 ga_process_member(ctf_id_t curtype, char *line) 290 { 291 ga_member_cb_data_t md = { 0 }; 292 293 if (ga_parse_member(line, &md.gmcb_memname, &md.gmcb_offdef) < 0) 294 return (parse_warn("Couldn't parse member")); 295 296 return (ga_member_find(curtype, &md)); 297 } 298 299 static int 300 ga_process_line(char *line) 301 { 302 static int curtype = -1; 303 static int blanks = 0; 304 305 if (strlen(line) == 0) { 306 blanks++; 307 return (1); 308 } else if (blanks) { 309 if (!isspace(line[0])) 310 curtype = -1; 311 blanks = 0; 312 } 313 314 if (line[0] == '\\') { 315 if (line[1] == '#') { 316 /* dump, verbatim, lines that begin with "\#" */ 317 (void) fprintf(out, "%s\n", line + 1); 318 } 319 return (1); 320 321 } else if (line[0] == '#') { 322 /* 323 * This is a comment of some sort; is it a line number 324 * comment? Those look like '# 53 "filename.c"'. GCC 325 * sometimes inserts them and removes all other vertical 326 * whitespace, so they should be treated as a "type 327 * terminator" like a blank line is. 328 */ 329 if (isdigit(line[2])) { 330 /* line number, terminate type */ 331 curtype = -1; 332 } 333 return (1); 334 } 335 if (curtype == -1) 336 return ((curtype = ga_process_name(line))); 337 else 338 return (ga_process_member(curtype, line)); 339 } 340 341 proc_ops_t ga_ops = { 342 NULL, 343 ga_process_line, 344 NULL 345 }; 346