type.c revision 4767187d0d7924bfe5028fe3e29e3b018bc9c99b
1/* 2 * This file is part of ltrace. 3 * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. 4 * Copyright (C) 2007,2008 Juan Cespedes 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of the 9 * License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, but 12 * WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 19 * 02110-1301 USA 20 */ 21 22#include <assert.h> 23#include <stdlib.h> 24 25#include "type.h" 26#include "sysdep.h" 27#include "expr.h" 28 29struct arg_type_info * 30type_get_simple(enum arg_type type) 31{ 32#define HANDLE(T) { \ 33 static struct arg_type_info t = { T }; \ 34 case T: \ 35 return &t; \ 36 } 37 38 switch (type) { 39 HANDLE(ARGTYPE_UNKNOWN) 40 HANDLE(ARGTYPE_VOID) 41 HANDLE(ARGTYPE_INT) 42 HANDLE(ARGTYPE_UINT) 43 HANDLE(ARGTYPE_LONG) 44 HANDLE(ARGTYPE_ULONG) 45 HANDLE(ARGTYPE_OCTAL) 46 HANDLE(ARGTYPE_CHAR) 47 HANDLE(ARGTYPE_SHORT) 48 HANDLE(ARGTYPE_USHORT) 49 HANDLE(ARGTYPE_FLOAT) 50 HANDLE(ARGTYPE_DOUBLE) 51 52#undef HANDLE 53 54 case ARGTYPE_STRING_N: 55 56 case ARGTYPE_ARRAY: 57 case ARGTYPE_ENUM: 58 case ARGTYPE_STRUCT: 59 case ARGTYPE_POINTER: 60 assert(!"Not a simple type!"); 61 }; 62 abort(); 63} 64 65struct enum_entry { 66 char *key; 67 int own_key; 68 int value; 69}; 70 71void 72type_init_enum(struct arg_type_info *info) 73{ 74 info->type = ARGTYPE_ENUM; 75 VECT_INIT(&info->u.entries, struct enum_entry); 76} 77 78int 79type_enum_add(struct arg_type_info *info, 80 const char *key, int own_key, int value) 81{ 82 assert(info->type == ARGTYPE_ENUM); 83 struct enum_entry entry = { (char *)key, own_key, value }; 84 return VECT_PUSHBACK(&info->u.entries, &entry); 85} 86 87size_t 88type_enum_size(struct arg_type_info *info) 89{ 90 assert(info->type == ARGTYPE_ENUM); 91 return vect_size(&info->u.entries); 92} 93 94const char * 95type_enum_get(struct arg_type_info *info, int value) 96{ 97 assert(info->type == ARGTYPE_ENUM); 98 size_t i; 99 for (i = 0; i < vect_size(&info->u.entries); ++i) { 100 struct enum_entry *entry = VECT_ELEMENT(&info->u.entries, 101 struct enum_entry, i); 102 if (value == entry->value) 103 return entry->key; 104 } 105 return NULL; 106} 107 108static void 109enum_entry_dtor(struct enum_entry *entry, void *data) 110{ 111 if (entry->own_key) 112 free(entry->key); 113} 114 115static void 116type_enum_destroy(struct arg_type_info *info) 117{ 118 VECT_DESTROY(&info->u.entries, struct enum_entry, 119 enum_entry_dtor, NULL); 120} 121 122struct struct_field { 123 struct arg_type_info *info; 124 int own_info; 125}; 126 127void 128type_init_struct(struct arg_type_info *info) 129{ 130 info->type = ARGTYPE_STRUCT; 131 VECT_INIT(&info->u.entries, struct struct_field); 132} 133 134int 135type_struct_add(struct arg_type_info *info, 136 struct arg_type_info *field_info, int own) 137{ 138 assert(info->type == ARGTYPE_STRUCT); 139 struct struct_field field = { field_info, own }; 140 return VECT_PUSHBACK(&info->u.entries, &field); 141} 142 143struct arg_type_info * 144type_struct_get(struct arg_type_info *info, size_t idx) 145{ 146 assert(info->type == ARGTYPE_STRUCT); 147 struct struct_field *field = VECT_ELEMENT(&info->u.entries, 148 struct struct_field, idx); 149 if (field == NULL) 150 return NULL; 151 return field->info; 152} 153 154size_t 155type_struct_size(struct arg_type_info *info) 156{ 157 assert(info->type == ARGTYPE_STRUCT); 158 return vect_size(&info->u.entries); 159} 160 161static void 162struct_field_dtor(struct struct_field *field, void *data) 163{ 164 if (field->own_info) { 165 type_destroy(field->info); 166 free(field->info); 167 } 168} 169 170static void 171type_struct_destroy(struct arg_type_info *info) 172{ 173 VECT_DESTROY(&info->u.entries, struct struct_field, 174 struct_field_dtor, NULL); 175} 176 177static int 178layout_struct(struct Process *proc, struct arg_type_info *info, 179 size_t *sizep, size_t *alignmentp, size_t *offsetofp) 180{ 181 size_t sz = 0; 182 size_t max_alignment = 0; 183 size_t i; 184 size_t offsetof_field = (size_t)-1; 185 if (offsetofp != NULL) 186 offsetof_field = *offsetofp; 187 188 assert(info->type == ARGTYPE_STRUCT); 189 for (i = 0; i < vect_size(&info->u.entries); ++i) { 190 struct struct_field *field 191 = VECT_ELEMENT(&info->u.entries, 192 struct struct_field, i); 193 194 size_t alignment = type_alignof(proc, field->info); 195 if (alignment == (size_t)-1) 196 return -1; 197 198 /* Add padding to SZ to align the next element. */ 199 sz = align(sz, alignment); 200 if (i == offsetof_field) { 201 *offsetofp = sz; 202 if (sizep == NULL && alignmentp == NULL) 203 return 0; 204 } 205 206 size_t size = type_sizeof(proc, field->info); 207 if (size == (size_t)-1) 208 return -1; 209 sz += size; 210 211 if (alignment > max_alignment) 212 max_alignment = alignment; 213 } 214 215 if (max_alignment > 0) 216 sz = align(sz, max_alignment); 217 218 if (sizep != NULL) 219 *sizep = sz; 220 221 if (alignmentp != NULL) 222 *alignmentp = max_alignment; 223 224 return 0; 225} 226 227void 228type_init_array(struct arg_type_info *info, 229 struct arg_type_info *element_info, int own_info, 230 struct expr_node *length_expr, int own_length) 231{ 232 info->type = ARGTYPE_ARRAY; 233 info->u.array_info.elt_type = element_info; 234 info->u.array_info.own_info = own_info; 235 info->u.array_info.length = length_expr; 236 info->u.array_info.own_length = own_length; 237} 238 239void 240type_init_string(struct arg_type_info *info, 241 struct expr_node *length_expr, int own_length) 242{ 243 info->type = ARGTYPE_STRING_N; 244 info->u.string_n_info.length = length_expr; 245 info->u.string_n_info.own_length = own_length; 246} 247 248static void 249type_array_destroy(struct arg_type_info *info) 250{ 251 if (info->u.array_info.own_info) { 252 type_destroy(info->u.array_info.elt_type); 253 free(info->u.array_info.elt_type); 254 } 255 if (info->u.array_info.own_length) { 256 expr_destroy(info->u.array_info.length); 257 free(info->u.array_info.length); 258 } 259} 260 261static void 262type_string_n_destroy(struct arg_type_info *info) 263{ 264 if (info->u.array_info.own_length) { 265 expr_destroy(info->u.string_n_info.length); 266 free(info->u.string_n_info.length); 267 } 268} 269 270void 271type_init_pointer(struct arg_type_info *info, 272 struct arg_type_info *pointee_info, int own_info) 273{ 274 info->type = ARGTYPE_POINTER; 275 info->u.ptr_info.info = pointee_info; 276 info->u.ptr_info.own_info = own_info; 277} 278 279static void 280type_pointer_destroy(struct arg_type_info *info) 281{ 282 if (info->u.ptr_info.own_info) { 283 type_destroy(info->u.ptr_info.info); 284 free(info->u.ptr_info.info); 285 } 286} 287 288void 289type_destroy(struct arg_type_info *info) 290{ 291 if (info == NULL) 292 return; 293 294 switch (info->type) { 295 case ARGTYPE_ENUM: 296 return type_enum_destroy(info); 297 298 case ARGTYPE_STRUCT: 299 type_struct_destroy(info); 300 break; 301 302 case ARGTYPE_ARRAY: 303 type_array_destroy(info); 304 break; 305 306 case ARGTYPE_POINTER: 307 type_pointer_destroy(info); 308 break; 309 310 case ARGTYPE_STRING_N: 311 type_string_n_destroy(info); 312 313 case ARGTYPE_UNKNOWN: 314 case ARGTYPE_VOID: 315 case ARGTYPE_INT: 316 case ARGTYPE_UINT: 317 case ARGTYPE_LONG: 318 case ARGTYPE_ULONG: 319 case ARGTYPE_OCTAL: 320 case ARGTYPE_CHAR: 321 case ARGTYPE_SHORT: 322 case ARGTYPE_USHORT: 323 case ARGTYPE_FLOAT: 324 case ARGTYPE_DOUBLE: 325 break; 326 } 327} 328 329#ifdef ARCH_HAVE_SIZEOF 330size_t arch_type_sizeof(struct Process *proc, struct arg_type_info * arg); 331#else 332size_t 333arch_type_sizeof(struct Process *proc, struct arg_type_info * arg) 334{ 335 /* Use default value. */ 336 return (size_t)-2; 337} 338#endif 339 340#ifdef ARCH_HAVE_ALIGNOF 341size_t arch_type_alignof(struct Process *proc, struct arg_type_info * arg); 342#else 343size_t 344arch_type_alignof(struct Process *proc, struct arg_type_info * arg) 345{ 346 /* Use default value. */ 347 return (size_t)-2; 348} 349#endif 350 351/* We need to support alignments that are not power of two. E.g. long 352 * double on x86 has alignment of 12. */ 353size_t 354align(size_t sz, size_t alignment) 355{ 356 assert(alignment != 0); 357 358 if ((sz % alignment) != 0) 359 sz = ((sz / alignment) + 1) * alignment; 360 361 return sz; 362} 363 364size_t 365type_sizeof(struct Process *proc, struct arg_type_info *type) 366{ 367 size_t arch_size = arch_type_sizeof(proc, type); 368 if (arch_size != (size_t)-2) 369 return arch_size; 370 371 switch (type->type) { 372 size_t size; 373 case ARGTYPE_CHAR: 374 return sizeof(char); 375 376 case ARGTYPE_SHORT: 377 case ARGTYPE_USHORT: 378 return sizeof(short); 379 380 case ARGTYPE_INT: 381 case ARGTYPE_UINT: 382 case ARGTYPE_ENUM: 383 return sizeof(int); 384 385 case ARGTYPE_LONG: 386 case ARGTYPE_ULONG: 387 return sizeof(long); 388 389 case ARGTYPE_FLOAT: 390 return sizeof(float); 391 392 case ARGTYPE_DOUBLE: 393 return sizeof(double); 394 395 case ARGTYPE_STRUCT: 396 if (layout_struct(proc, type, &size, NULL, NULL) < 0) 397 return (size_t)-1; 398 return size; 399 400 case ARGTYPE_STRING_N: 401 /* String is a char* in disguise. */ 402 case ARGTYPE_POINTER: 403 return sizeof(void *); 404 405 case ARGTYPE_ARRAY: 406 if (expr_is_compile_constant(type->u.array_info.length)) { 407 long l; 408 if (expr_eval_constant(type->u.array_info.length, 409 &l) < 0) 410 return -1; 411 412 struct arg_type_info *elt_ti 413 = type->u.array_info.elt_type; 414 415 size_t elt_size = type_sizeof(proc, elt_ti); 416 if (elt_size == (size_t)-1) 417 return (size_t)-1; 418 419 return ((size_t)l) * elt_size; 420 421 } else { 422 /* Flexible arrays don't count into the 423 * sizeof. */ 424 return 0; 425 } 426 427 case ARGTYPE_VOID: 428 return 0; 429 430 /* XXX these are in fact formatting conventions, not 431 * data types. They should be handled differently. */ 432 case ARGTYPE_OCTAL: 433 case ARGTYPE_UNKNOWN: 434 return sizeof(long); 435 } 436 437 abort(); 438} 439 440#undef alignof 441#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st)) 442 443size_t 444type_alignof(struct Process *proc, struct arg_type_info *type) 445{ 446 size_t arch_alignment = arch_type_alignof(proc, type); 447 if (arch_alignment != (size_t)-2) 448 return arch_alignment; 449 450 struct { char c; char C; } cC; 451 struct { char c; short s; } cs; 452 struct { char c; int i; } ci; 453 struct { char c; long l; } cl; 454 struct { char c; void* p; } cp; 455 struct { char c; float f; } cf; 456 struct { char c; double d; } cd; 457 458 static size_t char_alignment = alignof(C, cC); 459 static size_t short_alignment = alignof(s, cs); 460 static size_t int_alignment = alignof(i, ci); 461 static size_t long_alignment = alignof(l, cl); 462 static size_t ptr_alignment = alignof(p, cp); 463 static size_t float_alignment = alignof(f, cf); 464 static size_t double_alignment = alignof(d, cd); 465 466 switch (type->type) { 467 size_t alignment; 468 case ARGTYPE_LONG: 469 case ARGTYPE_ULONG: 470 return long_alignment; 471 case ARGTYPE_CHAR: 472 return char_alignment; 473 case ARGTYPE_SHORT: 474 case ARGTYPE_USHORT: 475 return short_alignment; 476 case ARGTYPE_FLOAT: 477 return float_alignment; 478 case ARGTYPE_DOUBLE: 479 return double_alignment; 480 case ARGTYPE_POINTER: 481 return ptr_alignment; 482 483 case ARGTYPE_ARRAY: 484 return type_alignof(proc, type->u.array_info.elt_type); 485 486 case ARGTYPE_STRUCT: 487 if (layout_struct(proc, type, NULL, &alignment, NULL) < 0) 488 return (size_t)-1; 489 return alignment; 490 491 default: 492 return int_alignment; 493 } 494} 495 496size_t 497type_offsetof(struct Process *proc, struct arg_type_info *type, size_t emt) 498{ 499 assert(type->type == ARGTYPE_STRUCT 500 || type->type == ARGTYPE_ARRAY 501 /* XXX Temporary, this will be removed. */ 502 || type->type == ARGTYPE_STRING_N); 503 504 switch (type->type) { 505 size_t alignment; 506 size_t size; 507 case ARGTYPE_ARRAY: 508 alignment = type_alignof(proc, type->u.array_info.elt_type); 509 if (alignment == (size_t)-1) 510 return (size_t)-1; 511 512 size = type_sizeof(proc, type->u.array_info.elt_type); 513 if (size == (size_t)-1) 514 return (size_t)-1; 515 516 return emt * align(size, alignment); 517 518 case ARGTYPE_STRING_N: 519 return emt; 520 521 case ARGTYPE_STRUCT: 522 if (layout_struct(proc, type, NULL, NULL, &emt) < 0) 523 return (size_t)-1; 524 return emt; 525 526 default: 527 abort (); 528 } 529} 530 531struct arg_type_info * 532type_element(struct arg_type_info *info, size_t emt) 533{ 534 assert(info->type == ARGTYPE_STRUCT 535 || info->type == ARGTYPE_ARRAY 536 /* XXX Temporary, this will be removed. */ 537 || info->type == ARGTYPE_STRING_N); 538 539 switch (info->type) { 540 case ARGTYPE_ARRAY: 541 return info->u.array_info.elt_type; 542 543 case ARGTYPE_STRUCT: 544 assert(emt < type_struct_size(info)); 545 return type_struct_get(info, emt); 546 547 case ARGTYPE_STRING_N: 548 return type_get_simple(ARGTYPE_CHAR); 549 550 default: 551 abort (); 552 } 553} 554