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