1 1.1 joerg //===------------------------- AddressSpace.hpp ---------------------------===// 2 1.1 joerg // 3 1.1 joerg // The LLVM Compiler Infrastructure 4 1.1 joerg // 5 1.1 joerg // This file is dual licensed under the MIT and the University of Illinois Open 6 1.1 joerg // Source Licenses. See LICENSE.TXT for details. 7 1.1 joerg // 8 1.1 joerg // 9 1.1 joerg // Abstracts accessing local vs remote address spaces. 10 1.1 joerg // 11 1.1 joerg //===----------------------------------------------------------------------===// 12 1.1 joerg 13 1.1 joerg #ifndef __ADDRESSSPACE_HPP__ 14 1.1 joerg #define __ADDRESSSPACE_HPP__ 15 1.1 joerg 16 1.1 joerg #include <sys/rbtree.h> 17 1.1 joerg #include <cassert> 18 1.1 joerg #include <cstddef> 19 1.1 joerg #include <cstdint> 20 1.1 joerg #include <cstdlib> 21 1.1 joerg #include <cstring> 22 1.1 joerg #include <dlfcn.h> 23 1.1 joerg #include <elf.h> 24 1.1 joerg #include <link.h> 25 1.1 joerg #include <pthread.h> 26 1.1 joerg 27 1.1 joerg #include "dwarf2.h" 28 1.1 joerg 29 1.1 joerg namespace _Unwind { 30 1.1 joerg 31 1.1 joerg static int rangeCmp(void *, const void *, const void *); 32 1.1 joerg static int rangeCmpKey(void *, const void *, const void *); 33 1.1 joerg static int dsoTableCmp(void *, const void *, const void *); 34 1.1 joerg static int dsoTableCmpKey(void *, const void *, const void *); 35 1.1 joerg static int phdr_callback(struct dl_phdr_info *, size_t, void *); 36 1.1 joerg 37 1.1 joerg struct unw_proc_info_t { 38 1.1 joerg uintptr_t data_base; // Base address for data-relative relocations 39 1.1 joerg uintptr_t start_ip; // Start address of function 40 1.1 joerg uintptr_t end_ip; // First address after end of function 41 1.1 joerg uintptr_t lsda; // Address of Language Specific Data Area 42 1.1 joerg uintptr_t handler; // Personality routine 43 1.1 joerg uintptr_t extra_args; // Extra stack space for frameless routines 44 1.1 joerg uintptr_t unwind_info; // Address of DWARF unwind info 45 1.1 joerg }; 46 1.1 joerg 47 1.1 joerg /// LocalAddressSpace is used as a template parameter to UnwindCursor when 48 1.1 joerg /// unwinding a thread in the same process. The wrappers compile away, 49 1.1 joerg /// making local unwinds fast. 50 1.1 joerg class LocalAddressSpace { 51 1.1 joerg public: 52 1.1 joerg typedef uintptr_t pint_t; 53 1.1 joerg typedef intptr_t sint_t; 54 1.1 joerg 55 1.1 joerg typedef void (*findPCRange_t)(LocalAddressSpace &, pint_t, pint_t &pcStart, 56 1.1 joerg pint_t &pcEnd); 57 1.1 joerg 58 1.1 joerg LocalAddressSpace(findPCRange_t findPCRange_) 59 1.1 joerg : findPCRange(findPCRange_), needsReload(true) { 60 1.1 joerg static const rb_tree_ops_t segmentTreeOps = { 61 1.1 joerg rangeCmp, rangeCmpKey, offsetof(Range, range_link), NULL 62 1.1 joerg }; 63 1.1 joerg static const rb_tree_ops_t dsoTreeOps = { 64 1.1 joerg dsoTableCmp, dsoTableCmpKey, offsetof(Range, dso_link), NULL 65 1.1 joerg }; 66 1.1 joerg rb_tree_init(&segmentTree, &segmentTreeOps); 67 1.1 joerg rb_tree_init(&dsoTree, &dsoTreeOps); 68 1.1 joerg pthread_rwlock_init(&fdeTreeLock, NULL); 69 1.1 joerg } 70 1.1 joerg 71 1.4 joerg uint8_t get8(pint_t addr) { 72 1.4 joerg uint8_t val; 73 1.4 joerg memcpy(&val, (void *)addr, sizeof(val)); 74 1.4 joerg return val; 75 1.4 joerg } 76 1.1 joerg 77 1.4 joerg uint16_t get16(pint_t addr) { 78 1.4 joerg uint16_t val; 79 1.4 joerg memcpy(&val, (void *)addr, sizeof(val)); 80 1.4 joerg return val; 81 1.4 joerg } 82 1.1 joerg 83 1.4 joerg uint32_t get32(pint_t addr) { 84 1.4 joerg uint32_t val; 85 1.4 joerg memcpy(&val, (void *)addr, sizeof(val)); 86 1.4 joerg return val; 87 1.4 joerg } 88 1.1 joerg 89 1.4 joerg uint64_t get64(pint_t addr) { 90 1.4 joerg uint64_t val; 91 1.4 joerg memcpy(&val, (void *)addr, sizeof(val)); 92 1.4 joerg return val; 93 1.4 joerg } 94 1.1 joerg 95 1.1 joerg uintptr_t getP(pint_t addr) { 96 1.1 joerg if (sizeof(uintptr_t) == sizeof(uint32_t)) 97 1.1 joerg return get32(addr); 98 1.1 joerg else 99 1.1 joerg return get64(addr); 100 1.1 joerg } 101 1.1 joerg 102 1.1 joerg uint64_t getULEB128(pint_t &addr, pint_t end) { 103 1.1 joerg uint64_t result = 0; 104 1.1 joerg uint8_t byte; 105 1.1 joerg int bit = 0; 106 1.1 joerg do { 107 1.1 joerg uint64_t b; 108 1.1 joerg 109 1.1 joerg assert(addr != end); 110 1.1 joerg 111 1.1 joerg byte = get8(addr++); 112 1.1 joerg b = byte & 0x7f; 113 1.1 joerg 114 1.1 joerg assert(bit < 64); 115 1.1 joerg assert(b << bit >> bit == b); 116 1.1 joerg 117 1.1 joerg result |= b << bit; 118 1.1 joerg bit += 7; 119 1.1 joerg } while (byte >= 0x80); 120 1.1 joerg return result; 121 1.1 joerg } 122 1.1 joerg 123 1.1 joerg int64_t getSLEB128(pint_t &addr, pint_t end) { 124 1.1 joerg uint64_t result = 0; 125 1.1 joerg uint8_t byte; 126 1.1 joerg int bit = 0; 127 1.1 joerg do { 128 1.1 joerg uint64_t b; 129 1.1 joerg 130 1.1 joerg assert(addr != end); 131 1.1 joerg 132 1.1 joerg byte = get8(addr++); 133 1.1 joerg b = byte & 0x7f; 134 1.1 joerg 135 1.1 joerg assert(bit < 64); 136 1.1 joerg assert(b << bit >> bit == b); 137 1.1 joerg 138 1.1 joerg result |= b << bit; 139 1.1 joerg bit += 7; 140 1.1 joerg } while (byte >= 0x80); 141 1.1 joerg // sign extend negative numbers 142 1.1 joerg if ((byte & 0x40) != 0) 143 1.9 christos result |= (~0ULL) << bit; 144 1.1 joerg return result; 145 1.1 joerg } 146 1.1 joerg 147 1.1 joerg pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding, 148 1.1 joerg const unw_proc_info_t *ctx) { 149 1.1 joerg pint_t startAddr = addr; 150 1.1 joerg const uint8_t *p = (uint8_t *)addr; 151 1.1 joerg pint_t result; 152 1.1 joerg 153 1.1 joerg if (encoding == DW_EH_PE_omit) 154 1.1 joerg return 0; 155 1.1 joerg if (encoding == DW_EH_PE_aligned) { 156 1.1 joerg addr = (addr + sizeof(pint_t) - 1) & sizeof(pint_t); 157 1.1 joerg return getP(addr); 158 1.1 joerg } 159 1.1 joerg 160 1.1 joerg // first get value 161 1.1 joerg switch (encoding & 0x0F) { 162 1.1 joerg case DW_EH_PE_ptr: 163 1.1 joerg result = getP(addr); 164 1.1 joerg p += sizeof(pint_t); 165 1.1 joerg addr = (pint_t)p; 166 1.1 joerg break; 167 1.1 joerg case DW_EH_PE_uleb128: 168 1.1 joerg result = getULEB128(addr, end); 169 1.1 joerg break; 170 1.1 joerg case DW_EH_PE_udata2: 171 1.1 joerg result = get16(addr); 172 1.1 joerg p += 2; 173 1.1 joerg addr = (pint_t)p; 174 1.1 joerg break; 175 1.1 joerg case DW_EH_PE_udata4: 176 1.1 joerg result = get32(addr); 177 1.1 joerg p += 4; 178 1.1 joerg addr = (pint_t)p; 179 1.1 joerg break; 180 1.1 joerg case DW_EH_PE_udata8: 181 1.1 joerg result = get64(addr); 182 1.1 joerg p += 8; 183 1.1 joerg addr = (pint_t)p; 184 1.1 joerg break; 185 1.1 joerg case DW_EH_PE_sleb128: 186 1.1 joerg result = getSLEB128(addr, end); 187 1.1 joerg break; 188 1.1 joerg case DW_EH_PE_sdata2: 189 1.1 joerg result = (int16_t)get16(addr); 190 1.1 joerg p += 2; 191 1.1 joerg addr = (pint_t)p; 192 1.1 joerg break; 193 1.1 joerg case DW_EH_PE_sdata4: 194 1.1 joerg result = (int32_t)get32(addr); 195 1.1 joerg p += 4; 196 1.1 joerg addr = (pint_t)p; 197 1.1 joerg break; 198 1.1 joerg case DW_EH_PE_sdata8: 199 1.1 joerg result = get64(addr); 200 1.1 joerg p += 8; 201 1.1 joerg addr = (pint_t)p; 202 1.1 joerg break; 203 1.1 joerg case DW_EH_PE_omit: 204 1.1 joerg result = 0; 205 1.1 joerg break; 206 1.1 joerg default: 207 1.1 joerg assert(0 && "unknown pointer encoding"); 208 1.1 joerg } 209 1.1 joerg 210 1.1 joerg // then add relative offset 211 1.1 joerg switch (encoding & 0x70) { 212 1.1 joerg case DW_EH_PE_absptr: 213 1.1 joerg // do nothing 214 1.1 joerg break; 215 1.1 joerg case DW_EH_PE_pcrel: 216 1.1 joerg result += startAddr; 217 1.1 joerg break; 218 1.1 joerg case DW_EH_PE_textrel: 219 1.1 joerg assert(0 && "DW_EH_PE_textrel pointer encoding not supported"); 220 1.1 joerg break; 221 1.1 joerg case DW_EH_PE_datarel: 222 1.1 joerg assert(ctx != NULL && "DW_EH_PE_datarel without context"); 223 1.1 joerg if (ctx) 224 1.1 joerg result += ctx->data_base; 225 1.1 joerg break; 226 1.1 joerg case DW_EH_PE_funcrel: 227 1.1 joerg assert(ctx != NULL && "DW_EH_PE_funcrel without context"); 228 1.1 joerg if (ctx) 229 1.1 joerg result += ctx->start_ip; 230 1.1 joerg break; 231 1.1 joerg case DW_EH_PE_aligned: 232 1.1 joerg __builtin_unreachable(); 233 1.1 joerg default: 234 1.1 joerg assert(0 && "unknown pointer encoding"); 235 1.1 joerg break; 236 1.1 joerg } 237 1.1 joerg 238 1.1 joerg if (encoding & DW_EH_PE_indirect) 239 1.1 joerg result = getP(result); 240 1.1 joerg 241 1.1 joerg return result; 242 1.1 joerg } 243 1.1 joerg 244 1.1 joerg bool findFDE(pint_t pc, pint_t &fdeStart, pint_t &data_base) { 245 1.1 joerg Range *n; 246 1.1 joerg for (;;) { 247 1.1 joerg pthread_rwlock_rdlock(&fdeTreeLock); 248 1.1 joerg n = (Range *)rb_tree_find_node(&segmentTree, &pc); 249 1.1 joerg pthread_rwlock_unlock(&fdeTreeLock); 250 1.1 joerg if (n != NULL) 251 1.1 joerg break; 252 1.1 joerg if (!needsReload) 253 1.1 joerg break; 254 1.1 joerg lazyReload(); 255 1.1 joerg } 256 1.1 joerg if (n == NULL) 257 1.1 joerg return false; 258 1.1 joerg if (n->hdr_start == 0) { 259 1.1 joerg fdeStart = n->hdr_base; 260 1.3 joerg data_base = n->data_base; 261 1.1 joerg return true; 262 1.1 joerg } 263 1.1 joerg 264 1.1 joerg pint_t base = n->hdr_base; 265 1.1 joerg pint_t first = n->hdr_start; 266 1.8 joerg for (pint_t len = n->hdr_entries; len > 1; ) { 267 1.8 joerg pint_t next = first + (len / 2) * 8; 268 1.1 joerg pint_t nextPC = base + (int32_t)get32(next); 269 1.1 joerg if (nextPC == pc) { 270 1.1 joerg first = next; 271 1.1 joerg break; 272 1.1 joerg } 273 1.1 joerg if (nextPC < pc) { 274 1.1 joerg first = next; 275 1.8 joerg len -= (len / 2); 276 1.8 joerg } else { 277 1.8 joerg len /= 2; 278 1.8 joerg } 279 1.1 joerg } 280 1.1 joerg fdeStart = base + (int32_t)get32(first + 4); 281 1.3 joerg data_base = n->data_base; 282 1.1 joerg return true; 283 1.1 joerg } 284 1.1 joerg 285 1.1 joerg bool addFDE(pint_t pcStart, pint_t pcEnd, pint_t fde) { 286 1.1 joerg Range *n = (Range *)malloc(sizeof(*n)); 287 1.1 joerg n->hdr_base = fde; 288 1.1 joerg n->hdr_start = 0; 289 1.1 joerg n->hdr_entries = 0; 290 1.1 joerg n->first_pc = pcStart; 291 1.1 joerg n->last_pc = pcEnd; 292 1.1 joerg n->data_base = 0; 293 1.1 joerg n->ehframe_base = 0; 294 1.10 joerg pthread_rwlock_wrlock(&fdeTreeLock); 295 1.6 joerg if (static_cast<Range *>(rb_tree_insert_node(&segmentTree, n)) == n) { 296 1.1 joerg pthread_rwlock_unlock(&fdeTreeLock); 297 1.1 joerg return true; 298 1.1 joerg } 299 1.1 joerg free(n); 300 1.1 joerg pthread_rwlock_unlock(&fdeTreeLock); 301 1.1 joerg return false; 302 1.1 joerg } 303 1.1 joerg 304 1.1 joerg bool removeFDE(pint_t pcStart, pint_t pcEnd, pint_t fde) { 305 1.1 joerg pthread_rwlock_wrlock(&fdeTreeLock); 306 1.6 joerg Range *n = static_cast<Range *>(rb_tree_find_node(&segmentTree, &pcStart)); 307 1.1 joerg if (n == NULL) { 308 1.1 joerg pthread_rwlock_unlock(&fdeTreeLock); 309 1.1 joerg return false; 310 1.1 joerg } 311 1.1 joerg assert(n->first_pc == pcStart); 312 1.1 joerg assert(n->last_pc == pcEnd); 313 1.1 joerg assert(n->hdr_base == fde); 314 1.1 joerg assert(n->hdr_start == 0); 315 1.1 joerg assert(n->hdr_entries == 0); 316 1.1 joerg assert(n->data_base == 0); 317 1.1 joerg assert(n->ehframe_base == 0); 318 1.1 joerg rb_tree_remove_node(&segmentTree, n); 319 1.1 joerg free(n); 320 1.1 joerg pthread_rwlock_unlock(&fdeTreeLock); 321 1.1 joerg return true; 322 1.1 joerg } 323 1.1 joerg 324 1.1 joerg void removeDSO(pint_t ehFrameBase) { 325 1.1 joerg pthread_rwlock_wrlock(&fdeTreeLock); 326 1.1 joerg Range *n; 327 1.1 joerg n = (Range *)rb_tree_find_node(&dsoTree, &ehFrameBase); 328 1.1 joerg if (n == NULL) { 329 1.1 joerg pthread_rwlock_unlock(&fdeTreeLock); 330 1.1 joerg return; 331 1.1 joerg } 332 1.1 joerg rb_tree_remove_node(&dsoTree, n); 333 1.1 joerg rb_tree_remove_node(&segmentTree, n); 334 1.1 joerg free(n); 335 1.1 joerg pthread_rwlock_unlock(&fdeTreeLock); 336 1.1 joerg } 337 1.1 joerg 338 1.1 joerg void setLazyReload() { 339 1.1 joerg pthread_rwlock_wrlock(&fdeTreeLock); 340 1.1 joerg needsReload = true; 341 1.1 joerg pthread_rwlock_unlock(&fdeTreeLock); 342 1.1 joerg } 343 1.1 joerg 344 1.1 joerg private: 345 1.1 joerg findPCRange_t findPCRange; 346 1.1 joerg bool needsReload; 347 1.1 joerg pthread_rwlock_t fdeTreeLock; 348 1.1 joerg rb_tree_t segmentTree; 349 1.1 joerg rb_tree_t dsoTree; 350 1.1 joerg 351 1.1 joerg friend int phdr_callback(struct dl_phdr_info *, size_t, void *); 352 1.1 joerg friend int rangeCmp(void *, const void *, const void *); 353 1.1 joerg friend int rangeCmpKey(void *, const void *, const void *); 354 1.1 joerg friend int dsoTableCmp(void *, const void *, const void *); 355 1.1 joerg friend int dsoTableCmpKey(void *, const void *, const void *); 356 1.1 joerg 357 1.1 joerg void updateRange(); 358 1.1 joerg 359 1.1 joerg struct Range { 360 1.1 joerg rb_node_t range_link; 361 1.1 joerg rb_node_t dso_link; 362 1.1 joerg pint_t hdr_base; // Pointer to FDE if hdr_start == 0 363 1.1 joerg pint_t hdr_start; 364 1.1 joerg pint_t hdr_entries; 365 1.1 joerg pint_t first_pc; 366 1.1 joerg pint_t last_pc; 367 1.1 joerg pint_t data_base; 368 1.1 joerg pint_t ehframe_base; 369 1.1 joerg }; 370 1.1 joerg 371 1.1 joerg void lazyReload() { 372 1.1 joerg pthread_rwlock_wrlock(&fdeTreeLock); 373 1.1 joerg dl_iterate_phdr(phdr_callback, this); 374 1.1 joerg needsReload = false; 375 1.1 joerg pthread_rwlock_unlock(&fdeTreeLock); 376 1.1 joerg } 377 1.1 joerg 378 1.1 joerg void addDSO(pint_t header, pint_t data_base) { 379 1.1 joerg if (header == 0) 380 1.1 joerg return; 381 1.1 joerg if (get8(header) != 1) 382 1.1 joerg return; 383 1.1 joerg if (get8(header + 3) != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) 384 1.1 joerg return; 385 1.1 joerg pint_t end = header + 4; 386 1.1 joerg pint_t ehframe_base = getEncodedP(end, 0, get8(header + 1), NULL); 387 1.1 joerg pint_t entries = getEncodedP(end, 0, get8(header + 2), NULL); 388 1.1 joerg pint_t start = (end + 3) & ~pint_t(3); 389 1.1 joerg if (entries == 0) 390 1.1 joerg return; 391 1.1 joerg Range *n = (Range *)malloc(sizeof(*n)); 392 1.1 joerg n->hdr_base = header; 393 1.1 joerg n->hdr_start = start; 394 1.1 joerg n->hdr_entries = entries; 395 1.1 joerg n->first_pc = header + (int32_t)get32(n->hdr_start); 396 1.1 joerg pint_t tmp; 397 1.1 joerg (*findPCRange)( 398 1.1 joerg *this, header + (int32_t)get32(n->hdr_start + (entries - 1) * 8 + 4), 399 1.1 joerg tmp, n->last_pc); 400 1.1 joerg n->data_base = data_base; 401 1.1 joerg n->ehframe_base = ehframe_base; 402 1.1 joerg 403 1.7 joerg if (static_cast<Range *>(rb_tree_insert_node(&segmentTree, n)) != n) { 404 1.1 joerg free(n); 405 1.1 joerg return; 406 1.1 joerg } 407 1.1 joerg rb_tree_insert_node(&dsoTree, n); 408 1.1 joerg } 409 1.1 joerg }; 410 1.1 joerg 411 1.1 joerg static int phdr_callback(struct dl_phdr_info *info, size_t size, void *data_) { 412 1.1 joerg LocalAddressSpace *data = (LocalAddressSpace *)data_; 413 1.1 joerg size_t eh_frame = 0, data_base = 0; 414 1.1 joerg const Elf_Phdr *hdr = info->dlpi_phdr; 415 1.1 joerg const Elf_Phdr *last_hdr = hdr + info->dlpi_phnum; 416 1.1 joerg const Elf_Dyn *dyn; 417 1.1 joerg 418 1.1 joerg for (; hdr != last_hdr; ++hdr) { 419 1.1 joerg switch (hdr->p_type) { 420 1.1 joerg case PT_GNU_EH_FRAME: 421 1.1 joerg eh_frame = info->dlpi_addr + hdr->p_vaddr; 422 1.1 joerg break; 423 1.1 joerg case PT_DYNAMIC: 424 1.1 joerg dyn = (const Elf_Dyn *)(info->dlpi_addr + hdr->p_vaddr); 425 1.1 joerg while (dyn->d_tag != DT_NULL) { 426 1.1 joerg if (dyn->d_tag == DT_PLTGOT) { 427 1.1 joerg data_base = info->dlpi_addr + dyn->d_un.d_ptr; 428 1.1 joerg break; 429 1.1 joerg } 430 1.1 joerg ++dyn; 431 1.1 joerg } 432 1.1 joerg } 433 1.1 joerg } 434 1.1 joerg 435 1.1 joerg if (eh_frame) 436 1.1 joerg data->addDSO(eh_frame, data_base); 437 1.1 joerg 438 1.1 joerg return 0; 439 1.1 joerg } 440 1.1 joerg 441 1.1 joerg static int rangeCmp(void *context, const void *n1_, const void *n2_) { 442 1.2 joerg const LocalAddressSpace::Range *n1 = (const LocalAddressSpace::Range *)n1_; 443 1.2 joerg const LocalAddressSpace::Range *n2 = (const LocalAddressSpace::Range *)n2_; 444 1.1 joerg 445 1.1 joerg if (n1->first_pc < n2->first_pc) 446 1.1 joerg return -1; 447 1.1 joerg if (n1->first_pc > n2->first_pc) 448 1.1 joerg return 1; 449 1.1 joerg assert(n1->last_pc == n2->last_pc); 450 1.1 joerg return 0; 451 1.1 joerg } 452 1.1 joerg 453 1.1 joerg static int rangeCmpKey(void *context, const void *n_, const void *pc_) { 454 1.2 joerg const LocalAddressSpace::Range *n = (const LocalAddressSpace::Range *)n_; 455 1.2 joerg const LocalAddressSpace::pint_t *pc = (const LocalAddressSpace::pint_t *)pc_; 456 1.1 joerg if (n->last_pc < *pc) 457 1.1 joerg return -1; 458 1.1 joerg if (n->first_pc > *pc) 459 1.1 joerg return 1; 460 1.1 joerg return 0; 461 1.1 joerg } 462 1.1 joerg 463 1.1 joerg static int dsoTableCmp(void *context, const void *n1_, const void *n2_) { 464 1.2 joerg const LocalAddressSpace::Range *n1 = (const LocalAddressSpace::Range *)n1_; 465 1.2 joerg const LocalAddressSpace::Range *n2 = (const LocalAddressSpace::Range *)n2_; 466 1.1 joerg 467 1.1 joerg if (n1->ehframe_base < n2->ehframe_base) 468 1.1 joerg return -1; 469 1.1 joerg if (n1->ehframe_base > n2->ehframe_base) 470 1.1 joerg return 1; 471 1.1 joerg return 0; 472 1.1 joerg } 473 1.1 joerg 474 1.1 joerg static int dsoTableCmpKey(void *context, const void *n_, const void *ptr_) { 475 1.2 joerg const LocalAddressSpace::Range *n = (const LocalAddressSpace::Range *)n_; 476 1.2 joerg const LocalAddressSpace::pint_t *ptr = (const LocalAddressSpace::pint_t *)ptr_; 477 1.1 joerg if (n->ehframe_base < *ptr) 478 1.1 joerg return -1; 479 1.1 joerg if (n->ehframe_base > *ptr) 480 1.1 joerg return 1; 481 1.1 joerg return 0; 482 1.1 joerg } 483 1.1 joerg 484 1.1 joerg } // namespace _Unwind 485 1.1 joerg 486 1.1 joerg #endif // __ADDRESSSPACE_HPP__ 487