1 // Copyright 2014 Renato Tegon Forti, Antony Polukhin.
2 // Copyright 2015-2019 Antony Polukhin.
3 //
4 // Distributed under the Boost Software License, Version 1.0.
5 // (See accompanying file LICENSE_1_0.txt
6 // or copy at http://www.boost.org/LICENSE_1_0.txt)
7 
8 #ifndef BOOST_DLL_DETAIL_POSIX_ELF_INFO_HPP
9 #define BOOST_DLL_DETAIL_POSIX_ELF_INFO_HPP
10 
11 #include <boost/dll/config.hpp>
12 
13 #ifdef BOOST_HAS_PRAGMA_ONCE
14 # pragma once
15 #endif
16 
17 #include <cstring>
18 #include <fstream>
19 #include <limits>
20 
21 #include <boost/cstdint.hpp>
22 #include <boost/throw_exception.hpp>
23 
24 namespace boost { namespace dll { namespace detail {
25 
26 template <class AddressOffsetT>
27 struct Elf_Ehdr_template {
28   unsigned char     e_ident[16];    /* Magic number and other info */
29   boost::uint16_t   e_type;         /* Object file type */
30   boost::uint16_t   e_machine;      /* Architecture */
31   boost::uint32_t   e_version;      /* Object file version */
32   AddressOffsetT    e_entry;        /* Entry point virtual address */
33   AddressOffsetT    e_phoff;        /* Program header table file offset */
34   AddressOffsetT    e_shoff;        /* Section header table file offset */
35   boost::uint32_t   e_flags;        /* Processor-specific flags */
36   boost::uint16_t   e_ehsize;       /* ELF header size in bytes */
37   boost::uint16_t   e_phentsize;    /* Program header table entry size */
38   boost::uint16_t   e_phnum;        /* Program header table entry count */
39   boost::uint16_t   e_shentsize;    /* Section header table entry size */
40   boost::uint16_t   e_shnum;        /* Section header table entry count */
41   boost::uint16_t   e_shstrndx;     /* Section header string table index */
42 };
43 
44 typedef Elf_Ehdr_template<boost::uint32_t> Elf32_Ehdr_;
45 typedef Elf_Ehdr_template<boost::uint64_t> Elf64_Ehdr_;
46 
47 template <class AddressOffsetT>
48 struct Elf_Shdr_template {
49   boost::uint32_t   sh_name;        /* Section name (string tbl index) */
50   boost::uint32_t   sh_type;        /* Section type */
51   AddressOffsetT    sh_flags;       /* Section flags */
52   AddressOffsetT    sh_addr;        /* Section virtual addr at execution */
53   AddressOffsetT    sh_offset;      /* Section file offset */
54   AddressOffsetT    sh_size;        /* Section size in bytes */
55   boost::uint32_t   sh_link;        /* Link to another section */
56   boost::uint32_t   sh_info;        /* Additional section information */
57   AddressOffsetT    sh_addralign;   /* Section alignment */
58   AddressOffsetT    sh_entsize;     /* Entry size if section holds table */
59 };
60 
61 typedef Elf_Shdr_template<boost::uint32_t> Elf32_Shdr_;
62 typedef Elf_Shdr_template<boost::uint64_t> Elf64_Shdr_;
63 
64 template <class AddressOffsetT>
65 struct Elf_Sym_template;
66 
67 template <>
68 struct Elf_Sym_template<boost::uint32_t> {
69   typedef boost::uint32_t AddressOffsetT;
70 
71   boost::uint32_t   st_name;    /* Symbol name (string tbl index) */
72   AddressOffsetT    st_value;   /* Symbol value */
73   AddressOffsetT    st_size;    /* Symbol size */
74   unsigned char     st_info;    /* Symbol type and binding */
75   unsigned char     st_other;   /* Symbol visibility */
76   boost::uint16_t   st_shndx;   /* Section index */
77 };
78 
79 template <>
80 struct Elf_Sym_template<boost::uint64_t> {
81   typedef boost::uint64_t AddressOffsetT;
82 
83   boost::uint32_t   st_name;    /* Symbol name (string tbl index) */
84   unsigned char     st_info;    /* Symbol type and binding */
85   unsigned char     st_other;   /* Symbol visibility */
86   boost::uint16_t   st_shndx;   /* Section index */
87   AddressOffsetT    st_value;   /* Symbol value */
88   AddressOffsetT    st_size;    /* Symbol size */
89 };
90 
91 
92 typedef Elf_Sym_template<boost::uint32_t> Elf32_Sym_;
93 typedef Elf_Sym_template<boost::uint64_t> Elf64_Sym_;
94 
95 template <class AddressOffsetT>
96 class elf_info {
97     typedef boost::dll::detail::Elf_Ehdr_template<AddressOffsetT>  header_t;
98     typedef boost::dll::detail::Elf_Shdr_template<AddressOffsetT>  section_t;
99     typedef boost::dll::detail::Elf_Sym_template<AddressOffsetT>   symbol_t;
100 
101     BOOST_STATIC_CONSTANT(boost::uint32_t, SHT_SYMTAB_ = 2);
102     BOOST_STATIC_CONSTANT(boost::uint32_t, SHT_STRTAB_ = 3);
103 
104     BOOST_STATIC_CONSTANT(unsigned char, STB_LOCAL_ = 0);   /* Local symbol */
105     BOOST_STATIC_CONSTANT(unsigned char, STB_GLOBAL_ = 1);  /* Global symbol */
106     BOOST_STATIC_CONSTANT(unsigned char, STB_WEAK_ = 2);    /* Weak symbol */
107 
108     /* Symbol visibility specification encoded in the st_other field.  */
109     BOOST_STATIC_CONSTANT(unsigned char, STV_DEFAULT_ = 0);      /* Default symbol visibility rules */
110     BOOST_STATIC_CONSTANT(unsigned char, STV_INTERNAL_ = 1);     /* Processor specific hidden class */
111     BOOST_STATIC_CONSTANT(unsigned char, STV_HIDDEN_ = 2);       /* Sym unavailable in other modules */
112     BOOST_STATIC_CONSTANT(unsigned char, STV_PROTECTED_ = 3);    /* Not preemptible, not exported */
113 
114 public:
parsing_supported(std::ifstream & fs)115     static bool parsing_supported(std::ifstream& fs) {
116         const unsigned char magic_bytes[5] = {
117             0x7f, 'E', 'L', 'F', sizeof(boost::uint32_t) == sizeof(AddressOffsetT) ? 1 : 2
118         };
119 
120         unsigned char ch;
121         fs.seekg(0);
122         for (std::size_t i = 0; i < sizeof(magic_bytes); ++i) {
123             fs >> ch;
124             if (ch != magic_bytes[i]) {
125                 return false;
126             }
127         }
128 
129         return true;
130     }
131 
sections(std::ifstream & fs)132     static std::vector<std::string> sections(std::ifstream& fs) {
133         std::vector<std::string> ret;
134         std::vector<char> names;
135         sections_names_raw(fs, names);
136 
137         const char* name_begin = &names[0];
138         const char* const name_end = name_begin + names.size();
139         ret.reserve(header(fs).e_shnum);
140         do {
141             ret.push_back(name_begin);
142             name_begin += ret.back().size() + 1;
143         } while (name_begin != name_end);
144 
145         return ret;
146     }
147 
148 private:
149     template <class Integer>
checked_seekg(std::ifstream & fs,Integer pos)150     static void checked_seekg(std::ifstream& fs, Integer pos) {
151         /* TODO: use cmp_less, cmp_greater
152         if ((std::numeric_limits<std::streamoff>::max)() < pos) {
153             boost::throw_exception(std::runtime_error("Integral overflow while getting info from ELF file"));
154         }
155         if ((std::numeric_limits<std::streamoff>::min)() > pos){
156             boost::throw_exception(std::runtime_error("Integral underflow while getting info from ELF file"));
157         }
158         */
159         fs.seekg(static_cast<std::streamoff>(pos));
160     }
161 
162     template <class T>
read_raw(std::ifstream & fs,T & value,std::size_t size=sizeof (T))163     static void read_raw(std::ifstream& fs, T& value, std::size_t size = sizeof(T)) {
164         fs.read(reinterpret_cast<char*>(&value), size);
165     }
166 
header(std::ifstream & fs)167     static header_t header(std::ifstream& fs) {
168         header_t elf;
169 
170         fs.seekg(0);
171         read_raw(fs, elf);
172 
173         return elf;
174     }
175 
sections_names_raw(std::ifstream & fs,std::vector<char> & sections)176     static void sections_names_raw(std::ifstream& fs, std::vector<char>& sections) {
177         const header_t elf = header(fs);
178 
179         section_t section_names_section;
180         checked_seekg(fs, elf.e_shoff + elf.e_shstrndx * sizeof(section_t));
181         read_raw(fs, section_names_section);
182 
183         sections.resize(static_cast<std::size_t>(section_names_section.sh_size));
184         checked_seekg(fs, section_names_section.sh_offset);
185         read_raw(fs, sections[0], static_cast<std::size_t>(section_names_section.sh_size));
186     }
187 
symbols_text(std::ifstream & fs,std::vector<symbol_t> & symbols,std::vector<char> & text)188     static void symbols_text(std::ifstream& fs, std::vector<symbol_t>& symbols, std::vector<char>& text) {
189         const header_t elf = header(fs);
190         checked_seekg(fs, elf.e_shoff);
191 
192         for (std::size_t i = 0; i < elf.e_shnum; ++i) {
193             section_t section;
194             read_raw(fs, section);
195 
196             if (section.sh_type == SHT_SYMTAB_) {
197                 symbols.resize(static_cast<std::size_t>(section.sh_size / sizeof(symbol_t)));
198 
199                 const std::ifstream::pos_type pos = fs.tellg();
200                 checked_seekg(fs, section.sh_offset);
201                 read_raw(fs, symbols[0], static_cast<std::size_t>(section.sh_size - (section.sh_size % sizeof(symbol_t))) );
202                 checked_seekg(fs, pos);
203             } else if (section.sh_type == SHT_STRTAB_) {
204                 text.resize(static_cast<std::size_t>(section.sh_size));
205 
206                 const std::ifstream::pos_type pos = fs.tellg();
207                 checked_seekg(fs, section.sh_offset);
208                 read_raw(fs, text[0], static_cast<std::size_t>(section.sh_size));
209                 checked_seekg(fs, pos);
210             }
211         }
212     }
213 
is_visible(const symbol_t & sym)214     static bool is_visible(const symbol_t& sym) BOOST_NOEXCEPT {
215         // `(sym.st_info >> 4) != STB_LOCAL_ && !!sym.st_size` check also workarounds the
216         // GCC's issue https://sourceware.org/bugzilla/show_bug.cgi?id=13621
217         return (sym.st_other & 0x03) == STV_DEFAULT_ && (sym.st_info >> 4) != STB_LOCAL_ && !!sym.st_size;
218     }
219 
220 public:
symbols(std::ifstream & fs)221     static std::vector<std::string> symbols(std::ifstream& fs) {
222         std::vector<std::string> ret;
223 
224         std::vector<symbol_t> symbols;
225         std::vector<char>   text;
226         symbols_text(fs, symbols, text);
227 
228         ret.reserve(symbols.size());
229         for (std::size_t i = 0; i < symbols.size(); ++i) {
230             if (is_visible(symbols[i])) {
231                 ret.push_back(&text[0] + symbols[i].st_name);
232                 if (ret.back().empty()) {
233                     ret.pop_back(); // Do not show empty names
234                 }
235             }
236         }
237 
238         return ret;
239     }
240 
symbols(std::ifstream & fs,const char * section_name)241     static std::vector<std::string> symbols(std::ifstream& fs, const char* section_name) {
242         std::vector<std::string> ret;
243 
244         std::size_t index = 0;
245         std::size_t ptrs_in_section_count = 0;
246         {
247             std::vector<char> names;
248             sections_names_raw(fs, names);
249 
250             const header_t elf = header(fs);
251 
252             for (; index < elf.e_shnum; ++index) {
253                 section_t section;
254                 checked_seekg(fs, elf.e_shoff + index * sizeof(section_t));
255                 read_raw(fs, section);
256 
257                 if (!std::strcmp(&names[0] + section.sh_name, section_name)) {
258                     if (!section.sh_entsize) {
259                         section.sh_entsize = 1;
260                     }
261                     ptrs_in_section_count = static_cast<std::size_t>(section.sh_size / section.sh_entsize);
262                     break;
263                 }
264             }
265         }
266 
267         std::vector<symbol_t> symbols;
268         std::vector<char>   text;
269         symbols_text(fs, symbols, text);
270 
271         if (ptrs_in_section_count < symbols.size()) {
272             ret.reserve(ptrs_in_section_count);
273         } else {
274             ret.reserve(symbols.size());
275         }
276 
277         for (std::size_t i = 0; i < symbols.size(); ++i) {
278             if (symbols[i].st_shndx == index && is_visible(symbols[i])) {
279                 ret.push_back(&text[0] + symbols[i].st_name);
280                 if (ret.back().empty()) {
281                     ret.pop_back(); // Do not show empty names
282                 }
283             }
284         }
285 
286         return ret;
287     }
288 };
289 
290 typedef elf_info<boost::uint32_t> elf_info32;
291 typedef elf_info<boost::uint64_t> elf_info64;
292 
293 }}} // namespace boost::dll::detail
294 
295 #endif // BOOST_DLL_DETAIL_POSIX_ELF_INFO_HPP
296