1 //===------------------------- AddressSpace.hpp ---------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
9 // Abstracts accessing local vs remote address spaces.
11 //===----------------------------------------------------------------------===//
13 #ifndef __ADDRESSSPACE_HPP__
14 #define __ADDRESSSPACE_HPP__
21 #ifndef _LIBUNWIND_USE_DLADDR
22 #if !defined(_LIBUNWIND_IS_BAREMETAL) && !defined(_WIN32)
23 #define _LIBUNWIND_USE_DLADDR 1
25 #define _LIBUNWIND_USE_DLADDR 0
29 #if _LIBUNWIND_USE_DLADDR
34 #include <mach-o/getsect.h>
36 bool checkKeyMgrRegisteredFDEs(uintptr_t targetAddr, void *&fde);
40 #include "libunwind.h"
43 #include "EHHeaderParser.hpp"
44 #include "Registers.hpp"
48 struct dyld_unwind_sections
50 const struct mach_header* mh;
51 const void* dwarf_section;
52 uintptr_t dwarf_section_length;
53 const void* compact_unwind_section;
54 uintptr_t compact_unwind_section_length;
56 #if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \
57 && (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)) \
58 || defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
59 // In 10.7.0 or later, libSystem.dylib implements this function.
60 extern "C" bool _dyld_find_unwind_sections(void *, dyld_unwind_sections *);
62 // In 10.6.x and earlier, we need to implement this functionality. Note
63 // that this requires a newer version of libmacho (from cctools) than is
64 // present in libSystem on 10.6.x (for getsectiondata).
65 static inline bool _dyld_find_unwind_sections(void* addr,
66 dyld_unwind_sections* info) {
67 // Find mach-o image containing address.
69 if (!dladdr(addr, &dlinfo))
72 const struct mach_header_64 *mh = (const struct mach_header_64 *)dlinfo.dli_fbase;
74 const struct mach_header *mh = (const struct mach_header *)dlinfo.dli_fbase;
77 // Initialize the return struct
78 info->mh = (const struct mach_header *)mh;
79 info->dwarf_section = getsectiondata(mh, "__TEXT", "__eh_frame", &info->dwarf_section_length);
80 info->compact_unwind_section = getsectiondata(mh, "__TEXT", "__unwind_info", &info->compact_unwind_section_length);
82 if (!info->dwarf_section) {
83 info->dwarf_section_length = 0;
86 if (!info->compact_unwind_section) {
87 info->compact_unwind_section_length = 0;
94 #elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_LIBUNWIND_IS_BAREMETAL)
96 // When statically linked on bare-metal, the symbols for the EH table are looked
97 // up without going through the dynamic loader.
99 // The following linker script may be used to produce the necessary sections and symbols.
100 // Unless the --eh-frame-hdr linker option is provided, the section is not generated
101 // and does not take space in the output file.
105 // __eh_frame_start = .;
106 // KEEP(*(.eh_frame))
107 // __eh_frame_end = .;
112 // KEEP(*(.eh_frame_hdr))
115 // __eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0;
116 // __eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0;
118 extern char __eh_frame_start;
119 extern char __eh_frame_end;
121 #if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
122 extern char __eh_frame_hdr_start;
123 extern char __eh_frame_hdr_end;
126 #elif defined(_LIBUNWIND_ARM_EHABI) && defined(_LIBUNWIND_IS_BAREMETAL)
128 // When statically linked on bare-metal, the symbols for the EH table are looked
129 // up without going through the dynamic loader.
130 extern char __exidx_start;
131 extern char __exidx_end;
133 #elif defined(_LIBUNWIND_ARM_EHABI) || defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
135 // ELF-based systems may use dl_iterate_phdr() to access sections
136 // containing unwinding information. The ElfW() macro for pointer-size
137 // independent ELF header traversal is not provided by <link.h> on some
138 // systems (e.g., FreeBSD). On these systems the data structures are
139 // just called Elf_XXX. Define ElfW() locally.
147 #define ElfW(type) Elf_##type
152 namespace libunwind {
154 /// Used by findUnwindSections() to return info about needed sections.
155 struct UnwindInfoSections {
156 #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) || defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) || \
157 defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND)
158 // No dso_base for SEH or ARM EHABI.
161 #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
162 uintptr_t dwarf_section;
163 uintptr_t dwarf_section_length;
165 #if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
166 uintptr_t dwarf_index_section;
167 uintptr_t dwarf_index_section_length;
169 #if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND)
170 uintptr_t compact_unwind_section;
171 uintptr_t compact_unwind_section_length;
173 #if defined(_LIBUNWIND_ARM_EHABI)
174 uintptr_t arm_section;
175 uintptr_t arm_section_length;
180 /// LocalAddressSpace is used as a template parameter to UnwindCursor when
181 /// unwinding a thread in the same process. The wrappers compile away,
182 /// making local unwinds fast.
183 class _LIBUNWIND_HIDDEN LocalAddressSpace {
185 typedef uintptr_t pint_t;
186 typedef intptr_t sint_t;
187 uint8_t get8(pint_t addr) {
189 memcpy(&val, (void *)addr, sizeof(val));
192 uint16_t get16(pint_t addr) {
194 memcpy(&val, (void *)addr, sizeof(val));
197 uint32_t get32(pint_t addr) {
199 memcpy(&val, (void *)addr, sizeof(val));
202 uint64_t get64(pint_t addr) {
204 memcpy(&val, (void *)addr, sizeof(val));
207 double getDouble(pint_t addr) {
209 memcpy(&val, (void *)addr, sizeof(val));
212 v128 getVector(pint_t addr) {
214 memcpy(&val, (void *)addr, sizeof(val));
217 uintptr_t getP(pint_t addr);
218 uint64_t getRegister(pint_t addr);
219 static uint64_t getULEB128(pint_t &addr, pint_t end);
220 static int64_t getSLEB128(pint_t &addr, pint_t end);
222 pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
223 pint_t datarelBase = 0);
224 bool findFunctionName(pint_t addr, char *buf, size_t bufLen,
226 bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info);
227 bool findOtherFDE(pint_t targetAddr, pint_t &fde);
229 static LocalAddressSpace sThisAddressSpace;
232 inline uintptr_t LocalAddressSpace::getP(pint_t addr) {
233 #if __SIZEOF_POINTER__ == 8
240 inline uint64_t LocalAddressSpace::getRegister(pint_t addr) {
241 #if __SIZEOF_POINTER__ == 8 || defined(__mips64)
248 /// Read a ULEB128 into a 64-bit word.
249 inline uint64_t LocalAddressSpace::getULEB128(pint_t &addr, pint_t end) {
250 const uint8_t *p = (uint8_t *)addr;
251 const uint8_t *pend = (uint8_t *)end;
258 _LIBUNWIND_ABORT("truncated uleb128 expression");
262 if (bit >= 64 || b << bit >> bit != b) {
263 _LIBUNWIND_ABORT("malformed uleb128 expression");
268 } while (*p++ >= 0x80);
273 /// Read a SLEB128 into a 64-bit word.
274 inline int64_t LocalAddressSpace::getSLEB128(pint_t &addr, pint_t end) {
275 const uint8_t *p = (uint8_t *)addr;
276 const uint8_t *pend = (uint8_t *)end;
282 _LIBUNWIND_ABORT("truncated sleb128 expression");
284 result |= ((byte & 0x7f) << bit);
286 } while (byte & 0x80);
287 // sign extend negative numbers
288 if ((byte & 0x40) != 0)
289 result |= (-1ULL) << bit;
294 inline LocalAddressSpace::pint_t
295 LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
296 pint_t datarelBase) {
297 pint_t startAddr = addr;
298 const uint8_t *p = (uint8_t *)addr;
302 switch (encoding & 0x0F) {
308 case DW_EH_PE_uleb128:
309 result = (pint_t)getULEB128(addr, end);
311 case DW_EH_PE_udata2:
312 result = get16(addr);
316 case DW_EH_PE_udata4:
317 result = get32(addr);
321 case DW_EH_PE_udata8:
322 result = (pint_t)get64(addr);
326 case DW_EH_PE_sleb128:
327 result = (pint_t)getSLEB128(addr, end);
329 case DW_EH_PE_sdata2:
330 // Sign extend from signed 16-bit value.
331 result = (pint_t)(int16_t)get16(addr);
335 case DW_EH_PE_sdata4:
336 // Sign extend from signed 32-bit value.
337 result = (pint_t)(int32_t)get32(addr);
341 case DW_EH_PE_sdata8:
342 result = (pint_t)get64(addr);
347 _LIBUNWIND_ABORT("unknown pointer encoding");
350 // then add relative offset
351 switch (encoding & 0x70) {
352 case DW_EH_PE_absptr:
358 case DW_EH_PE_textrel:
359 _LIBUNWIND_ABORT("DW_EH_PE_textrel pointer encoding not supported");
361 case DW_EH_PE_datarel:
362 // DW_EH_PE_datarel is only valid in a few places, so the parameter has a
363 // default value of 0, and we abort in the event that someone calls this
364 // function with a datarelBase of 0 and DW_EH_PE_datarel encoding.
365 if (datarelBase == 0)
366 _LIBUNWIND_ABORT("DW_EH_PE_datarel is invalid with a datarelBase of 0");
367 result += datarelBase;
369 case DW_EH_PE_funcrel:
370 _LIBUNWIND_ABORT("DW_EH_PE_funcrel pointer encoding not supported");
372 case DW_EH_PE_aligned:
373 _LIBUNWIND_ABORT("DW_EH_PE_aligned pointer encoding not supported");
376 _LIBUNWIND_ABORT("unknown pointer encoding");
380 if (encoding & DW_EH_PE_indirect)
381 result = getP(result);
386 inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr,
387 UnwindInfoSections &info) {
389 dyld_unwind_sections dyldInfo;
390 if (_dyld_find_unwind_sections((void *)targetAddr, &dyldInfo)) {
391 info.dso_base = (uintptr_t)dyldInfo.mh;
392 #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
393 info.dwarf_section = (uintptr_t)dyldInfo.dwarf_section;
394 info.dwarf_section_length = dyldInfo.dwarf_section_length;
396 info.compact_unwind_section = (uintptr_t)dyldInfo.compact_unwind_section;
397 info.compact_unwind_section_length = dyldInfo.compact_unwind_section_length;
400 #elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_LIBUNWIND_IS_BAREMETAL)
401 // Bare metal is statically linked, so no need to ask the dynamic loader
402 info.dwarf_section_length = (uintptr_t)(&__eh_frame_end - &__eh_frame_start);
403 info.dwarf_section = (uintptr_t)(&__eh_frame_start);
404 _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: section %p length %p",
405 (void *)info.dwarf_section, (void *)info.dwarf_section_length);
406 #if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
407 info.dwarf_index_section = (uintptr_t)(&__eh_frame_hdr_start);
408 info.dwarf_index_section_length = (uintptr_t)(&__eh_frame_hdr_end - &__eh_frame_hdr_start);
409 _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: index section %p length %p",
410 (void *)info.dwarf_index_section, (void *)info.dwarf_index_section_length);
412 if (info.dwarf_section_length)
414 #elif defined(_LIBUNWIND_ARM_EHABI) && defined(_LIBUNWIND_IS_BAREMETAL)
415 // Bare metal is statically linked, so no need to ask the dynamic loader
416 info.arm_section = (uintptr_t)(&__exidx_start);
417 info.arm_section_length = (uintptr_t)(&__exidx_end - &__exidx_start);
418 _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: section %p length %p",
419 (void *)info.arm_section, (void *)info.arm_section_length);
420 if (info.arm_section && info.arm_section_length)
422 #elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_WIN32)
424 HANDLE process = GetCurrentProcess();
427 if (!EnumProcessModules(process, mods, sizeof(mods), &needed))
430 for (unsigned i = 0; i < (needed / sizeof(HMODULE)); i++) {
431 PIMAGE_DOS_HEADER pidh = (PIMAGE_DOS_HEADER)mods[i];
432 PIMAGE_NT_HEADERS pinh = (PIMAGE_NT_HEADERS)((BYTE *)pidh + pidh->e_lfanew);
433 PIMAGE_FILE_HEADER pifh = (PIMAGE_FILE_HEADER)&pinh->FileHeader;
434 PIMAGE_SECTION_HEADER pish = IMAGE_FIRST_SECTION(pinh);
435 bool found_obj = false;
436 bool found_hdr = false;
438 info.dso_base = (uintptr_t)mods[i];
439 for (unsigned j = 0; j < pifh->NumberOfSections; j++, pish++) {
440 uintptr_t begin = pish->VirtualAddress + (uintptr_t)mods[i];
441 uintptr_t end = begin + pish->Misc.VirtualSize;
442 if (!strncmp((const char *)pish->Name, ".text",
443 IMAGE_SIZEOF_SHORT_NAME)) {
444 if (targetAddr >= begin && targetAddr < end)
446 } else if (!strncmp((const char *)pish->Name, ".eh_frame",
447 IMAGE_SIZEOF_SHORT_NAME)) {
448 info.dwarf_section = begin;
449 info.dwarf_section_length = pish->Misc.VirtualSize;
452 if (found_obj && found_hdr)
457 #elif defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32)
458 // Don't even bother, since Windows has functions that do all this stuff
461 #elif defined(_LIBUNWIND_ARM_EHABI) && defined(__BIONIC__) && \
462 (__ANDROID_API__ < 21)
465 (uintptr_t)dl_unwind_find_exidx((_Unwind_Ptr)targetAddr, &length);
466 info.arm_section_length = (uintptr_t)length;
467 if (info.arm_section && info.arm_section_length)
469 #elif defined(_LIBUNWIND_ARM_EHABI) || defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
470 struct dl_iterate_cb_data {
471 LocalAddressSpace *addressSpace;
472 UnwindInfoSections *sects;
473 uintptr_t targetAddr;
476 dl_iterate_cb_data cb_data = {this, &info, targetAddr};
477 int found = dl_iterate_phdr(
478 [](struct dl_phdr_info *pinfo, size_t, void *data) -> int {
479 auto cbdata = static_cast<dl_iterate_cb_data *>(data);
480 bool found_obj = false;
481 bool found_hdr = false;
484 assert(cbdata->sects);
486 if (cbdata->targetAddr < pinfo->dlpi_addr) {
490 #if !defined(Elf_Half)
491 typedef ElfW(Half) Elf_Half;
493 #if !defined(Elf_Phdr)
494 typedef ElfW(Phdr) Elf_Phdr;
496 #if !defined(Elf_Addr) && defined(__ANDROID__)
497 typedef ElfW(Addr) Elf_Addr;
500 #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
501 #if !defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
502 #error "_LIBUNWIND_SUPPORT_DWARF_UNWIND requires _LIBUNWIND_SUPPORT_DWARF_INDEX on this platform."
504 size_t object_length;
505 #if defined(__ANDROID__)
506 Elf_Addr image_base =
508 ? reinterpret_cast<Elf_Addr>(pinfo->dlpi_phdr) -
509 reinterpret_cast<const Elf_Phdr *>(pinfo->dlpi_phdr)
514 for (Elf_Half i = 0; i < pinfo->dlpi_phnum; i++) {
515 const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i];
516 if (phdr->p_type == PT_LOAD) {
517 uintptr_t begin = pinfo->dlpi_addr + phdr->p_vaddr;
518 #if defined(__ANDROID__)
519 if (pinfo->dlpi_addr == 0 && phdr->p_vaddr < image_base)
520 begin = begin + image_base;
522 uintptr_t end = begin + phdr->p_memsz;
523 if (cbdata->targetAddr >= begin && cbdata->targetAddr < end) {
524 cbdata->sects->dso_base = begin;
525 object_length = phdr->p_memsz;
528 } else if (phdr->p_type == PT_GNU_EH_FRAME) {
529 EHHeaderParser<LocalAddressSpace>::EHHeaderInfo hdrInfo;
530 uintptr_t eh_frame_hdr_start = pinfo->dlpi_addr + phdr->p_vaddr;
531 #if defined(__ANDROID__)
532 if (pinfo->dlpi_addr == 0 && phdr->p_vaddr < image_base)
533 eh_frame_hdr_start = eh_frame_hdr_start + image_base;
535 cbdata->sects->dwarf_index_section = eh_frame_hdr_start;
536 cbdata->sects->dwarf_index_section_length = phdr->p_memsz;
537 found_hdr = EHHeaderParser<LocalAddressSpace>::decodeEHHdr(
538 *cbdata->addressSpace, eh_frame_hdr_start, phdr->p_memsz,
541 cbdata->sects->dwarf_section = hdrInfo.eh_frame_ptr;
545 if (found_obj && found_hdr) {
546 cbdata->sects->dwarf_section_length = object_length;
551 #else // defined(_LIBUNWIND_ARM_EHABI)
552 for (Elf_Half i = 0; i < pinfo->dlpi_phnum; i++) {
553 const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i];
554 if (phdr->p_type == PT_LOAD) {
555 uintptr_t begin = pinfo->dlpi_addr + phdr->p_vaddr;
556 uintptr_t end = begin + phdr->p_memsz;
557 if (cbdata->targetAddr >= begin && cbdata->targetAddr < end)
559 } else if (phdr->p_type == PT_ARM_EXIDX) {
560 uintptr_t exidx_start = pinfo->dlpi_addr + phdr->p_vaddr;
561 cbdata->sects->arm_section = exidx_start;
562 cbdata->sects->arm_section_length = phdr->p_memsz;
566 return found_obj && found_hdr;
570 return static_cast<bool>(found);
577 inline bool LocalAddressSpace::findOtherFDE(pint_t targetAddr, pint_t &fde) {
579 return checkKeyMgrRegisteredFDEs(targetAddr, *((void**)&fde));
581 // TO DO: if OS has way to dynamically register FDEs, check that.
588 inline bool LocalAddressSpace::findFunctionName(pint_t addr, char *buf,
590 unw_word_t *offset) {
591 #if _LIBUNWIND_USE_DLADDR
593 if (dladdr((void *)addr, &dyldInfo)) {
594 if (dyldInfo.dli_sname != NULL) {
595 snprintf(buf, bufLen, "%s", dyldInfo.dli_sname);
596 *offset = (addr - (pint_t) dyldInfo.dli_saddr);
608 /// RemoteAddressSpace is used as a template parameter to UnwindCursor when
609 /// unwinding a thread in the another process. The other process can be a
610 /// different endianness and a different pointer size which is handled by
611 /// the P template parameter.
612 template <typename P>
613 class RemoteAddressSpace {
615 RemoteAddressSpace(task_t task) : fTask(task) {}
617 typedef typename P::uint_t pint_t;
619 uint8_t get8(pint_t addr);
620 uint16_t get16(pint_t addr);
621 uint32_t get32(pint_t addr);
622 uint64_t get64(pint_t addr);
623 pint_t getP(pint_t addr);
624 uint64_t getRegister(pint_t addr);
625 uint64_t getULEB128(pint_t &addr, pint_t end);
626 int64_t getSLEB128(pint_t &addr, pint_t end);
627 pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
628 pint_t datarelBase = 0);
629 bool findFunctionName(pint_t addr, char *buf, size_t bufLen,
631 bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info);
632 bool findOtherFDE(pint_t targetAddr, pint_t &fde);
634 void *localCopy(pint_t addr);
639 template <typename P> uint8_t RemoteAddressSpace<P>::get8(pint_t addr) {
640 return *((uint8_t *)localCopy(addr));
643 template <typename P> uint16_t RemoteAddressSpace<P>::get16(pint_t addr) {
644 return P::E::get16(*(uint16_t *)localCopy(addr));
647 template <typename P> uint32_t RemoteAddressSpace<P>::get32(pint_t addr) {
648 return P::E::get32(*(uint32_t *)localCopy(addr));
651 template <typename P> uint64_t RemoteAddressSpace<P>::get64(pint_t addr) {
652 return P::E::get64(*(uint64_t *)localCopy(addr));
655 template <typename P>
656 typename P::uint_t RemoteAddressSpace<P>::getP(pint_t addr) {
657 return P::getP(*(uint64_t *)localCopy(addr));
660 template <typename P>
661 typename P::uint_t OtherAddressSpace<P>::getRegister(pint_t addr) {
662 return P::getRegister(*(uint64_t *)localCopy(addr));
665 template <typename P>
666 uint64_t OtherAddressSpace<P>::getULEB128(pint_t &addr, pint_t end) {
667 uintptr_t size = (end - addr);
668 LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t) localCopy(addr);
669 LocalAddressSpace::pint_t sladdr = laddr;
670 uint64_t result = LocalAddressSpace::getULEB128(laddr, laddr + size);
671 addr += (laddr - sladdr);
675 template <typename P>
676 int64_t RemoteAddressSpace<P>::getSLEB128(pint_t &addr, pint_t end) {
677 uintptr_t size = (end - addr);
678 LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t) localCopy(addr);
679 LocalAddressSpace::pint_t sladdr = laddr;
680 uint64_t result = LocalAddressSpace::getSLEB128(laddr, laddr + size);
681 addr += (laddr - sladdr);
685 template <typename P> void *RemoteAddressSpace<P>::localCopy(pint_t addr) {
689 template <typename P>
690 bool RemoteAddressSpace<P>::findFunctionName(pint_t addr, char *buf,
692 unw_word_t *offset) {
696 /// unw_addr_space is the base class that abstract unw_addr_space_t type in
697 /// libunwind.h points to.
698 struct unw_addr_space {
703 /// unw_addr_space_i386 is the concrete instance that a unw_addr_space_t points
704 /// to when examining
705 /// a 32-bit intel process.
706 struct unw_addr_space_i386 : public unw_addr_space {
707 unw_addr_space_i386(task_t task) : oas(task) {}
708 RemoteAddressSpace<Pointer32<LittleEndian>> oas;
711 /// unw_addr_space_x86_64 is the concrete instance that a unw_addr_space_t
712 /// points to when examining
713 /// a 64-bit intel process.
714 struct unw_addr_space_x86_64 : public unw_addr_space {
715 unw_addr_space_x86_64(task_t task) : oas(task) {}
716 RemoteAddressSpace<Pointer64<LittleEndian>> oas;
719 /// unw_addr_space_ppc is the concrete instance that a unw_addr_space_t points
720 /// to when examining
721 /// a 32-bit PowerPC process.
722 struct unw_addr_space_ppc : public unw_addr_space {
723 unw_addr_space_ppc(task_t task) : oas(task) {}
724 RemoteAddressSpace<Pointer32<BigEndian>> oas;
727 /// unw_addr_space_ppc is the concrete instance that a unw_addr_space_t points
728 /// to when examining a 64-bit PowerPC process.
729 struct unw_addr_space_ppc64 : public unw_addr_space {
730 unw_addr_space_ppc64(task_t task) : oas(task) {}
731 RemoteAddressSpace<Pointer64<LittleEndian>> oas;
736 } // namespace libunwind
738 #endif // __ADDRESSSPACE_HPP__