blob: d60bd5cc03da2a6561cacffb5d97a9b3c1614be2 [file] [log] [blame]
Armando Montanez31f0f652019-01-03 18:32:36 +00001//===- 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
19using llvm::MemoryBufferRef;
20using llvm::object::ELFObjectFile;
21
22using namespace llvm;
23using namespace llvm::object;
Armando Montanez31f0f652019-01-03 18:32:36 +000024using namespace llvm::ELF;
25
26namespace llvm {
27namespace elfabi {
28
Armando Montanezfe7ab3c2019-01-16 17:47:16 +000029// Simple struct to hold relevant .dynamic entries.
30struct DynamicEntries {
31 uint64_t StrTabAddr = 0;
32 uint64_t StrSize = 0;
33 Optional<uint64_t> SONameOffset;
Armando Montanez56d18122019-01-18 20:56:03 +000034 std::vector<uint64_t> NeededLibNames;
Armando Montanezfe7ab3c2019-01-16 17:47:16 +000035};
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.
43static 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.
61Error 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.
76template <class ELFT>
77static 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 Montanez56d18122019-01-18 20:56:03 +000098 case DT_NEEDED:
99 Dyn.NeededLibNames.push_back(Entry.d_un.d_val);
100 break;
Armando Montanezfe7ab3c2019-01-16 17:47:16 +0000101 }
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 Montanez56d18122019-01-18 20:56:03 +0000118 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 Montanezfe7ab3c2019-01-16 17:47:16 +0000126
127 return Error::success();
128}
129
Armando Montanez31f0f652019-01-03 18:32:36 +0000130/// Returns a new ELFStub with all members populated from an ELFObjectFile.
131/// @param ElfObj Source ELFObjectFile.
132template <class ELFT>
Armando Montanezfe7ab3c2019-01-16 17:47:16 +0000133static Expected<std::unique_ptr<ELFStub>>
Armando Montanez31f0f652019-01-03 18:32:36 +0000134buildStub(const ELFObjectFile<ELFT> &ElfObj) {
Armando Montanezfe7ab3c2019-01-16 17:47:16 +0000135 using Elf_Dyn_Range = typename ELFT::DynRange;
136 using Elf_Phdr_Range = typename ELFT::PhdrRange;
Armando Montanez31f0f652019-01-03 18:32:36 +0000137 std::unique_ptr<ELFStub> DestStub = make_unique<ELFStub>();
138 const ELFFile<ELFT> *ElfFile = ElfObj.getELFFile();
Armando Montanezfe7ab3c2019-01-16 17:47:16 +0000139 // Fetch .dynamic table.
140 Expected<Elf_Dyn_Range> DynTable = ElfFile->dynamicEntries();
141 if (!DynTable) {
142 return DynTable.takeError();
143 }
Armando Montanez31f0f652019-01-03 18:32:36 +0000144
Armando Montanezfe7ab3c2019-01-16 17:47:16 +0000145 // 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 Montanez31f0f652019-01-03 18:32:36 +0000167 DestStub->Arch = ElfFile->getHeader()->e_machine;
168
Armando Montanezfe7ab3c2019-01-16 17:47:16 +0000169 // 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 Montanez56d18122019-01-18 20:56:03 +0000179 // 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 Montanez31f0f652019-01-03 18:32:36 +0000189 // TODO: Populate Symbols from .dynsym table and linked string table.
190
191 return std::move(DestStub);
192}
193
194Expected<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