cmake/Source/cmMachO.cxx

358 lines
9.2 KiB
C++
Raw Normal View History

2016-10-30 18:24:19 +01:00
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
2015-04-27 22:25:09 +02:00
#include "cmMachO.h"
2020-02-01 23:06:01 +01:00
#include <cstddef>
2017-04-14 19:02:05 +02:00
#include <string>
#include <vector>
2015-04-27 22:25:09 +02:00
2020-02-01 23:06:01 +01:00
#include <cm/memory>
#include "cmsys/FStream.hxx"
#include "cmAlgorithms.h"
2015-04-27 22:25:09 +02:00
// Include the Mach-O format information system header.
#include <mach-o/fat.h>
2016-07-09 11:21:54 +02:00
#include <mach-o/loader.h>
2015-04-27 22:25:09 +02:00
/**
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 {
2016-07-09 11:21:54 +02:00
// 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();
}
2015-04-27 22:25:09 +02:00
2016-07-09 11:21:54 +02:00
// read from the file and fill a data structure
template <typename T>
bool read(cmsys::ifstream& fin, T& v)
{
2022-03-29 21:10:50 +02:00
return static_cast<bool>(fin.read(reinterpret_cast<char*>(&v), sizeof(T)));
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
2016-07-09 11:21:54 +02:00
// 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()) {
2015-04-27 22:25:09 +02:00
return true;
2016-07-09 11:21:54 +02:00
}
2022-03-29 21:10:50 +02:00
return static_cast<bool>(
fin.read(reinterpret_cast<char*>(&v[0]), sizeof(T) * v.size()));
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
}
// Contains header and load commands for a single Mach-O file
class cmMachOHeaderAndLoadCommands
{
public:
// A load_command and its associated data
struct RawLoadCommand
2016-07-09 11:21:54 +02:00
{
2020-08-30 11:54:41 +02:00
uint32_t type(const cmMachOHeaderAndLoadCommands& m) const
2016-07-09 11:21:54 +02:00
{
if (this->LoadCommand.size() < sizeof(load_command)) {
2015-04-27 22:25:09 +02:00
return 0;
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
const load_command* cmd =
reinterpret_cast<const load_command*>(&this->LoadCommand[0]);
2020-08-30 11:54:41 +02:00
return m.swap(cmd->cmd);
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
std::vector<char> LoadCommand;
2016-07-09 11:21:54 +02:00
};
2015-04-27 22:25:09 +02:00
cmMachOHeaderAndLoadCommands(bool _swap)
: Swap(_swap)
2016-07-09 11:21:54 +02:00
{
}
2019-11-11 23:01:05 +01:00
virtual ~cmMachOHeaderAndLoadCommands() = default;
2015-04-27 22:25:09 +02:00
virtual bool read_mach_o(cmsys::ifstream& fin) = 0;
const std::vector<RawLoadCommand>& load_commands() const
2016-07-09 11:21:54 +02:00
{
2015-04-27 22:25:09 +02:00
return this->LoadCommands;
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
uint32_t swap(uint32_t v) const
2016-07-09 11:21:54 +02:00
{
if (this->Swap) {
2015-04-27 22:25:09 +02:00
char* c = reinterpret_cast<char*>(&v);
std::swap(c[0], c[3]);
std::swap(c[1], c[2]);
}
2016-07-09 11:21:54 +02:00
return v;
}
2015-04-27 22:25:09 +02:00
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.
2020-02-01 23:06:01 +01:00
template <typename T>
2015-04-27 22:25:09 +02:00
class cmMachOHeaderAndLoadCommandsImpl : public cmMachOHeaderAndLoadCommands
{
public:
cmMachOHeaderAndLoadCommandsImpl(bool _swap)
: cmMachOHeaderAndLoadCommands(_swap)
2016-07-09 11:21:54 +02:00
{
}
2018-01-26 17:06:56 +01:00
bool read_mach_o(cmsys::ifstream& fin) override
2016-07-09 11:21:54 +02:00
{
if (!read(fin, this->Header)) {
2015-04-27 22:25:09 +02:00
return false;
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
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);
2016-07-09 11:21:54 +02:00
return read_load_commands(this->Header.ncmds, this->Header.sizeofcmds,
2015-04-27 22:25:09 +02:00
fin);
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
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);
2016-07-09 11:21:54 +02:00
for (uint32_t i = 0; i < ncmds; i++) {
2015-04-27 22:25:09 +02:00
load_command lc;
2016-07-09 11:21:54 +02:00
if (!peek(fin, lc)) {
2015-04-27 22:25:09 +02:00
return false;
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
lc.cmd = swap(lc.cmd);
lc.cmdsize = swap(lc.cmdsize);
size_read += lc.cmdsize;
RawLoadCommand& c = this->LoadCommands[i];
c.LoadCommand.resize(lc.cmdsize);
2016-07-09 11:21:54 +02:00
if (!read(fin, c.LoadCommand)) {
2015-04-27 22:25:09 +02:00
return false;
}
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
2016-07-09 11:21:54 +02:00
if (size_read != sizeofcmds) {
2015-04-27 22:25:09 +02:00
this->LoadCommands.clear();
return false;
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
return true;
}
class cmMachOInternal
{
public:
cmMachOInternal(const char* fname);
2020-08-30 11:54:41 +02:00
cmMachOInternal(const cmMachOInternal&) = delete;
2015-04-27 22:25:09 +02:00
~cmMachOInternal();
2020-08-30 11:54:41 +02:00
cmMachOInternal& operator=(const cmMachOInternal&) = delete;
2015-04-27 22:25:09 +02:00
// 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
2020-08-30 11:54:41 +02:00
std::vector<std::unique_ptr<cmMachOHeaderAndLoadCommands>> MachOList;
2015-04-27 22:25:09 +02:00
};
cmMachOInternal::cmMachOInternal(const char* fname)
: Fin(fname)
{
// Quit now if the file could not be opened.
2016-07-09 11:21:54 +02:00
if (!this->Fin || !this->Fin.get()) {
2015-04-27 22:25:09 +02:00
this->ErrorMessage = "Error opening input file.";
return;
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
2016-07-09 11:21:54 +02:00
if (!this->Fin.seekg(0)) {
2015-04-27 22:25:09 +02:00
this->ErrorMessage = "Error seeking to beginning of file.";
return;
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
// Read the binary identification block.
uint32_t magic = 0;
2016-07-09 11:21:54 +02:00
if (!peek(this->Fin, magic)) {
2015-04-27 22:25:09 +02:00
this->ErrorMessage = "Error reading Mach-O identification.";
return;
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
// Verify the binary identification.
2016-07-09 11:21:54 +02:00
if (!(magic == MH_CIGAM || magic == MH_MAGIC || magic == MH_CIGAM_64 ||
magic == MH_MAGIC_64 || magic == FAT_CIGAM || magic == FAT_MAGIC)) {
2015-04-27 22:25:09 +02:00
this->ErrorMessage = "File does not have a valid Mach-O identification.";
return;
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
2016-07-09 11:21:54 +02:00
if (magic == FAT_MAGIC || magic == FAT_CIGAM) {
2015-04-27 22:25:09 +02:00
// this is a universal binary
fat_header header;
2016-07-09 11:21:54 +02:00
if (!read(this->Fin, header)) {
2015-04-27 22:25:09 +02:00
this->ErrorMessage = "Error reading fat header.";
return;
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
// read fat_archs
this->FatArchs.resize(OSSwapBigToHostInt32(header.nfat_arch));
2016-07-09 11:21:54 +02:00
if (!read(this->Fin, this->FatArchs)) {
2015-04-27 22:25:09 +02:00
this->ErrorMessage = "Error reading fat header archs.";
return;
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
// parse each Mach-O file
2018-01-26 17:06:56 +01:00
for (const auto& arch : this->FatArchs) {
2016-07-09 11:21:54 +02:00
if (!this->read_mach_o(OSSwapBigToHostInt32(arch.offset))) {
2015-04-27 22:25:09 +02:00
return;
}
}
2016-07-09 11:21:54 +02:00
} else {
2015-04-27 22:25:09 +02:00
// parse Mach-O file at the beginning of the file
this->read_mach_o(0);
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
}
2020-08-30 11:54:41 +02:00
cmMachOInternal::~cmMachOInternal() = default;
2015-04-27 22:25:09 +02:00
bool cmMachOInternal::read_mach_o(uint32_t file_offset)
{
2016-07-09 11:21:54 +02:00
if (!this->Fin.seekg(file_offset)) {
2015-04-27 22:25:09 +02:00
this->ErrorMessage = "Failed to locate Mach-O content.";
return false;
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
uint32_t magic;
2016-07-09 11:21:54 +02:00
if (!peek(this->Fin, magic)) {
2015-04-27 22:25:09 +02:00
this->ErrorMessage = "Error reading Mach-O identification.";
return false;
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
2020-08-30 11:54:41 +02:00
std::unique_ptr<cmMachOHeaderAndLoadCommands> f;
2016-07-09 11:21:54 +02:00
if (magic == MH_CIGAM || magic == MH_MAGIC) {
2015-04-27 22:25:09 +02:00
bool swap = false;
2016-07-09 11:21:54 +02:00
if (magic == MH_CIGAM) {
2015-04-27 22:25:09 +02:00
swap = true;
}
2020-08-30 11:54:41 +02:00
f = cm::make_unique<cmMachOHeaderAndLoadCommandsImpl<mach_header>>(swap);
2016-07-09 11:21:54 +02:00
} else if (magic == MH_CIGAM_64 || magic == MH_MAGIC_64) {
2015-04-27 22:25:09 +02:00
bool swap = false;
2016-07-09 11:21:54 +02:00
if (magic == MH_CIGAM_64) {
2015-04-27 22:25:09 +02:00
swap = true;
}
2020-08-30 11:54:41 +02:00
f =
cm::make_unique<cmMachOHeaderAndLoadCommandsImpl<mach_header_64>>(swap);
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
2016-07-09 11:21:54 +02:00
if (f && f->read_mach_o(this->Fin)) {
2020-08-30 11:54:41 +02:00
this->MachOList.push_back(std::move(f));
2016-07-09 11:21:54 +02:00
} else {
2015-04-27 22:25:09 +02:00
this->ErrorMessage = "Failed to read Mach-O header.";
return false;
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
return true;
}
//============================================================================
// External class implementation.
2016-07-09 11:21:54 +02:00
cmMachO::cmMachO(const char* fname)
2020-02-01 23:06:01 +01:00
: Internal(cm::make_unique<cmMachOInternal>(fname))
2015-04-27 22:25:09 +02:00
{
}
2020-02-01 23:06:01 +01:00
cmMachO::~cmMachO() = default;
2015-04-27 22:25:09 +02:00
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)
{
2016-07-09 11:21:54 +02:00
if (this->Internal->MachOList.empty()) {
2015-04-27 22:25:09 +02:00
return false;
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
// grab the first Mach-O and get the install name from that one
2020-08-30 11:54:41 +02:00
std::unique_ptr<cmMachOHeaderAndLoadCommands>& macho =
this->Internal->MachOList[0];
2016-07-09 11:21:54 +02:00
for (size_t i = 0; i < macho->load_commands().size(); i++) {
const cmMachOHeaderAndLoadCommands::RawLoadCommand& cmd =
2015-04-27 22:25:09 +02:00
macho->load_commands()[i];
2020-08-30 11:54:41 +02:00
uint32_t lc_cmd = cmd.type(*macho);
2016-07-09 11:21:54 +02:00
if (lc_cmd == LC_ID_DYLIB || lc_cmd == LC_LOAD_WEAK_DYLIB ||
lc_cmd == LC_LOAD_DYLIB) {
if (sizeof(dylib_command) < cmd.LoadCommand.size()) {
2022-03-29 21:10:50 +02:00
uint32_t namelen = static_cast<uint32_t>(cmd.LoadCommand.size() -
sizeof(dylib_command));
2015-04-27 22:25:09 +02:00
install_name.assign(&cmd.LoadCommand[sizeof(dylib_command)], namelen);
return true;
}
}
2016-07-09 11:21:54 +02:00
}
2015-04-27 22:25:09 +02:00
return false;
}
void cmMachO::PrintInfo(std::ostream& /*os*/) const
{
}