/* Copyright 2017 - 2026 R. Thomas
* Copyright 2017 - 2026 Quarkslab
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "spdlog/fmt/fmt.h"
#include "LIEF/MachO/DyldChainedFixups.hpp"
#include "LIEF/MachO/ChainedBindingInfo.hpp"
#include "LIEF/MachO/hash.hpp"
#include "LIEF/MachO/SegmentCommand.hpp"
#include "LIEF/MachO/DylibCommand.hpp"
#include "LIEF/MachO/RelocationFixup.hpp"
#include "LIEF/MachO/Symbol.hpp"
#include "MachO/Structures.hpp"
#include "MachO/ChainedFixup.hpp"
#include "MachO/ChainedBindingInfoList.hpp"
namespace LIEF::MachO {
DyldChainedFixups::~DyldChainedFixups() = default;
DyldChainedFixups::DyldChainedFixups() = default;
DyldChainedFixups& DyldChainedFixups::operator=(const DyldChainedFixups& other) {
if (&other != this) {
data_offset_ = other.data_offset_;
data_size_ = other.data_size_;
}
return *this;
}
DyldChainedFixups::DyldChainedFixups(const DyldChainedFixups& other) :
LoadCommand::LoadCommand(other),
data_offset_{other.data_offset_},
data_size_{other.data_size_} {}
DyldChainedFixups::DyldChainedFixups(const details::linkedit_data_command& cmd) :
LoadCommand::LoadCommand{LoadCommand::TYPE(cmd.cmd), cmd.cmdsize},
data_offset_{cmd.dataoff},
data_size_{cmd.datasize} {}
void DyldChainedFixups::update_with(
const details::dyld_chained_fixups_header& header
) {
fixups_version_ = header.fixups_version;
starts_offset_ = header.starts_offset;
imports_offset_ = header.imports_offset;
symbols_offset_ = header.symbols_offset;
imports_count_ = header.imports_count;
symbols_format_ = header.symbols_format;
imports_format_ = static_cast<DYLD_CHAINED_FORMAT>(header.imports_format);
}
void DyldChainedFixups::accept(Visitor& visitor) const {
visitor.visit(*this);
}
std::ostream& DyldChainedFixups::print(std::ostream& os) const {
auto segments = chained_starts_in_segments();
os << "chained fixups header\n";
os << fmt::format("fixups_version = {}\n", fixups_version());
os << fmt::format("starts_offset = {}\n", starts_offset());
os << fmt::format("imports_offset = {}\n", imports_offset());
os << fmt::format("symbols_offset = {}\n", symbols_offset());
os << fmt::format("imports_count = {}\n", imports_count());
os << fmt::format("imports_format = {}\n", to_string(imports_format()));
os << fmt::format("symbols_format = {}\n", symbols_format());
os << "chained start in image\n";
os << fmt::format("seg_count = {}\n", segments.size());
for (size_t i = 0; i < segments.size(); ++i) {
const chained_starts_in_segment& info = segments[i];
static constexpr char PREFIX[] = " ";
os << fmt::format("{}seg_offset[{}] = {:03d} ({})\n", PREFIX, i, info.offset,
info.segment.name());
}
for (size_t i = 0; i < segments.size(); ++i) {
const chained_starts_in_segment& info = segments[i];
if (info.offset == 0) {
continue;
}
os << fmt::format("chained starts in segment {} ({})\n", i,
info.segment.name());
os << info << "\n";
for (const Relocation& reloc : info.segment.relocations()) {
if (const auto* r = reloc.cast<RelocationFixup>()) {
os << fmt::format("[RELOC] {:#010x}: {:#010x}\n", r->address(),
r->target());
}
}
os << "\n";
}
// Print chained imports
os << "chained imports\n";
for (const ChainedBindingInfo& bind : bindings()) {
std::string segment_name;
std::string libname;
std::string symbol;
if (const auto* segment = bind.segment()) {
segment_name = segment->name();
}
if (const auto* lib = bind.library()) {
libname = lib->name();
auto pos = libname.rfind('/');
if (pos != std::string::npos) {
libname = libname.substr(pos + 1);
}
}
if (const auto* sym = bind.symbol()) {
symbol = sym->name();
}
os << fmt::format("{}{:#010x}: {} ({}) addend: {:#x}\n", segment_name,
bind.address(), symbol, libname,
bind.sign_extended_addend());
}
return os;
}
DyldChainedFixups::chained_starts_in_segment::chained_starts_in_segment(
uint32_t offset, const details::dyld_chained_starts_in_segment& info,
SegmentCommand& segment
) :
offset{offset},
size{info.size},
page_size{info.page_size},
segment_offset{info.segment_offset},
max_valid_pointer{info.max_valid_pointer},
pointer_format{static_cast<DYLD_CHAINED_PTR_FORMAT>(info.pointer_format)},
segment{segment} {}
std::ostream&
operator<<(std::ostream& os,
const DyldChainedFixups::chained_starts_in_segment& info) {
os << fmt::format("size = {}\n", info.size);
os << fmt::format("page_size = {:#x}\n", info.page_size);
os << fmt::format("pointer_format = {}\n", to_string(info.pointer_format));
os << fmt::format("segment_offset = {:#x}\n", info.segment_offset);
os << fmt::format("max_valid_pointer = {:#x}\n", info.max_valid_pointer);
os << fmt::format("page_count = {}\n", info.page_count());
for (size_t i = 0; i < info.page_count(); ++i) {
os << fmt::format(" page_start[{}] = {}\n", i, info.page_start[i]);
}
return os;
}
}