Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 1 | //===- ELFObjHandler.cpp --------------------------------------------------===// |
| 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 | #include "ELFObjHandler.h" |
| 11 | #include "llvm/Object/Binary.h" |
| 12 | #include "llvm/Object/ELFObjectFile.h" |
| 13 | #include "llvm/Object/ELFTypes.h" |
| 14 | #include "llvm/Support/Errc.h" |
| 15 | #include "llvm/Support/Error.h" |
| 16 | #include "llvm/Support/MemoryBuffer.h" |
| 17 | #include "llvm/TextAPI/ELF/ELFStub.h" |
| 18 | |
| 19 | using llvm::MemoryBufferRef; |
| 20 | using llvm::object::ELFObjectFile; |
| 21 | |
| 22 | using namespace llvm; |
| 23 | using namespace llvm::object; |
Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 24 | using namespace llvm::ELF; |
| 25 | |
| 26 | namespace llvm { |
| 27 | namespace elfabi { |
| 28 | |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 29 | // Simple struct to hold relevant .dynamic entries. |
| 30 | struct DynamicEntries { |
| 31 | uint64_t StrTabAddr = 0; |
| 32 | uint64_t StrSize = 0; |
| 33 | Optional<uint64_t> SONameOffset; |
Armando Montanez | 56d1812 | 2019-01-18 20:56:03 +0000 | [diff] [blame^] | 34 | std::vector<uint64_t> NeededLibNames; |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 35 | }; |
| 36 | |
| 37 | /// This function behaves similarly to StringRef::substr(), but attempts to |
| 38 | /// terminate the returned StringRef at the first null terminator. If no null |
| 39 | /// terminator is found, an error is returned. |
| 40 | /// |
| 41 | /// @param Str Source string to create a substring from. |
| 42 | /// @param Offset The start index of the desired substring. |
| 43 | static Expected<StringRef> terminatedSubstr(StringRef Str, size_t Offset) { |
| 44 | size_t StrEnd = Str.find('\0', Offset); |
| 45 | if (StrEnd == StringLiteral::npos) { |
| 46 | return createError( |
| 47 | "String overran bounds of string table (no null terminator)"); |
| 48 | } |
| 49 | |
| 50 | size_t StrLen = StrEnd - Offset; |
| 51 | return Str.substr(Offset, StrLen); |
| 52 | } |
| 53 | |
| 54 | /// This function takes an error, and appends a string of text to the end of |
| 55 | /// that error. Since "appending" to an Error isn't supported behavior of an |
| 56 | /// Error, this function technically creates a new error with the combined |
| 57 | /// message and consumes the old error. |
| 58 | /// |
| 59 | /// @param Err Source error. |
| 60 | /// @param After Text to append at the end of Err's error message. |
| 61 | Error appendToError(Error Err, StringRef After) { |
| 62 | std::string Message; |
| 63 | raw_string_ostream Stream(Message); |
| 64 | Stream << Err; |
| 65 | Stream << " " << After; |
| 66 | consumeError(std::move(Err)); |
| 67 | return createError(Stream.str().c_str()); |
| 68 | } |
| 69 | |
| 70 | /// This function populates a DynamicEntries struct using an ELFT::DynRange. |
| 71 | /// After populating the struct, the members are validated with |
| 72 | /// some basic sanity checks. |
| 73 | /// |
| 74 | /// @param Dyn Target DynamicEntries struct to populate. |
| 75 | /// @param DynTable Source dynamic table. |
| 76 | template <class ELFT> |
| 77 | static Error populateDynamic(DynamicEntries &Dyn, |
| 78 | typename ELFT::DynRange DynTable) { |
| 79 | if (DynTable.empty()) |
| 80 | return createError("No .dynamic section found"); |
| 81 | |
| 82 | // Search .dynamic for relevant entries. |
| 83 | bool FoundDynStr = false; |
| 84 | bool FoundDynStrSz = false; |
| 85 | for (auto &Entry : DynTable) { |
| 86 | switch (Entry.d_tag) { |
| 87 | case DT_SONAME: |
| 88 | Dyn.SONameOffset = Entry.d_un.d_val; |
| 89 | break; |
| 90 | case DT_STRTAB: |
| 91 | Dyn.StrTabAddr = Entry.d_un.d_ptr; |
| 92 | FoundDynStr = true; |
| 93 | break; |
| 94 | case DT_STRSZ: |
| 95 | Dyn.StrSize = Entry.d_un.d_val; |
| 96 | FoundDynStrSz = true; |
| 97 | break; |
Armando Montanez | 56d1812 | 2019-01-18 20:56:03 +0000 | [diff] [blame^] | 98 | case DT_NEEDED: |
| 99 | Dyn.NeededLibNames.push_back(Entry.d_un.d_val); |
| 100 | break; |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 101 | } |
| 102 | } |
| 103 | |
| 104 | if (!FoundDynStr) { |
| 105 | return createError( |
| 106 | "Couldn't locate dynamic string table (no DT_STRTAB entry)"); |
| 107 | } |
| 108 | if (!FoundDynStrSz) { |
| 109 | return createError( |
| 110 | "Couldn't determine dynamic string table size (no DT_STRSZ entry)"); |
| 111 | } |
| 112 | if (Dyn.SONameOffset.hasValue() && *Dyn.SONameOffset >= Dyn.StrSize) { |
| 113 | return createStringError( |
| 114 | object_error::parse_failed, |
| 115 | "DT_SONAME string offset (0x%016x) outside of dynamic string table", |
| 116 | *Dyn.SONameOffset); |
| 117 | } |
Armando Montanez | 56d1812 | 2019-01-18 20:56:03 +0000 | [diff] [blame^] | 118 | for (uint64_t Offset : Dyn.NeededLibNames) { |
| 119 | if (Offset >= Dyn.StrSize) { |
| 120 | return createStringError( |
| 121 | object_error::parse_failed, |
| 122 | "DT_NEEDED string offset (0x%016x) outside of dynamic string table", |
| 123 | Offset); |
| 124 | } |
| 125 | } |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 126 | |
| 127 | return Error::success(); |
| 128 | } |
| 129 | |
Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 130 | /// Returns a new ELFStub with all members populated from an ELFObjectFile. |
| 131 | /// @param ElfObj Source ELFObjectFile. |
| 132 | template <class ELFT> |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 133 | static Expected<std::unique_ptr<ELFStub>> |
Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 134 | buildStub(const ELFObjectFile<ELFT> &ElfObj) { |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 135 | using Elf_Dyn_Range = typename ELFT::DynRange; |
| 136 | using Elf_Phdr_Range = typename ELFT::PhdrRange; |
Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 137 | std::unique_ptr<ELFStub> DestStub = make_unique<ELFStub>(); |
| 138 | const ELFFile<ELFT> *ElfFile = ElfObj.getELFFile(); |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 139 | // Fetch .dynamic table. |
| 140 | Expected<Elf_Dyn_Range> DynTable = ElfFile->dynamicEntries(); |
| 141 | if (!DynTable) { |
| 142 | return DynTable.takeError(); |
| 143 | } |
Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 144 | |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 145 | // Fetch program headers. |
| 146 | Expected<Elf_Phdr_Range> PHdrs = ElfFile->program_headers(); |
| 147 | if (!PHdrs) { |
| 148 | return PHdrs.takeError(); |
| 149 | } |
| 150 | |
| 151 | // Collect relevant .dynamic entries. |
| 152 | DynamicEntries DynEnt; |
| 153 | if (Error Err = populateDynamic<ELFT>(DynEnt, *DynTable)) |
| 154 | return std::move(Err); |
| 155 | |
| 156 | // Convert .dynstr address to an offset. |
| 157 | Expected<const uint8_t *> DynStrPtr = |
| 158 | ElfFile->toMappedAddr(DynEnt.StrTabAddr); |
| 159 | if (!DynStrPtr) |
| 160 | return appendToError(DynStrPtr.takeError(), |
| 161 | "when locating .dynstr section contents"); |
| 162 | |
| 163 | StringRef DynStr(reinterpret_cast<const char *>(DynStrPtr.get()), |
| 164 | DynEnt.StrSize); |
| 165 | |
| 166 | // Populate Arch from ELF header. |
Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 167 | DestStub->Arch = ElfFile->getHeader()->e_machine; |
| 168 | |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 169 | // Populate SoName from .dynamic entries and dynamic string table. |
| 170 | if (DynEnt.SONameOffset.hasValue()) { |
| 171 | Expected<StringRef> NameOrErr = |
| 172 | terminatedSubstr(DynStr, *DynEnt.SONameOffset); |
| 173 | if (!NameOrErr) { |
| 174 | return appendToError(NameOrErr.takeError(), "when reading DT_SONAME"); |
| 175 | } |
| 176 | DestStub->SoName = *NameOrErr; |
| 177 | } |
| 178 | |
Armando Montanez | 56d1812 | 2019-01-18 20:56:03 +0000 | [diff] [blame^] | 179 | // Populate NeededLibs from .dynamic entries and dynamic string table. |
| 180 | for (uint64_t NeededStrOffset : DynEnt.NeededLibNames) { |
| 181 | Expected<StringRef> LibNameOrErr = |
| 182 | terminatedSubstr(DynStr, NeededStrOffset); |
| 183 | if (!LibNameOrErr) { |
| 184 | return appendToError(LibNameOrErr.takeError(), "when reading DT_NEEDED"); |
| 185 | } |
| 186 | DestStub->NeededLibs.push_back(*LibNameOrErr); |
| 187 | } |
| 188 | |
Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 189 | // TODO: Populate Symbols from .dynsym table and linked string table. |
| 190 | |
| 191 | return std::move(DestStub); |
| 192 | } |
| 193 | |
| 194 | Expected<std::unique_ptr<ELFStub>> readELFFile(MemoryBufferRef Buf) { |
| 195 | Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(Buf); |
| 196 | if (!BinOrErr) { |
| 197 | return BinOrErr.takeError(); |
| 198 | } |
| 199 | |
| 200 | Binary *Bin = BinOrErr->get(); |
| 201 | if (auto Obj = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) { |
| 202 | return buildStub(*Obj); |
| 203 | } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) { |
| 204 | return buildStub(*Obj); |
| 205 | } else if (auto Obj = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) { |
| 206 | return buildStub(*Obj); |
| 207 | } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) { |
| 208 | return buildStub(*Obj); |
| 209 | } |
| 210 | |
| 211 | return createStringError(errc::not_supported, "Unsupported binary format"); |
| 212 | } |
| 213 | |
| 214 | } // end namespace elfabi |
| 215 | } // end namespace llvm |