Andrew Geissler | 87f5cff | 2022-09-30 13:13:31 -0500 | [diff] [blame] | 1 | From f07c08e115e27cddf5a0030dc6332bbee1bd9c6a Mon Sep 17 00:00:00 2001 |
| 2 | From: Alan Modra <amodra@gmail.com> |
| 3 | Date: Thu, 21 Jul 2022 08:38:14 +0930 |
| 4 | Subject: [PATCH] binutils/dwarf.c: abbrev caching |
| 5 | |
| 6 | I'm inclined to think that abbrev caching is counter-productive. The |
| 7 | time taken to search the list of abbrevs converted to internal form is |
| 8 | non-zero, and it's easy to decode the raw abbrevs. It's especially |
| 9 | silly to cache empty lists of decoded abbrevs (happens with zero |
| 10 | padding in .debug_abbrev), or abbrevs as they are displayed when there |
| 11 | is no further use of those abbrevs. This patch stops caching in those |
| 12 | cases. |
| 13 | |
| 14 | * dwarf.c (record_abbrev_list_for_cu): Add free_list param. |
| 15 | Put abbrevs on abbrev_lists here. |
| 16 | (new_abbrev_list): Delete function. |
| 17 | (process_abbrev_set): Return newly allocated list. Move |
| 18 | abbrev base, offset and size checking to.. |
| 19 | (find_and_process_abbrev_set): ..here, new function. Handle |
| 20 | lookup of cached abbrevs here, and calculate start and end |
| 21 | for process_abbrev_set. Return free_list if newly alloc'd. |
| 22 | (process_debug_info): Consolidate cached list lookup, new list |
| 23 | alloc and processing into find_and_process_abbrev_set call. |
| 24 | Free list when not cached. |
| 25 | (display_debug_abbrev): Similarly. |
| 26 | |
| 27 | Upstream-Status: Backport [https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;h=f07c08e115e27cddf5a0030dc6332bbee1bd9c6a] |
| 28 | |
| 29 | Signed-off-by: Pgowda <pgowda.cve@gmail.com> |
| 30 | --- |
| 31 | binutils/dwarf.c | 208 +++++++++++++++++++++++++---------------------- |
| 32 | 1 file changed, 110 insertions(+), 98 deletions(-) |
| 33 | |
| 34 | diff --git a/binutils/dwarf.c b/binutils/dwarf.c |
| 35 | index 267ed3bb382..2fc352f74c5 100644 |
| 36 | --- a/binutils/dwarf.c |
| 37 | +++ b/binutils/dwarf.c |
| 38 | @@ -882,8 +882,15 @@ static unsigned long next_free_abbrev_m |
| 39 | #define ABBREV_MAP_ENTRIES_INCREMENT 8 |
| 40 | |
| 41 | static void |
| 42 | -record_abbrev_list_for_cu (dwarf_vma start, dwarf_vma end, abbrev_list * list) |
| 43 | +record_abbrev_list_for_cu (dwarf_vma start, dwarf_vma end, |
| 44 | + abbrev_list *list, abbrev_list *free_list) |
| 45 | { |
| 46 | + if (free_list != NULL) |
| 47 | + { |
| 48 | + list->next = abbrev_lists; |
| 49 | + abbrev_lists = list; |
| 50 | + } |
| 51 | + |
| 52 | if (cu_abbrev_map == NULL) |
| 53 | { |
| 54 | num_abbrev_map_entries = INITIAL_NUM_ABBREV_MAP_ENTRIES; |
| 55 | @@ -936,20 +943,6 @@ free_all_abbrevs (void) |
| 56 | } |
| 57 | |
| 58 | static abbrev_list * |
| 59 | -new_abbrev_list (dwarf_vma abbrev_base, dwarf_vma abbrev_offset) |
| 60 | -{ |
| 61 | - abbrev_list * list = (abbrev_list *) xcalloc (sizeof * list, 1); |
| 62 | - |
| 63 | - list->abbrev_base = abbrev_base; |
| 64 | - list->abbrev_offset = abbrev_offset; |
| 65 | - |
| 66 | - list->next = abbrev_lists; |
| 67 | - abbrev_lists = list; |
| 68 | - |
| 69 | - return list; |
| 70 | -} |
| 71 | - |
| 72 | -static abbrev_list * |
| 73 | find_abbrev_list_by_abbrev_offset (dwarf_vma abbrev_base, |
| 74 | dwarf_vma abbrev_offset) |
| 75 | { |
| 76 | @@ -966,7 +959,7 @@ find_abbrev_list_by_abbrev_offset (dwarf |
| 77 | /* Find the abbreviation map for the CU that includes OFFSET. |
| 78 | OFFSET is an absolute offset from the start of the .debug_info section. */ |
| 79 | /* FIXME: This function is going to slow down readelf & objdump. |
| 80 | - Consider using a better algorithm to mitigate this effect. */ |
| 81 | + Not caching abbrevs is likely the answer. */ |
| 82 | |
| 83 | static abbrev_map * |
| 84 | find_abbrev_map_by_offset (dwarf_vma offset) |
| 85 | @@ -1033,40 +1026,18 @@ add_abbrev_attr (unsigned long attrib |
| 86 | list->last_abbrev->last_attr = attr; |
| 87 | } |
| 88 | |
| 89 | -/* Processes the (partial) contents of a .debug_abbrev section. |
| 90 | - Returns NULL if the end of the section was encountered. |
| 91 | - Returns the address after the last byte read if the end of |
| 92 | - an abbreviation set was found. */ |
| 93 | +/* Return processed (partial) contents of a .debug_abbrev section. |
| 94 | + Returns NULL on errors. */ |
| 95 | |
| 96 | -static unsigned char * |
| 97 | +static abbrev_list * |
| 98 | process_abbrev_set (struct dwarf_section *section, |
| 99 | - dwarf_vma abbrev_base, |
| 100 | - dwarf_vma abbrev_size, |
| 101 | - dwarf_vma abbrev_offset, |
| 102 | - abbrev_list *list) |
| 103 | + unsigned char *start, |
| 104 | + unsigned char *end) |
| 105 | { |
| 106 | - if (abbrev_base >= section->size |
| 107 | - || abbrev_size > section->size - abbrev_base) |
| 108 | - { |
| 109 | - /* PR 17531: file:4bcd9ce9. */ |
| 110 | - warn (_("Debug info is corrupted, abbrev size (%lx) is larger than " |
| 111 | - "abbrev section size (%lx)\n"), |
| 112 | - (unsigned long) (abbrev_base + abbrev_size), |
| 113 | - (unsigned long) section->size); |
| 114 | - return NULL; |
| 115 | - } |
| 116 | - if (abbrev_offset >= abbrev_size) |
| 117 | - { |
| 118 | - warn (_("Debug info is corrupted, abbrev offset (%lx) is larger than " |
| 119 | - "abbrev section size (%lx)\n"), |
| 120 | - (unsigned long) abbrev_offset, |
| 121 | - (unsigned long) abbrev_size); |
| 122 | - return NULL; |
| 123 | - } |
| 124 | + abbrev_list *list = xmalloc (sizeof (*list)); |
| 125 | + list->first_abbrev = NULL; |
| 126 | + list->last_abbrev = NULL; |
| 127 | |
| 128 | - unsigned char *start = section->start + abbrev_base; |
| 129 | - unsigned char *end = start + abbrev_size; |
| 130 | - start += abbrev_offset; |
| 131 | while (start < end) |
| 132 | { |
| 133 | unsigned long entry; |
| 134 | @@ -1079,14 +1050,18 @@ process_abbrev_set (struct dwarf_section |
| 135 | /* A single zero is supposed to end the set according |
| 136 | to the standard. If there's more, then signal that to |
| 137 | the caller. */ |
| 138 | - if (start == end) |
| 139 | - return NULL; |
| 140 | - if (entry == 0) |
| 141 | - return start; |
| 142 | + if (start == end || entry == 0) |
| 143 | + { |
| 144 | + list->start_of_next_abbrevs = start != end ? start : NULL; |
| 145 | + return list; |
| 146 | + } |
| 147 | |
| 148 | READ_ULEB (tag, start, end); |
| 149 | if (start == end) |
| 150 | - return NULL; |
| 151 | + { |
| 152 | + free (list); |
| 153 | + return NULL; |
| 154 | + } |
| 155 | |
| 156 | children = *start++; |
| 157 | |
| 158 | @@ -1121,9 +1096,67 @@ process_abbrev_set (struct dwarf_section |
| 159 | /* Report the missing single zero which ends the section. */ |
| 160 | error (_(".debug_abbrev section not zero terminated\n")); |
| 161 | |
| 162 | + free (list); |
| 163 | return NULL; |
| 164 | } |
| 165 | |
| 166 | +/* Return a sequence of abbrevs in SECTION starting at ABBREV_BASE |
| 167 | + plus ABBREV_OFFSET and finishing at ABBREV_BASE + ABBREV_SIZE. |
| 168 | + If FREE_LIST is non-NULL search the already decoded abbrevs on |
| 169 | + abbrev_lists first and if found set *FREE_LIST to NULL. If |
| 170 | + searching doesn't find a matching abbrev, set *FREE_LIST to the |
| 171 | + newly allocated list. If FREE_LIST is NULL, no search is done and |
| 172 | + the returned abbrev_list is always newly allocated. */ |
| 173 | + |
| 174 | +static abbrev_list * |
| 175 | +find_and_process_abbrev_set (struct dwarf_section *section, |
| 176 | + dwarf_vma abbrev_base, |
| 177 | + dwarf_vma abbrev_size, |
| 178 | + dwarf_vma abbrev_offset, |
| 179 | + abbrev_list **free_list) |
| 180 | +{ |
| 181 | + if (free_list) |
| 182 | + *free_list = NULL; |
| 183 | + |
| 184 | + if (abbrev_base >= section->size |
| 185 | + || abbrev_size > section->size - abbrev_base) |
| 186 | + { |
| 187 | + /* PR 17531: file:4bcd9ce9. */ |
| 188 | + warn (_("Debug info is corrupted, abbrev size (%lx) is larger than " |
| 189 | + "abbrev section size (%lx)\n"), |
| 190 | + (unsigned long) (abbrev_base + abbrev_size), |
| 191 | + (unsigned long) section->size); |
| 192 | + return NULL; |
| 193 | + } |
| 194 | + if (abbrev_offset >= abbrev_size) |
| 195 | + { |
| 196 | + warn (_("Debug info is corrupted, abbrev offset (%lx) is larger than " |
| 197 | + "abbrev section size (%lx)\n"), |
| 198 | + (unsigned long) abbrev_offset, |
| 199 | + (unsigned long) abbrev_size); |
| 200 | + return NULL; |
| 201 | + } |
| 202 | + |
| 203 | + unsigned char *start = section->start + abbrev_base + abbrev_offset; |
| 204 | + unsigned char *end = section->start + abbrev_base + abbrev_size; |
| 205 | + abbrev_list *list = NULL; |
| 206 | + if (free_list) |
| 207 | + list = find_abbrev_list_by_abbrev_offset (abbrev_base, abbrev_offset); |
| 208 | + if (list == NULL) |
| 209 | + { |
| 210 | + list = process_abbrev_set (section, start, end); |
| 211 | + if (list) |
| 212 | + { |
| 213 | + list->abbrev_base = abbrev_base; |
| 214 | + list->abbrev_offset = abbrev_offset; |
| 215 | + list->next = NULL; |
| 216 | + } |
| 217 | + if (free_list) |
| 218 | + *free_list = list; |
| 219 | + } |
| 220 | + return list; |
| 221 | +} |
| 222 | + |
| 223 | static const char * |
| 224 | get_TAG_name (unsigned long tag) |
| 225 | { |
| 226 | @@ -3670,7 +3703,6 @@ process_debug_info (struct dwarf_section |
| 227 | dwarf_vma cu_offset; |
| 228 | unsigned int offset_size; |
| 229 | struct cu_tu_set * this_set; |
| 230 | - abbrev_list * list; |
| 231 | unsigned char *end_cu; |
| 232 | |
| 233 | hdrptr = start; |
| 234 | @@ -3726,22 +3758,18 @@ process_debug_info (struct dwarf_section |
| 235 | abbrev_size = this_set->section_sizes [DW_SECT_ABBREV]; |
| 236 | } |
| 237 | |
| 238 | - list = find_abbrev_list_by_abbrev_offset (abbrev_base, |
| 239 | - compunit.cu_abbrev_offset); |
| 240 | - if (list == NULL) |
| 241 | - { |
| 242 | - unsigned char * next; |
| 243 | - |
| 244 | - list = new_abbrev_list (abbrev_base, |
| 245 | - compunit.cu_abbrev_offset); |
| 246 | - next = process_abbrev_set (&debug_displays[abbrev_sec].section, |
| 247 | - abbrev_base, abbrev_size, |
| 248 | - compunit.cu_abbrev_offset, list); |
| 249 | - list->start_of_next_abbrevs = next; |
| 250 | - } |
| 251 | - |
| 252 | + abbrev_list *list; |
| 253 | + abbrev_list *free_list; |
| 254 | + list = find_and_process_abbrev_set (&debug_displays[abbrev_sec].section, |
| 255 | + abbrev_base, abbrev_size, |
| 256 | + compunit.cu_abbrev_offset, |
| 257 | + &free_list); |
| 258 | start = end_cu; |
| 259 | - record_abbrev_list_for_cu (cu_offset, start - section_begin, list); |
| 260 | + if (list != NULL && list->first_abbrev != NULL) |
| 261 | + record_abbrev_list_for_cu (cu_offset, start - section_begin, |
| 262 | + list, free_list); |
| 263 | + else if (free_list != NULL) |
| 264 | + free_abbrev_list (free_list); |
| 265 | } |
| 266 | |
| 267 | for (start = section_begin, unit = 0; start < end; unit++) |
| 268 | @@ -3757,7 +3785,6 @@ process_debug_info (struct dwarf_section |
| 269 | struct cu_tu_set *this_set; |
| 270 | dwarf_vma abbrev_base; |
| 271 | size_t abbrev_size; |
| 272 | - abbrev_list * list = NULL; |
| 273 | unsigned char *end_cu; |
| 274 | |
| 275 | hdrptr = start; |
| 276 | @@ -3936,20 +3963,10 @@ process_debug_info (struct dwarf_section |
| 277 | } |
| 278 | |
| 279 | /* Process the abbrevs used by this compilation unit. */ |
| 280 | - list = find_abbrev_list_by_abbrev_offset (abbrev_base, |
| 281 | - compunit.cu_abbrev_offset); |
| 282 | - if (list == NULL) |
| 283 | - { |
| 284 | - unsigned char *next; |
| 285 | - |
| 286 | - list = new_abbrev_list (abbrev_base, |
| 287 | - compunit.cu_abbrev_offset); |
| 288 | - next = process_abbrev_set (&debug_displays[abbrev_sec].section, |
| 289 | - abbrev_base, abbrev_size, |
| 290 | - compunit.cu_abbrev_offset, list); |
| 291 | - list->start_of_next_abbrevs = next; |
| 292 | - } |
| 293 | - |
| 294 | + abbrev_list *list; |
| 295 | + list = find_and_process_abbrev_set (&debug_displays[abbrev_sec].section, |
| 296 | + abbrev_base, abbrev_size, |
| 297 | + compunit.cu_abbrev_offset, NULL); |
| 298 | level = 0; |
| 299 | last_level = level; |
| 300 | saved_level = -1; |
| 301 | @@ -4128,6 +4145,8 @@ process_debug_info (struct dwarf_section |
| 302 | if (entry->children) |
| 303 | ++level; |
| 304 | } |
| 305 | + if (list != NULL) |
| 306 | + free_abbrev_list (list); |
| 307 | } |
| 308 | |
| 309 | /* Set num_debug_info_entries here so that it can be used to check if |
| 310 | @@ -6353,24 +6372,15 @@ display_debug_abbrev (struct dwarf_secti |
| 311 | |
| 312 | do |
| 313 | { |
| 314 | - abbrev_list * list; |
| 315 | - dwarf_vma offset; |
| 316 | - |
| 317 | - offset = start - section->start; |
| 318 | - list = find_abbrev_list_by_abbrev_offset (0, offset); |
| 319 | + dwarf_vma offset = start - section->start; |
| 320 | + abbrev_list *list = find_and_process_abbrev_set (section, 0, |
| 321 | + section->size, offset, |
| 322 | + NULL); |
| 323 | if (list == NULL) |
| 324 | - { |
| 325 | - list = new_abbrev_list (0, offset); |
| 326 | - start = process_abbrev_set (section, 0, section->size, offset, list); |
| 327 | - list->start_of_next_abbrevs = start; |
| 328 | - } |
| 329 | - else |
| 330 | - start = list->start_of_next_abbrevs; |
| 331 | - |
| 332 | - if (list->first_abbrev == NULL) |
| 333 | - continue; |
| 334 | + break; |
| 335 | |
| 336 | - printf (_(" Number TAG (0x%lx)\n"), (long) offset); |
| 337 | + if (list->first_abbrev) |
| 338 | + printf (_(" Number TAG (0x%lx)\n"), (long) offset); |
| 339 | |
| 340 | for (entry = list->first_abbrev; entry; entry = entry->next) |
| 341 | { |
| 342 | @@ -6391,6 +6401,8 @@ display_debug_abbrev (struct dwarf_secti |
| 343 | putchar ('\n'); |
| 344 | } |
| 345 | } |
| 346 | + start = list->start_of_next_abbrevs; |
| 347 | + free_abbrev_list (list); |
| 348 | } |
| 349 | while (start); |
| 350 | |