123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing for details. */
- #include "cmMachO.h"
- #include "cmsys/FStream.hxx"
- #include <algorithm>
- #include <stddef.h>
- #include <string>
- #include <vector>
- // Include the Mach-O format information system header.
- #include <mach-o/fat.h>
- #include <mach-o/loader.h>
- /**
- https://developer.apple.com/library/mac/documentation/
- DeveloperTools/Conceptual/MachORuntime/index.html
- A Mach-O file has 3 major regions: header, load commands and segments.
- Data Structures are provided from <mach-o/loader.h> which
- correspond to the file structure.
- The header can be either a struct mach_header or struct mach_header_64.
- One can peek at the first 4 bytes to identify the type of header.
- Following is the load command region which starts with
- struct load_command, and is followed by n number of load commands.
- In the case of a universal binary (an archive of multiple Mach-O files),
- the file begins with a struct fat_header and is followed by multiple
- struct fat_arch instances. The struct fat_arch indicates the offset
- for each Mach-O file.
- */
- namespace {
- // peek in the file
- template <typename T>
- bool peek(cmsys::ifstream& fin, T& v)
- {
- std::streampos p = fin.tellg();
- if (!fin.read(reinterpret_cast<char*>(&v), sizeof(T))) {
- return false;
- }
- fin.seekg(p);
- return fin.good();
- }
- // read from the file and fill a data structure
- template <typename T>
- bool read(cmsys::ifstream& fin, T& v)
- {
- if (!fin.read(reinterpret_cast<char*>(&v), sizeof(T))) {
- return false;
- }
- return true;
- }
- // read from the file and fill multiple data structures where
- // the vector has been resized
- template <typename T>
- bool read(cmsys::ifstream& fin, std::vector<T>& v)
- {
- // nothing to read
- if (v.empty()) {
- return true;
- }
- if (!fin.read(reinterpret_cast<char*>(&v[0]), sizeof(T) * v.size())) {
- return false;
- }
- return true;
- }
- }
- // Contains header and load commands for a single Mach-O file
- class cmMachOHeaderAndLoadCommands
- {
- public:
- // A load_command and its associated data
- struct RawLoadCommand
- {
- uint32_t type(const cmMachOHeaderAndLoadCommands* m) const
- {
- if (this->LoadCommand.size() < sizeof(load_command)) {
- return 0;
- }
- const load_command* cmd =
- reinterpret_cast<const load_command*>(&this->LoadCommand[0]);
- return m->swap(cmd->cmd);
- }
- std::vector<char> LoadCommand;
- };
- cmMachOHeaderAndLoadCommands(bool _swap)
- : Swap(_swap)
- {
- }
- virtual ~cmMachOHeaderAndLoadCommands() {}
- virtual bool read_mach_o(cmsys::ifstream& fin) = 0;
- const std::vector<RawLoadCommand>& load_commands() const
- {
- return this->LoadCommands;
- }
- uint32_t swap(uint32_t v) const
- {
- if (this->Swap) {
- char* c = reinterpret_cast<char*>(&v);
- std::swap(c[0], c[3]);
- std::swap(c[1], c[2]);
- }
- return v;
- }
- protected:
- bool read_load_commands(uint32_t ncmds, uint32_t sizeofcmds,
- cmsys::ifstream& fin);
- bool Swap;
- std::vector<RawLoadCommand> LoadCommands;
- };
- // Implementation for reading Mach-O header and load commands.
- // This is 32 or 64 bit arch specific.
- template <class T>
- class cmMachOHeaderAndLoadCommandsImpl : public cmMachOHeaderAndLoadCommands
- {
- public:
- cmMachOHeaderAndLoadCommandsImpl(bool _swap)
- : cmMachOHeaderAndLoadCommands(_swap)
- {
- }
- bool read_mach_o(cmsys::ifstream& fin) override
- {
- if (!read(fin, this->Header)) {
- return false;
- }
- this->Header.cputype = swap(this->Header.cputype);
- this->Header.cpusubtype = swap(this->Header.cpusubtype);
- this->Header.filetype = swap(this->Header.filetype);
- this->Header.ncmds = swap(this->Header.ncmds);
- this->Header.sizeofcmds = swap(this->Header.sizeofcmds);
- this->Header.flags = swap(this->Header.flags);
- return read_load_commands(this->Header.ncmds, this->Header.sizeofcmds,
- fin);
- }
- protected:
- T Header;
- };
- bool cmMachOHeaderAndLoadCommands::read_load_commands(uint32_t ncmds,
- uint32_t sizeofcmds,
- cmsys::ifstream& fin)
- {
- uint32_t size_read = 0;
- this->LoadCommands.resize(ncmds);
- for (uint32_t i = 0; i < ncmds; i++) {
- load_command lc;
- if (!peek(fin, lc)) {
- return false;
- }
- lc.cmd = swap(lc.cmd);
- lc.cmdsize = swap(lc.cmdsize);
- size_read += lc.cmdsize;
- RawLoadCommand& c = this->LoadCommands[i];
- c.LoadCommand.resize(lc.cmdsize);
- if (!read(fin, c.LoadCommand)) {
- return false;
- }
- }
- if (size_read != sizeofcmds) {
- this->LoadCommands.clear();
- return false;
- }
- return true;
- }
- class cmMachOInternal
- {
- public:
- cmMachOInternal(const char* fname);
- ~cmMachOInternal();
- // read a Mach-O file
- bool read_mach_o(uint32_t file_offset);
- // the file we are reading
- cmsys::ifstream Fin;
- // The archs in the universal binary
- // If the binary is not a universal binary, this will be empty.
- std::vector<fat_arch> FatArchs;
- // the error message while parsing
- std::string ErrorMessage;
- // the list of Mach-O's
- std::vector<cmMachOHeaderAndLoadCommands*> MachOList;
- };
- cmMachOInternal::cmMachOInternal(const char* fname)
- : Fin(fname)
- {
- // Quit now if the file could not be opened.
- if (!this->Fin || !this->Fin.get()) {
- this->ErrorMessage = "Error opening input file.";
- return;
- }
- if (!this->Fin.seekg(0)) {
- this->ErrorMessage = "Error seeking to beginning of file.";
- return;
- }
- // Read the binary identification block.
- uint32_t magic = 0;
- if (!peek(this->Fin, magic)) {
- this->ErrorMessage = "Error reading Mach-O identification.";
- return;
- }
- // Verify the binary identification.
- if (!(magic == MH_CIGAM || magic == MH_MAGIC || magic == MH_CIGAM_64 ||
- magic == MH_MAGIC_64 || magic == FAT_CIGAM || magic == FAT_MAGIC)) {
- this->ErrorMessage = "File does not have a valid Mach-O identification.";
- return;
- }
- if (magic == FAT_MAGIC || magic == FAT_CIGAM) {
- // this is a universal binary
- fat_header header;
- if (!read(this->Fin, header)) {
- this->ErrorMessage = "Error reading fat header.";
- return;
- }
- // read fat_archs
- this->FatArchs.resize(OSSwapBigToHostInt32(header.nfat_arch));
- if (!read(this->Fin, this->FatArchs)) {
- this->ErrorMessage = "Error reading fat header archs.";
- return;
- }
- // parse each Mach-O file
- for (const auto& arch : this->FatArchs) {
- if (!this->read_mach_o(OSSwapBigToHostInt32(arch.offset))) {
- return;
- }
- }
- } else {
- // parse Mach-O file at the beginning of the file
- this->read_mach_o(0);
- }
- }
- cmMachOInternal::~cmMachOInternal()
- {
- for (auto& i : this->MachOList) {
- delete i;
- }
- }
- bool cmMachOInternal::read_mach_o(uint32_t file_offset)
- {
- if (!this->Fin.seekg(file_offset)) {
- this->ErrorMessage = "Failed to locate Mach-O content.";
- return false;
- }
- uint32_t magic;
- if (!peek(this->Fin, magic)) {
- this->ErrorMessage = "Error reading Mach-O identification.";
- return false;
- }
- cmMachOHeaderAndLoadCommands* f = nullptr;
- if (magic == MH_CIGAM || magic == MH_MAGIC) {
- bool swap = false;
- if (magic == MH_CIGAM) {
- swap = true;
- }
- f = new cmMachOHeaderAndLoadCommandsImpl<mach_header>(swap);
- } else if (magic == MH_CIGAM_64 || magic == MH_MAGIC_64) {
- bool swap = false;
- if (magic == MH_CIGAM_64) {
- swap = true;
- }
- f = new cmMachOHeaderAndLoadCommandsImpl<mach_header_64>(swap);
- }
- if (f && f->read_mach_o(this->Fin)) {
- this->MachOList.push_back(f);
- } else {
- delete f;
- this->ErrorMessage = "Failed to read Mach-O header.";
- return false;
- }
- return true;
- }
- //============================================================================
- // External class implementation.
- cmMachO::cmMachO(const char* fname)
- : Internal(nullptr)
- {
- this->Internal = new cmMachOInternal(fname);
- }
- cmMachO::~cmMachO()
- {
- delete this->Internal;
- }
- std::string const& cmMachO::GetErrorMessage() const
- {
- return this->Internal->ErrorMessage;
- }
- bool cmMachO::Valid() const
- {
- return !this->Internal->MachOList.empty();
- }
- bool cmMachO::GetInstallName(std::string& install_name)
- {
- if (this->Internal->MachOList.empty()) {
- return false;
- }
- // grab the first Mach-O and get the install name from that one
- cmMachOHeaderAndLoadCommands* macho = this->Internal->MachOList[0];
- for (size_t i = 0; i < macho->load_commands().size(); i++) {
- const cmMachOHeaderAndLoadCommands::RawLoadCommand& cmd =
- macho->load_commands()[i];
- uint32_t lc_cmd = cmd.type(macho);
- if (lc_cmd == LC_ID_DYLIB || lc_cmd == LC_LOAD_WEAK_DYLIB ||
- lc_cmd == LC_LOAD_DYLIB) {
- if (sizeof(dylib_command) < cmd.LoadCommand.size()) {
- uint32_t namelen = cmd.LoadCommand.size() - sizeof(dylib_command);
- install_name.assign(&cmd.LoadCommand[sizeof(dylib_command)], namelen);
- return true;
- }
- }
- }
- return false;
- }
- void cmMachO::PrintInfo(std::ostream& /*os*/) const
- {
- }
|