Apache Mesos
elf.hpp
Go to the documentation of this file.
1 // Licensed to the Apache Software Foundation (ASF) under one
2 // or more contributor license agreements. See the NOTICE file
3 // distributed with this work for additional information
4 // regarding copyright ownership. The ASF licenses this file
5 // to you under the Apache License, Version 2.0 (the
6 // "License"); you may not use this file except in compliance
7 // with the License. You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #ifndef __STOUT_ELF_HPP__
18 #define __STOUT_ELF_HPP__
19 
20 #include <map>
21 #include <string>
22 #include <vector>
23 
24 #include <elfio/elfio.hpp>
25 
26 #include <stout/error.hpp>
27 #include <stout/foreach.hpp>
28 #include <stout/nothing.hpp>
29 #include <stout/option.hpp>
30 #include <stout/result.hpp>
31 #include <stout/stringify.hpp>
32 #include <stout/strings.hpp>
33 #include <stout/try.hpp>
34 #include <stout/version.hpp>
35 
36 namespace elf {
37 
38 enum Class
39 {
40  CLASSNONE = ELFCLASSNONE,
41  CLASS32 = ELFCLASS32,
42  CLASS64 = ELFCLASS64,
43 };
44 
45 enum class SectionType
46 {
47  DYNAMIC = SHT_DYNAMIC,
48  NOTE = SHT_NOTE,
49  PROGBITS = SHT_PROGBITS,
50 };
51 
52 enum class DynamicTag
53 {
54  STRTAB = DT_STRTAB,
55  SONAME = DT_SONAME,
56  NEEDED = DT_NEEDED,
57 };
58 
59 
60 // This class is used to represent the contents of a standard ELF
61 // binary. The current implementation is incomplete.
62 //
63 // NOTE: We use ELFIO under the hood to do our elf parsing for us.
64 // Unfortunately, ELFIO reads the *entire* elf file into memory when
65 // it is first loaded instead of relying on something like `mmap` to
66 // only page in the parts of the file we are interested in. If this
67 // becomes a problem, we will have to use something like `libelf`
68 // instead (but note that libelf is not header-only and will
69 // therefore introduce a runtime library dependency).
70 class File
71 {
72 public:
73  // TODO(klueska): Return a unique_ptr here.
74  static Try<File*> load(const std::string& path)
75  {
76  File* file = new File();
77 
78  if (!file->elf.load(path)) {
79  delete file;
80  return Error("Unknown error during elfio::load");
81  }
82 
83  // Create the mapping from section type to sections.
84  foreach (ELFIO::section* section, file->elf.sections) {
85  SectionType section_type = SectionType(section->get_type());
86  file->sections_by_type[section_type].push_back(section);
87  }
88 
89  return file;
90  }
91 
92  // Returns the ELF class of an ELF file (CLASS32, or CLASS64).
94  {
95  Class c = Class(elf.get_class());
96  if (c == CLASSNONE) {
97  return Error("Unknown error");
98  }
99  return c;
100  }
101 
102 
103  // Extract the strings associated with the provided
104  // `DynamicTag` from all DYNAMIC sections in the ELF binary.
106  {
107  if (sections_by_type.count(SectionType::DYNAMIC) == 0) {
108  return Error("No DYNAMIC sections found");
109  }
110 
111  std::vector<std::string> strings;
112 
113  foreach (ELFIO::section* section,
114  sections_by_type.at(SectionType::DYNAMIC)) {
115  auto accessor = ELFIO::dynamic_section_accessor(elf, section);
116 
117  for (ELFIO::Elf_Xword i = 0; i < accessor.get_entries_num(); ++i) {
118  ELFIO::Elf_Xword entry_tag;
119  ELFIO::Elf_Xword entry_value;
120  std::string entry_string;
121 
122  if (!accessor.get_entry(i, entry_tag, entry_value, entry_string)) {
123  return Error("Failed to get entry from DYNAMIC section");
124  }
125 
126  if (tag == DynamicTag(entry_tag)) {
127  strings.push_back(entry_string);
128  }
129  }
130  }
131 
132  return strings;
133  }
134 
135  // Get the ABI version of the ELF file by parsing the contents of
136  // the `.note.ABI-tag` section. This section is Linux specific and
137  // adheres to the format described in the links below.
138  //
139  // NOTE: Not all ELF files have a `.note.ABI-tag` section (even on
140  // Linux), so we return a `Result` to allow the return value to be
141  // `None()`.
142  //
143  // https://refspecs.linuxfoundation.org/LSB_1.2.0/gLSB/noteabitag.html
144  // http://flint.cs.yale.edu/cs422/doc/ELF_Format.pdf
146  {
147  ELFIO::section* section = elf.sections[".note.ABI-tag"];
148 
149  if (section == nullptr) {
150  return None();
151  }
152 
153  if (SectionType(section->get_type()) != SectionType::NOTE) {
154  return Error("Section '.note.ABI-tag' is not a NOTE section");
155  }
156 
157  auto accessor = ELFIO::note_section_accessor(elf, section);
158 
159  if (accessor.get_notes_num() != 1) {
160  return Error("Section '.note.ABI-tag' does not have exactly one entry");
161  }
162 
163  ELFIO::Elf_Word type;
164  std::string name;
165  void* descriptor;
166  ELFIO::Elf_Word descriptor_size;
167 
168  if (!accessor.get_note(0, type, name, descriptor, descriptor_size)) {
169  return Error("Failed to get entry from '.note.ABI-tag' section");
170  }
171 
172  // The note in a `.note.ABI-tag` section must have `type == 1`.
173  if (type != 1) {
174  return Error("Corrupt tag type '" + stringify(type) + "' from"
175  " entry in '.note.ABI-tag' section");
176  }
177 
178  // Linux mandates `name == GNU`.
179  if (name != "GNU") {
180  return Error("Corrupt label '" + name + "' from"
181  " entry in '.note.ABI-tag' section");
182  }
183 
184  // The version array consists of 4 32-bit numbers, with the
185  // first number fixed at 0 (meaning it is a Linux ELF file), and
186  // the rest specifying the ABI version. For example, if the array
187  // contains {0, 2, 3, 99}, this signifies a Linux ELF file
188  // with an ABI version of 2.3.99.
189  std::vector<uint32_t> version(
190  (uint32_t*)descriptor,
191  (uint32_t*)((char*)descriptor + descriptor_size));
192 
193  if (version.size() != 4 || version[0] != 0) {
194  return Error("Corrupt version '" + stringify(version) + "'"
195  " from entry in '.note.ABI-tag' section");
196  }
197 
198  return Version(version[1], version[2], version[3]);
199  }
200 
201  // Return the contents of the .interp section, if such a
202  // section exits. For Linux ELF executables, the .interp
203  // section contains the path to the program loader.
204  //
205  // https://refspecs.linuxfoundation.org/LSB_1.2.0/gLSB/specialsections.html
207  {
208  ELFIO::section* section = elf.sections[".interp"];
209 
210  if (section == nullptr) {
211  return None();
212  }
213 
214  if (SectionType(section->get_type()) != SectionType::PROGBITS) {
215  return Error("Section '.interp' is not a PROGBITS section");
216  }
217 
218  return std::string(section->get_data(), section->get_size());
219  }
220 
221 private:
222  explicit File() {}
223 
224  ELFIO::elfio elf;
225  std::map<SectionType, std::vector<ELFIO::section*>> sections_by_type;
226 };
227 
228 } // namespace elf {
229 
230 #endif // __STOUT_ELF_HPP__
Definition: path.hpp:29
Definition: errorbase.hpp:36
Definition: check.hpp:33
Definition: elf.hpp:36
Definition: format.hpp:45
Definition: check.hpp:30
Definition: elf.hpp:41
Definition: elf.hpp:70
URI file(const std::string &path)
Creates a file URI with the given path on the local host.
Definition: file.hpp:33
SectionType
Definition: elf.hpp:45
process::Future< Version > version()
Definition: version.hpp:32
Result< Version > get_abi_version() const
Definition: elf.hpp:145
Try< std::vector< std::string > > get_dynamic_strings(DynamicTag tag) const
Definition: elf.hpp:105
Definition: none.hpp:27
DynamicTag
Definition: elf.hpp:52
Definition: version.hpp:41
Try< uint32_t > type(const std::string &path)
Try< Class > get_class() const
Definition: elf.hpp:93
Definition: elf.hpp:40
std::string stringify(int flags)
static Try< File * > load(const std::string &path)
Definition: elf.hpp:74
Definition: elf.hpp:42
constexpr const char * name
Definition: shell.hpp:41
Result< std::string > get_interpreter() const
Definition: elf.hpp:206
Class
Definition: elf.hpp:38