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