1 /* Return location expression list. 2 Copyright (C) 2000, 2001, 2002, 2004 Red Hat, Inc. 3 Written by Ulrich Drepper <drepper (at) redhat.com>, 2000. 4 5 This program is Open Source software; you can redistribute it and/or 6 modify it under the terms of the Open Software License version 1.0 as 7 published by the Open Source Initiative. 8 9 You should have received a copy of the Open Software License along 10 with this program; if not, you may obtain a copy of the Open Software 11 License version 1.0 from http://www.opensource.org/licenses/osl.php or 12 by writing the Open Source Initiative c/o Lawrence Rosen, Esq., 13 3001 King Ranch Road, Ukiah, CA 95482. */ 14 15 #ifdef HAVE_CONFIG_H 16 # include <config.h> 17 #endif 18 19 #include <dwarf.h> 20 #include <search.h> 21 #include <stdlib.h> 22 23 #include <libdwP.h> 24 25 26 struct loclist 27 { 28 uint8_t atom; 29 Dwarf_Word number; 30 Dwarf_Word number2; 31 Dwarf_Word offset; 32 struct loclist *next; 33 }; 34 35 36 static int 37 loc_compare (const void *p1, const void *p2) 38 { 39 const struct loc_s *l1 = (const struct loc_s *) p1; 40 const struct loc_s *l2 = (const struct loc_s *) p2; 41 42 if ((uintptr_t) l1->addr < (uintptr_t) l2->addr) 43 return -1; 44 if ((uintptr_t) l1->addr > (uintptr_t) l2->addr) 45 return 1; 46 47 return 0; 48 } 49 50 51 int 52 dwarf_getloclist (attr, llbuf, listlen) 53 Dwarf_Attribute *attr; 54 Dwarf_Loc **llbuf; 55 size_t *listlen; 56 { 57 /* Must by one of the attribute listed below. */ 58 if (attr->code != DW_AT_location 59 && attr->code != DW_AT_data_member_location 60 && attr->code != DW_AT_vtable_elem_location 61 && attr->code != DW_AT_string_length 62 && attr->code != DW_AT_use_location 63 && attr->code != DW_AT_return_addr) 64 { 65 __libdw_seterrno (DWARF_E_NO_LOCLIST); 66 return -1; 67 } 68 69 struct Dwarf_CU *cu = attr->cu; 70 Dwarf *dbg = cu->dbg; 71 72 /* Must have the form data4 or data8 which act as an offset. */ 73 Dwarf_Block block; 74 if (dwarf_formblock (attr, &block) != 0) 75 return -1; 76 77 /* Check whether we already looked at this list. */ 78 struct loc_s fake = { .addr = block.data }; 79 struct loc_s **found = tfind (&fake, &cu->locs, loc_compare); 80 if (found != NULL) 81 { 82 /* We already saw it. */ 83 *llbuf = (*found)->loc; 84 *listlen = (*found)->nloc; 85 86 return 0; 87 } 88 89 unsigned char *data = block.data; 90 unsigned char *const end_data = data + block.length; 91 92 struct loclist *loclist = NULL; 93 unsigned int n = 0; 94 /* Decode the opcodes. It is possible in some situations to have a 95 block of size zero. */ 96 while (data < end_data) 97 { 98 struct loclist *newloc; 99 newloc = (struct loclist *) alloca (sizeof (struct loclist)); 100 newloc->number = 0; 101 newloc->number2 = 0; 102 newloc->offset = data - block.data; 103 newloc->next = loclist; 104 loclist = newloc; 105 ++n; 106 107 switch ((newloc->atom = *data++)) 108 { 109 case DW_OP_addr: 110 /* Address, depends on address size of CU. */ 111 if (cu->address_size == 4) 112 { 113 if (unlikely (data + 4 > end_data)) 114 { 115 invalid: 116 __libdw_seterrno (DWARF_E_INVALID_DWARF); 117 return -1; 118 } 119 120 newloc->number = read_4ubyte_unaligned_inc (dbg, data); 121 } 122 else 123 { 124 if (unlikely (data + 8 > end_data)) 125 goto invalid; 126 127 newloc->number = read_8ubyte_unaligned_inc (dbg, data); 128 } 129 break; 130 131 case DW_OP_deref: 132 case DW_OP_dup: 133 case DW_OP_drop: 134 case DW_OP_over: 135 case DW_OP_swap: 136 case DW_OP_rot: 137 case DW_OP_xderef: 138 case DW_OP_abs: 139 case DW_OP_and: 140 case DW_OP_div: 141 case DW_OP_minus: 142 case DW_OP_mod: 143 case DW_OP_mul: 144 case DW_OP_neg: 145 case DW_OP_not: 146 case DW_OP_or: 147 case DW_OP_plus: 148 case DW_OP_shl: 149 case DW_OP_shr: 150 case DW_OP_shra: 151 case DW_OP_xor: 152 case DW_OP_eq: 153 case DW_OP_ge: 154 case DW_OP_gt: 155 case DW_OP_le: 156 case DW_OP_lt: 157 case DW_OP_ne: 158 case DW_OP_lit0 ... DW_OP_lit31: 159 case DW_OP_reg0 ... DW_OP_reg31: 160 case DW_OP_nop: 161 case DW_OP_push_object_address: 162 case DW_OP_call_ref: 163 /* No operand. */ 164 break; 165 166 case DW_OP_const1u: 167 case DW_OP_pick: 168 case DW_OP_deref_size: 169 case DW_OP_xderef_size: 170 if (unlikely (data >= end_data)) 171 goto invalid; 172 173 newloc->number = *data++; 174 break; 175 176 case DW_OP_const1s: 177 if (unlikely (data >= end_data)) 178 goto invalid; 179 180 newloc->number = *((int8_t *) data); 181 ++data; 182 break; 183 184 case DW_OP_const2u: 185 if (unlikely (data + 2 > end_data)) 186 goto invalid; 187 188 newloc->number = read_2ubyte_unaligned_inc (dbg, data); 189 break; 190 191 case DW_OP_const2s: 192 case DW_OP_skip: 193 case DW_OP_bra: 194 case DW_OP_call2: 195 if (unlikely (data + 2 > end_data)) 196 goto invalid; 197 198 newloc->number = read_2sbyte_unaligned_inc (dbg, data); 199 break; 200 201 case DW_OP_const4u: 202 if (unlikely (data + 4 > end_data)) 203 goto invalid; 204 205 newloc->number = read_4ubyte_unaligned_inc (dbg, data); 206 break; 207 208 case DW_OP_const4s: 209 case DW_OP_call4: 210 if (unlikely (data + 4 > end_data)) 211 goto invalid; 212 213 newloc->number = read_4sbyte_unaligned_inc (dbg, data); 214 break; 215 216 case DW_OP_const8u: 217 if (unlikely (data + 8 > end_data)) 218 goto invalid; 219 220 newloc->number = read_8ubyte_unaligned_inc (dbg, data); 221 break; 222 223 case DW_OP_const8s: 224 if (unlikely (data + 8 > end_data)) 225 goto invalid; 226 227 newloc->number = read_8sbyte_unaligned_inc (dbg, data); 228 break; 229 230 case DW_OP_constu: 231 case DW_OP_plus_uconst: 232 case DW_OP_regx: 233 case DW_OP_piece: 234 /* XXX Check size. */ 235 get_uleb128 (newloc->number, data); 236 break; 237 238 case DW_OP_consts: 239 case DW_OP_breg0 ... DW_OP_breg31: 240 case DW_OP_fbreg: 241 /* XXX Check size. */ 242 get_sleb128 (newloc->number, data); 243 break; 244 245 case DW_OP_bregx: 246 /* XXX Check size. */ 247 get_uleb128 (newloc->number, data); 248 get_sleb128 (newloc->number2, data); 249 break; 250 251 default: 252 goto invalid; 253 } 254 } 255 256 if (unlikely (n == 0)) 257 { 258 /* This is not allowed. 259 260 XXX Is it? */ 261 goto invalid; 262 } 263 264 /* Allocate the array. */ 265 Dwarf_Loc *result = libdw_alloc (dbg, Dwarf_Loc, sizeof (Dwarf_Loc), n); 266 267 /* Store the result. */ 268 *llbuf = result; 269 *listlen = n; 270 271 do 272 { 273 /* We populate the array from the back since the list is 274 backwards. */ 275 --n; 276 result[n].atom = loclist->atom; 277 result[n].number = loclist->number; 278 result[n].number2 = loclist->number2; 279 result[n].offset = loclist->offset; 280 281 loclist = loclist->next; 282 } 283 while (n > 0); 284 285 /* Insert a record in the search tree so that we can find it again 286 later. */ 287 struct loc_s *newp = libdw_alloc (dbg, struct loc_s, sizeof (struct loc_s), 288 1); 289 newp->addr = block.data; 290 newp->loc = result; 291 newp->nloc = *listlen; 292 (void) tsearch (newp, &cu->locs, loc_compare); 293 294 /* We did it. */ 295 return 0; 296 } 297