/* 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 <algorithm>
#include "logging.hpp"
#include "Object.tcc"
#include "Binary.tcc"
#include "LIEF/Visitor.hpp"
#include "LIEF/utils.hpp"
#include "LIEF/BinaryStream/SpanStream.hpp"
#include "LIEF/MachO/Binary.hpp"
#include "LIEF/MachO/BuildVersion.hpp"
#include "LIEF/MachO/Builder.hpp"
#include "LIEF/MachO/ChainedBindingInfo.hpp"
#include "LIEF/MachO/CodeSignature.hpp"
#include "LIEF/MachO/CodeSignatureDir.hpp"
#include "LIEF/MachO/DataInCode.hpp"
#include "LIEF/MachO/DyldBindingInfo.hpp"
#include "LIEF/MachO/DyldChainedFixups.hpp"
#include "LIEF/MachO/DyldEnvironment.hpp"
#include "LIEF/MachO/DyldExportsTrie.hpp"
#include "LIEF/MachO/DyldInfo.hpp"
#include "LIEF/MachO/DylibCommand.hpp"
#include "LIEF/MachO/DylinkerCommand.hpp"
#include "LIEF/MachO/DynamicSymbolCommand.hpp"
#include "LIEF/MachO/EncryptionInfo.hpp"
#include "LIEF/MachO/ExportInfo.hpp"
#include "LIEF/MachO/FunctionStarts.hpp"
#include "LIEF/MachO/FunctionVariants.hpp"
#include "LIEF/MachO/FunctionVariantFixups.hpp"
#include "LIEF/MachO/LazyLoadDylibInfo.hpp"
#include "LIEF/MachO/AtomInfo.hpp"
#include "LIEF/MachO/IndirectBindingInfo.hpp"
#include "LIEF/MachO/LinkEdit.hpp"
#include "LIEF/MachO/LinkerOptHint.hpp"
#include "LIEF/MachO/MainCommand.hpp"
#include "LIEF/MachO/NoteCommand.hpp"
#include "LIEF/MachO/RPathCommand.hpp"
#include "LIEF/MachO/Relocation.hpp"
#include "LIEF/MachO/RelocationFixup.hpp"
#include "LIEF/MachO/Routine.hpp"
#include "LIEF/MachO/Section.hpp"
#include "LIEF/MachO/SegmentCommand.hpp"
#include "LIEF/MachO/SegmentSplitInfo.hpp"
#include "LIEF/MachO/SourceVersion.hpp"
#include "LIEF/MachO/SubClient.hpp"
#include "LIEF/MachO/SubFramework.hpp"
#include "LIEF/MachO/Symbol.hpp"
#include "LIEF/MachO/SymbolCommand.hpp"
#include "LIEF/MachO/ThreadCommand.hpp"
#include "LIEF/MachO/TwoLevelHints.hpp"
#include "LIEF/MachO/UUIDCommand.hpp"
#include "LIEF/MachO/VersionMin.hpp"
#include "MachO/Structures.hpp"
#include "internal_utils.hpp"
namespace LIEF::MachO {
bool Binary::KeyCmp::operator()(const Relocation* lhs,
const Relocation* rhs) const {
return *lhs < *rhs;
}
Binary::Binary() :
LIEF::Binary(LIEF::Binary::FORMATS::MACHO) {}
LIEF::Binary::sections_t Binary::get_abstract_sections() {
LIEF::Binary::sections_t result;
it_sections sections = this->sections();
std::transform(sections.begin(), sections.end(), std::back_inserter(result),
[](Section& s) { return &s; });
return result;
}
// LIEF Interface
// ==============
void Binary::patch_address(uint64_t address,
const std::vector<uint8_t>& patch_value,
LIEF::Binary::VA_TYPES) {
// Find the segment associated with the virtual address
SegmentCommand* segment_topatch = segment_from_virtual_address(address);
if (segment_topatch == nullptr) {
LIEF_ERR("Unable to find segment associated with address: {:#x}", address);
return;
}
const uint64_t offset = address - segment_topatch->virtual_address();
span<uint8_t> content = segment_topatch->content();
if (offset > content.size() || (offset + patch_value.size()) > content.size()) {
LIEF_ERR("Patch value ({} bytes @{:#x}) out of segment bounds (limit: {:#x})",
patch_value.size(), offset, content.size());
return;
}
std::move(patch_value.begin(), patch_value.end(), content.data() + offset);
}
void Binary::patch_address(uint64_t address, uint64_t patch_value, size_t size,
LIEF::Binary::VA_TYPES) {
if (size > sizeof(patch_value)) {
LIEF_ERR("Invalid size: {:#x}", size);
return;
}
SegmentCommand* segment_topatch = segment_from_virtual_address(address);
if (segment_topatch == nullptr) {
LIEF_ERR("Unable to find segment associated with address: {:#x}", address);
return;
}
const uint64_t offset = address - segment_topatch->virtual_address();
span<uint8_t> content = segment_topatch->content();
if (offset > content.size() || (offset + size) > content.size()) {
LIEF_ERR("Patch value ({} bytes @{:#x}) out of segment bounds (limit: {:#x})",
size, offset, content.size());
return;
}
switch (size) {
case sizeof(uint8_t):
{
auto X = static_cast<uint8_t>(patch_value);
memcpy(content.data() + offset, &X, sizeof(uint8_t));
break;
}
case sizeof(uint16_t):
{
auto X = static_cast<uint16_t>(patch_value);
memcpy(content.data() + offset, &X, sizeof(uint16_t));
break;
}
case sizeof(uint32_t):
{
auto X = static_cast<uint32_t>(patch_value);
memcpy(content.data() + offset, &X, sizeof(uint32_t));
break;
}
case sizeof(uint64_t):
{
auto X = static_cast<uint64_t>(patch_value);
memcpy(content.data() + offset, &X, sizeof(uint64_t));
break;
}
default:
{
LIEF_ERR("Provided size ({}) does not match integer size", size);
return;
}
}
}
span<const uint8_t>
Binary::get_content_from_virtual_address(uint64_t virtual_address,
uint64_t size,
LIEF::Binary::VA_TYPES) const {
const SegmentCommand* segment = segment_from_virtual_address(virtual_address);
if (segment == nullptr) {
LIEF_ERR("Unable to find segment associated with address: {:#x}",
virtual_address);
return {};
}
span<const uint8_t> content = segment->content();
const uint64_t offset = virtual_address - segment->virtual_address();
uint64_t checked_size = size;
if (offset > content.size() || (offset + checked_size) > content.size()) {
checked_size = checked_size - (offset + checked_size - content.size());
}
return {content.data() + offset, static_cast<size_t>(checked_size)};
}
uint64_t Binary::entrypoint() const {
if (const MainCommand* cmd = main_command()) {
return imagebase() + cmd->entrypoint();
}
if (const ThreadCommand* cmd = thread_command()) {
auto range = va_ranges();
const uint64_t pc = cmd->pc();
if (range.start <= pc && pc < range.end) {
return pc;
}
return imagebase() + cmd->pc();
}
LIEF_WARN("Neither LC_MAIN nor LC_THREAD/LC_UNIXTHREAD found");
return 0;
}
LIEF::Binary::symbols_t Binary::get_abstract_symbols() {
LIEF::Binary::symbols_t syms;
syms.reserve(symbols_.size());
std::transform(symbols_.begin(), symbols_.end(), std::back_inserter(syms),
[](const std::unique_ptr<Symbol>& s) { return s.get(); });
return syms;
}
LIEF::Binary::functions_t Binary::get_abstract_exported_functions() const {
LIEF::Binary::functions_t result;
it_const_exported_symbols syms = exported_symbols();
std::transform(syms.begin(), syms.end(), std::back_inserter(result),
[](const Symbol& s) {
return Function(s.name(), s.value(), Function::FLAGS::EXPORTED);
});
return result;
}
LIEF::Binary::functions_t Binary::get_abstract_imported_functions() const {
LIEF::Binary::functions_t result;
it_const_imported_symbols syms = imported_symbols();
std::transform(syms.begin(), syms.end(), std::back_inserter(result),
[](const Symbol& s) {
return Function(s.name(), s.value(), Function::FLAGS::IMPORTED);
});
return result;
}
std::vector<std::string> Binary::get_abstract_imported_libraries() const {
std::vector<std::string> result;
for (const DylibCommand& lib : libraries()) {
result.push_back(lib.name());
}
return result;
}
Binary::it_const_relocations Binary::relocations() const {
relocations_t result;
for (const SegmentCommand* segment : segments_) {
std::transform(segment->relocations_.begin(), segment->relocations_.end(),
std::inserter(result, result.begin()),
[](const std::unique_ptr<Relocation>& r) { return r.get(); });
}
for (const Section* section : sections_) {
std::transform(section->relocations_.begin(), section->relocations_.end(),
std::inserter(result, result.begin()),
[](const std::unique_ptr<Relocation>& r) { return r.get(); });
}
{
std::scoped_lock lock(mu_);
relocations_ = std::move(result);
}
return relocations_;
}
LIEF::Binary::relocations_t Binary::get_abstract_relocations() {
LIEF::Binary::relocations_t relocations;
it_relocations macho_relocations = this->relocations();
relocations.reserve(macho_relocations.size());
for (Relocation& r : macho_relocations) {
relocations.push_back(&r);
}
return relocations;
}
// Symbols
// =======
bool Binary::is_exported(const Symbol& symbol) {
return !symbol.is_external() && symbol.has_export_info();
}
bool Binary::is_imported(const Symbol& symbol) {
return symbol.is_external() && !symbol.has_export_info();
}
const Symbol* Binary::get_symbol(const std::string& name) const {
const auto it_symbol = std::find_if(symbols_.begin(), symbols_.end(),
[&name](const std::unique_ptr<Symbol>& sym) {
return sym->name() == name;
});
if (it_symbol == symbols_.end()) {
return nullptr;
}
return it_symbol->get();
}
void Binary::write(const std::string& filename) {
Builder::write(*this, filename);
}
void Binary::write(const std::string& filename, Builder::config_t config) {
Builder::write(*this, filename, config);
}
void Binary::write(std::ostream& os) {
Builder::write(*this, os);
}
void Binary::write(std::ostream& os, Builder::config_t config) {
Builder::write(*this, os, config);
}
const Section* Binary::section_from_offset(uint64_t offset) const {
const auto it_section =
std::find_if(sections_.cbegin(), sections_.cend(),
[offset](const Section* section) {
return section->offset() <= offset &&
offset < (section->offset() + section->size());
});
if (it_section == sections_.cend()) {
return nullptr;
}
return *it_section;
}
const Section* Binary::section_from_virtual_address(uint64_t address) const {
const auto it_section = std::find_if(
sections_.begin(), sections_.end(), [address](const Section* section) {
return section->virtual_address() <= address &&
address < (section->virtual_address() + section->size());
}
);
if (it_section == sections_.end()) {
return nullptr;
}
return *it_section;
}
const SegmentCommand*
Binary::segment_from_virtual_address(uint64_t virtual_address) const {
auto it_segment =
std::find_if(segments_.begin(), segments_.end(),
[virtual_address](const SegmentCommand* segment) {
return segment->virtual_address() <= virtual_address &&
virtual_address < (segment->virtual_address() +
segment->virtual_size());
});
if (it_segment == segments_.end()) {
return nullptr;
}
return *it_segment;
}
size_t Binary::segment_index(const SegmentCommand& segment) const {
return segment.index();
}
const SegmentCommand* Binary::segment_from_offset(uint64_t offset) const {
if (offset_seg_.empty()) {
return nullptr;
}
const auto it_begin = offset_seg_.begin();
if (offset < it_begin->first) {
return nullptr;
}
auto it = offset_seg_.lower_bound(offset);
if (it != offset_seg_.end() && (it->first == offset || it == it_begin)) {
SegmentCommand* seg = it->second;
if (seg->file_offset() <= offset &&
offset < (seg->file_offset() + seg->file_size()))
{
return seg;
}
}
const auto it_end = offset_seg_.crbegin();
if (it == offset_seg_.end() && offset >= it_end->first) {
SegmentCommand* seg = it_end->second;
if (seg->file_offset() <= offset &&
offset < (seg->file_offset() + seg->file_size()))
{
return seg;
}
}
if (it == it_begin) {
/* It did not pass the first check */
return nullptr;
}
--it;
SegmentCommand* seg = it->second;
if (seg->file_offset() <= offset &&
offset < (seg->file_offset() + seg->file_size()))
{
return seg;
}
return nullptr;
}
ok_error_t Binary::shift_linkedit(size_t width) {
SegmentCommand* linkedit = get_segment("__LINKEDIT");
if (linkedit == nullptr) {
LIEF_INFO("__LINKEDIT segment not found");
return make_error_code(lief_errors::not_found);
}
const uint64_t lnk_offset = linkedit->file_offset();
/* const uint64_t lnk_size = linkedit->file_size(); */
/* const uint64_t lnk_end = lnk_offset + lnk_size; */
if (SymbolCommand* sym_cmd = symbol_command()) {
if (lnk_offset <= sym_cmd->symbol_offset()) {
sym_cmd->symbol_offset(sym_cmd->symbol_offset() + width);
}
if (lnk_offset <= sym_cmd->strings_offset()) {
sym_cmd->strings_offset(sym_cmd->strings_offset() + width);
}
}
if (DataInCode* data_code_cmd = data_in_code()) {
if (lnk_offset <= data_code_cmd->data_offset()) {
data_code_cmd->data_offset(data_code_cmd->data_offset() + width);
}
}
if (CodeSignature* sig = code_signature()) {
if (lnk_offset <= sig->data_offset()) {
sig->data_offset(sig->data_offset() + width);
}
}
if (CodeSignatureDir* sig_dir = code_signature_dir()) {
if (lnk_offset <= sig_dir->data_offset()) {
sig_dir->data_offset(sig_dir->data_offset() + width);
}
}
if (SegmentSplitInfo* ssi = segment_split_info()) {
if (lnk_offset <= ssi->data_offset()) {
ssi->data_offset(ssi->data_offset() + width);
}
}
if (FunctionStarts* fs = function_starts()) {
if (lnk_offset <= fs->data_offset()) {
fs->data_offset(fs->data_offset() + width);
}
}
if (DynamicSymbolCommand* dyn_cmd = dynamic_symbol_command()) {
if (lnk_offset <= dyn_cmd->toc_offset()) {
dyn_cmd->toc_offset(dyn_cmd->toc_offset() + width);
}
if (lnk_offset <= dyn_cmd->module_table_offset()) {
dyn_cmd->module_table_offset(dyn_cmd->module_table_offset() + width);
}
if (lnk_offset <= dyn_cmd->external_reference_symbol_offset()) {
dyn_cmd->external_reference_symbol_offset(
dyn_cmd->external_reference_symbol_offset() + width
);
}
if (lnk_offset <= dyn_cmd->indirect_symbol_offset()) {
dyn_cmd->indirect_symbol_offset(dyn_cmd->indirect_symbol_offset() + width);
}
if (lnk_offset <= dyn_cmd->external_relocation_offset()) {
dyn_cmd->external_relocation_offset(dyn_cmd->external_relocation_offset() +
width);
}
if (lnk_offset <= dyn_cmd->local_relocation_offset()) {
dyn_cmd->local_relocation_offset(dyn_cmd->local_relocation_offset() + width);
}
}
if (DyldInfo* dyld = dyld_info()) {
if (lnk_offset <= dyld->rebase().first) {
dyld->set_rebase_offset(dyld->rebase().first + width);
}
if (lnk_offset <= dyld->bind().first) {
dyld->set_bind_offset(dyld->bind().first + width);
}
if (lnk_offset <= dyld->weak_bind().first) {
dyld->set_weak_bind_offset(dyld->weak_bind().first + width);
}
if (lnk_offset <= dyld->lazy_bind().first) {
dyld->set_lazy_bind_offset(dyld->lazy_bind().first + width);
}
if (lnk_offset <= dyld->export_info().first) {
dyld->set_export_offset(dyld->export_info().first + width);
}
}
if (DyldChainedFixups* fixups = dyld_chained_fixups()) {
fixups->data_offset(fixups->data_offset() + width);
}
if (DyldExportsTrie* exports = dyld_exports_trie()) {
exports->data_offset(exports->data_offset() + width);
}
if (LinkerOptHint* opt = linker_opt_hint()) {
opt->data_offset(opt->data_offset() + width);
}
if (TwoLevelHints* two = two_level_hints()) {
two->offset(two->offset() + width);
}
if (AtomInfo* info = atom_info()) {
info->data_offset(info->data_offset() + width);
}
if (FunctionVariants* func_variants = function_variants()) {
func_variants->data_offset(func_variants->data_offset() + width);
}
if (FunctionVariantFixups* func_variant_fixups = function_variant_fixups()) {
func_variant_fixups->data_offset(func_variant_fixups->data_offset() + width);
}
linkedit->file_offset(linkedit->file_offset() + width);
linkedit->virtual_address(linkedit->virtual_address() + width);
for (const std::unique_ptr<Section>& section : linkedit->sections_) {
if (lnk_offset <= section->offset()) {
section->offset(section->offset() + width);
section->virtual_address(section->virtual_address() + width);
}
}
refresh_seg_offset();
return ok();
}
void Binary::sort_segments() {
auto start = commands_.end();
auto end = commands_.end();
for (auto it = commands_.begin(); it != commands_.end(); ++it) {
if (start == commands_.end() && SegmentCommand::classof(it->get())) {
start = it;
}
if (SegmentCommand::classof(it->get())) {
end = it;
}
}
++end;
bool all_segments =
std::all_of(start, end, [](const std::unique_ptr<LoadCommand>& cmd) {
return SegmentCommand::classof(cmd.get());
});
if (!all_segments) {
LIEF_ERR("Non-contiguous segment commands, sort aborted");
return;
}
std::sort(start, end,
[](const std::unique_ptr<LoadCommand>& lhs,
const std::unique_ptr<LoadCommand>& rhs) {
return lhs->as<SegmentCommand>()->virtual_address() <
rhs->as<SegmentCommand>()->virtual_address();
});
segments_.clear();
offset_seg_.clear();
for (auto it = start; it != end; ++it) {
SegmentCommand& seg = *(*it)->as<SegmentCommand>();
seg.index_ = segments_.size();
if (can_cache_segment(seg)) {
offset_seg_[seg.file_offset()] = &seg;
}
segments_.push_back(&seg);
}
}
void Binary::shift_command(size_t width, uint64_t from_offset) {
const SegmentCommand* segment = segment_from_offset(from_offset);
uint64_t __text_base_addr = 0;
uint64_t virtual_address = 0;
if (segment != nullptr) {
virtual_address =
segment->virtual_address() + from_offset - segment->file_offset();
}
if (const SegmentCommand* text = get_segment("__TEXT")) {
__text_base_addr = text->virtual_address();
}
// Shift symbols command
// =====================
if (SymbolCommand* sym_cmd = symbol_command()) {
if (sym_cmd->symbol_offset() > from_offset) {
sym_cmd->symbol_offset(sym_cmd->symbol_offset() + width);
}
if (sym_cmd->strings_offset() > from_offset) {
sym_cmd->strings_offset(sym_cmd->strings_offset() + width);
}
for (std::unique_ptr<Symbol>& s : symbols_) {
if (s->type() == Symbol::TYPE::SECTION) {
if (s->value() > virtual_address) {
s->value(s->value() + width);
}
}
}
}
// Data In Code
// ============
if (DataInCode* data_code_cmd = data_in_code()) {
if (data_code_cmd->data_offset() > from_offset) {
data_code_cmd->data_offset(data_code_cmd->data_offset() + width);
}
}
// Code Signature
// ==============
if (CodeSignature* sig = code_signature()) {
if (sig->data_offset() > from_offset) {
sig->data_offset(sig->data_offset() + width);
}
}
if (CodeSignatureDir* sig_dir = code_signature_dir()) {
if (sig_dir->data_offset() > from_offset) {
sig_dir->data_offset(sig_dir->data_offset() + width);
}
}
if (SegmentSplitInfo* ssi = segment_split_info()) {
if (ssi->data_offset() > from_offset) {
ssi->data_offset(ssi->data_offset() + width);
}
}
// Shift Main Command
// ==================
if (MainCommand* main_cmd = main_command()) {
if ((__text_base_addr + main_cmd->entrypoint()) > virtual_address) {
main_cmd->entrypoint(main_cmd->entrypoint() + width);
}
}
// Patch function starts
// =====================
if (FunctionStarts* fs = function_starts()) {
if (fs->data_offset() > from_offset) {
fs->data_offset(fs->data_offset() + width);
}
for (uint64_t& address : fs->functions()) {
if ((__text_base_addr + address) > virtual_address) {
address += width;
}
}
}
// Dynamic symbol command
// ======================
if (DynamicSymbolCommand* dyn_cmd = dynamic_symbol_command()) {
if (dyn_cmd->toc_offset() > from_offset) {
dyn_cmd->toc_offset(dyn_cmd->toc_offset() + width);
}
if (dyn_cmd->module_table_offset() > from_offset) {
dyn_cmd->module_table_offset(dyn_cmd->module_table_offset() + width);
}
if (dyn_cmd->external_reference_symbol_offset() > from_offset) {
dyn_cmd->external_reference_symbol_offset(
dyn_cmd->external_reference_symbol_offset() + width
);
}
if (dyn_cmd->indirect_symbol_offset() > from_offset) {
dyn_cmd->indirect_symbol_offset(dyn_cmd->indirect_symbol_offset() + width);
}
if (dyn_cmd->external_relocation_offset() > from_offset) {
dyn_cmd->external_relocation_offset(dyn_cmd->external_relocation_offset() +
width);
}
if (dyn_cmd->local_relocation_offset() > from_offset) {
dyn_cmd->local_relocation_offset(dyn_cmd->local_relocation_offset() + width);
}
}
// Patch Dyld
// ==========
if (DyldInfo* dyld = dyld_info()) {
// Shift underlying containers offset
if (dyld->rebase().first > from_offset) {
dyld->set_rebase_offset(dyld->rebase().first + width);
}
if (dyld->bind().first > from_offset) {
dyld->set_bind_offset(dyld->bind().first + width);
}
if (dyld->weak_bind().first > from_offset) {
dyld->set_weak_bind_offset(dyld->weak_bind().first + width);
}
if (dyld->lazy_bind().first > from_offset) {
dyld->set_lazy_bind_offset(dyld->lazy_bind().first + width);
}
if (dyld->export_info().first > from_offset) {
dyld->set_export_offset(dyld->export_info().first + width);
}
// Shift Relocations
// -----------------
// TODO: Optimize this code
for (Relocation& reloc : relocations()) {
if (reloc.address() > virtual_address) {
if (is64_) {
patch_relocation<uint64_t>(reloc, /* from */ virtual_address,
/* shift */ width);
} else {
patch_relocation<uint32_t>(reloc, /* from */ virtual_address,
/* shift */ width);
}
reloc.address(reloc.address() + width);
}
}
// Shift Export Info
// -----------------
for (ExportInfo& info : dyld->exports()) {
if (info.address() > from_offset) {
info.address(info.address() + width);
}
}
// Shift bindings
// --------------
for (DyldBindingInfo& info : dyld->bindings()) {
if (info.address() > virtual_address) {
info.address(info.address() + width);
}
}
}
if (DyldChainedFixups* fixups = dyld_chained_fixups()) {
fixups->data_offset(fixups->data_offset() + width);
// Update relocations
for (auto& entry : fixups->chained_starts_in_segments()) {
for (auto& reloc : entry.segment.relocations()) {
if (auto* fixup = reloc.cast<RelocationFixup>()) {
if (fixup->offset() > from_offset) {
fixup->offset(fixup->offset() + width);
}
if (fixup->target() > virtual_address) {
fixup->target(fixup->target() + width);
}
// No need to update the virtual address since
// it is bound to the offset
}
}
}
for (ChainedBindingInfo& bind : fixups->bindings()) {
if (bind.offset() > from_offset) {
bind.offset(bind.offset() + width);
}
if (bind.address() > virtual_address) {
bind.address(bind.address() + width);
}
// We don't need to update the virtual address,
// as it is bound to the offset
}
}
if (DyldExportsTrie* exports = dyld_exports_trie()) {
for (ExportInfo& info : exports->exports()) {
if (info.address() > from_offset) {
info.address(info.address() + width);
}
}
if (exports->data_offset() > from_offset) {
exports->data_offset(exports->data_offset() + width);
}
}
if (LinkerOptHint* opt = linker_opt_hint()) {
if (opt->data_offset() > from_offset) {
opt->data_offset(opt->data_offset() + width);
}
}
if (TwoLevelHints* two = two_level_hints()) {
if (two->offset() > from_offset) {
two->offset(two->offset() + width);
}
}
if (Routine* routine = routine_command()) {
if (routine->init_address() > virtual_address) {
routine->init_address(routine->init_address() + width);
}
}
if (AtomInfo* info = atom_info()) {
if (info->data_offset() > from_offset) {
info->data_offset(info->data_offset() + width);
}
}
if (FunctionVariants* func_variants = function_variants()) {
if (func_variants->data_offset() > from_offset) {
func_variants->data_offset(func_variants->data_offset() + width);
}
for (FunctionVariants::RuntimeTable& table : func_variants->runtime_table()) {
for (FunctionVariants::RuntimeTableEntry& entry : table.entries()) {
// impl() is an image-base relative offset (like LC_FUNCTION_STARTS).
// When another_table() is set it is an index into the runtime tables,
// not an address, and must be left untouched.
if (entry.another_table()) {
continue;
}
if ((__text_base_addr + entry.impl()) > virtual_address) {
entry.impl(entry.impl() + width);
}
}
}
}
if (FunctionVariantFixups* func_variant_fixups = function_variant_fixups()) {
if (func_variant_fixups->data_offset() > from_offset) {
func_variant_fixups->data_offset(func_variant_fixups->data_offset() + width);
}
}
for (LazyLoadDylibInfo& lazy : lazy_load_dylib_infos()) {
if (lazy.data_offset() > from_offset) {
lazy.data_offset(lazy.data_offset() + width);
}
if (lazy.chain_start_image_offset() != 0 &&
__text_base_addr + lazy.chain_start_image_offset() > virtual_address)
{
lazy.chain_start_image_offset(lazy.chain_start_image_offset() + width);
}
if (lazy.flag_image_offset() != 0 &&
__text_base_addr + lazy.flag_image_offset() > virtual_address)
{
lazy.flag_image_offset(lazy.flag_image_offset() + width);
}
// NOTE(romain): Updates on Fixup's address are currently NOT committed in
// the final binary (c.f. the associated TODO in the builder)
for (LazyLoadDylibInfo::Fixup& fixup : lazy.fixups()) {
if (fixup.address() > virtual_address) {
fixup.address(fixup.address() + width);
}
}
}
for_commands<EncryptionInfo>([from_offset, width](EncryptionInfo& enc) {
if (enc.crypt_offset() > from_offset) {
enc.crypt_offset(enc.crypt_offset() + width);
}
});
for_commands<NoteCommand>([from_offset, width](NoteCommand& note) {
if (note.note_offset() > from_offset) {
note.note_offset(note.note_offset() + width);
}
});
}
ok_error_t Binary::shift(size_t value) {
value = align(value, page_size());
Header& header = this->header();
// Offset of the load commands table
const uint64_t loadcommands_start =
is64_ ? sizeof(details::mach_header_64) : sizeof(details::mach_header);
// +------------------------+ <---------- __TEXT.start
// | Mach-O Header |
// +------------------------+ <===== loadcommands_start
// | |
// | Load Command Table |
// | |
// +------------------------+ <===== loadcommands_end
// |************************|
// |************************| Assembly code
// |************************|
// +------------------------+ <---------- __TEXT.end
const uint64_t loadcommands_end = loadcommands_start + header.sizeof_cmds();
// Segment that wraps this load command table
SegmentCommand* load_cmd_segment = segment_from_offset(loadcommands_end);
if (load_cmd_segment == nullptr) {
LIEF_ERR("Segment for load command space not found");
return make_error_code(lief_errors::file_format_error);
}
LIEF_DEBUG("LC Table wrapped by {} / End offset: {:#x} (size: {:x})",
load_cmd_segment->name(), loadcommands_end,
load_cmd_segment->data_.size());
load_cmd_segment->content_insert(loadcommands_end, value);
// 1. Shift all commands
// =====================
for (std::unique_ptr<LoadCommand>& cmd : commands_) {
if (cmd->command_offset() >= loadcommands_end) {
cmd->command_offset(cmd->command_offset() + value);
}
}
shift_command(value, loadcommands_end);
const uint64_t loadcommands_end_va =
loadcommands_end + load_cmd_segment->virtual_address();
LIEF_DEBUG("loadcommands_end: {:#018x}", loadcommands_end);
LIEF_DEBUG("loadcommands_end_va: {:#018x}", loadcommands_end_va);
// Shift Segment and sections
// ==========================
for (SegmentCommand* segment : segments_) {
// Extend the virtual size of the segment containing our shift
if (segment->file_offset() <= loadcommands_end &&
loadcommands_end < (segment->file_offset() + segment->file_size()))
{
LIEF_DEBUG("Extending '{}' by {:x}", segment->name(), value);
segment->virtual_size(segment->virtual_size() + value);
segment->file_size(segment->file_size() + value);
for (const std::unique_ptr<Section>& section : segment->sections_) {
if (section->offset() >= loadcommands_end) {
section->offset(section->offset() + value);
section->virtual_address(section->virtual_address() + value);
}
}
} else {
if (segment->virtual_address() >= loadcommands_end_va) {
segment->virtual_address(segment->virtual_address() + value);
}
if (segment->file_offset() >= loadcommands_end) {
segment->file_offset(segment->file_offset() + value);
}
for (const std::unique_ptr<Section>& section : segment->sections_) {
if (section->virtual_address() >= loadcommands_end_va) {
section->virtual_address(section->virtual_address() + value);
}
if (section->offset() >= loadcommands_end) {
section->offset(section->offset() + value);
}
}
}
}
refresh_seg_offset();
available_command_space_ += value;
return ok();
}
LoadCommand* Binary::add(std::unique_ptr<LoadCommand> command) {
const int32_t size_aligned = align(command->size(), pointer_size());
// Check there is enough space between the
// load command table and the raw content
if (auto result = ensure_command_space(size_aligned); is_err(result)) {
LIEF_ERR("Failed to ensure command space {}: {}", size_aligned,
to_string(get_error(result)));
return nullptr;
}
available_command_space_ -= size_aligned;
Header& header = this->header();
// Get border of the load command table
const uint64_t loadcommands_start =
is64_ ? sizeof(details::mach_header_64) : sizeof(details::mach_header);
const uint64_t loadcommands_end = loadcommands_start + header.sizeof_cmds();
// Update the Header according to the command that will be added
header.sizeof_cmds(header.sizeof_cmds() + size_aligned);
header.nb_cmds(header.nb_cmds() + 1);
// Get the segment handling the LC table
SegmentCommand* load_cmd_segment = segment_from_offset(loadcommands_end);
if (load_cmd_segment == nullptr) {
LIEF_WARN("Failed to get last load command");
return nullptr;
}
span<const uint8_t> content_ref = load_cmd_segment->content();
std::vector<uint8_t> content = {content_ref.begin(), content_ref.end()};
// Copy the command data
std::copy(command->data().begin(), command->data().end(),
content.begin() + loadcommands_end);
load_cmd_segment->content(std::move(content));
// Add the command in the Binary
command->command_offset(loadcommands_end);
// Update cache
if (DylibCommand::classof(command.get())) {
libraries_.push_back(command->as<DylibCommand>());
}
if (LazyLoadDylibInfo::classof(command.get())) {
lazy_load_dylib_infos_.push_back(command->as<LazyLoadDylibInfo>());
}
if (SegmentCommand::classof(command.get())) {
add_cached_segment(*command->as<SegmentCommand>());
}
return commands_.emplace_back(std::move(command)).get();
}
LoadCommand* Binary::add(const LoadCommand& command, size_t index) {
// An out-of-bounds index is interpreted as a push_back
if (index >= commands_.size()) {
return add(command);
}
const size_t size_aligned = align(command.size(), pointer_size());
LIEF_DEBUG("available_command_space_: {:#08x} (required: {:#08x})",
available_command_space_, size_aligned);
if (auto result = ensure_command_space(size_aligned); is_err(result)) {
LIEF_ERR("Failed to ensure command space {}: {}", size_aligned,
to_string(get_error(result)));
return nullptr;
}
available_command_space_ -= size_aligned;
// Update the Header according to the new command
Header& header = this->header();
header.sizeof_cmds(header.sizeof_cmds() + size_aligned);
header.nb_cmds(header.nb_cmds() + 1);
// Get offset of the LC border
LoadCommand* cmd_border = commands_[index].get();
uint64_t border_off = cmd_border->command_offset();
std::unique_ptr<LoadCommand> copy{command.clone()};
copy->command_offset(cmd_border->command_offset());
// Patch LC offsets that follow the LC border
for (std::unique_ptr<LoadCommand>& lc : commands_) {
if (lc->command_offset() >= border_off) {
lc->command_offset(lc->command_offset() + size_aligned);
}
}
if (auto* lib = copy->cast<DylibCommand>()) {
libraries_.push_back(lib);
}
if (auto* lazy = copy->cast<LazyLoadDylibInfo>()) {
lazy_load_dylib_infos_.push_back(lazy);
}
if (auto* segment = copy->cast<SegmentCommand>()) {
add_cached_segment(*segment);
}
return commands_.insert(commands_.begin() + index, std::move(copy))->get();
}
bool Binary::remove(const LoadCommand& command) {
const auto it =
std::find_if(commands_.begin(), commands_.end(),
[&command](const std::unique_ptr<LoadCommand>& cmd) {
return *cmd == command;
});
if (it == commands_.end()) {
LIEF_ERR("Unable to find command: {}", to_string(command));
return false;
}
LoadCommand* cmd_rm = it->get();
if (auto* lib = cmd_rm->cast<DylibCommand>()) {
auto it_cache = std::find(libraries_.begin(), libraries_.end(), cmd_rm);
if (it_cache == libraries_.end()) {
LIEF_WARN("Library {} not found in cache; binary may be inconsistent",
lib->name());
} else {
libraries_.erase(it_cache);
}
}
if (cmd_rm->cast<LazyLoadDylibInfo>() != nullptr) {
auto it_cache = std::find(lazy_load_dylib_infos_.begin(),
lazy_load_dylib_infos_.end(), cmd_rm);
if (it_cache != lazy_load_dylib_infos_.end()) {
lazy_load_dylib_infos_.erase(it_cache);
}
}
if (const auto* seg = cmd_rm->cast<const SegmentCommand>()) {
auto it_cache = std::find(segments_.begin(), segments_.end(), cmd_rm);
if (it_cache == segments_.end()) {
LIEF_WARN("Segment {} not found in cache; binary may be inconsistent",
seg->name());
} else {
// Update the indexes to keep a consistent state
for (auto it = it_cache; it != segments_.end(); ++it) {
(*it)->index_--;
}
segments_.erase(it_cache);
}
}
const uint64_t cmd_rm_offset = cmd_rm->command_offset();
for (std::unique_ptr<LoadCommand>& cmd : commands_) {
if (cmd->command_offset() >= cmd_rm_offset) {
cmd->command_offset(cmd->command_offset() - cmd_rm->size());
}
}
Header& header = this->header();
header.sizeof_cmds(header.sizeof_cmds() - cmd_rm->size());
header.nb_cmds(header.nb_cmds() - 1);
available_command_space_ += cmd_rm->size();
commands_.erase(it);
refresh_seg_offset();
return true;
}
bool Binary::remove(LoadCommand::TYPE type) {
bool removed = false;
while (LoadCommand* cmd = get(type)) {
removed = remove(*cmd);
}
return removed;
}
bool Binary::remove_command(size_t index) {
if (index >= commands_.size()) {
return false;
}
return remove(*commands_[index]);
}
bool Binary::has(LoadCommand::TYPE type) const {
const auto it = std::find_if(commands_.begin(), commands_.end(),
[type](const std::unique_ptr<LoadCommand>& cmd) {
return cmd->command() == type;
});
return it != commands_.end();
}
const LoadCommand* Binary::get(LoadCommand::TYPE type) const {
const auto it = std::find_if(commands_.begin(), commands_.end(),
[type](const std::unique_ptr<LoadCommand>& cmd) {
return cmd->command() == type;
});
if (it == commands_.end()) {
return nullptr;
}
return it->get();
}
bool Binary::extend(const LoadCommand& command, uint64_t size) {
const auto it =
std::find_if(commands_.begin(), commands_.end(),
[&command](const std::unique_ptr<LoadCommand>& cmd) {
return *cmd == command;
});
if (it == commands_.end()) {
LIEF_ERR("Unable to find command: {}", to_string(command));
return false;
}
LoadCommand* cmd = it->get();
const size_t size_aligned = align(size, pointer_size());
if (auto result = ensure_command_space(size_aligned); is_err(result)) {
LIEF_ERR("Failed to ensure command space {}: {}", size_aligned,
to_string(get_error(result)));
return false;
}
available_command_space_ -= size_aligned;
for (std::unique_ptr<LoadCommand>& lc : commands_) {
if (lc->command_offset() > cmd->command_offset()) {
lc->command_offset(lc->command_offset() + size_aligned);
}
}
cmd->size(cmd->size() + size_aligned);
cmd->original_data_.resize(cmd->original_data_.size() + size_aligned);
// Update Header
// =============
Header& header = this->header();
header.sizeof_cmds(header.sizeof_cmds() + size_aligned);
return true;
}
bool Binary::extend_segment(const SegmentCommand& segment, size_t size) {
const auto it_segment =
std::find_if(segments_.begin(), segments_.end(),
[&segment](const SegmentCommand* s) { return segment == *s; });
if (it_segment == segments_.end()) {
LIEF_ERR("Unable to find segment: '{}'", segment.name());
return false;
}
SegmentCommand* target_segment = *it_segment;
const uint64_t last_offset =
target_segment->file_offset() + target_segment->file_size();
const uint64_t last_va =
target_segment->virtual_address() + target_segment->virtual_size();
const int32_t size_aligned = align(size, pointer_size());
shift_command(size_aligned, last_offset - 4);
// Shift Segment and sections
// ==========================
for (SegmentCommand* segment : segments_) {
if (segment->virtual_address() >= last_va) {
segment->virtual_address(segment->virtual_address() + size_aligned);
}
if (segment->file_offset() >= last_offset) {
segment->file_offset(segment->file_offset() + size_aligned);
}
for (const std::unique_ptr<Section>& section : segment->sections_) {
if (section->virtual_address() >= last_va) {
section->virtual_address(section->virtual_address() + size_aligned);
}
if (section->offset() >= last_offset) {
section->offset(section->offset() + size_aligned);
}
}
}
target_segment->virtual_size(target_segment->virtual_size() + size_aligned);
target_segment->file_size(target_segment->file_size() + size_aligned);
target_segment->content_resize(target_segment->file_size());
refresh_seg_offset();
return true;
}
bool Binary::extend_section(Section& section, size_t size) {
// All sections must keep their requested alignment.
//
// As per current implementation of `shift` method, space is allocated between
// the last load command and the first section by shifting everything to the
// "right". After that we shift `section` and all other sections that come before
// it to the "left", so that we create a gap of at least `size` wide after the
// current `section`. Finally, we assign new size to the `section`.
//
// Let's say we are extending section S.
// There might be sections P that come prior S, and there might be sections A
// that come after S. We try to keep relative relationships between sections in
// groups P and A, such that relative offsets from one section to another one are
// unchanged, however preserving the same relationship between sections from
// different groups is impossible. We achieve this by shifting P and S to the
// left by size rounded up to the maximum common alignment factor.
const uint64_t loadcommands_start =
is64_ ? sizeof(details::mach_header_64) : sizeof(details::mach_header);
const uint64_t loadcommands_end = loadcommands_start + header().sizeof_cmds();
SegmentCommand* load_cmd_segment = segment_from_offset(loadcommands_end);
if (load_cmd_segment == nullptr) {
LIEF_ERR("Segment for load command space not found");
return false;
}
if (section.segment() != load_cmd_segment) {
LIEF_ERR("Cannot extend section in non-first segment '{}'",
section.segment_name());
return false;
}
// Note: if we are extending an empty section, then there may be many zero-sized
// sections at this offset `section.offset()`, and one non-empty section.
// Select sections that we need to shift to the left.
// Sections that come after the current one are not considered for shifting.
// Note: we compare sections by end_offset to exclude non-empty section that
// starts at the current offset.
sections_cache_t sections_to_shift;
for (Section& s : sections()) {
if (s.offset() == 0 ||
s.offset() + s.size() > section.offset() + section.size())
{
continue;
}
sections_to_shift.push_back(&s);
}
assert(!sections_to_shift.empty());
// Stable-sort by offset in ascending order as well as preserving original order.
std::stable_sort(sections_to_shift.begin(), sections_to_shift.end(),
[](const Section* a, const Section* b) {
return a->offset() < b->offset();
});
// We do not want to shift empty sections that were added after the current one.
auto it =
std::find(sections_to_shift.begin(), sections_to_shift.end(), §ion);
assert(it != sections_to_shift.end());
sections_to_shift.erase(std::next(it), sections_to_shift.end());
// Find maximum alignment
auto it_maxa =
std::max_element(sections_to_shift.begin(), sections_to_shift.end(),
[](const Section* a, const Section* b) {
return a->alignment() < b->alignment();
});
const size_t max_alignment = 1 << (*it_maxa)->alignment();
// Resize command space, if needed.
const size_t shift_value = align(size, max_alignment);
if (auto result = ensure_command_space(shift_value); is_err(result)) {
LIEF_ERR("Failed to ensure command space {}: {}", shift_value,
to_string(get_error(result)));
return false;
}
available_command_space_ -= shift_value;
// Shift selected sections to allocate requested space for `section`.
for (Section* s : sections_to_shift) {
s->offset(s->offset() - shift_value);
s->address(s->address() - shift_value);
}
// Extend the given `section`.
section.size(section.size() + shift_value);
return true;
}
void Binary::remove_section(const std::string& name, bool clear) {
Section* sec_to_delete = get_section(name);
if (sec_to_delete == nullptr) {
LIEF_ERR("Section '{}' not found", name);
return;
}
SegmentCommand* segment = sec_to_delete->segment();
if (segment == nullptr) {
LIEF_ERR("Section {} has no associated segment, cannot remove",
sec_to_delete->name());
return;
}
remove_section(segment->name(), name, clear);
}
void Binary::remove_section(const std::string& segname, const std::string& secname,
bool clear) {
Section* sec_to_delete = get_section(segname, secname);
if (sec_to_delete == nullptr) {
LIEF_ERR("Section '{}' not found in segment '{}'", secname, segname);
return;
}
SegmentCommand* segment = sec_to_delete->segment();
if (segment == nullptr) {
LIEF_ERR("Section {} has no associated segment, cannot remove",
sec_to_delete->name());
return;
}
if (clear) {
sec_to_delete->clear(0);
}
segment->numberof_sections(segment->numberof_sections() - 1);
auto it_section =
std::find_if(segment->sections_.begin(), segment->sections_.end(),
[sec_to_delete](const std::unique_ptr<Section>& s) {
return *s == *sec_to_delete;
});
if (it_section == segment->sections_.end()) {
LIEF_WARN("Section not found");
return;
}
const size_t lc_offset = segment->command_offset();
const size_t section_struct_size =
is64_ ? sizeof(details::section_64) : sizeof(details::section_32);
segment->size_ -= section_struct_size;
header().sizeof_cmds(header().sizeof_cmds() - section_struct_size);
for (std::unique_ptr<LoadCommand>& lc : commands_) {
if (lc->command_offset() > lc_offset) {
lc->command_offset(lc->command_offset() - section_struct_size);
}
}
available_command_space_ += section_struct_size;
std::unique_ptr<Section>& section = *it_section;
// Remove from cache
auto it_cache = std::find_if(sections_.begin(), sections_.end(),
[§ion](const Section* sec) {
return section.get() == sec;
});
if (it_cache == sections_.end()) {
LIEF_WARN("Section {} not found in cache; binary may be inconsistent",
section->name());
} else {
sections_.erase(it_cache);
}
segment->sections_.erase(it_section);
}
Section* Binary::add_section(const Section& section) {
SegmentCommand* _TEXT_segment = get_segment("__TEXT");
if (_TEXT_segment == nullptr) {
LIEF_ERR("__TEXT segment not found");
return nullptr;
}
return add_section(*_TEXT_segment, section);
}
Section* Binary::add_section(const SegmentCommand& segment,
const Section& section) {
const auto it_segment =
std::find_if(segments_.begin(), segments_.end(),
[&segment](const SegmentCommand* s) { return segment == *s; });
if (it_segment == segments_.end()) {
LIEF_ERR("Unable to find segment: '{}'", segment.name());
return nullptr;
}
SegmentCommand* target_segment = *it_segment;
span<const uint8_t> content_ref = section.content();
Section::content_t content = {content_ref.begin(), content_ref.end()};
auto new_section = section.clone();
if (section.offset() == 0) {
// Section offset is not defined: we need to allocate space enough to fit its
// content.
const size_t hdr_size =
is64_ ? sizeof(details::section_64) : sizeof(details::section_32);
const size_t alignment = 1 << section.alignment();
const size_t needed_size = hdr_size + content.size() + alignment;
// Request size with a gap of alignment, so we would have enough room
// to adjust section's offset to satisfy its alignment requirements.
if (auto result = ensure_command_space(needed_size); is_err(result)) {
LIEF_ERR("Failed to ensure command space {}: {}", needed_size,
to_string(get_error(result)));
return nullptr;
}
if (!extend(*target_segment, hdr_size)) { // adjusts available_command_space_
LIEF_ERR("Unable to extend segment '{}' by {:#x}", segment.name(), hdr_size);
return nullptr;
}
const uint64_t loadcommands_start =
is64_ ? sizeof(details::mach_header_64) : sizeof(details::mach_header);
const uint64_t loadcommands_end = loadcommands_start + header().sizeof_cmds();
// let new_offset supposedly point to the contents of the first section
uint64_t new_offset = loadcommands_end + available_command_space_;
new_offset -= content.size();
new_offset = align_down(new_offset, alignment);
// put section data in front of the first section
new_section->offset(new_offset);
available_command_space_ = new_offset - loadcommands_end;
}
// Compute offset, virtual address etc for the new section
// =======================================================
if (section.size() == 0) {
new_section->size(content.size());
}
if (section.virtual_address() == 0) {
new_section->virtual_address(target_segment->virtual_address() +
new_section->offset());
}
new_section->segment_ = target_segment;
target_segment->numberof_sections(target_segment->numberof_sections() + 1);
// Copy the new section in the cache
sections_.push_back(new_section.get());
// Copy data to segment
const uint64_t relative_offset =
new_section->offset() - target_segment->file_offset();
std::move(content.begin(), content.end(),
target_segment->data_.begin() + relative_offset);
target_segment->sections_.push_back(std::move(new_section));
return target_segment->sections_.back().get();
}
LoadCommand* Binary::add(const SegmentCommand& segment) {
/*
* To add a new segment in a Mach-O file, we need to:
*
* 1. Allocate space for a new Load command: LC_SEGMENT_64 / LC_SEGMENT
* which must include the sections
* 2. Allocate space for the content of the provided segment
*
* For #1, the logic is to shift all the content after the end of the load
* command table. This modification is described in
* doc/sphinx/tutorials/11_macho_modification.rst.
*
* For #2, the easiest way is to place the content at the end of the Mach-O file
* and to make the LC_SEGMENT point to this area. It works as expected as long as
* the binary does not need to be signed.
*
* If the binary has to be signed, codesign and the underlying Apple libraries
* enforce that there is not data after the __LINKEDIT segment, otherwise we get
* this kind of error: "main executable failed strict validation".
* To comply with this check, we can shift the __LINKEDIT segment (c.f.
* ``shift_linkedit(...)``) such as the data of the new segment are located
* before __LINKEDIT. Nevertheless, we can't shift __LINKEDIT by an arbitrary
* value. For ARM and ARM64, ld/dyld enforces a segment alignment of "4 * 4096"
* as coded in ``Options::reconfigureDefaults`` of
* ``ld64-609/src/ld/Option.cpp``:
*
* ```cpp
* ...
* <rdar://problem/13070042> Only third party apps should have 16KB page segments
* by default if (fEncryptable) { if (fSegmentAlignment == 4096)
* fSegmentAlignment = 4096*4;
* }
*
* // <rdar://problem/12258065> ARM64 needs 16KB page size for user land code
* // <rdar://problem/15974532> make armv7[s] use 16KB pages in user land code
* for iOS 8 or later if (fArchitecture == CPU_TYPE_ARM64 || (fArchitecture ==
* CPU_TYPE_ARM) ) { fSegmentAlignment = 4096*4;
* }
* ```
* Therefore, we must shift __LINKEDIT by at least 4 * 0x1000 for Mach-O files
* targeting ARM
*/
LIEF_DEBUG("Adding the new segment '{}' ({} bytes)", segment.name(),
segment.content().size());
const uint32_t alignment = page_size();
const uint64_t new_fsize = align(segment.content().size(), alignment);
SegmentCommand new_segment = segment;
if (new_segment.file_size() == 0) {
new_segment.file_size(new_fsize);
new_segment.content_resize(new_fsize);
}
if (new_segment.virtual_size() == 0) {
const uint64_t new_size = align(new_segment.file_size(), alignment);
new_segment.virtual_size(new_size);
}
if (segment.sections().size() > 0) {
new_segment.nb_sections_ = segment.sections().size();
}
if (is64_) {
new_segment.command(LoadCommand::TYPE::SEGMENT_64);
size_t needed_size = sizeof(details::segment_command_64);
needed_size += new_segment.numberof_sections() * sizeof(details::section_64);
new_segment.size(needed_size);
} else {
new_segment.command(LoadCommand::TYPE::SEGMENT);
size_t needed_size = sizeof(details::segment_command_32);
needed_size += new_segment.numberof_sections() * sizeof(details::section_32);
new_segment.size(needed_size);
}
LIEF_DEBUG(" -> sizeof(LC_SEGMENT): {}", new_segment.size());
// Insert the segment before __LINKEDIT
const auto it_linkedit =
std::find_if(commands_.begin(), commands_.end(),
[](const std::unique_ptr<LoadCommand>& cmd) {
if (!SegmentCommand::classof(cmd.get())) {
return false;
}
return cmd->as<SegmentCommand>()->name() == "__LINKEDIT";
});
const bool has_linkedit = it_linkedit != commands_.end();
size_t pos = std::distance(commands_.begin(), it_linkedit);
LIEF_DEBUG(" -> index: {}", pos);
auto* new_cmd = add(new_segment, pos);
if (new_cmd == nullptr) {
LIEF_WARN("Failed to insert segment '{}'", segment.name());
return nullptr;
}
auto* segment_added = new_cmd->as<SegmentCommand>();
if (!has_linkedit) {
/* If there are not __LINKEDIT segment we can point the Segment's content to
* the EOF NOTE(romain): I don't know if a binary without a __LINKEDIT segment
* exists
*/
range_t new_va_ranges = this->va_ranges();
range_t new_off_ranges = off_ranges();
if (segment.virtual_address() == 0 && segment_added->virtual_size() != 0) {
const uint64_t new_va = align(new_va_ranges.end, alignment);
segment_added->virtual_address(new_va);
uint64_t current_va = segment_added->virtual_address();
for (Section& section : segment_added->sections()) {
section.virtual_address(current_va);
current_va += section.size();
}
}
if (segment.file_offset() == 0 && segment_added->virtual_size() != 0) {
const uint64_t new_offset = align(new_off_ranges.end, alignment);
segment_added->file_offset(new_offset);
uint64_t current_offset = new_offset;
for (Section& section : segment_added->sections()) {
section.offset(current_offset);
current_offset += section.size();
}
}
refresh_seg_offset();
return segment_added;
}
uint64_t lnk_offset = 0;
uint64_t lnk_va = 0;
if (const SegmentCommand* lnk = get_segment("__LINKEDIT")) {
lnk_offset = lnk->file_offset();
lnk_va = lnk->virtual_address();
}
// Make space for the content of the new segment
shift_linkedit(new_fsize);
LIEF_DEBUG(" -> offset : {:#08x}", lnk_offset);
LIEF_DEBUG(" -> virtual address: {:#08x}", lnk_va);
segment_added->virtual_address(lnk_va);
segment_added->virtual_size(segment_added->virtual_size());
uint64_t current_va = segment_added->virtual_address();
for (Section& section : segment_added->sections()) {
section.virtual_address(current_va);
current_va += section.size();
}
segment_added->file_offset(lnk_offset);
uint64_t current_offset = lnk_offset;
for (Section& section : segment_added->sections()) {
section.offset(current_offset);
current_offset += section.size();
}
if (DyldChainedFixups* fixup = dyld_chained_fixups()) {
DyldChainedFixups::chained_starts_in_segment new_info =
DyldChainedFixups::chained_starts_in_segment::create_empty_chained(
*segment_added
);
fixup->add(std::move(new_info));
}
refresh_seg_offset();
return segment_added;
}
size_t Binary::add_cached_segment(SegmentCommand& segment) {
// The new segment should be put **before** the __LINKEDIT segment
const auto it_linkedit =
std::find_if(segments_.begin(), segments_.end(), [](SegmentCommand* cmd) {
return cmd->name() == "__LINKEDIT";
});
if (it_linkedit == segments_.end()) {
LIEF_DEBUG("No __LINKEDIT segment found");
segment.index_ = segments_.size();
segments_.push_back(&segment);
} else {
segment.index_ = (*it_linkedit)->index();
// Update indexes
for (auto it = it_linkedit; it != segments_.end(); ++it) {
(*it)->index_++;
}
segments_.insert(it_linkedit, &segment);
}
offset_seg_[segment.file_offset()] = &segment;
if (LinkEdit::segmentof(segment)) {
auto& linkedit = static_cast<LinkEdit&>(segment);
linkedit.dyld_ = dyld_info();
linkedit.chained_fixups_ = dyld_chained_fixups();
}
refresh_seg_offset();
return segment.index();
}
bool Binary::unexport(const std::string& name) {
for (const std::unique_ptr<Symbol>& s : symbols_) {
if (s->name() == name && s->has_export_info()) {
return unexport(*s);
}
}
return false;
}
bool Binary::unexport(const Symbol& sym) {
if (DyldInfo* dyld = dyld_info()) {
const auto it_export =
std::find_if(dyld->export_info_.begin(), dyld->export_info_.end(),
[&sym](const std::unique_ptr<ExportInfo>& info) {
return info->has_symbol() && *info->symbol() == sym;
});
// The symbol is not exported
if (it_export == dyld->export_info_.end()) {
return false;
}
dyld->export_info_.erase(it_export);
return true;
}
if (DyldExportsTrie* exports = dyld_exports_trie()) {
const auto it_export =
std::find_if(exports->export_info_.begin(), exports->export_info_.end(),
[&sym](const std::unique_ptr<ExportInfo>& info) {
return info->has_symbol() && *info->symbol() == sym;
});
// The symbol is not exported
if (it_export == exports->export_info_.end()) {
return false;
}
exports->export_info_.erase(it_export);
return true;
}
LIEF_INFO("Neither LC_DYLD_INFO nor LC_DYLD_CHAINED_FIXUPS found");
return false;
}
bool Binary::remove(const Symbol& sym) {
unexport(sym);
const auto it_sym = std::find_if(symbols_.begin(), symbols_.end(),
[&sym](const std::unique_ptr<Symbol>& s) {
return s.get() == &sym;
});
if (it_sym == symbols_.end()) {
return false;
}
if (DynamicSymbolCommand* dyst = dynamic_symbol_command()) {
dyst->indirect_symbols_.erase(std::remove_if(dyst->indirect_symbols_.begin(),
dyst->indirect_symbols_.end(),
[&sym](const Symbol* s) {
return s == &sym;
}),
dyst->indirect_symbols_.end());
}
symbols_.erase(it_sym);
return true;
}
bool Binary::remove_symbol(const std::string& name) {
bool removed = false;
while (const Symbol* s = get_symbol(name)) {
if (!remove(*s)) {
break;
}
removed = true;
}
return removed;
}
bool Binary::can_remove(const Symbol& sym) const {
/*
* We consider that a symbol can be removed, if and only if
* there are no binding associated with
*/
if (const DyldInfo* dyld = dyld_info()) {
for (const DyldBindingInfo& binding : dyld->bindings()) {
if (binding.has_symbol() && binding.symbol()->name() == sym.name()) {
return false;
}
}
}
if (const DyldChainedFixups* fixups = dyld_chained_fixups()) {
for (const ChainedBindingInfo& binding : fixups->bindings()) {
if (binding.has_symbol() && binding.symbol()->name() == sym.name()) {
return false;
}
}
}
return true;
}
bool Binary::can_remove_symbol(const std::string& name) const {
std::vector<const Symbol*> syms;
for (const std::unique_ptr<Symbol>& s : symbols_) {
if (s->name() == name) {
syms.push_back(s.get());
}
}
return std::all_of(syms.begin(), syms.end(),
[this](const Symbol* s) { return can_remove(*s); });
}
bool Binary::remove_signature() {
if (const CodeSignature* cs = code_signature()) {
return remove(*cs);
}
LIEF_WARN("No signature found");
return false;
}
LoadCommand* Binary::add(const DylibCommand& library) {
return add(*library.as<LoadCommand>());
}
LoadCommand* Binary::add_library(const std::string& name) {
return add(DylibCommand::load_dylib(name));
}
std::vector<uint8_t> Binary::raw() {
std::vector<uint8_t> buffer;
Builder::write(*this, buffer);
return buffer;
}
result<uint64_t>
Binary::virtual_address_to_offset(uint64_t virtual_address) const {
const SegmentCommand* segment = segment_from_virtual_address(virtual_address);
// file_size() is 0 if segment is not file-backed, e.g. a `__DATA` segment that
// only has a ZEROFILL section. In such case, there is no file offset available.
if (segment == nullptr || segment->file_size() == 0) {
return make_error_code(lief_errors::conversion_error);
}
const uint64_t base_address =
segment->virtual_address() - segment->file_offset();
return virtual_address - base_address;
}
result<uint64_t> Binary::offset_to_virtual_address(uint64_t offset,
uint64_t slide) const {
const SegmentCommand* segment = segment_from_offset(offset);
if (segment == nullptr) {
return slide + offset;
}
const uint64_t delta = segment->virtual_address() - segment->file_offset();
const uint64_t imgbase = imagebase();
if (slide == 0) {
return delta + offset;
}
if (imgbase == 0) {
return slide + segment->virtual_address() + (offset - segment->file_offset());
}
return (segment->virtual_address() - imgbase) + slide +
(offset - segment->file_offset());
}
bool Binary::disable_pie() {
if (is_pie()) {
header().remove(Header::FLAGS::PIE);
return true;
}
return false;
}
const Section* Binary::get_section(const std::string& name) const {
const auto it_section =
std::find_if(sections_.begin(), sections_.end(),
[&name](const Section* sec) { return sec->name() == name; });
if (it_section == sections_.end()) {
return nullptr;
}
return *it_section;
}
const Section* Binary::get_section(const std::string& segname,
const std::string& secname) const {
if (const SegmentCommand* seg = get_segment(segname)) {
if (const Section* sec = seg->get_section(secname)) {
return sec;
}
}
return nullptr;
}
const SegmentCommand* Binary::get_segment(const std::string& name) const {
const auto it_segment = std::find_if(segments_.begin(), segments_.end(),
[&name](const SegmentCommand* seg) {
return seg->name() == name;
});
if (it_segment == segments_.end()) {
return nullptr;
}
return *it_segment;
}
uint64_t Binary::imagebase() const {
if (const SegmentCommand* _TEXT = get_segment("__TEXT")) {
return _TEXT->virtual_address();
}
return 0;
}
std::string Binary::loader() const {
if (const DylinkerCommand* cmd = dylinker()) {
return cmd->name();
}
return "";
}
Binary::range_t Binary::va_ranges() const {
auto min = uint64_t(-1);
uint64_t max = 0;
for (const SegmentCommand* segment : segments_) {
if (segment->name_ == "__PAGEZERO") {
continue;
}
min = std::min<uint64_t>(min, segment->virtual_address());
max = std::max(max, segment->virtual_address() + segment->virtual_size());
}
if (min == uint64_t(-1)) {
return {0, 0};
}
return {min, max};
}
Binary::range_t Binary::off_ranges() const {
auto min = uint64_t(-1);
uint64_t max = 0;
for (const SegmentCommand* segment : segments_) {
min = std::min<uint64_t>(min, segment->file_offset());
max = std::max(max, segment->file_offset() + segment->file_size());
}
if (min == uint64_t(-1)) {
return {0, 0};
}
return {min, max};
}
Binary::range_t Binary::tlv_initial_content_range() const {
uint64_t low = 0;
uint64_t high = 0;
for (const Section& S : sections()) {
switch (S.type()) {
default: break;
case Section::TYPE::THREAD_LOCAL_REGULAR:
case Section::TYPE::THREAD_LOCAL_ZEROFILL:
{
if (low == 0) {
low = S.virtual_address();
high = low + S.size();
} else {
high = S.virtual_address() + S.size();
}
break;
}
}
}
return {low, high};
}
LIEF::Binary::functions_t Binary::ctor_functions() const {
LIEF::Binary::functions_t functions;
for (const Section& section : sections()) {
if (section.type() != Section::TYPE::MOD_INIT_FUNC_POINTERS) {
continue;
}
span<const uint8_t> content = section.content();
if (is64_) {
const size_t nb_fnc = content.size() / sizeof(uint64_t);
const auto* aptr = reinterpret_cast<const uint64_t*>(content.data());
for (size_t i = 0; i < nb_fnc; ++i) {
functions.emplace_back("ctor_" + std::to_string(i), aptr[i],
Function::FLAGS::CONSTRUCTOR);
}
} else {
const size_t nb_fnc = content.size() / sizeof(uint32_t);
const auto* aptr = reinterpret_cast<const uint32_t*>(content.data());
for (size_t i = 0; i < nb_fnc; ++i) {
functions.emplace_back("ctor_" + std::to_string(i), aptr[i],
Function::FLAGS::CONSTRUCTOR);
}
}
}
return functions;
}
LIEF::Binary::functions_t Binary::functions() const {
static const auto func_cmd = [](const Function& lhs, const Function& rhs) {
return lhs.address() < rhs.address();
};
std::set<Function, decltype(func_cmd)> functions_set(func_cmd);
LIEF::Binary::functions_t unwind_functions = this->unwind_functions();
LIEF::Binary::functions_t ctor_functions = this->ctor_functions();
LIEF::Binary::functions_t exported = get_abstract_exported_functions();
std::move(unwind_functions.begin(), unwind_functions.end(),
std::inserter(functions_set, functions_set.end()));
std::move(ctor_functions.begin(), ctor_functions.end(),
std::inserter(functions_set, functions_set.end()));
std::move(exported.begin(), exported.end(),
std::inserter(functions_set, functions_set.end()));
return {functions_set.begin(), functions_set.end()};
}
LIEF::Binary::functions_t Binary::unwind_functions() const {
static constexpr size_t UNWIND_COMPRESSED = 3;
static constexpr size_t UNWIND_UNCOMPRESSED = 2;
// Container to store functions with unique addresses
static const auto fcmd = [](const Function& l, const Function& r) {
return l.address() < r.address();
};
std::set<Function, decltype(fcmd)> functions(fcmd);
// Look for the __unwind_info section
const Section* unwind_section = get_section("__unwind_info");
if (unwind_section == nullptr) {
return {};
}
SpanStream vs = unwind_section->content();
// Get section content
const auto hdr = vs.read<details::unwind_info_section_header>();
if (!hdr) {
LIEF_ERR("Failed to read unwind section header");
return {};
}
vs.setpos(hdr->index_section_offset);
size_t lsda_start = -1lu;
size_t lsda_stop = 0;
for (size_t i = 0; i < hdr->index_count; ++i) {
const auto section_hdr =
vs.read<details::unwind_info_section_header_index_entry>();
if (!section_hdr) {
LIEF_ERR("Failed to read function info at index {:d}", i);
break;
}
functions.emplace(section_hdr->function_offset);
const size_t second_lvl_off = section_hdr->second_level_pages_section_offset;
const size_t lsda_off = section_hdr->lsda_index_array_section_offset;
lsda_start = std::min(lsda_off, lsda_start);
lsda_stop = std::max(lsda_off, lsda_stop);
if (second_lvl_off > 0 &&
vs.can_read<details::unwind_info_regular_second_level_page_header>(
second_lvl_off
))
{
const size_t saved_pos = vs.pos();
{
vs.setpos(second_lvl_off);
const auto lvl_hdr =
vs.peek<details::unwind_info_regular_second_level_page_header>(
second_lvl_off
);
if (!lvl_hdr) {
break;
}
if (lvl_hdr->kind == UNWIND_COMPRESSED) {
const auto lvl_compressed_hdr =
vs.read<details::unwind_info_compressed_second_level_page_header>();
if (!lvl_compressed_hdr) {
LIEF_ERR("Failed to read lvl_compressed_hdr");
break;
}
vs.setpos(second_lvl_off + lvl_compressed_hdr->entry_page_offset);
for (size_t j = 0; j < lvl_compressed_hdr->entry_count; ++j) {
auto entry = vs.read<uint32_t>();
if (!entry) {
break;
}
uint32_t func_off = section_hdr->function_offset + (*entry & 0xffffff);
functions.emplace(func_off);
}
} else if (lvl_hdr->kind == UNWIND_UNCOMPRESSED) {
LIEF_WARN("UNWIND_UNCOMPRESSED is not yet supported");
} else {
LIEF_WARN("Unknown 2nd level kind: {:d}", lvl_hdr->kind);
}
}
vs.setpos(saved_pos);
}
}
const size_t nb_lsda =
lsda_stop > lsda_start ?
(lsda_stop - lsda_start) /
sizeof(details::unwind_info_section_header_lsda_index_entry) :
0;
vs.setpos(lsda_start);
for (size_t i = 0; i < nb_lsda; ++i) {
const auto hdr =
vs.read<details::unwind_info_section_header_lsda_index_entry>();
if (!hdr) {
LIEF_ERR("Failed to read LSDA at index {:d}", i);
break;
}
functions.emplace(hdr->function_offset);
}
return {functions.begin(), functions.end()};
}
// UUID
// ++++
UUIDCommand* Binary::uuid() {
return command<UUIDCommand>();
}
const UUIDCommand* Binary::uuid() const {
return command<UUIDCommand>();
}
// MainCommand
// +++++++++++
MainCommand* Binary::main_command() {
return command<MainCommand>();
}
const MainCommand* Binary::main_command() const {
return command<MainCommand>();
}
// DylinkerCommand
// +++++++++++++++
DylinkerCommand* Binary::dylinker() {
return command<DylinkerCommand>();
}
const DylinkerCommand* Binary::dylinker() const {
return command<DylinkerCommand>();
}
// DyldInfo
// ++++++++
DyldInfo* Binary::dyld_info() {
return command<DyldInfo>();
}
const DyldInfo* Binary::dyld_info() const {
return command<DyldInfo>();
}
// Function Starts
// +++++++++++++++
FunctionStarts* Binary::function_starts() {
return command<FunctionStarts>();
}
const FunctionStarts* Binary::function_starts() const {
return command<FunctionStarts>();
}
// Source Version
// ++++++++++++++
SourceVersion* Binary::source_version() {
return command<SourceVersion>();
}
const SourceVersion* Binary::source_version() const {
return command<SourceVersion>();
}
// Version Min
// +++++++++++
VersionMin* Binary::version_min() {
return command<VersionMin>();
}
const VersionMin* Binary::version_min() const {
return command<VersionMin>();
}
// Routine Command
// +++++++++++++++
Routine* Binary::routine_command() {
return command<Routine>();
}
const Routine* Binary::routine_command() const {
return command<Routine>();
}
// Thread command
// ++++++++++++++
ThreadCommand* Binary::thread_command() {
return command<ThreadCommand>();
}
const ThreadCommand* Binary::thread_command() const {
return command<ThreadCommand>();
}
// RPath command
// +++++++++++++
RPathCommand* Binary::rpath() {
return command<RPathCommand>();
}
const RPathCommand* Binary::rpath() const {
return command<RPathCommand>();
}
Binary::it_rpaths Binary::rpaths() {
return {commands_, [](const std::unique_ptr<LoadCommand>& cmd) {
return RPathCommand::classof(cmd.get());
}};
}
Binary::it_const_rpaths Binary::rpaths() const {
return {commands_, [](const std::unique_ptr<LoadCommand>& cmd) {
return RPathCommand::classof(cmd.get());
}};
}
// SymbolCommand command
// +++++++++++++++++++++
SymbolCommand* Binary::symbol_command() {
return command<SymbolCommand>();
}
const SymbolCommand* Binary::symbol_command() const {
return command<SymbolCommand>();
}
// DynamicSymbolCommand command
// ++++++++++++++++++++++++++++
DynamicSymbolCommand* Binary::dynamic_symbol_command() {
return command<DynamicSymbolCommand>();
}
const DynamicSymbolCommand* Binary::dynamic_symbol_command() const {
return command<DynamicSymbolCommand>();
}
// CodeSignature command
// +++++++++++++++++++++
const CodeSignature* Binary::code_signature() const {
if (const auto* cmd = get(LoadCommand::TYPE::CODE_SIGNATURE)) {
return cmd->as<const CodeSignature>();
}
return nullptr;
}
// CodeSignatureDir command
// ++++++++++++++++++++++++
const CodeSignatureDir* Binary::code_signature_dir() const {
if (const auto* cmd = get(LoadCommand::TYPE::DYLIB_CODE_SIGN_DRS)) {
return cmd->as<const CodeSignatureDir>();
}
return nullptr;
}
// DataInCode command
// ++++++++++++++++++
DataInCode* Binary::data_in_code() {
return command<DataInCode>();
}
const DataInCode* Binary::data_in_code() const {
return command<DataInCode>();
}
// SegmentSplitInfo command
// ++++++++++++++++++++++++
SegmentSplitInfo* Binary::segment_split_info() {
return command<SegmentSplitInfo>();
}
const SegmentSplitInfo* Binary::segment_split_info() const {
return command<SegmentSplitInfo>();
}
// SubClient command
// ++++++++++++++++++++
Binary::it_sub_clients Binary::subclients() {
return {commands_, [](const std::unique_ptr<LoadCommand>& cmd) {
return SubClient::classof(cmd.get());
}};
}
Binary::it_const_sub_clients Binary::subclients() const {
return {commands_, [](const std::unique_ptr<LoadCommand>& cmd) {
return SubClient::classof(cmd.get());
}};
}
bool Binary::has_subclients() const {
return has_command<SubClient>();
}
// SubFramework command
// ++++++++++++++++++++
SubFramework* Binary::sub_framework() {
return command<SubFramework>();
}
const SubFramework* Binary::sub_framework() const {
return command<SubFramework>();
}
// DyldEnvironment command
// +++++++++++++++++++++++
DyldEnvironment* Binary::dyld_environment() {
return command<DyldEnvironment>();
}
const DyldEnvironment* Binary::dyld_environment() const {
return command<DyldEnvironment>();
}
// EncryptionInfo command
// +++++++++++++++++++++++
EncryptionInfo* Binary::encryption_info() {
return command<EncryptionInfo>();
}
const EncryptionInfo* Binary::encryption_info() const {
return command<EncryptionInfo>();
}
// BuildVersion command
// ++++++++++++++++++++
BuildVersion* Binary::build_version() {
return command<BuildVersion>();
}
const BuildVersion* Binary::build_version() const {
return command<BuildVersion>();
}
// DyldChainedFixups command
// ++++++++++++++++++++
DyldChainedFixups* Binary::dyld_chained_fixups() {
return command<DyldChainedFixups>();
}
const DyldChainedFixups* Binary::dyld_chained_fixups() const {
return command<DyldChainedFixups>();
}
// DyldExportsTrie command
// +++++++++++++++++++++++
DyldExportsTrie* Binary::dyld_exports_trie() {
return command<DyldExportsTrie>();
}
const DyldExportsTrie* Binary::dyld_exports_trie() const {
return command<DyldExportsTrie>();
}
// Linker Optimization Hint command
// ++++++++++++++++++++++++++++++++
const LinkerOptHint* Binary::linker_opt_hint() const {
if (const auto* cmd = get(LoadCommand::TYPE::LINKER_OPTIMIZATION_HINT)) {
return cmd->as<const LinkerOptHint>();
}
return nullptr;
}
// Two Level Hints Command
// ++++++++++++++++++++++++++++++++
const TwoLevelHints* Binary::two_level_hints() const {
if (const auto* cmd = get(LoadCommand::TYPE::TWOLEVEL_HINTS)) {
return cmd->as<const TwoLevelHints>();
}
return nullptr;
}
// AtomInfo
// ++++++++++++++++++++++++++++++++
const AtomInfo* Binary::atom_info() const {
if (const auto* cmd = get(LoadCommand::TYPE::ATOM_INFO)) {
return cmd->as<const AtomInfo>();
}
return nullptr;
}
// Notes
// ++++++++++++++++++++++++++++++++
Binary::it_notes Binary::notes() {
return {commands_, [](const std::unique_ptr<LoadCommand>& cmd) {
return NoteCommand::classof(cmd.get());
}};
}
Binary::it_const_notes Binary::notes() const {
return {commands_, [](const std::unique_ptr<LoadCommand>& cmd) {
return NoteCommand::classof(cmd.get());
}};
}
// FunctionVariants
// ++++++++++++++++++++++++++++++++
const FunctionVariants* Binary::function_variants() const {
if (const auto* cmd = get(LoadCommand::TYPE::FUNCTION_VARIANTS)) {
return cmd->as<const FunctionVariants>();
}
return nullptr;
}
// FunctionVariantFixups
// ++++++++++++++++++++++++++++++++
const FunctionVariantFixups* Binary::function_variant_fixups() const {
if (const auto* cmd = get(LoadCommand::TYPE::FUNCTION_VARIANT_FIXUPS)) {
return cmd->as<const FunctionVariantFixups>();
}
return nullptr;
}
Binary::it_bindings Binary::bindings() const {
if (const DyldInfo* dyld = dyld_info()) {
auto begin = BindingInfoIterator(*dyld, 0);
auto end = BindingInfoIterator(*dyld, dyld->binding_info_.size());
// NOLINTNEXTLINE(performance-move-const-arg)
return make_range(std::move(begin), std::move(end));
}
if (const DyldChainedFixups* fixup = dyld_chained_fixups()) {
auto begin = BindingInfoIterator(*fixup, 0);
auto end = BindingInfoIterator(*fixup, fixup->all_bindings_.size());
// NOLINTNEXTLINE(performance-move-const-arg)
return make_range(std::move(begin), std::move(end));
}
auto begin = BindingInfoIterator(*this, 0);
auto end = BindingInfoIterator(*this, indirect_bindings_.size());
// NOLINTNEXTLINE(performance-move-const-arg)
return make_range(std::move(begin), std::move(end));
}
result<uint64_t> Binary::get_function_address(const std::string& name) const {
const std::string alt_name = '_' + name;
for (const Symbol& sym : symbols()) {
if (sym.value() == 0) {
continue;
}
if (sym.name() == name || sym.name() == alt_name) {
return sym.value();
}
}
return LIEF::Binary::get_function_address(name);
}
void Binary::accept(LIEF::Visitor& visitor) const {
visitor.visit(*this);
}
Symbol& Binary::add(const Symbol& symbol) {
symbols_.push_back(std::make_unique<Symbol>(symbol));
return *symbols_.back();
}
Symbol* Binary::add_local_symbol(uint64_t address, const std::string& name) {
auto sym = std::make_unique<Symbol>();
sym->category_ = Symbol::CATEGORY::LOCAL;
sym->origin_ = Symbol::ORIGIN::SYMTAB;
sym->numberof_sections_ = 0;
sym->description_ = static_cast<uint16_t>(/* N_NO_DEAD_STRIP */ 0x20);
sym->value(address);
sym->name(name);
return symbols_.emplace_back(std::move(sym)).get();
}
ExportInfo* Binary::add_exported_function(uint64_t address,
const std::string& name) {
if (Symbol* symbol = add_local_symbol(address, name)) {
uint64_t trie_offset = address;
if (trie_offset >= imagebase()) {
trie_offset -= imagebase();
}
if (DyldExportsTrie* exports = dyld_exports_trie()) {
auto export_info = std::make_unique<ExportInfo>(trie_offset, 0);
export_info->symbol_ = symbol;
export_info->address(trie_offset);
symbol->export_info_ = export_info.get();
return exports->add(std::move(export_info));
}
if (DyldInfo* info = dyld_info()) {
auto export_info = std::make_unique<ExportInfo>(trie_offset, 0);
export_info->symbol_ = symbol;
export_info->address(trie_offset);
symbol->export_info_ = export_info.get();
return info->add(std::move(export_info));
}
}
return nullptr;
}
const DylibCommand* Binary::find_library(const std::string& name) const {
auto it = std::find_if(libraries_.begin(), libraries_.end(),
[&name](const DylibCommand* cmd) {
const std::string& libpath = cmd->name();
return libpath == name ||
libname(libpath).value_or("") == name;
});
return it == libraries_.end() ? nullptr : *it;
}
void Binary::refresh_seg_offset() {
offset_seg_.clear();
for (SegmentCommand* segment : segments_) {
if (!can_cache_segment(*segment)) {
continue;
}
offset_seg_[segment->file_offset()] = segment;
}
}
Binary::stub_iterator Binary::symbol_stubs() const {
static stub_iterator empty_iterator(Stub::Iterator{}, Stub::Iterator{});
std::vector<const Section*> stub_sections;
stub_sections.reserve(3);
uint32_t total = 0;
for (const Section& section : sections()) {
if (section.type() != Section::TYPE::SYMBOL_STUBS) {
continue;
}
const uint32_t stride = section.reserved2();
if (stride == 0) {
continue;
}
const uint32_t count = section.content().size() / stride;
if (count == 0) {
continue;
}
total += count;
stub_sections.push_back(§ion);
}
if (stub_sections.empty() || total == 0) {
return empty_iterator;
}
Stub::Iterator begin({header_.cpu_type(), header_.cpu_subtype()},
std::move(stub_sections), 0);
Stub::Iterator end({Header::CPU_TYPE::ANY, 0}, {}, total);
return make_range(std::move(begin), std::move(end));
}
bool Binary::can_cache_segment(const SegmentCommand& segment) {
if (segment.file_offset() > 0 && segment.file_size() > 0) {
return true;
}
if (segment.name() == "__TEXT") {
// In some cases (c.f. <samples>/MachO/issue_1130.macho)
// the __TEXT segment can have a file_size set to 0 while it is logically
// relevant to cache it
return true;
}
return false;
}
Binary::~Binary() = default;
std::ostream& Binary::print(std::ostream& os) const {
os << "Header {\n" << indent(LIEF::to_string(header()), 2) << "}\n";
if (auto cmds = commands(); !cmds.empty()) {
os << fmt::format("Commands (#{})\n", cmds.size());
for (size_t i = 0; i < cmds.size(); ++i) {
os << fmt::format(" Command #{:02} {{\n", i)
<< indent(LIEF::to_string(cmds[i]), 4) << " }\n";
}
}
{
const auto segs = segments();
if (!segs.empty()) {
os << fmt::format("Segments (#{})\n", segs.size());
for (size_t i = 0; i < segs.size(); ++i) {
os << fmt::format(" Segment #{:02} {{\n", i)
<< indent(LIEF::to_string(segs[i]), 4) << " }\n";
}
}
}
{
const auto secs = sections();
if (!secs.empty()) {
os << fmt::format("Sections (#{})\n", secs.size());
for (size_t i = 0; i < secs.size(); ++i) {
os << fmt::format(" Section #{:02} {{\n", i)
<< indent(LIEF::to_string(secs[i]), 4) << " }\n";
}
}
}
if (auto libs = libraries(); !libs.empty()) {
os << fmt::format("Libraries (#{})\n", libs.size());
for (const DylibCommand& lib : libs) {
os << " " << lib << '\n';
}
}
if (auto syms = symbols(); !syms.empty()) {
os << fmt::format("Symbols (#{})\n", syms.size());
for (const Symbol& symbol : syms) {
os << " " << symbol << '\n';
}
}
if (auto syms = exported_symbols(); !syms.empty()) {
os << fmt::format("Exported Symbols (#{})\n", syms.size());
for (const Symbol& symbol : syms) {
os << " " << symbol << '\n';
}
}
if (auto syms = imported_symbols(); !syms.empty()) {
os << fmt::format("Imported Symbols (#{})\n", syms.size());
for (const Symbol& symbol : syms) {
os << " " << symbol << '\n';
}
}
if (auto relocs = relocations(); !relocs.empty()) {
os << fmt::format("Relocations (#{})\n", relocs.size());
for (const Relocation& R : relocs) {
os << " " << R << '\n';
}
}
if (auto paths = rpaths(); !paths.empty()) {
os << fmt::format("RPaths (#{})\n", paths.size());
for (const RPathCommand& rp : paths) {
os << " " << rp << '\n';
}
}
if (auto n = notes(); !n.empty()) {
os << fmt::format("Notes (#{})\n", n.size());
for (size_t i = 0; i < n.size(); ++i) {
os << fmt::format(" Note #{:02} {{\n", i)
<< indent(LIEF::to_string(n[i]), 4) << " }\n";
}
}
if (auto fsets = filesets(); !fsets.empty()) {
os << fmt::format("Filesets (#{})\n", fsets.size());
for (const Binary& fs : fsets) {
os << indent(LIEF::to_string(fs), 2);
}
}
if (const UUIDCommand* cmd = uuid()) {
os << "UUID {\n" << indent(LIEF::to_string(*cmd), 2) << "}\n";
}
if (const MainCommand* cmd = main_command()) {
os << "Main Command {\n" << indent(LIEF::to_string(*cmd), 2) << "}\n";
}
if (const DylinkerCommand* cmd = dylinker()) {
os << "Dylinker {\n" << indent(LIEF::to_string(*cmd), 2) << "}\n";
}
if (const DyldInfo* info = dyld_info()) {
os << "Dyld Info {\n" << indent(LIEF::to_string(*info), 2) << "}\n";
}
if (const FunctionStarts* fs = function_starts()) {
os << "Function Starts {\n" << indent(LIEF::to_string(*fs), 2) << "}\n";
}
if (const SourceVersion* sv = source_version()) {
os << "Source Version {\n" << indent(LIEF::to_string(*sv), 2) << "}\n";
}
if (const BuildVersion* bv = build_version()) {
os << "Build Version {\n" << indent(LIEF::to_string(*bv), 2) << "}\n";
}
if (const VersionMin* vm = version_min()) {
os << "Version Min {\n" << indent(LIEF::to_string(*vm), 2) << "}\n";
}
if (const CodeSignature* cs = code_signature()) {
os << "Code Signature {\n" << indent(LIEF::to_string(*cs), 2) << "}\n";
}
if (const CodeSignatureDir* csd = code_signature_dir()) {
os << "Code Signature Dir {\n" << indent(LIEF::to_string(*csd), 2) << "}\n";
}
if (const DataInCode* dic = data_in_code()) {
os << "Data In Code {\n" << indent(LIEF::to_string(*dic), 2) << "}\n";
}
if (const DyldChainedFixups* dcf = dyld_chained_fixups()) {
os << "Dyld Chained Fixups {\n" << indent(LIEF::to_string(*dcf), 2) << "}\n";
}
if (const DyldExportsTrie* det = dyld_exports_trie()) {
os << "Dyld Exports Trie {\n" << indent(LIEF::to_string(*det), 2) << "}\n";
}
if (const EncryptionInfo* ei = encryption_info()) {
os << "Encryption Info {\n" << indent(LIEF::to_string(*ei), 2) << "}\n";
}
if (const TwoLevelHints* tlh = two_level_hints()) {
os << "Two Level Hints {\n" << indent(LIEF::to_string(*tlh), 2) << "}\n";
}
if (const SubFramework* sf = sub_framework()) {
os << "Sub Framework {\n" << indent(LIEF::to_string(*sf), 2) << "}\n";
}
if (const DyldEnvironment* de = dyld_environment()) {
os << "Dyld Environment {\n" << indent(LIEF::to_string(*de), 2) << "}\n";
}
return os;
}
}