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