diff options
Diffstat (limited to 'boost/dll/detail/macho_info.hpp')
-rw-r--r-- | boost/dll/detail/macho_info.hpp | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/boost/dll/detail/macho_info.hpp b/boost/dll/detail/macho_info.hpp new file mode 100644 index 0000000000..6911908238 --- /dev/null +++ b/boost/dll/detail/macho_info.hpp @@ -0,0 +1,321 @@ +// Copyright 2014 Renato Tegon Forti, Antony Polukhin. +// Copyright 2015 Antony Polukhin. +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_DLL_DETAIL_MACHO_INFO_HPP +#define BOOST_DLL_DETAIL_MACHO_INFO_HPP + +#include <boost/config.hpp> + +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/filesystem/fstream.hpp> +#include <boost/dll/detail/x_info_interface.hpp> + +namespace boost { namespace dll { namespace detail { + +typedef int integer_t; +typedef int vm_prot_t; +typedef integer_t cpu_type_t; +typedef integer_t cpu_subtype_t; + +template <class AddressOffsetT> +struct mach_header_template { + boost::uint32_t magic; + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + boost::uint32_t filetype; + boost::uint32_t ncmds; + boost::uint32_t sizeofcmds; + boost::uint32_t flags[sizeof(AddressOffsetT) / sizeof(uint32_t)]; // Flags and reserved +}; + +typedef mach_header_template<boost::uint32_t> mach_header_32_; +typedef mach_header_template<boost::uint64_t> mach_header_64_; + +struct load_command_ { + boost::uint32_t cmd; /* type of command */ + boost::uint32_t cmdsize; +}; + +struct load_command_types { + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_SEGMENT_ = 0x1); /* segment of this file to be mapped */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_SYMTAB_ = 0x2); /* link-edit stab symbol table info */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_SYMSEG_ = 0x3); /* link-edit gdb symbol table info (obsolete) */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_THREAD_ = 0x4); /* thread */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_UNIXTHREAD_ = 0x5); /* unix thread (includes a stack) */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_LOADFVMLIB_ = 0x6); /* load a specified fixed VM shared library */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_IDFVMLIB_ = 0x7); /* fixed VM shared library identification */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_IDENT_ = 0x8); /* object identification info (obsolete) */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_FVMFILE_ = 0x9); /* fixed VM file inclusion (internal use) */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_PREPAGE_ = 0xa); /* prepage command (internal use) */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_DYSYMTAB_ = 0xb); /* dynamic link-edit symbol table info */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_LOAD_DYLIB_ = 0xc); /* load a dynamically linked shared library */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_ID_DYLIB_ = 0xd); /* dynamically linked shared lib ident */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_LOAD_DYLINKER_ = 0xe); /* load a dynamic linker */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_ID_DYLINKER_ = 0xf); /* dynamic linker identification */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_PREBOUND_DYLIB_ = 0x10); /* modules prebound for a dynamically linked shared library */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_ROUTINES_ = 0x11); /* image routines */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_SUB_FRAMEWORK_ = 0x12); /* sub framework */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_SUB_UMBRELLA_ = 0x13); /* sub umbrella */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_SUB_CLIENT_ = 0x14); /* sub client */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_SUB_LIBRARY_ = 0x15); /* sub library */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_TWOLEVEL_HINTS_ = 0x16); /* two-level namespace lookup hints */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_PREBIND_CKSUM_ = 0x17); /* prebind checksum */ +/* + * After MacOS X 10.1 when a new load command is added that is required to be + * understood by the dynamic linker for the image to execute properly the + * LC_REQ_DYLD bit will be or'ed into the load command constant. If the dynamic + * linker sees such a load command it it does not understand will issue a + * "unknown load command required for execution" error and refuse to use the + * image. Other load commands without this bit that are not understood will + * simply be ignored. + */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_REQ_DYLD_ = 0x80000000); + +/* + * load a dynamically linked shared library that is allowed to be missing + * (all symbols are weak imported). + */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_LOAD_WEAK_DYLIB_ = (0x18 | LC_REQ_DYLD_)); + + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_SEGMENT_64_ = 0x19); /* 64-bit segment of this file to be mapped */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_ROUTINES_64_ = 0x1a); /* 64-bit image routines */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_UUID_ = 0x1b); /* the uuid */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_RPATH_ = (0x1c | LC_REQ_DYLD_)); /* runpath additions */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_CODE_SIGNATURE_ = 0x1d); /* local of code signature */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_SEGMENT_SPLIT_INFO_= 0x1e); /* local of info to split segments */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_REEXPORT_DYLIB_ = (0x1f | LC_REQ_DYLD_)); /* load and re-export dylib */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_LAZY_LOAD_DYLIB_ = 0x20); /* delay load of dylib until first use */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_ENCRYPTION_INFO_ = 0x21); /* encrypted segment information */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_DYLD_INFO_ = 0x22); /* compressed dyld information */ + BOOST_STATIC_CONSTANT(boost::uint32_t, LC_DYLD_INFO_ONLY_ = (0x22|LC_REQ_DYLD_)); /* compressed dyld information only */ +}; + +template <class AddressOffsetT> +struct segment_command_template { + boost::uint32_t cmd; /* LC_SEGMENT_ */ + boost::uint32_t cmdsize; /* includes sizeof section structs */ + char segname[16]; /* segment name */ + AddressOffsetT vmaddr; /* memory address of this segment */ + AddressOffsetT vmsize; /* memory size of this segment */ + AddressOffsetT fileoff; /* file offset of this segment */ + AddressOffsetT filesize; /* amount to map from the file */ + vm_prot_t maxprot; /* maximum VM protection */ + vm_prot_t initprot; /* initial VM protection */ + boost::uint32_t nsects; /* number of sections in segment */ + boost::uint32_t flags; /* flags */ +}; + +typedef segment_command_template<boost::uint32_t> segment_command_32_; +typedef segment_command_template<boost::uint64_t> segment_command_64_; + +template <class AddressOffsetT> +struct section_template { + char sectname[16]; /* name of this section */ + char segname[16]; /* segment this section goes in */ + AddressOffsetT addr; /* memory address of this section */ + AddressOffsetT size; /* size in bytes of this section */ + boost::uint32_t offset; /* file offset of this section */ + boost::uint32_t align; /* section alignment (power of 2) */ + boost::uint32_t reloff; /* file offset of relocation entries */ + boost::uint32_t nreloc; /* number of relocation entries */ + boost::uint32_t flags; /* flags (section type and attributes)*/ + boost::uint32_t reserved[1 + sizeof(AddressOffsetT) / sizeof(uint32_t)]; +}; + +typedef section_template<boost::uint32_t> section_32_; +typedef section_template<boost::uint64_t> section_64_; + +struct symtab_command_ { + boost::uint32_t cmd; /* LC_SYMTAB_ */ + boost::uint32_t cmdsize; /* sizeof(struct symtab_command) */ + boost::uint32_t symoff; /* symbol table offset */ + boost::uint32_t nsyms; /* number of symbol table entries */ + boost::uint32_t stroff; /* string table offset */ + boost::uint32_t strsize; /* string table size in bytes */ +}; + +template <class AddressOffsetT> +struct nlist_template { + boost::uint32_t n_strx; + boost::uint8_t n_type; + boost::uint8_t n_sect; + boost::uint16_t n_desc; + AddressOffsetT n_value; +}; + +typedef nlist_template<boost::uint32_t> nlist_32_; +typedef nlist_template<boost::uint64_t> nlist_64_; + +template <class AddressOffsetT> +class macho_info: public x_info_interface { + boost::filesystem::ifstream& f_; + + typedef boost::dll::detail::mach_header_template<AddressOffsetT> header_t; + typedef boost::dll::detail::load_command_ load_command_t; + typedef boost::dll::detail::segment_command_template<AddressOffsetT> segment_t; + typedef boost::dll::detail::section_template<AddressOffsetT> section_t; + typedef boost::dll::detail::symtab_command_ symbol_header_t; + typedef boost::dll::detail::nlist_template<AddressOffsetT> nlist_t; + + BOOST_STATIC_CONSTANT(boost::uint32_t, SEGMENT_CMD_NUMBER = (sizeof(AddressOffsetT) > 4 ? load_command_types::LC_SEGMENT_64_ : load_command_types::LC_SEGMENT_)); + +public: + static bool parsing_supported(boost::filesystem::ifstream& f) { + static const uint32_t magic_bytes = (sizeof(AddressOffsetT) <= sizeof(uint32_t) ? 0xfeedface : 0xfeedfacf); + + uint32_t magic; + f.seekg(0); + f.read(reinterpret_cast<char*>(&magic), sizeof(magic)); + return (magic_bytes == magic); + } + + explicit macho_info(boost::filesystem::ifstream& f) BOOST_NOEXCEPT + : f_(f) + {} + +private: + template <class T> + inline void read_raw(T& value, std::size_t size = sizeof(T)) const { + f_.read(reinterpret_cast<char*>(&value), size); + } + + template <class F> + void command_finder(uint32_t cmd_num, F callback_f) { + const header_t h = header(); + load_command_t command; + f_.seekg(sizeof(header_t)); + for (std::size_t i = 0; i < h.ncmds; ++i) { + const boost::filesystem::ifstream::pos_type pos = f_.tellg(); + read_raw(command); + if (command.cmd != cmd_num) { + f_.seekg(pos + static_cast<boost::filesystem::ifstream::pos_type>(command.cmdsize)); + continue; + } + + f_.seekg(pos); + callback_f(*this); + f_.seekg(pos + static_cast<boost::filesystem::ifstream::pos_type>(command.cmdsize)); + } + } + + struct section_names_gather { + std::vector<std::string>& ret; + + void operator()(const macho_info& f) const { + segment_t segment; + f.read_raw(segment); + + section_t section; + ret.reserve(ret.size() + segment.nsects); + for (std::size_t j = 0; j < segment.nsects; ++j) { + f.read_raw(section); + // `segname` goes right after the `sectname`. + // Forcing `sectname` to end on '\0' + section.segname[0] = '\0'; + ret.push_back(section.sectname); + if (ret.back().empty()) { + ret.pop_back(); // Do not show empty names + } + } + } + }; + + struct symbol_names_gather { + std::vector<std::string>& ret; + std::size_t section_index; + + void operator()(const macho_info& f) const { + symbol_header_t symbh; + f.read_raw(symbh); + ret.reserve(ret.size() + symbh.nsyms); + + nlist_t symbol; + std::string symbol_name; + for (std::size_t j = 0; j < symbh.nsyms; ++j) { + f.f_.seekg(symbh.symoff + j * sizeof(nlist_t)); + f.read_raw(symbol); + if (!symbol.n_strx) { + continue; // Symbol has no name + } + + if ((symbol.n_type & 0x0e) != 0xe || !symbol.n_sect) { + continue; // Symbol has no section + } + + if (section_index && section_index != symbol.n_sect) { + continue; // Not in the required section + } + + f.f_.seekg(symbh.stroff + symbol.n_strx); + getline(f.f_, symbol_name, '\0'); + if (symbol_name.empty()) { + continue; + } + + if (symbol_name[0] == '_') { + // Linker adds additional '_' symbol. Could not find official docs for that case. + ret.push_back(symbol_name.c_str() + 1); + } else { + ret.push_back(symbol_name); + } + } + } + }; + +public: + std::vector<std::string> sections() { + std::vector<std::string> ret; + section_names_gather f = { ret }; + command_finder(SEGMENT_CMD_NUMBER, f); + return ret; + } + +private: + inline header_t header() { + header_t h; + + f_.seekg(0); + read_raw(h); + + return h; + } + +public: + std::vector<std::string> symbols() { + std::vector<std::string> ret; + symbol_names_gather f = { ret, 0 }; + command_finder(load_command_types::LC_SYMTAB_, f); + return ret; + } + + std::vector<std::string> symbols(const char* section_name) { + // Not very optimal solution + std::vector<std::string> ret = sections(); + std::vector<std::string>::iterator it = std::find(ret.begin(), ret.end(), section_name); + if (it == ret.end()) { + // No section with such name + ret.clear(); + return ret; + } + + // section indexes start from 1 + symbol_names_gather f = { ret, static_cast<std::size_t>(1 + (it - ret.begin())) }; + ret.clear(); + command_finder(load_command_types::LC_SYMTAB_, f); + return ret; + } +}; + +typedef macho_info<boost::uint32_t> macho_info32; +typedef macho_info<boost::uint64_t> macho_info64; + +}}} // namespace boost::dll::detail + +#endif // BOOST_DLL_DETAIL_MACHO_INFO_HPP |