Home | History | Annotate | Download | only in Object
      1 //===- ELF.h - ELF object file implementation -------------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file declares the ELFFile template class.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #ifndef LLVM_OBJECT_ELF_H
     15 #define LLVM_OBJECT_ELF_H
     16 
     17 #include "llvm/ADT/ArrayRef.h"
     18 #include "llvm/ADT/SmallVector.h"
     19 #include "llvm/ADT/StringRef.h"
     20 #include "llvm/BinaryFormat/ELF.h"
     21 #include "llvm/Object/ELFTypes.h"
     22 #include "llvm/Object/Error.h"
     23 #include "llvm/Support/Endian.h"
     24 #include "llvm/Support/Error.h"
     25 #include <cassert>
     26 #include <cstddef>
     27 #include <cstdint>
     28 #include <limits>
     29 #include <utility>
     30 
     31 namespace llvm {
     32 namespace object {
     33 
     34 StringRef getELFRelocationTypeName(uint32_t Machine, uint32_t Type);
     35 StringRef getELFSectionTypeName(uint32_t Machine, uint32_t Type);
     36 
     37 // Subclasses of ELFFile may need this for template instantiation
     38 inline std::pair<unsigned char, unsigned char>
     39 getElfArchType(StringRef Object) {
     40   if (Object.size() < ELF::EI_NIDENT)
     41     return std::make_pair((uint8_t)ELF::ELFCLASSNONE,
     42                           (uint8_t)ELF::ELFDATANONE);
     43   return std::make_pair((uint8_t)Object[ELF::EI_CLASS],
     44                         (uint8_t)Object[ELF::EI_DATA]);
     45 }
     46 
     47 static inline Error createError(StringRef Err) {
     48   return make_error<StringError>(Err, object_error::parse_failed);
     49 }
     50 
     51 template <class ELFT>
     52 class ELFFile {
     53 public:
     54   LLVM_ELF_IMPORT_TYPES_ELFT(ELFT)
     55   using uintX_t = typename ELFT::uint;
     56   using Elf_Ehdr = typename ELFT::Ehdr;
     57   using Elf_Shdr = typename ELFT::Shdr;
     58   using Elf_Sym = typename ELFT::Sym;
     59   using Elf_Dyn = typename ELFT::Dyn;
     60   using Elf_Phdr = typename ELFT::Phdr;
     61   using Elf_Rel = typename ELFT::Rel;
     62   using Elf_Rela = typename ELFT::Rela;
     63   using Elf_Verdef = typename ELFT::Verdef;
     64   using Elf_Verdaux = typename ELFT::Verdaux;
     65   using Elf_Verneed = typename ELFT::Verneed;
     66   using Elf_Vernaux = typename ELFT::Vernaux;
     67   using Elf_Versym = typename ELFT::Versym;
     68   using Elf_Hash = typename ELFT::Hash;
     69   using Elf_GnuHash = typename ELFT::GnuHash;
     70   using Elf_Dyn_Range = typename ELFT::DynRange;
     71   using Elf_Shdr_Range = typename ELFT::ShdrRange;
     72   using Elf_Sym_Range = typename ELFT::SymRange;
     73   using Elf_Rel_Range = typename ELFT::RelRange;
     74   using Elf_Rela_Range = typename ELFT::RelaRange;
     75   using Elf_Phdr_Range = typename ELFT::PhdrRange;
     76 
     77   const uint8_t *base() const {
     78     return reinterpret_cast<const uint8_t *>(Buf.data());
     79   }
     80 
     81   size_t getBufSize() const { return Buf.size(); }
     82 
     83 private:
     84   StringRef Buf;
     85 
     86   ELFFile(StringRef Object);
     87 
     88 public:
     89   const Elf_Ehdr *getHeader() const {
     90     return reinterpret_cast<const Elf_Ehdr *>(base());
     91   }
     92 
     93   template <typename T>
     94   Expected<const T *> getEntry(uint32_t Section, uint32_t Entry) const;
     95   template <typename T>
     96   Expected<const T *> getEntry(const Elf_Shdr *Section, uint32_t Entry) const;
     97 
     98   Expected<StringRef> getStringTable(const Elf_Shdr *Section) const;
     99   Expected<StringRef> getStringTableForSymtab(const Elf_Shdr &Section) const;
    100   Expected<StringRef> getStringTableForSymtab(const Elf_Shdr &Section,
    101                                               Elf_Shdr_Range Sections) const;
    102 
    103   Expected<ArrayRef<Elf_Word>> getSHNDXTable(const Elf_Shdr &Section) const;
    104   Expected<ArrayRef<Elf_Word>> getSHNDXTable(const Elf_Shdr &Section,
    105                                              Elf_Shdr_Range Sections) const;
    106 
    107   void VerifyStrTab(const Elf_Shdr *sh) const;
    108 
    109   StringRef getRelocationTypeName(uint32_t Type) const;
    110   void getRelocationTypeName(uint32_t Type,
    111                              SmallVectorImpl<char> &Result) const;
    112 
    113   /// \brief Get the symbol for a given relocation.
    114   Expected<const Elf_Sym *> getRelocationSymbol(const Elf_Rel *Rel,
    115                                                 const Elf_Shdr *SymTab) const;
    116 
    117   static Expected<ELFFile> create(StringRef Object);
    118 
    119   bool isMipsELF64() const {
    120     return getHeader()->e_machine == ELF::EM_MIPS &&
    121            getHeader()->getFileClass() == ELF::ELFCLASS64;
    122   }
    123 
    124   bool isMips64EL() const {
    125     return isMipsELF64() &&
    126            getHeader()->getDataEncoding() == ELF::ELFDATA2LSB;
    127   }
    128 
    129   Expected<Elf_Shdr_Range> sections() const;
    130 
    131   Expected<Elf_Sym_Range> symbols(const Elf_Shdr *Sec) const {
    132     if (!Sec)
    133       return makeArrayRef<Elf_Sym>(nullptr, nullptr);
    134     return getSectionContentsAsArray<Elf_Sym>(Sec);
    135   }
    136 
    137   Expected<Elf_Rela_Range> relas(const Elf_Shdr *Sec) const {
    138     return getSectionContentsAsArray<Elf_Rela>(Sec);
    139   }
    140 
    141   Expected<Elf_Rel_Range> rels(const Elf_Shdr *Sec) const {
    142     return getSectionContentsAsArray<Elf_Rel>(Sec);
    143   }
    144 
    145   /// \brief Iterate over program header table.
    146   Expected<Elf_Phdr_Range> program_headers() const {
    147     if (getHeader()->e_phnum && getHeader()->e_phentsize != sizeof(Elf_Phdr))
    148       return createError("invalid e_phentsize");
    149     if (getHeader()->e_phoff +
    150             (getHeader()->e_phnum * getHeader()->e_phentsize) >
    151         getBufSize())
    152       return createError("program headers longer than binary");
    153     auto *Begin =
    154         reinterpret_cast<const Elf_Phdr *>(base() + getHeader()->e_phoff);
    155     return makeArrayRef(Begin, Begin + getHeader()->e_phnum);
    156   }
    157 
    158   Expected<StringRef> getSectionStringTable(Elf_Shdr_Range Sections) const;
    159   Expected<uint32_t> getSectionIndex(const Elf_Sym *Sym, Elf_Sym_Range Syms,
    160                                      ArrayRef<Elf_Word> ShndxTable) const;
    161   Expected<const Elf_Shdr *> getSection(const Elf_Sym *Sym,
    162                                         const Elf_Shdr *SymTab,
    163                                         ArrayRef<Elf_Word> ShndxTable) const;
    164   Expected<const Elf_Shdr *> getSection(const Elf_Sym *Sym,
    165                                         Elf_Sym_Range Symtab,
    166                                         ArrayRef<Elf_Word> ShndxTable) const;
    167   Expected<const Elf_Shdr *> getSection(uint32_t Index) const;
    168 
    169   Expected<const Elf_Sym *> getSymbol(const Elf_Shdr *Sec,
    170                                       uint32_t Index) const;
    171 
    172   Expected<StringRef> getSectionName(const Elf_Shdr *Section) const;
    173   Expected<StringRef> getSectionName(const Elf_Shdr *Section,
    174                                      StringRef DotShstrtab) const;
    175   template <typename T>
    176   Expected<ArrayRef<T>> getSectionContentsAsArray(const Elf_Shdr *Sec) const;
    177   Expected<ArrayRef<uint8_t>> getSectionContents(const Elf_Shdr *Sec) const;
    178 };
    179 
    180 using ELF32LEFile = ELFFile<ELFType<support::little, false>>;
    181 using ELF64LEFile = ELFFile<ELFType<support::little, true>>;
    182 using ELF32BEFile = ELFFile<ELFType<support::big, false>>;
    183 using ELF64BEFile = ELFFile<ELFType<support::big, true>>;
    184 
    185 template <class ELFT>
    186 inline Expected<const typename ELFT::Shdr *>
    187 getSection(typename ELFT::ShdrRange Sections, uint32_t Index) {
    188   if (Index >= Sections.size())
    189     return createError("invalid section index");
    190   return &Sections[Index];
    191 }
    192 
    193 template <class ELFT>
    194 inline Expected<uint32_t>
    195 getExtendedSymbolTableIndex(const typename ELFT::Sym *Sym,
    196                             const typename ELFT::Sym *FirstSym,
    197                             ArrayRef<typename ELFT::Word> ShndxTable) {
    198   assert(Sym->st_shndx == ELF::SHN_XINDEX);
    199   unsigned Index = Sym - FirstSym;
    200   if (Index >= ShndxTable.size())
    201     return createError("index past the end of the symbol table");
    202 
    203   // The size of the table was checked in getSHNDXTable.
    204   return ShndxTable[Index];
    205 }
    206 
    207 template <class ELFT>
    208 Expected<uint32_t>
    209 ELFFile<ELFT>::getSectionIndex(const Elf_Sym *Sym, Elf_Sym_Range Syms,
    210                                ArrayRef<Elf_Word> ShndxTable) const {
    211   uint32_t Index = Sym->st_shndx;
    212   if (Index == ELF::SHN_XINDEX) {
    213     auto ErrorOrIndex = getExtendedSymbolTableIndex<ELFT>(
    214         Sym, Syms.begin(), ShndxTable);
    215     if (!ErrorOrIndex)
    216       return ErrorOrIndex.takeError();
    217     return *ErrorOrIndex;
    218   }
    219   if (Index == ELF::SHN_UNDEF || Index >= ELF::SHN_LORESERVE)
    220     return 0;
    221   return Index;
    222 }
    223 
    224 template <class ELFT>
    225 Expected<const typename ELFT::Shdr *>
    226 ELFFile<ELFT>::getSection(const Elf_Sym *Sym, const Elf_Shdr *SymTab,
    227                           ArrayRef<Elf_Word> ShndxTable) const {
    228   auto SymsOrErr = symbols(SymTab);
    229   if (!SymsOrErr)
    230     return SymsOrErr.takeError();
    231   return getSection(Sym, *SymsOrErr, ShndxTable);
    232 }
    233 
    234 template <class ELFT>
    235 Expected<const typename ELFT::Shdr *>
    236 ELFFile<ELFT>::getSection(const Elf_Sym *Sym, Elf_Sym_Range Symbols,
    237                           ArrayRef<Elf_Word> ShndxTable) const {
    238   auto IndexOrErr = getSectionIndex(Sym, Symbols, ShndxTable);
    239   if (!IndexOrErr)
    240     return IndexOrErr.takeError();
    241   uint32_t Index = *IndexOrErr;
    242   if (Index == 0)
    243     return nullptr;
    244   return getSection(Index);
    245 }
    246 
    247 template <class ELFT>
    248 inline Expected<const typename ELFT::Sym *>
    249 getSymbol(typename ELFT::SymRange Symbols, uint32_t Index) {
    250   if (Index >= Symbols.size())
    251     return createError("invalid symbol index");
    252   return &Symbols[Index];
    253 }
    254 
    255 template <class ELFT>
    256 Expected<const typename ELFT::Sym *>
    257 ELFFile<ELFT>::getSymbol(const Elf_Shdr *Sec, uint32_t Index) const {
    258   auto SymtabOrErr = symbols(Sec);
    259   if (!SymtabOrErr)
    260     return SymtabOrErr.takeError();
    261   return object::getSymbol<ELFT>(*SymtabOrErr, Index);
    262 }
    263 
    264 template <class ELFT>
    265 template <typename T>
    266 Expected<ArrayRef<T>>
    267 ELFFile<ELFT>::getSectionContentsAsArray(const Elf_Shdr *Sec) const {
    268   if (Sec->sh_entsize != sizeof(T) && sizeof(T) != 1)
    269     return createError("invalid sh_entsize");
    270 
    271   uintX_t Offset = Sec->sh_offset;
    272   uintX_t Size = Sec->sh_size;
    273 
    274   if (Size % sizeof(T))
    275     return createError("size is not a multiple of sh_entsize");
    276   if ((std::numeric_limits<uintX_t>::max() - Offset < Size) ||
    277       Offset + Size > Buf.size())
    278     return createError("invalid section offset");
    279 
    280   const T *Start = reinterpret_cast<const T *>(base() + Offset);
    281   return makeArrayRef(Start, Size / sizeof(T));
    282 }
    283 
    284 template <class ELFT>
    285 Expected<ArrayRef<uint8_t>>
    286 ELFFile<ELFT>::getSectionContents(const Elf_Shdr *Sec) const {
    287   return getSectionContentsAsArray<uint8_t>(Sec);
    288 }
    289 
    290 template <class ELFT>
    291 StringRef ELFFile<ELFT>::getRelocationTypeName(uint32_t Type) const {
    292   return getELFRelocationTypeName(getHeader()->e_machine, Type);
    293 }
    294 
    295 template <class ELFT>
    296 void ELFFile<ELFT>::getRelocationTypeName(uint32_t Type,
    297                                           SmallVectorImpl<char> &Result) const {
    298   if (!isMipsELF64()) {
    299     StringRef Name = getRelocationTypeName(Type);
    300     Result.append(Name.begin(), Name.end());
    301   } else {
    302     // The Mips N64 ABI allows up to three operations to be specified per
    303     // relocation record. Unfortunately there's no easy way to test for the
    304     // presence of N64 ELFs as they have no special flag that identifies them
    305     // as being N64. We can safely assume at the moment that all Mips
    306     // ELFCLASS64 ELFs are N64. New Mips64 ABIs should provide enough
    307     // information to disambiguate between old vs new ABIs.
    308     uint8_t Type1 = (Type >> 0) & 0xFF;
    309     uint8_t Type2 = (Type >> 8) & 0xFF;
    310     uint8_t Type3 = (Type >> 16) & 0xFF;
    311 
    312     // Concat all three relocation type names.
    313     StringRef Name = getRelocationTypeName(Type1);
    314     Result.append(Name.begin(), Name.end());
    315 
    316     Name = getRelocationTypeName(Type2);
    317     Result.append(1, '/');
    318     Result.append(Name.begin(), Name.end());
    319 
    320     Name = getRelocationTypeName(Type3);
    321     Result.append(1, '/');
    322     Result.append(Name.begin(), Name.end());
    323   }
    324 }
    325 
    326 template <class ELFT>
    327 Expected<const typename ELFT::Sym *>
    328 ELFFile<ELFT>::getRelocationSymbol(const Elf_Rel *Rel,
    329                                    const Elf_Shdr *SymTab) const {
    330   uint32_t Index = Rel->getSymbol(isMips64EL());
    331   if (Index == 0)
    332     return nullptr;
    333   return getEntry<Elf_Sym>(SymTab, Index);
    334 }
    335 
    336 template <class ELFT>
    337 Expected<StringRef>
    338 ELFFile<ELFT>::getSectionStringTable(Elf_Shdr_Range Sections) const {
    339   uint32_t Index = getHeader()->e_shstrndx;
    340   if (Index == ELF::SHN_XINDEX)
    341     Index = Sections[0].sh_link;
    342 
    343   if (!Index) // no section string table.
    344     return "";
    345   if (Index >= Sections.size())
    346     return createError("invalid section index");
    347   return getStringTable(&Sections[Index]);
    348 }
    349 
    350 template <class ELFT> ELFFile<ELFT>::ELFFile(StringRef Object) : Buf(Object) {}
    351 
    352 template <class ELFT>
    353 Expected<ELFFile<ELFT>> ELFFile<ELFT>::create(StringRef Object) {
    354   if (sizeof(Elf_Ehdr) > Object.size())
    355     return createError("Invalid buffer");
    356   return ELFFile(Object);
    357 }
    358 
    359 template <class ELFT>
    360 bool compareAddr(uint64_t VAddr, const Elf_Phdr_Impl<ELFT> *Phdr) {
    361   return VAddr < Phdr->p_vaddr;
    362 }
    363 
    364 template <class ELFT>
    365 Expected<typename ELFT::ShdrRange> ELFFile<ELFT>::sections() const {
    366   const uintX_t SectionTableOffset = getHeader()->e_shoff;
    367   if (SectionTableOffset == 0)
    368     return ArrayRef<Elf_Shdr>();
    369 
    370   if (getHeader()->e_shentsize != sizeof(Elf_Shdr))
    371     return createError(
    372         "invalid section header entry size (e_shentsize) in ELF header");
    373 
    374   const uint64_t FileSize = Buf.size();
    375 
    376   if (SectionTableOffset + sizeof(Elf_Shdr) > FileSize)
    377     return createError("section header table goes past the end of the file");
    378 
    379   // Invalid address alignment of section headers
    380   if (SectionTableOffset & (alignof(Elf_Shdr) - 1))
    381     return createError("invalid alignment of section headers");
    382 
    383   const Elf_Shdr *First =
    384       reinterpret_cast<const Elf_Shdr *>(base() + SectionTableOffset);
    385 
    386   uintX_t NumSections = getHeader()->e_shnum;
    387   if (NumSections == 0)
    388     NumSections = First->sh_size;
    389 
    390   if (NumSections > UINT64_MAX / sizeof(Elf_Shdr))
    391     return createError("section table goes past the end of file");
    392 
    393   const uint64_t SectionTableSize = NumSections * sizeof(Elf_Shdr);
    394 
    395   // Section table goes past end of file!
    396   if (SectionTableOffset + SectionTableSize > FileSize)
    397     return createError("section table goes past the end of file");
    398 
    399   return makeArrayRef(First, NumSections);
    400 }
    401 
    402 template <class ELFT>
    403 template <typename T>
    404 Expected<const T *> ELFFile<ELFT>::getEntry(uint32_t Section,
    405                                             uint32_t Entry) const {
    406   auto SecOrErr = getSection(Section);
    407   if (!SecOrErr)
    408     return SecOrErr.takeError();
    409   return getEntry<T>(*SecOrErr, Entry);
    410 }
    411 
    412 template <class ELFT>
    413 template <typename T>
    414 Expected<const T *> ELFFile<ELFT>::getEntry(const Elf_Shdr *Section,
    415                                             uint32_t Entry) const {
    416   if (sizeof(T) != Section->sh_entsize)
    417     return createError("invalid sh_entsize");
    418   size_t Pos = Section->sh_offset + Entry * sizeof(T);
    419   if (Pos + sizeof(T) > Buf.size())
    420     return createError("invalid section offset");
    421   return reinterpret_cast<const T *>(base() + Pos);
    422 }
    423 
    424 template <class ELFT>
    425 Expected<const typename ELFT::Shdr *>
    426 ELFFile<ELFT>::getSection(uint32_t Index) const {
    427   auto TableOrErr = sections();
    428   if (!TableOrErr)
    429     return TableOrErr.takeError();
    430   return object::getSection<ELFT>(*TableOrErr, Index);
    431 }
    432 
    433 template <class ELFT>
    434 Expected<StringRef>
    435 ELFFile<ELFT>::getStringTable(const Elf_Shdr *Section) const {
    436   if (Section->sh_type != ELF::SHT_STRTAB)
    437     return createError("invalid sh_type for string table, expected SHT_STRTAB");
    438   auto V = getSectionContentsAsArray<char>(Section);
    439   if (!V)
    440     return V.takeError();
    441   ArrayRef<char> Data = *V;
    442   if (Data.empty())
    443     return createError("empty string table");
    444   if (Data.back() != '\0')
    445     return createError("string table non-null terminated");
    446   return StringRef(Data.begin(), Data.size());
    447 }
    448 
    449 template <class ELFT>
    450 Expected<ArrayRef<typename ELFT::Word>>
    451 ELFFile<ELFT>::getSHNDXTable(const Elf_Shdr &Section) const {
    452   auto SectionsOrErr = sections();
    453   if (!SectionsOrErr)
    454     return SectionsOrErr.takeError();
    455   return getSHNDXTable(Section, *SectionsOrErr);
    456 }
    457 
    458 template <class ELFT>
    459 Expected<ArrayRef<typename ELFT::Word>>
    460 ELFFile<ELFT>::getSHNDXTable(const Elf_Shdr &Section,
    461                              Elf_Shdr_Range Sections) const {
    462   assert(Section.sh_type == ELF::SHT_SYMTAB_SHNDX);
    463   auto VOrErr = getSectionContentsAsArray<Elf_Word>(&Section);
    464   if (!VOrErr)
    465     return VOrErr.takeError();
    466   ArrayRef<Elf_Word> V = *VOrErr;
    467   auto SymTableOrErr = object::getSection<ELFT>(Sections, Section.sh_link);
    468   if (!SymTableOrErr)
    469     return SymTableOrErr.takeError();
    470   const Elf_Shdr &SymTable = **SymTableOrErr;
    471   if (SymTable.sh_type != ELF::SHT_SYMTAB &&
    472       SymTable.sh_type != ELF::SHT_DYNSYM)
    473     return createError("invalid sh_type");
    474   if (V.size() != (SymTable.sh_size / sizeof(Elf_Sym)))
    475     return createError("invalid section contents size");
    476   return V;
    477 }
    478 
    479 template <class ELFT>
    480 Expected<StringRef>
    481 ELFFile<ELFT>::getStringTableForSymtab(const Elf_Shdr &Sec) const {
    482   auto SectionsOrErr = sections();
    483   if (!SectionsOrErr)
    484     return SectionsOrErr.takeError();
    485   return getStringTableForSymtab(Sec, *SectionsOrErr);
    486 }
    487 
    488 template <class ELFT>
    489 Expected<StringRef>
    490 ELFFile<ELFT>::getStringTableForSymtab(const Elf_Shdr &Sec,
    491                                        Elf_Shdr_Range Sections) const {
    492 
    493   if (Sec.sh_type != ELF::SHT_SYMTAB && Sec.sh_type != ELF::SHT_DYNSYM)
    494     return createError(
    495         "invalid sh_type for symbol table, expected SHT_SYMTAB or SHT_DYNSYM");
    496   auto SectionOrErr = object::getSection<ELFT>(Sections, Sec.sh_link);
    497   if (!SectionOrErr)
    498     return SectionOrErr.takeError();
    499   return getStringTable(*SectionOrErr);
    500 }
    501 
    502 template <class ELFT>
    503 Expected<StringRef>
    504 ELFFile<ELFT>::getSectionName(const Elf_Shdr *Section) const {
    505   auto SectionsOrErr = sections();
    506   if (!SectionsOrErr)
    507     return SectionsOrErr.takeError();
    508   auto Table = getSectionStringTable(*SectionsOrErr);
    509   if (!Table)
    510     return Table.takeError();
    511   return getSectionName(Section, *Table);
    512 }
    513 
    514 template <class ELFT>
    515 Expected<StringRef> ELFFile<ELFT>::getSectionName(const Elf_Shdr *Section,
    516                                                   StringRef DotShstrtab) const {
    517   uint32_t Offset = Section->sh_name;
    518   if (Offset == 0)
    519     return StringRef();
    520   if (Offset >= DotShstrtab.size())
    521     return createError("invalid string offset");
    522   return StringRef(DotShstrtab.data() + Offset);
    523 }
    524 
    525 /// This function returns the hash value for a symbol in the .dynsym section
    526 /// Name of the API remains consistent as specified in the libelf
    527 /// REF : http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#hash
    528 inline unsigned hashSysV(StringRef SymbolName) {
    529   unsigned h = 0, g;
    530   for (char C : SymbolName) {
    531     h = (h << 4) + C;
    532     g = h & 0xf0000000L;
    533     if (g != 0)
    534       h ^= g >> 24;
    535     h &= ~g;
    536   }
    537   return h;
    538 }
    539 
    540 } // end namespace object
    541 } // end namespace llvm
    542 
    543 #endif // LLVM_OBJECT_ELF_H
    544