Roland McGrath | 7452e19 | 2010-01-06 11:11:25 -0800 | [diff] [blame] | 1 | /* Compute size of an aggregate type from DWARF. |
Mark Wielaard | b50014f | 2014-10-02 14:00:47 +0200 | [diff] [blame] | 2 | Copyright (C) 2010, 2014 Red Hat, Inc. |
Mark Wielaard | de2ed97 | 2012-06-05 17:15:16 +0200 | [diff] [blame] | 3 | This file is part of elfutils. |
Roland McGrath | 7452e19 | 2010-01-06 11:11:25 -0800 | [diff] [blame] | 4 | |
Mark Wielaard | de2ed97 | 2012-06-05 17:15:16 +0200 | [diff] [blame] | 5 | This file is free software; you can redistribute it and/or modify |
| 6 | it under the terms of either |
Roland McGrath | 7452e19 | 2010-01-06 11:11:25 -0800 | [diff] [blame] | 7 | |
Mark Wielaard | de2ed97 | 2012-06-05 17:15:16 +0200 | [diff] [blame] | 8 | * the GNU Lesser General Public License as published by the Free |
| 9 | Software Foundation; either version 3 of the License, or (at |
| 10 | your option) any later version |
| 11 | |
| 12 | or |
| 13 | |
| 14 | * the GNU General Public License as published by the Free |
| 15 | Software Foundation; either version 2 of the License, or (at |
| 16 | your option) any later version |
| 17 | |
| 18 | or both in parallel, as here. |
| 19 | |
| 20 | elfutils is distributed in the hope that it will be useful, but |
Roland McGrath | 7452e19 | 2010-01-06 11:11:25 -0800 | [diff] [blame] | 21 | WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 23 | General Public License for more details. |
| 24 | |
Mark Wielaard | de2ed97 | 2012-06-05 17:15:16 +0200 | [diff] [blame] | 25 | You should have received copies of the GNU General Public License and |
| 26 | the GNU Lesser General Public License along with this program. If |
| 27 | not, see <http://www.gnu.org/licenses/>. */ |
Roland McGrath | 7452e19 | 2010-01-06 11:11:25 -0800 | [diff] [blame] | 28 | |
| 29 | #ifdef HAVE_CONFIG_H |
| 30 | # include <config.h> |
| 31 | #endif |
| 32 | |
| 33 | #include <dwarf.h> |
| 34 | #include "libdwP.h" |
| 35 | |
| 36 | |
| 37 | static Dwarf_Die * |
| 38 | get_type (Dwarf_Die *die, Dwarf_Attribute *attr_mem, Dwarf_Die *type_mem) |
| 39 | { |
Mark Wielaard | 3a36e8a | 2014-10-06 22:00:16 +0200 | [diff] [blame] | 40 | Dwarf_Die *type = INTUSE(dwarf_formref_die) |
Roland McGrath | 7452e19 | 2010-01-06 11:11:25 -0800 | [diff] [blame] | 41 | (INTUSE(dwarf_attr_integrate) (die, DW_AT_type, attr_mem), type_mem); |
Mark Wielaard | 3a36e8a | 2014-10-06 22:00:16 +0200 | [diff] [blame] | 42 | |
| 43 | if (INTUSE(dwarf_peel_type) (type, type) != 0) |
| 44 | return NULL; |
| 45 | |
| 46 | return type; |
Roland McGrath | 7452e19 | 2010-01-06 11:11:25 -0800 | [diff] [blame] | 47 | } |
| 48 | |
| 49 | static int |
| 50 | array_size (Dwarf_Die *die, Dwarf_Word *size, |
| 51 | Dwarf_Attribute *attr_mem, Dwarf_Die *type_mem) |
| 52 | { |
| 53 | Dwarf_Word eltsize; |
| 54 | if (INTUSE(dwarf_aggregate_size) (get_type (die, attr_mem, type_mem), |
| 55 | &eltsize) != 0) |
| 56 | return -1; |
| 57 | |
| 58 | /* An array can have DW_TAG_subrange_type or DW_TAG_enumeration_type |
| 59 | children instead that give the size of each dimension. */ |
| 60 | |
| 61 | Dwarf_Die child; |
| 62 | if (INTUSE(dwarf_child) (die, &child) != 0) |
| 63 | return -1; |
| 64 | |
| 65 | bool any = false; |
| 66 | Dwarf_Word total = 0; |
| 67 | do |
| 68 | { |
| 69 | Dwarf_Word count; |
| 70 | switch (INTUSE(dwarf_tag) (&child)) |
| 71 | { |
| 72 | case DW_TAG_subrange_type: |
| 73 | /* This has either DW_AT_count or DW_AT_upper_bound. */ |
| 74 | if (INTUSE(dwarf_attr_integrate) (&child, DW_AT_count, |
| 75 | attr_mem) != NULL) |
| 76 | { |
| 77 | if (INTUSE(dwarf_formudata) (attr_mem, &count) != 0) |
| 78 | return -1; |
| 79 | } |
| 80 | else |
| 81 | { |
| 82 | Dwarf_Sword upper; |
| 83 | Dwarf_Sword lower; |
| 84 | if (INTUSE(dwarf_formsdata) (INTUSE(dwarf_attr_integrate) |
| 85 | (&child, DW_AT_upper_bound, |
| 86 | attr_mem), &upper) != 0) |
| 87 | return -1; |
| 88 | |
| 89 | /* Having DW_AT_lower_bound is optional. */ |
| 90 | if (INTUSE(dwarf_attr_integrate) (&child, DW_AT_lower_bound, |
| 91 | attr_mem) != NULL) |
| 92 | { |
| 93 | if (INTUSE(dwarf_formsdata) (attr_mem, &lower) != 0) |
| 94 | return -1; |
| 95 | } |
| 96 | else |
| 97 | { |
| 98 | /* Determine default lower bound from language, |
| 99 | as per "4.12 Subrange Type Entries". */ |
| 100 | Dwarf_Die cu = CUDIE (die->cu); |
| 101 | switch (INTUSE(dwarf_srclang) (&cu)) |
| 102 | { |
| 103 | case DW_LANG_C: |
| 104 | case DW_LANG_C89: |
| 105 | case DW_LANG_C99: |
Mark Wielaard | 9f8c08e | 2014-11-24 22:17:51 +0100 | [diff] [blame] | 106 | case DW_LANG_C11: |
Roland McGrath | 7452e19 | 2010-01-06 11:11:25 -0800 | [diff] [blame] | 107 | case DW_LANG_C_plus_plus: |
Mark Wielaard | 9f8c08e | 2014-11-24 22:17:51 +0100 | [diff] [blame] | 108 | case DW_LANG_C_plus_plus_11: |
| 109 | case DW_LANG_C_plus_plus_14: |
Mark Wielaard | c4010b8 | 2012-07-24 13:20:41 +0200 | [diff] [blame] | 110 | case DW_LANG_ObjC: |
Roland McGrath | 7452e19 | 2010-01-06 11:11:25 -0800 | [diff] [blame] | 111 | case DW_LANG_ObjC_plus_plus: |
| 112 | case DW_LANG_Java: |
| 113 | case DW_LANG_D: |
| 114 | case DW_LANG_UPC: |
Mark Wielaard | 9f8c08e | 2014-11-24 22:17:51 +0100 | [diff] [blame] | 115 | case DW_LANG_Go: |
Roland McGrath | 7452e19 | 2010-01-06 11:11:25 -0800 | [diff] [blame] | 116 | lower = 0; |
| 117 | break; |
| 118 | |
| 119 | case DW_LANG_Ada83: |
| 120 | case DW_LANG_Ada95: |
| 121 | case DW_LANG_Cobol74: |
| 122 | case DW_LANG_Cobol85: |
| 123 | case DW_LANG_Fortran77: |
| 124 | case DW_LANG_Fortran90: |
| 125 | case DW_LANG_Fortran95: |
| 126 | case DW_LANG_Pascal83: |
| 127 | case DW_LANG_Modula2: |
| 128 | case DW_LANG_PL1: |
| 129 | lower = 1; |
| 130 | break; |
| 131 | |
| 132 | default: |
| 133 | return -1; |
| 134 | } |
| 135 | } |
| 136 | if (unlikely (lower > upper)) |
| 137 | return -1; |
| 138 | count = upper - lower + 1; |
| 139 | } |
| 140 | break; |
| 141 | |
| 142 | case DW_TAG_enumeration_type: |
| 143 | /* We have to find the DW_TAG_enumerator child with the |
| 144 | highest value to know the array's element count. */ |
| 145 | count = 0; |
| 146 | Dwarf_Die enum_child; |
| 147 | int has_children = INTUSE(dwarf_child) (die, &enum_child); |
| 148 | if (has_children < 0) |
| 149 | return -1; |
| 150 | if (has_children > 0) |
| 151 | do |
| 152 | if (INTUSE(dwarf_tag) (&enum_child) == DW_TAG_enumerator) |
| 153 | { |
| 154 | Dwarf_Word value; |
| 155 | if (INTUSE(dwarf_formudata) (INTUSE(dwarf_attr_integrate) |
| 156 | (&enum_child, DW_AT_const_value, |
| 157 | attr_mem), &value) != 0) |
| 158 | return -1; |
| 159 | if (value >= count) |
| 160 | count = value + 1; |
| 161 | } |
| 162 | while (INTUSE(dwarf_siblingof) (&enum_child, &enum_child) > 0); |
| 163 | break; |
| 164 | |
| 165 | default: |
| 166 | continue; |
| 167 | } |
| 168 | |
| 169 | /* This is a subrange_type or enumeration_type and we've set COUNT. |
| 170 | Now determine the stride for this array dimension. */ |
| 171 | Dwarf_Word stride = eltsize; |
| 172 | if (INTUSE(dwarf_attr_integrate) (&child, DW_AT_byte_stride, |
| 173 | attr_mem) != NULL) |
| 174 | { |
| 175 | if (INTUSE(dwarf_formudata) (attr_mem, &stride) != 0) |
| 176 | return -1; |
| 177 | } |
| 178 | else if (INTUSE(dwarf_attr_integrate) (&child, DW_AT_bit_stride, |
| 179 | attr_mem) != NULL) |
| 180 | { |
| 181 | if (INTUSE(dwarf_formudata) (attr_mem, &stride) != 0) |
| 182 | return -1; |
| 183 | if (stride % 8) /* XXX maybe compute in bits? */ |
| 184 | return -1; |
| 185 | stride /= 8; |
| 186 | } |
| 187 | |
| 188 | any = true; |
| 189 | total += stride * count; |
| 190 | } |
| 191 | while (INTUSE(dwarf_siblingof) (&child, &child) == 0); |
| 192 | |
| 193 | if (!any) |
| 194 | return -1; |
| 195 | |
| 196 | *size = total; |
| 197 | return 0; |
| 198 | } |
| 199 | |
| 200 | static int |
| 201 | aggregate_size (Dwarf_Die *die, Dwarf_Word *size, Dwarf_Die *type_mem) |
| 202 | { |
| 203 | Dwarf_Attribute attr_mem; |
| 204 | |
| 205 | if (INTUSE(dwarf_attr_integrate) (die, DW_AT_byte_size, &attr_mem) != NULL) |
| 206 | return INTUSE(dwarf_formudata) (&attr_mem, size); |
| 207 | |
| 208 | switch (INTUSE(dwarf_tag) (die)) |
| 209 | { |
Roland McGrath | 7452e19 | 2010-01-06 11:11:25 -0800 | [diff] [blame] | 210 | case DW_TAG_subrange_type: |
| 211 | return aggregate_size (get_type (die, &attr_mem, type_mem), |
| 212 | size, type_mem); /* Tail call. */ |
| 213 | |
| 214 | case DW_TAG_array_type: |
| 215 | return array_size (die, size, &attr_mem, type_mem); |
Mark Wielaard | b50014f | 2014-10-02 14:00:47 +0200 | [diff] [blame] | 216 | |
| 217 | /* Assume references and pointers have pointer size if not given an |
| 218 | explicit DW_AT_byte_size. */ |
| 219 | case DW_TAG_pointer_type: |
| 220 | case DW_TAG_reference_type: |
| 221 | case DW_TAG_rvalue_reference_type: |
| 222 | *size = die->cu->address_size; |
| 223 | return 0; |
Roland McGrath | 7452e19 | 2010-01-06 11:11:25 -0800 | [diff] [blame] | 224 | } |
| 225 | |
| 226 | /* Most types must give their size directly. */ |
| 227 | return -1; |
| 228 | } |
| 229 | |
| 230 | int |
| 231 | dwarf_aggregate_size (die, size) |
| 232 | Dwarf_Die *die; |
| 233 | Dwarf_Word *size; |
| 234 | { |
| 235 | Dwarf_Die type_mem; |
Mark Wielaard | 3a36e8a | 2014-10-06 22:00:16 +0200 | [diff] [blame] | 236 | |
| 237 | if (INTUSE (dwarf_peel_type) (die, die) != 0) |
| 238 | return -1; |
| 239 | |
Roland McGrath | 7452e19 | 2010-01-06 11:11:25 -0800 | [diff] [blame] | 240 | return aggregate_size (die, size, &type_mem); |
| 241 | } |
| 242 | INTDEF (dwarf_aggregate_size) |
Mark Wielaard | 3a36e8a | 2014-10-06 22:00:16 +0200 | [diff] [blame] | 243 | OLD_VERSION (dwarf_aggregate_size, ELFUTILS_0.144) |
| 244 | NEW_VERSION (dwarf_aggregate_size, ELFUTILS_0.161) |