xref: /NextBSD/contrib/llvm/projects/libunwind/src/AddressSpace.hpp (revision ea41b2069698db5cfd8b1d3888556269b4fdc668)
1 //===------------------------- AddressSpace.hpp ---------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
7 //
8 //
9 // Abstracts accessing local vs remote address spaces.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef __ADDRESSSPACE_HPP__
14 #define __ADDRESSSPACE_HPP__
15 
16 #include <stdint.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 #ifndef _LIBUNWIND_IS_BAREMETAL
22 #include <dlfcn.h>
23 #endif
24 
25 #ifdef __APPLE__
26 #include <mach-o/getsect.h>
27 namespace libunwind {
28    bool checkKeyMgrRegisteredFDEs(uintptr_t targetAddr, void *&fde);
29 }
30 #endif
31 
32 #include "libunwind.h"
33 #include "config.h"
34 #include "dwarf2.h"
35 #include "Registers.hpp"
36 
37 #if _LIBUNWIND_ARM_EHABI
38 #if defined(__FreeBSD__)
39 
40 #include <sys/link_elf.h>
41 typedef void *_Unwind_Ptr;
42 
43 #elif defined(__linux__)
44 
45 typedef long unsigned int *_Unwind_Ptr;
46 extern "C" _Unwind_Ptr __gnu_Unwind_Find_exidx(_Unwind_Ptr addr, int *len);
47 
48 // Emulate the BSD dl_unwind_find_exidx API when on a GNU libdl system.
49 #define dl_unwind_find_exidx __gnu_Unwind_Find_exidx
50 
51 #elif !defined(_LIBUNWIND_IS_BAREMETAL)
52 #include <link.h>
53 #else // !defined(_LIBUNWIND_IS_BAREMETAL)
54 // When statically linked on bare-metal, the symbols for the EH table are looked
55 // up without going through the dynamic loader.
56 struct EHTEntry {
57   uint32_t functionOffset;
58   uint32_t unwindOpcodes;
59 };
60 extern EHTEntry __exidx_start;
61 extern EHTEntry __exidx_end;
62 #endif // !defined(_LIBUNWIND_IS_BAREMETAL)
63 #endif // _LIBUNWIND_ARM_EHABI
64 
65 #if defined(__CloudABI__) || defined(__FreeBSD__) || defined(__linux__)
66 #if _LIBUNWIND_SUPPORT_DWARF_UNWIND && _LIBUNWIND_SUPPORT_DWARF_INDEX
67 #include <link.h>
68 // Macro for machine-independent access to the ELF program headers. This
69 // macro is not available on some systems (e.g., FreeBSD). On these
70 // systems the data structures are just called Elf_XXX. Define ElfW()
71 // locally.
72 #if !defined(ElfW)
73 #define ElfW(type) Elf_##type
74 #endif
75 #include "EHHeaderParser.hpp"
76 #endif
77 #endif
78 
79 namespace libunwind {
80 
81 /// Used by findUnwindSections() to return info about needed sections.
82 struct UnwindInfoSections {
83 #if _LIBUNWIND_SUPPORT_DWARF_UNWIND || _LIBUNWIND_SUPPORT_DWARF_INDEX ||       \
84     _LIBUNWIND_SUPPORT_COMPACT_UNWIND
85   // No dso_base for ARM EHABI.
86   uintptr_t       dso_base;
87 #endif
88 #if _LIBUNWIND_SUPPORT_DWARF_UNWIND
89   uintptr_t       dwarf_section;
90   uintptr_t       dwarf_section_length;
91 #endif
92 #if _LIBUNWIND_SUPPORT_DWARF_INDEX
93   uintptr_t       dwarf_index_section;
94   uintptr_t       dwarf_index_section_length;
95 #endif
96 #if _LIBUNWIND_SUPPORT_COMPACT_UNWIND
97   uintptr_t       compact_unwind_section;
98   uintptr_t       compact_unwind_section_length;
99 #endif
100 #if _LIBUNWIND_ARM_EHABI
101   uintptr_t       arm_section;
102   uintptr_t       arm_section_length;
103 #endif
104 };
105 
106 
107 /// LocalAddressSpace is used as a template parameter to UnwindCursor when
108 /// unwinding a thread in the same process.  The wrappers compile away,
109 /// making local unwinds fast.
110 class __attribute__((visibility("hidden"))) LocalAddressSpace {
111 public:
112 #ifdef __LP64__
113   typedef uint64_t pint_t;
114   typedef int64_t  sint_t;
115 #else
116   typedef uint32_t pint_t;
117   typedef int32_t  sint_t;
118 #endif
get8(pint_t addr)119   uint8_t         get8(pint_t addr) {
120     uint8_t val;
121     memcpy(&val, (void *)addr, sizeof(val));
122     return val;
123   }
get16(pint_t addr)124   uint16_t         get16(pint_t addr) {
125     uint16_t val;
126     memcpy(&val, (void *)addr, sizeof(val));
127     return val;
128   }
get32(pint_t addr)129   uint32_t         get32(pint_t addr) {
130     uint32_t val;
131     memcpy(&val, (void *)addr, sizeof(val));
132     return val;
133   }
get64(pint_t addr)134   uint64_t         get64(pint_t addr) {
135     uint64_t val;
136     memcpy(&val, (void *)addr, sizeof(val));
137     return val;
138   }
getDouble(pint_t addr)139   double           getDouble(pint_t addr) {
140     double val;
141     memcpy(&val, (void *)addr, sizeof(val));
142     return val;
143   }
getVector(pint_t addr)144   v128             getVector(pint_t addr) {
145     v128 val;
146     memcpy(&val, (void *)addr, sizeof(val));
147     return val;
148   }
149   uintptr_t       getP(pint_t addr);
150   static uint64_t getULEB128(pint_t &addr, pint_t end);
151   static int64_t  getSLEB128(pint_t &addr, pint_t end);
152 
153   pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
154                      pint_t datarelBase = 0);
155   bool findFunctionName(pint_t addr, char *buf, size_t bufLen,
156                         unw_word_t *offset);
157   bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info);
158   bool findOtherFDE(pint_t targetAddr, pint_t &fde);
159 
160   static LocalAddressSpace sThisAddressSpace;
161 };
162 
getP(pint_t addr)163 inline uintptr_t LocalAddressSpace::getP(pint_t addr) {
164 #ifdef __LP64__
165   return get64(addr);
166 #else
167   return get32(addr);
168 #endif
169 }
170 
171 /// Read a ULEB128 into a 64-bit word.
getULEB128(pint_t & addr,pint_t end)172 inline uint64_t LocalAddressSpace::getULEB128(pint_t &addr, pint_t end) {
173   const uint8_t *p = (uint8_t *)addr;
174   const uint8_t *pend = (uint8_t *)end;
175   uint64_t result = 0;
176   int bit = 0;
177   do {
178     uint64_t b;
179 
180     if (p == pend)
181       _LIBUNWIND_ABORT("truncated uleb128 expression");
182 
183     b = *p & 0x7f;
184 
185     if (bit >= 64 || b << bit >> bit != b) {
186       _LIBUNWIND_ABORT("malformed uleb128 expression");
187     } else {
188       result |= b << bit;
189       bit += 7;
190     }
191   } while (*p++ >= 0x80);
192   addr = (pint_t) p;
193   return result;
194 }
195 
196 /// Read a SLEB128 into a 64-bit word.
getSLEB128(pint_t & addr,pint_t end)197 inline int64_t LocalAddressSpace::getSLEB128(pint_t &addr, pint_t end) {
198   const uint8_t *p = (uint8_t *)addr;
199   const uint8_t *pend = (uint8_t *)end;
200   int64_t result = 0;
201   int bit = 0;
202   uint8_t byte;
203   do {
204     if (p == pend)
205       _LIBUNWIND_ABORT("truncated sleb128 expression");
206     byte = *p++;
207     result |= ((byte & 0x7f) << bit);
208     bit += 7;
209   } while (byte & 0x80);
210   // sign extend negative numbers
211   if ((byte & 0x40) != 0)
212     result |= (-1LL) << bit;
213   addr = (pint_t) p;
214   return result;
215 }
216 
217 inline LocalAddressSpace::pint_t
getEncodedP(pint_t & addr,pint_t end,uint8_t encoding,pint_t datarelBase)218 LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
219                                pint_t datarelBase) {
220   pint_t startAddr = addr;
221   const uint8_t *p = (uint8_t *)addr;
222   pint_t result;
223 
224   // first get value
225   switch (encoding & 0x0F) {
226   case DW_EH_PE_ptr:
227     result = getP(addr);
228     p += sizeof(pint_t);
229     addr = (pint_t) p;
230     break;
231   case DW_EH_PE_uleb128:
232     result = (pint_t)getULEB128(addr, end);
233     break;
234   case DW_EH_PE_udata2:
235     result = get16(addr);
236     p += 2;
237     addr = (pint_t) p;
238     break;
239   case DW_EH_PE_udata4:
240     result = get32(addr);
241     p += 4;
242     addr = (pint_t) p;
243     break;
244   case DW_EH_PE_udata8:
245     result = (pint_t)get64(addr);
246     p += 8;
247     addr = (pint_t) p;
248     break;
249   case DW_EH_PE_sleb128:
250     result = (pint_t)getSLEB128(addr, end);
251     break;
252   case DW_EH_PE_sdata2:
253     // Sign extend from signed 16-bit value.
254     result = (pint_t)(int16_t)get16(addr);
255     p += 2;
256     addr = (pint_t) p;
257     break;
258   case DW_EH_PE_sdata4:
259     // Sign extend from signed 32-bit value.
260     result = (pint_t)(int32_t)get32(addr);
261     p += 4;
262     addr = (pint_t) p;
263     break;
264   case DW_EH_PE_sdata8:
265     result = (pint_t)get64(addr);
266     p += 8;
267     addr = (pint_t) p;
268     break;
269   default:
270     _LIBUNWIND_ABORT("unknown pointer encoding");
271   }
272 
273   // then add relative offset
274   switch (encoding & 0x70) {
275   case DW_EH_PE_absptr:
276     // do nothing
277     break;
278   case DW_EH_PE_pcrel:
279     result += startAddr;
280     break;
281   case DW_EH_PE_textrel:
282     _LIBUNWIND_ABORT("DW_EH_PE_textrel pointer encoding not supported");
283     break;
284   case DW_EH_PE_datarel:
285     // DW_EH_PE_datarel is only valid in a few places, so the parameter has a
286     // default value of 0, and we abort in the event that someone calls this
287     // function with a datarelBase of 0 and DW_EH_PE_datarel encoding.
288     if (datarelBase == 0)
289       _LIBUNWIND_ABORT("DW_EH_PE_datarel is invalid with a datarelBase of 0");
290     result += datarelBase;
291     break;
292   case DW_EH_PE_funcrel:
293     _LIBUNWIND_ABORT("DW_EH_PE_funcrel pointer encoding not supported");
294     break;
295   case DW_EH_PE_aligned:
296     _LIBUNWIND_ABORT("DW_EH_PE_aligned pointer encoding not supported");
297     break;
298   default:
299     _LIBUNWIND_ABORT("unknown pointer encoding");
300     break;
301   }
302 
303   if (encoding & DW_EH_PE_indirect)
304     result = getP(result);
305 
306   return result;
307 }
308 
309 #ifdef __APPLE__
310   struct dyld_unwind_sections
311   {
312     const struct mach_header*   mh;
313     const void*                 dwarf_section;
314     uintptr_t                   dwarf_section_length;
315     const void*                 compact_unwind_section;
316     uintptr_t                   compact_unwind_section_length;
317   };
318   #if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \
319                                  && (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)) \
320       || defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
321     // In 10.7.0 or later, libSystem.dylib implements this function.
322     extern "C" bool _dyld_find_unwind_sections(void *, dyld_unwind_sections *);
323   #else
324     // In 10.6.x and earlier, we need to implement this functionality.
_dyld_find_unwind_sections(void * addr,dyld_unwind_sections * info)325     static inline bool _dyld_find_unwind_sections(void* addr,
326                                                     dyld_unwind_sections* info) {
327       // Find mach-o image containing address.
328       Dl_info dlinfo;
329       if (!dladdr(addr, &dlinfo))
330         return false;
331       const mach_header *mh = (const mach_header *)dlinfo.dli_saddr;
332 
333       // Find dwarf unwind section in that image.
334       unsigned long size;
335       const uint8_t *p = getsectiondata(mh, "__TEXT", "__eh_frame", &size);
336       if (!p)
337         return false;
338 
339       // Fill in return struct.
340       info->mh = mh;
341       info->dwarf_section = p;
342       info->dwarf_section_length = size;
343       info->compact_unwind_section = 0;
344       info->compact_unwind_section_length = 0;
345 
346       return true;
347     }
348   #endif
349 #endif
350 
findUnwindSections(pint_t targetAddr,UnwindInfoSections & info)351 inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr,
352                                                   UnwindInfoSections &info) {
353 #ifdef __APPLE__
354   dyld_unwind_sections dyldInfo;
355   if (_dyld_find_unwind_sections((void *)targetAddr, &dyldInfo)) {
356     info.dso_base                      = (uintptr_t)dyldInfo.mh;
357  #if _LIBUNWIND_SUPPORT_DWARF_UNWIND
358     info.dwarf_section                 = (uintptr_t)dyldInfo.dwarf_section;
359     info.dwarf_section_length          = dyldInfo.dwarf_section_length;
360  #endif
361     info.compact_unwind_section        = (uintptr_t)dyldInfo.compact_unwind_section;
362     info.compact_unwind_section_length = dyldInfo.compact_unwind_section_length;
363     return true;
364   }
365 #elif _LIBUNWIND_ARM_EHABI
366  #ifdef _LIBUNWIND_IS_BAREMETAL
367   // Bare metal is statically linked, so no need to ask the dynamic loader
368   info.arm_section =        (uintptr_t)(&__exidx_start);
369   info.arm_section_length = (uintptr_t)(&__exidx_end - &__exidx_start);
370  #else
371   int length = 0;
372   info.arm_section = (uintptr_t) dl_unwind_find_exidx(
373       (_Unwind_Ptr) targetAddr, &length);
374   info.arm_section_length = (uintptr_t)length;
375  #endif
376   _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: section %X length %x\n",
377                              info.arm_section, info.arm_section_length);
378   if (info.arm_section && info.arm_section_length)
379     return true;
380 #elif _LIBUNWIND_SUPPORT_DWARF_UNWIND
381 #if _LIBUNWIND_SUPPORT_DWARF_INDEX
382   struct dl_iterate_cb_data {
383     LocalAddressSpace *addressSpace;
384     UnwindInfoSections *sects;
385     uintptr_t targetAddr;
386   };
387 
388   dl_iterate_cb_data cb_data = {this, &info, targetAddr};
389   int found = dl_iterate_phdr(
390       [](struct dl_phdr_info *pinfo, size_t, void *data) -> int {
391         auto cbdata = static_cast<dl_iterate_cb_data *>(data);
392         size_t object_length;
393         bool found_obj = false;
394         bool found_hdr = false;
395 
396         assert(cbdata);
397         assert(cbdata->sects);
398 
399         if (cbdata->targetAddr < pinfo->dlpi_addr) {
400           return false;
401         }
402 
403 #if !defined(Elf_Half)
404         typedef ElfW(Half) Elf_Half;
405 #endif
406 #if !defined(Elf_Phdr)
407         typedef ElfW(Phdr) Elf_Phdr;
408 #endif
409 
410         for (Elf_Half i = 0; i < pinfo->dlpi_phnum; i++) {
411           const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i];
412           if (phdr->p_type == PT_LOAD) {
413             uintptr_t begin = pinfo->dlpi_addr + phdr->p_vaddr;
414             uintptr_t end = begin + phdr->p_memsz;
415             if (cbdata->targetAddr >= begin && cbdata->targetAddr < end) {
416               cbdata->sects->dso_base = begin;
417               object_length = phdr->p_memsz;
418               found_obj = true;
419             }
420           } else if (phdr->p_type == PT_GNU_EH_FRAME) {
421             EHHeaderParser<LocalAddressSpace>::EHHeaderInfo hdrInfo;
422             uintptr_t eh_frame_hdr_start = pinfo->dlpi_addr + phdr->p_vaddr;
423             cbdata->sects->dwarf_index_section = eh_frame_hdr_start;
424             cbdata->sects->dwarf_index_section_length = phdr->p_memsz;
425             EHHeaderParser<LocalAddressSpace>::decodeEHHdr(
426                 *cbdata->addressSpace, eh_frame_hdr_start, phdr->p_memsz,
427                 hdrInfo);
428             cbdata->sects->dwarf_section = hdrInfo.eh_frame_ptr;
429             found_hdr = true;
430           }
431         }
432 
433         if (found_obj && found_hdr) {
434           cbdata->sects->dwarf_section_length = object_length;
435           return true;
436         } else {
437           return false;
438         }
439       },
440       &cb_data);
441   return static_cast<bool>(found);
442 #else
443 #error "_LIBUNWIND_SUPPORT_DWARF_UNWIND requires _LIBUNWIND_SUPPORT_DWARF_INDEX on this platform."
444 #endif
445 #endif
446 
447   return false;
448 }
449 
450 
findOtherFDE(pint_t targetAddr,pint_t & fde)451 inline bool LocalAddressSpace::findOtherFDE(pint_t targetAddr, pint_t &fde) {
452 #ifdef __APPLE__
453   return checkKeyMgrRegisteredFDEs(targetAddr, *((void**)&fde));
454 #else
455   // TO DO: if OS has way to dynamically register FDEs, check that.
456   (void)targetAddr;
457   (void)fde;
458   return false;
459 #endif
460 }
461 
findFunctionName(pint_t addr,char * buf,size_t bufLen,unw_word_t * offset)462 inline bool LocalAddressSpace::findFunctionName(pint_t addr, char *buf,
463                                                 size_t bufLen,
464                                                 unw_word_t *offset) {
465 #ifndef _LIBUNWIND_IS_BAREMETAL
466   Dl_info dyldInfo;
467   if (dladdr((void *)addr, &dyldInfo)) {
468     if (dyldInfo.dli_sname != NULL) {
469       snprintf(buf, bufLen, "%s", dyldInfo.dli_sname);
470       *offset = (addr - (pint_t) dyldInfo.dli_saddr);
471       return true;
472     }
473   }
474 #endif
475   return false;
476 }
477 
478 
479 
480 #ifdef UNW_REMOTE
481 
482 /// OtherAddressSpace is used as a template parameter to UnwindCursor when
483 /// unwinding a thread in the another process.  The other process can be a
484 /// different endianness and a different pointer size which is handled by
485 /// the P template parameter.
486 template <typename P>
487 class OtherAddressSpace {
488 public:
OtherAddressSpace(task_t task)489   OtherAddressSpace(task_t task) : fTask(task) {}
490 
491   typedef typename P::uint_t pint_t;
492 
493   uint8_t   get8(pint_t addr);
494   uint16_t  get16(pint_t addr);
495   uint32_t  get32(pint_t addr);
496   uint64_t  get64(pint_t addr);
497   pint_t    getP(pint_t addr);
498   uint64_t  getULEB128(pint_t &addr, pint_t end);
499   int64_t   getSLEB128(pint_t &addr, pint_t end);
500   pint_t    getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
501                         pint_t datarelBase = 0);
502   bool      findFunctionName(pint_t addr, char *buf, size_t bufLen,
503                         unw_word_t *offset);
504   bool      findUnwindSections(pint_t targetAddr, UnwindInfoSections &info);
505   bool      findOtherFDE(pint_t targetAddr, pint_t &fde);
506 private:
507   void *localCopy(pint_t addr);
508 
509   task_t fTask;
510 };
511 
get8(pint_t addr)512 template <typename P> uint8_t OtherAddressSpace<P>::get8(pint_t addr) {
513   return *((uint8_t *)localCopy(addr));
514 }
515 
get16(pint_t addr)516 template <typename P> uint16_t OtherAddressSpace<P>::get16(pint_t addr) {
517   return P::E::get16(*(uint16_t *)localCopy(addr));
518 }
519 
get32(pint_t addr)520 template <typename P> uint32_t OtherAddressSpace<P>::get32(pint_t addr) {
521   return P::E::get32(*(uint32_t *)localCopy(addr));
522 }
523 
get64(pint_t addr)524 template <typename P> uint64_t OtherAddressSpace<P>::get64(pint_t addr) {
525   return P::E::get64(*(uint64_t *)localCopy(addr));
526 }
527 
528 template <typename P>
getP(pint_t addr)529 typename P::uint_t OtherAddressSpace<P>::getP(pint_t addr) {
530   return P::getP(*(uint64_t *)localCopy(addr));
531 }
532 
533 template <typename P>
getULEB128(pint_t & addr,pint_t end)534 uint64_t OtherAddressSpace<P>::getULEB128(pint_t &addr, pint_t end) {
535   uintptr_t size = (end - addr);
536   LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t) localCopy(addr);
537   LocalAddressSpace::pint_t sladdr = laddr;
538   uint64_t result = LocalAddressSpace::getULEB128(laddr, laddr + size);
539   addr += (laddr - sladdr);
540   return result;
541 }
542 
543 template <typename P>
getSLEB128(pint_t & addr,pint_t end)544 int64_t OtherAddressSpace<P>::getSLEB128(pint_t &addr, pint_t end) {
545   uintptr_t size = (end - addr);
546   LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t) localCopy(addr);
547   LocalAddressSpace::pint_t sladdr = laddr;
548   uint64_t result = LocalAddressSpace::getSLEB128(laddr, laddr + size);
549   addr += (laddr - sladdr);
550   return result;
551 }
552 
localCopy(pint_t addr)553 template <typename P> void *OtherAddressSpace<P>::localCopy(pint_t addr) {
554   // FIX ME
555 }
556 
557 template <typename P>
findFunctionName(pint_t addr,char * buf,size_t bufLen,unw_word_t * offset)558 bool OtherAddressSpace<P>::findFunctionName(pint_t addr, char *buf,
559                                             size_t bufLen, unw_word_t *offset) {
560   // FIX ME
561 }
562 
563 /// unw_addr_space is the base class that abstract unw_addr_space_t type in
564 /// libunwind.h points to.
565 struct unw_addr_space {
566   cpu_type_t cpuType;
567   task_t taskPort;
568 };
569 
570 /// unw_addr_space_i386 is the concrete instance that a unw_addr_space_t points
571 /// to when examining
572 /// a 32-bit intel process.
573 struct unw_addr_space_i386 : public unw_addr_space {
unw_addr_space_i386libunwind::unw_addr_space_i386574   unw_addr_space_i386(task_t task) : oas(task) {}
575   OtherAddressSpace<Pointer32<LittleEndian> > oas;
576 };
577 
578 /// unw_addr_space_x86_64 is the concrete instance that a unw_addr_space_t
579 /// points to when examining
580 /// a 64-bit intel process.
581 struct unw_addr_space_x86_64 : public unw_addr_space {
unw_addr_space_x86_64libunwind::unw_addr_space_x86_64582   unw_addr_space_x86_64(task_t task) : oas(task) {}
583   OtherAddressSpace<Pointer64<LittleEndian> > oas;
584 };
585 
586 /// unw_addr_space_ppc is the concrete instance that a unw_addr_space_t points
587 /// to when examining
588 /// a 32-bit PowerPC process.
589 struct unw_addr_space_ppc : public unw_addr_space {
unw_addr_space_ppclibunwind::unw_addr_space_ppc590   unw_addr_space_ppc(task_t task) : oas(task) {}
591   OtherAddressSpace<Pointer32<BigEndian> > oas;
592 };
593 
594 #endif // UNW_REMOTE
595 
596 } // namespace libunwind
597 
598 #endif // __ADDRESSSPACE_HPP__
599