mirror of
https://github.com/lief-project/LIEF.git
synced 2026-10-11 22:09:53 +00:00
2060 lines
72 KiB
C++
2060 lines
72 KiB
C++
/* 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 <cstring>
|
|
#include <vector>
|
|
|
|
#include "LIEF/BinaryStream/SpanStream.hpp"
|
|
#include "LIEF/utils.hpp"
|
|
#include "logging.hpp"
|
|
|
|
#include "LIEF/MachO/AtomInfo.hpp"
|
|
#include "LIEF/MachO/Binary.hpp"
|
|
#include "LIEF/MachO/BuildVersion.hpp"
|
|
#include "LIEF/MachO/Builder.hpp"
|
|
#include "LIEF/MachO/ChainedBindingInfo.hpp"
|
|
#include "LIEF/MachO/ChainedPointerAnalysis.hpp"
|
|
#include "LIEF/MachO/CodeSignature.hpp"
|
|
#include "LIEF/MachO/CodeSignatureDir.hpp"
|
|
#include "LIEF/MachO/DataInCode.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/EnumToString.hpp"
|
|
#include "LIEF/MachO/FunctionStarts.hpp"
|
|
#include "LIEF/MachO/FunctionVariantFixups.hpp"
|
|
#include "LIEF/MachO/FunctionVariants.hpp"
|
|
#include "LIEF/MachO/LazyLoadDylibInfo.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/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/VersionMin.hpp"
|
|
|
|
#include "MachO/ChainedBindingInfoList.hpp"
|
|
#include "MachO/ChainedFixup.hpp"
|
|
#include "MachO/Structures.hpp"
|
|
#include "MachO/exports_trie.hpp"
|
|
|
|
#include "internal_utils.hpp"
|
|
|
|
|
|
namespace LIEF::MachO {
|
|
|
|
|
|
template<class T>
|
|
size_t Builder::get_cmd_size(const LoadCommand& cmd) {
|
|
if (const auto* dylib = cmd.cast<DylibCommand>()) {
|
|
return align(sizeof(details::dylib_command) + dylib->name().size() + 1,
|
|
sizeof(typename T::uint));
|
|
}
|
|
|
|
if (const auto* linker = cmd.cast<DylinkerCommand>()) {
|
|
return align(sizeof(details::dylinker_command) + linker->name().size() + 1,
|
|
sizeof(typename T::uint));
|
|
}
|
|
|
|
if (const auto* rpath = cmd.cast<RPathCommand>()) {
|
|
return align(sizeof(details::rpath_command) + rpath->path().size() + 1,
|
|
sizeof(typename T::uint));
|
|
}
|
|
|
|
if (const auto* subframework = cmd.cast<SubFramework>()) {
|
|
return align(sizeof(details::sub_framework_command) +
|
|
subframework->umbrella().size() + 1,
|
|
sizeof(typename T::uint));
|
|
}
|
|
|
|
if (const auto* subclient = cmd.cast<SubClient>()) {
|
|
return align(sizeof(details::sub_client_command) + subclient->client().size() +
|
|
1,
|
|
sizeof(typename T::uint));
|
|
}
|
|
|
|
if (const auto* dyldenv = cmd.cast<DyldEnvironment>()) {
|
|
return align(sizeof(details::dylinker_command) + dyldenv->value().size() + 1,
|
|
sizeof(typename T::uint));
|
|
}
|
|
|
|
if (const auto* bversion = cmd.cast<BuildVersion>()) {
|
|
return align(sizeof(details::build_version_command) +
|
|
bversion->tools().size() *
|
|
sizeof(details::build_tool_version),
|
|
sizeof(typename T::uint));
|
|
}
|
|
return cmd.size();
|
|
}
|
|
|
|
template<typename T>
|
|
ok_error_t Builder::build_linkedit() {
|
|
// NOTE(romain): the order in which the linkedit_data_command are placed
|
|
// in the __LINKEDIT segment needs to follow cctools / checkout.c / dyld_order()
|
|
SegmentCommand* linkedit = binary_->get_segment("__LINKEDIT");
|
|
if (linkedit == nullptr) {
|
|
return ok();
|
|
}
|
|
linkedit_offset_ = linkedit->file_offset();
|
|
if (auto* dyld = binary_->dyld_info()) {
|
|
build<T>(*dyld);
|
|
}
|
|
if (auto* fixups = binary_->dyld_chained_fixups()) {
|
|
build<T>(*fixups);
|
|
}
|
|
if (auto* exports_trie = binary_->dyld_exports_trie()) {
|
|
build<T>(*exports_trie);
|
|
}
|
|
if (auto* func_variants = binary_->function_variants()) {
|
|
build<T>(*func_variants);
|
|
}
|
|
if (auto* func_variant_fixups = binary_->function_variant_fixups()) {
|
|
build<T>(*func_variant_fixups);
|
|
}
|
|
if (auto* split_info = binary_->segment_split_info()) {
|
|
build<T>(*split_info);
|
|
}
|
|
for (LazyLoadDylibInfo& lazy : binary_->lazy_load_dylib_infos()) {
|
|
build<T>(lazy);
|
|
}
|
|
if (auto* fstart = binary_->function_starts()) {
|
|
build<T>(*fstart);
|
|
}
|
|
if (auto* data = binary_->data_in_code()) {
|
|
build<T>(*data);
|
|
}
|
|
if (auto* atom_info = binary_->atom_info()) {
|
|
build<T>(*atom_info);
|
|
}
|
|
if (auto* sig_dir = binary_->code_signature_dir()) {
|
|
build<T>(*sig_dir);
|
|
}
|
|
if (auto* opt = binary_->linker_opt_hint()) {
|
|
build<T>(*opt);
|
|
}
|
|
if (auto* sym = binary_->symbol_command()) {
|
|
build<T>(*sym);
|
|
}
|
|
if (auto* dynsym = binary_->dynamic_symbol_command()) {
|
|
build<T>(*dynsym);
|
|
}
|
|
if (auto* code_signature = binary_->code_signature()) {
|
|
build<T>(*code_signature);
|
|
}
|
|
|
|
const uint64_t original_size = linkedit->file_size();
|
|
const uint64_t new_size = linkedit_.size();
|
|
if (original_size < new_size) {
|
|
LIEF_INFO("Delta __LINKEDIT data size: +{}", new_size - original_size);
|
|
} else if (original_size > new_size) {
|
|
LIEF_INFO("Delta __LINKEDIT data size: -{}", original_size - new_size);
|
|
} else {
|
|
LIEF_INFO("__LINKEDIT data built with the same size: {}", new_size);
|
|
}
|
|
return ok();
|
|
}
|
|
|
|
template<typename T>
|
|
ok_error_t Builder::build_segments() {
|
|
using section_t = typename T::section;
|
|
using segment_t = typename T::segment_command;
|
|
using uint__ = typename T::uint;
|
|
|
|
LIEF_DEBUG("[+] Rebuilding segments");
|
|
Binary* binary = binaries_.back();
|
|
for (SegmentCommand& segment : binary->segments()) {
|
|
LIEF_DEBUG("{}", segment.name());
|
|
segment_t segment_header{};
|
|
|
|
segment_header.cmd = static_cast<uint32_t>(segment.command());
|
|
segment_header.cmdsize = static_cast<uint32_t>(segment.size());
|
|
|
|
std::string_view seg_name = segment.name();
|
|
seg_name.copy(segment_header.segname, sizeof(segment_header.segname));
|
|
|
|
if (LinkEdit::segmentof(segment) && config_.linkedit) {
|
|
segment_header.vmsize =
|
|
static_cast<uint__>(align(linkedit_.size(), binary->page_size()));
|
|
segment_header.filesize = static_cast<uint__>(linkedit_.size());
|
|
} else {
|
|
segment_header.vmsize = static_cast<uint__>(segment.virtual_size());
|
|
segment_header.filesize = static_cast<uint__>(segment.file_size());
|
|
}
|
|
segment_header.vmaddr = static_cast<uint__>(segment.virtual_address());
|
|
segment_header.fileoff = static_cast<uint__>(segment.file_offset());
|
|
segment_header.maxprot = static_cast<uint32_t>(segment.max_protection());
|
|
segment_header.initprot = static_cast<uint32_t>(segment.init_protection());
|
|
segment_header.nsects = static_cast<uint32_t>(segment.numberof_sections());
|
|
segment_header.flags = static_cast<uint32_t>(segment.flags());
|
|
LIEF_DEBUG(" - Command offset: {:#x}", segment.command_offset());
|
|
|
|
span<const uint8_t> content = segment.content();
|
|
if (content.size() != segment.file_size() && !LinkEdit::segmentof(segment)) {
|
|
LIEF_ERR("{} content size and file_size are differents: {:#x} vs {:#x}",
|
|
segment.name(), content.size(), segment.file_size());
|
|
|
|
return make_error_code(lief_errors::build_error);
|
|
}
|
|
|
|
segment.original_data_.clear();
|
|
swap_endian_if_needed(segment_header);
|
|
std::move(reinterpret_cast<uint8_t*>(&segment_header),
|
|
reinterpret_cast<uint8_t*>(&segment_header) + sizeof(segment_t),
|
|
std::back_inserter(segment.original_data_));
|
|
|
|
// --------
|
|
// Sections
|
|
// --------
|
|
if (segment.sections().size() != segment.numberof_sections()) {
|
|
LIEF_ERR("segment.sections().size() != segment.numberof_sections()");
|
|
return make_error_code(lief_errors::build_error);
|
|
}
|
|
|
|
SegmentCommand::it_sections sections = segment.sections();
|
|
for (uint32_t i = 0; i < segment.numberof_sections(); ++i) {
|
|
const Section& section = sections[i];
|
|
std::string_view sec_name = section.name();
|
|
std::string_view segment_name = segment.name();
|
|
LIEF_DEBUG("{}", to_string(section));
|
|
section_t header{};
|
|
|
|
segment_name.copy(header.segname, sizeof(header.segname));
|
|
sec_name.copy(header.sectname, sizeof(header.sectname));
|
|
|
|
header.addr = static_cast<uint__>(section.address());
|
|
header.size = static_cast<uint__>(section.size());
|
|
header.offset = static_cast<uint32_t>(section.offset());
|
|
header.align = static_cast<uint32_t>(section.alignment());
|
|
header.reloff = static_cast<uint32_t>(section.relocation_offset());
|
|
header.nreloc = static_cast<uint32_t>(section.numberof_relocations());
|
|
header.flags = static_cast<uint32_t>(section.raw_flags());
|
|
header.reserved1 = static_cast<uint32_t>(section.reserved1());
|
|
header.reserved2 = static_cast<uint32_t>(section.reserved2());
|
|
|
|
if constexpr (std::is_same_v<section_t, details::section_64>) {
|
|
reinterpret_cast<details::section_64*>(&header)->reserved3 =
|
|
static_cast<uint32_t>(section.reserved3());
|
|
}
|
|
|
|
swap_endian_if_needed(header);
|
|
std::move(reinterpret_cast<uint8_t*>(&header),
|
|
reinterpret_cast<uint8_t*>(&header) + sizeof(section_t),
|
|
std::back_inserter(segment.original_data_));
|
|
}
|
|
}
|
|
return ok();
|
|
} // build_segment
|
|
|
|
|
|
template<typename T>
|
|
ok_error_t Builder::build(DylibCommand& library) {
|
|
LIEF_DEBUG("Build Dylib '{}'", library.name());
|
|
|
|
const uint32_t original_size = library.original_data_.size();
|
|
|
|
const uint32_t raw_size =
|
|
sizeof(details::dylib_command) + library.name().size() + 1;
|
|
const uint32_t size_needed =
|
|
std::max<uint32_t>(align(raw_size, sizeof(typename T::uint)), original_size);
|
|
const uint32_t padding = size_needed - raw_size;
|
|
|
|
if (library.original_data_.size() < size_needed || library.size() < size_needed)
|
|
{
|
|
LIEF_WARN("Not enough spaces to rebuild {}. Size required: {:#x} vs {:#x}",
|
|
library.name(), library.original_data_.size(), size_needed);
|
|
}
|
|
|
|
details::dylib_command raw_cmd{};
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(library.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(size_needed);
|
|
raw_cmd.name = static_cast<uint32_t>(sizeof(details::dylib_command));
|
|
raw_cmd.timestamp = static_cast<uint32_t>(library.timestamp());
|
|
raw_cmd.current_version =
|
|
static_cast<uint32_t>(DylibCommand::version2int(library.current_version()));
|
|
raw_cmd.compatibility_version = static_cast<uint32_t>(
|
|
DylibCommand::version2int(library.compatibility_version())
|
|
);
|
|
|
|
library.size_ = size_needed;
|
|
library.original_data_.clear();
|
|
|
|
// Write Header
|
|
swap_endian_if_needed(raw_cmd);
|
|
std::move(reinterpret_cast<uint8_t*>(&raw_cmd),
|
|
reinterpret_cast<uint8_t*>(&raw_cmd) + sizeof(raw_cmd),
|
|
std::back_inserter(library.original_data_));
|
|
|
|
// Write String
|
|
std::string_view libname = library.name();
|
|
std::move(libname.begin(), libname.end(),
|
|
std::back_inserter(library.original_data_));
|
|
library.original_data_.push_back(0);
|
|
library.original_data_.insert(library.original_data_.end(), padding, 0);
|
|
return ok();
|
|
}
|
|
|
|
|
|
template<typename T>
|
|
ok_error_t Builder::build(DylinkerCommand& linker) {
|
|
|
|
LIEF_DEBUG("Build dylinker '{}'", linker.name());
|
|
|
|
const uint32_t original_size = linker.original_data_.size();
|
|
const uint32_t raw_size =
|
|
sizeof(details::dylinker_command) + linker.name().size() + 1;
|
|
const uint32_t size_needed =
|
|
std::max<uint32_t>(align(raw_size, sizeof(typename T::uint)), original_size);
|
|
const uint32_t padding = size_needed - raw_size;
|
|
|
|
if (linker.original_data_.size() < size_needed || linker.size() < size_needed) {
|
|
LIEF_WARN("Not enough spaces to rebuild {}. Size required: {:#x} vs {:#x}",
|
|
linker.name(), linker.original_data_.size(), size_needed);
|
|
}
|
|
|
|
details::dylinker_command raw_cmd{};
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(linker.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(size_needed);
|
|
raw_cmd.name = static_cast<uint32_t>(sizeof(details::dylinker_command));
|
|
|
|
linker.size_ = size_needed;
|
|
linker.original_data_.clear();
|
|
|
|
// Write Header
|
|
swap_endian_if_needed(raw_cmd);
|
|
std::move(reinterpret_cast<uint8_t*>(&raw_cmd),
|
|
reinterpret_cast<uint8_t*>(&raw_cmd) + sizeof(raw_cmd),
|
|
std::back_inserter(linker.original_data_));
|
|
|
|
// Write String
|
|
std::string_view linkpath = linker.name();
|
|
std::move(linkpath.begin(), linkpath.end(),
|
|
std::back_inserter(linker.original_data_));
|
|
|
|
linker.original_data_.push_back(0);
|
|
linker.original_data_.insert(linker.original_data_.end(), padding, 0);
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(VersionMin& version_min) {
|
|
LIEF_DEBUG("Build '{}'", to_string(version_min.command()));
|
|
const uint32_t raw_size = sizeof(details::version_min_command);
|
|
const uint32_t size_needed = align(raw_size, sizeof(typename T::uint));
|
|
const uint32_t padding = size_needed - raw_size;
|
|
|
|
details::version_min_command raw_cmd{};
|
|
|
|
const VersionMin::version_t& version = version_min.version();
|
|
const VersionMin::version_t& sdk = version_min.sdk();
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(version_min.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(version_min.size());
|
|
raw_cmd.version =
|
|
static_cast<uint32_t>(version[0] << 16 | version[1] << 8 | version[2]);
|
|
raw_cmd.sdk = static_cast<uint32_t>(sdk[0] << 16 | sdk[1] << 8 | sdk[2]);
|
|
|
|
version_min.size_ = sizeof(details::version_min_command);
|
|
version_min.original_data_.clear();
|
|
swap_endian_if_needed(raw_cmd);
|
|
std::move(reinterpret_cast<uint8_t*>(&raw_cmd),
|
|
reinterpret_cast<uint8_t*>(&raw_cmd) +
|
|
sizeof(details::version_min_command),
|
|
std::back_inserter(version_min.original_data_));
|
|
version_min.original_data_.insert(version_min.original_data_.end(), padding, 0);
|
|
return ok();
|
|
}
|
|
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(SourceVersion& source_version) {
|
|
LIEF_DEBUG("Build '{}'", to_string(source_version.command()));
|
|
const uint32_t raw_size = sizeof(details::source_version_command);
|
|
const uint32_t size_needed = align(raw_size, sizeof(typename T::uint));
|
|
const uint32_t padding = size_needed - raw_size;
|
|
|
|
details::source_version_command raw_cmd{};
|
|
|
|
const SourceVersion::version_t& version = source_version.version();
|
|
raw_cmd.cmd = static_cast<uint32_t>(source_version.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(source_version.size());
|
|
raw_cmd.version = static_cast<uint64_t>(static_cast<uint64_t>(version[0]) << 40 |
|
|
static_cast<uint64_t>(version[1]) << 30 |
|
|
static_cast<uint64_t>(version[2]) << 20 |
|
|
static_cast<uint64_t>(version[3]) << 10 |
|
|
static_cast<uint64_t>(version[4]));
|
|
|
|
source_version.size_ = sizeof(details::source_version_command);
|
|
source_version.original_data_.clear();
|
|
swap_endian_if_needed(raw_cmd);
|
|
std::move(reinterpret_cast<uint8_t*>(&raw_cmd),
|
|
reinterpret_cast<uint8_t*>(&raw_cmd) +
|
|
sizeof(details::source_version_command),
|
|
std::back_inserter(source_version.original_data_));
|
|
source_version.original_data_.insert(source_version.original_data_.end(),
|
|
padding, 0);
|
|
return ok();
|
|
}
|
|
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(RPathCommand& rpath_cmd) {
|
|
LIEF_DEBUG("Build '{}'", to_string(rpath_cmd.command()));
|
|
|
|
const uint32_t original_size = rpath_cmd.original_data_.size();
|
|
|
|
const uint32_t raw_size =
|
|
sizeof(details::rpath_command) + rpath_cmd.path().size() + 1;
|
|
const uint32_t size_needed =
|
|
std::max<uint32_t>(align(raw_size, sizeof(typename T::uint)), original_size);
|
|
const uint32_t padding = size_needed - raw_size;
|
|
|
|
if (rpath_cmd.original_data_.size() < size_needed ||
|
|
rpath_cmd.size() < size_needed)
|
|
{
|
|
LIEF_WARN("Not enough room left to rebuild {} "
|
|
"required={:#x} available={:#x}",
|
|
rpath_cmd.path(), size_needed, rpath_cmd.original_data_.size());
|
|
}
|
|
|
|
|
|
details::rpath_command raw_cmd{};
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(rpath_cmd.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(size_needed);
|
|
raw_cmd.path = static_cast<uint32_t>(sizeof(details::rpath_command));
|
|
|
|
rpath_cmd.size_ = size_needed;
|
|
rpath_cmd.original_data_.clear();
|
|
|
|
// Write Header
|
|
swap_endian_if_needed(raw_cmd);
|
|
std::move(reinterpret_cast<uint8_t*>(&raw_cmd),
|
|
reinterpret_cast<uint8_t*>(&raw_cmd) + sizeof(raw_cmd),
|
|
std::back_inserter(rpath_cmd.original_data_));
|
|
|
|
// Write String
|
|
std::string_view rpath = rpath_cmd.path();
|
|
std::move(rpath.begin(), rpath.end(),
|
|
std::back_inserter(rpath_cmd.original_data_));
|
|
rpath_cmd.original_data_.push_back(0);
|
|
rpath_cmd.original_data_.insert(rpath_cmd.original_data_.end(), padding, 0);
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(Routine& routine) {
|
|
using routine_t = typename T::routines_command;
|
|
using uint__ = typename T::uint;
|
|
LIEF_DEBUG("Build '{}'", to_string(routine.command()));
|
|
|
|
routine_t raw_cmd{};
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(routine.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(routine.size());
|
|
|
|
raw_cmd.init_address = static_cast<uint__>(routine.init_address());
|
|
raw_cmd.init_module = static_cast<uint__>(routine.init_module());
|
|
raw_cmd.reserved1 = static_cast<uint__>(routine.reserved1());
|
|
raw_cmd.reserved2 = static_cast<uint__>(routine.reserved2());
|
|
raw_cmd.reserved3 = static_cast<uint__>(routine.reserved3());
|
|
raw_cmd.reserved4 = static_cast<uint__>(routine.reserved4());
|
|
raw_cmd.reserved5 = static_cast<uint__>(routine.reserved5());
|
|
raw_cmd.reserved6 = static_cast<uint__>(routine.reserved6());
|
|
|
|
routine.size_ = sizeof(routine_t);
|
|
routine.original_data_.clear();
|
|
swap_endian_if_needed(raw_cmd);
|
|
std::move(reinterpret_cast<uint8_t*>(&raw_cmd),
|
|
reinterpret_cast<uint8_t*>(&raw_cmd) + sizeof(routine_t),
|
|
std::back_inserter(routine.original_data_));
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(MainCommand& main_cmd) {
|
|
LIEF_DEBUG("Build '{}'", to_string(main_cmd.command()));
|
|
const uint32_t raw_size = sizeof(details::entry_point_command);
|
|
const uint32_t size_needed = align(raw_size, sizeof(typename T::uint));
|
|
const uint32_t padding = size_needed - raw_size;
|
|
|
|
details::entry_point_command raw_cmd{};
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(main_cmd.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(main_cmd.size());
|
|
raw_cmd.entryoff = static_cast<uint64_t>(main_cmd.entrypoint());
|
|
raw_cmd.stacksize = static_cast<uint64_t>(main_cmd.stack_size());
|
|
|
|
main_cmd.size_ = sizeof(details::entry_point_command);
|
|
main_cmd.original_data_.clear();
|
|
swap_endian_if_needed(raw_cmd);
|
|
std::move(reinterpret_cast<uint8_t*>(&raw_cmd),
|
|
reinterpret_cast<uint8_t*>(&raw_cmd) +
|
|
sizeof(details::entry_point_command),
|
|
std::back_inserter(main_cmd.original_data_));
|
|
main_cmd.original_data_.insert(main_cmd.original_data_.end(), padding, 0);
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(NoteCommand& note) {
|
|
LIEF_DEBUG("Build '{}'", to_string(note.command()));
|
|
details::note_command raw_cmd{};
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(note.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(note.size());
|
|
|
|
raw_cmd.offset = static_cast<uint32_t>(note.note_offset());
|
|
raw_cmd.size = static_cast<uint32_t>(note.note_size());
|
|
|
|
span<const char> owner = note.owner();
|
|
std::copy(std::begin(owner), std::end(owner), std::begin(raw_cmd.data_owner));
|
|
|
|
note.size_ = sizeof(details::note_command);
|
|
|
|
std::fill(note.original_data_.begin(), note.original_data_.end(), 0);
|
|
|
|
swap_endian_if_needed(raw_cmd);
|
|
std::copy(reinterpret_cast<uint8_t*>(&raw_cmd),
|
|
reinterpret_cast<uint8_t*>(&raw_cmd) + sizeof(raw_cmd),
|
|
reinterpret_cast<uint8_t*>(note.original_data_.data()));
|
|
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(DyldInfo& dyld_info) {
|
|
LIEF_DEBUG("Build '{}'", to_string(dyld_info.command()));
|
|
|
|
// WARNING: Force update of the relocation cache used by the following functions
|
|
// TODO(romain): This looks like a hack
|
|
binary_->relocations();
|
|
|
|
details::dyld_info_command raw_cmd{};
|
|
{
|
|
LIEF_DEBUG("linkedit_.size(): {:x}", linkedit_.size());
|
|
raw_cmd.rebase_off = linkedit_.size();
|
|
{
|
|
dyld_info.update_rebase_info(linkedit_);
|
|
}
|
|
raw_cmd.rebase_size = linkedit_.size() - raw_cmd.rebase_off;
|
|
if (raw_cmd.rebase_size > 0) {
|
|
raw_cmd.rebase_off += linkedit_offset_;
|
|
}
|
|
|
|
LIEF_DEBUG("LC_DYLD_INFO.rebase_off : {:#08x} -> {:#08x}",
|
|
dyld_info.rebase().first, raw_cmd.rebase_off);
|
|
|
|
LIEF_DEBUG("LC_DYLD_INFO.rebase_size: {:#08x} -> {:#08x}",
|
|
dyld_info.rebase().second, raw_cmd.rebase_size);
|
|
}
|
|
{
|
|
dyld_info.update_binding_info(linkedit_, raw_cmd);
|
|
if (raw_cmd.bind_size > 0) {
|
|
raw_cmd.bind_off += linkedit_offset_;
|
|
|
|
LIEF_DEBUG("LC_DYLD_INFO.bind_off : {:#08x} -> {:#08x}",
|
|
dyld_info.bind().first, raw_cmd.bind_off);
|
|
|
|
LIEF_DEBUG("LC_DYLD_INFO.bind_size: {:#08x} -> {:#08x}",
|
|
dyld_info.bind().second, raw_cmd.bind_size);
|
|
}
|
|
|
|
if (raw_cmd.weak_bind_size > 0) {
|
|
raw_cmd.weak_bind_off += linkedit_offset_;
|
|
|
|
LIEF_DEBUG("LC_DYLD_INFO.weak_bind_off : {:#08x} -> {:#08x}",
|
|
dyld_info.weak_bind().first, raw_cmd.weak_bind_off);
|
|
|
|
LIEF_DEBUG("LC_DYLD_INFO.weak_bind_size: {:#08x} -> {:#08x}",
|
|
dyld_info.weak_bind().second, raw_cmd.weak_bind_size);
|
|
}
|
|
|
|
if (raw_cmd.lazy_bind_size > 0) {
|
|
raw_cmd.lazy_bind_off += linkedit_offset_;
|
|
|
|
LIEF_DEBUG("LC_DYLD_INFO.lazy_bind_off : {:#08x} -> {:#08x}",
|
|
dyld_info.lazy_bind().first, raw_cmd.lazy_bind_off);
|
|
|
|
LIEF_DEBUG("LC_DYLD_INFO.lazy_bind_size: {:#08x} -> {:#08x}",
|
|
dyld_info.lazy_bind().second, raw_cmd.lazy_bind_size);
|
|
}
|
|
}
|
|
{
|
|
raw_cmd.export_off = linkedit_.size();
|
|
{
|
|
dyld_info.update_export_trie(linkedit_);
|
|
}
|
|
raw_cmd.export_size = linkedit_.size() - raw_cmd.export_off;
|
|
if (raw_cmd.export_size > 0) {
|
|
raw_cmd.export_off += linkedit_offset_;
|
|
}
|
|
LIEF_DEBUG("LC_DYLD_INFO.exports_off : {:#08x} -> {:#08x}",
|
|
dyld_info.export_info().first, raw_cmd.export_off);
|
|
|
|
LIEF_DEBUG("LC_DYLD_INFO.exports_size: {:#08x} -> {:#08x}",
|
|
dyld_info.export_info().second, raw_cmd.export_size);
|
|
}
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(dyld_info.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(dyld_info.size());
|
|
|
|
dyld_info.size_ = sizeof(details::dyld_info_command);
|
|
dyld_info.original_data_.clear();
|
|
dyld_info.original_data_.resize(dyld_info.size_);
|
|
swap_endian_if_needed(raw_cmd);
|
|
memcpy(dyld_info.original_data_.data(), &raw_cmd,
|
|
sizeof(details::dyld_info_command));
|
|
return ok();
|
|
}
|
|
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(FunctionStarts& function_starts) {
|
|
LIEF_DEBUG("Build '{}'", to_string(function_starts.command()));
|
|
details::linkedit_data_command raw_cmd{};
|
|
raw_cmd.dataoff = linkedit_.size();
|
|
|
|
uint64_t prev_address = 0;
|
|
for (uint64_t address : function_starts.functions()) {
|
|
uint64_t delta = address - prev_address;
|
|
linkedit_.write_uleb128(delta);
|
|
prev_address = address;
|
|
}
|
|
linkedit_.align(sizeof(typename T::uint));
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(function_starts.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(function_starts.size());
|
|
raw_cmd.datasize = linkedit_.size() - raw_cmd.dataoff;
|
|
raw_cmd.dataoff += linkedit_offset_;
|
|
|
|
|
|
LIEF_DEBUG("LC_FUNCTION_STARTS.offset: {:#08x} -> {:#x}",
|
|
function_starts.data_offset(), raw_cmd.dataoff);
|
|
LIEF_DEBUG("LC_FUNCTION_STARTS.size: {:#08x} -> {:#x}",
|
|
function_starts.data_size(), raw_cmd.datasize);
|
|
|
|
function_starts.size_ = sizeof(details::linkedit_data_command);
|
|
function_starts.original_data_.clear();
|
|
function_starts.original_data_.resize(function_starts.size_);
|
|
swap_endian_if_needed(raw_cmd);
|
|
memcpy(function_starts.original_data_.data(), &raw_cmd,
|
|
sizeof(details::linkedit_data_command));
|
|
return ok();
|
|
}
|
|
|
|
template<class MACHO_T>
|
|
inline ok_error_t
|
|
write_symbol(vector_iostream& nlist_table, Symbol& sym,
|
|
std::unordered_map<std::string_view, size_t>& offset_name_map) {
|
|
using nlist_t = typename MACHO_T::nlist;
|
|
const std::string& name = sym.name();
|
|
const auto it_name = offset_name_map.find(name);
|
|
|
|
if (it_name == offset_name_map.end()) {
|
|
LIEF_WARN("Can't find name offset for symbol {}", sym.name());
|
|
return make_error_code(lief_errors::not_found);
|
|
}
|
|
|
|
nlist_t nl;
|
|
nl.n_strx = static_cast<uint32_t>(it_name->second);
|
|
nl.n_type = static_cast<uint8_t>(sym.raw_type());
|
|
nl.n_sect = static_cast<uint32_t>(sym.numberof_sections());
|
|
nl.n_desc = static_cast<uint16_t>(sym.description());
|
|
nl.n_value = static_cast<typename MACHO_T::uint>(sym.value());
|
|
|
|
nlist_table.write(nl);
|
|
return ok();
|
|
}
|
|
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(SymbolCommand& symbol_command) {
|
|
// template <typename A>
|
|
// void SymbolTableAtom<A>::encode()
|
|
//{
|
|
// // Note: We lay out the symbol table so that the strings for the stabs
|
|
// (local) symbols are at the
|
|
// // end of the string pool. The stabs strings are not used when calculating
|
|
// the UUID for the image.
|
|
// // If the stabs strings were not last, the string offsets for all other
|
|
// symbols may vary which would alter the UUID.
|
|
//
|
|
// // reserve space for local symbols
|
|
// +---------------------+
|
|
// | |
|
|
// | symtab_command |
|
|
// | |
|
|
// +---------------------+
|
|
// | |
|
|
// | n_list |
|
|
// | |
|
|
// +---------------------+
|
|
// | |
|
|
// | string table |
|
|
// | |
|
|
// +---------------------+
|
|
|
|
using nlist_t = typename T::nlist;
|
|
|
|
std::vector<Symbol*> local_syms;
|
|
std::vector<Symbol*> ext_syms;
|
|
std::vector<Symbol*> undef_syms;
|
|
std::vector<Symbol*> other_syms;
|
|
std::vector<Symbol*> all_syms;
|
|
std::map<Symbol*, uint32_t> indirect_symbols;
|
|
|
|
std::vector<uint8_t> strtab;
|
|
std::vector<uint8_t> raw_nlist_table;
|
|
std::unordered_map<std::string_view, size_t> offset_name_map;
|
|
|
|
details::symtab_command symtab{};
|
|
DynamicSymbolCommand* dynsym = binary_->dynamic_symbol_command();
|
|
|
|
/* 1. Fill the n_list table */ {
|
|
for (Symbol& s : binary_->symbols()) {
|
|
if (s.origin() != Symbol::ORIGIN::SYMTAB) {
|
|
continue;
|
|
}
|
|
all_syms.push_back(&s);
|
|
switch (s.category()) {
|
|
case Symbol::CATEGORY::NONE: other_syms.push_back(&s); break;
|
|
case Symbol::CATEGORY::LOCAL: local_syms.push_back(&s); break;
|
|
case Symbol::CATEGORY::EXTERNAL: ext_syms.push_back(&s); break;
|
|
case Symbol::CATEGORY::UNDEFINED: undef_syms.push_back(&s); break;
|
|
|
|
case Symbol::CATEGORY::INDIRECT_ABS:
|
|
case Symbol::CATEGORY::INDIRECT_LOCAL:
|
|
case Symbol::CATEGORY::INDIRECT_ABS_LOCAL:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t offset_counter = 1;
|
|
std::vector<std::string> string_table_opt = optimize(
|
|
all_syms,
|
|
[](Symbol* const& sym) { return static_cast<const Symbol*>(sym)->name(); },
|
|
offset_counter, &offset_name_map
|
|
);
|
|
all_syms.clear();
|
|
|
|
// 0 index is reserved
|
|
vector_iostream raw_symbol_names;
|
|
raw_symbol_names.write<uint8_t>(0);
|
|
for (std::string_view name : string_table_opt) {
|
|
raw_symbol_names.write(name);
|
|
}
|
|
raw_symbol_names.align(8);
|
|
strtab = raw_symbol_names.raw();
|
|
}
|
|
|
|
/* 2. Fill the n_list table */ {
|
|
const size_t nb_symbols = local_syms.size() + ext_syms.size() +
|
|
undef_syms.size() + other_syms.size();
|
|
vector_iostream nlist_table;
|
|
nlist_table.set_endian_swap(should_swap());
|
|
nlist_table.reserve(nb_symbols * sizeof(nlist_t));
|
|
|
|
size_t isym = 0;
|
|
/* Local Symbols */ {
|
|
if (dynsym != nullptr) {
|
|
dynsym->idx_local_symbol(isym);
|
|
}
|
|
for (Symbol* sym : local_syms) {
|
|
indirect_symbols[sym] = isym;
|
|
write_symbol<T>(nlist_table, *sym, offset_name_map);
|
|
++isym;
|
|
}
|
|
if (dynsym != nullptr) {
|
|
dynsym->nb_local_symbols(local_syms.size());
|
|
}
|
|
}
|
|
|
|
/* External Symbols */ {
|
|
if (dynsym != nullptr) {
|
|
dynsym->idx_external_define_symbol(isym);
|
|
}
|
|
for (Symbol* sym : ext_syms) {
|
|
indirect_symbols[sym] = isym;
|
|
write_symbol<T>(nlist_table, *sym, offset_name_map);
|
|
++isym;
|
|
}
|
|
if (dynsym != nullptr) {
|
|
dynsym->nb_external_define_symbols(ext_syms.size());
|
|
}
|
|
}
|
|
/* Undefined Symbols */ {
|
|
if (dynsym != nullptr) {
|
|
dynsym->idx_undefined_symbol(isym);
|
|
}
|
|
for (Symbol* sym : undef_syms) {
|
|
indirect_symbols[sym] = isym;
|
|
write_symbol<T>(nlist_table, *sym, offset_name_map);
|
|
++isym;
|
|
}
|
|
if (dynsym != nullptr) {
|
|
dynsym->nb_undefined_symbols(undef_syms.size());
|
|
}
|
|
}
|
|
|
|
/* The other symbols [...] */ {
|
|
for (Symbol* sym : other_syms) {
|
|
indirect_symbols[sym] = isym;
|
|
write_symbol<T>(nlist_table, *sym, offset_name_map);
|
|
++isym;
|
|
}
|
|
}
|
|
nlist_table.align(binary_->is64_ ? 8 : 4);
|
|
|
|
raw_nlist_table = nlist_table.raw();
|
|
symtab.symoff = linkedit_offset_ + linkedit_.size();
|
|
symtab.nsyms = nb_symbols;
|
|
LIEF_DEBUG("LC_SYMTAB.nlist: {:#08x} -> {:#x}",
|
|
symbol_command.symbol_offset(), symtab.symoff);
|
|
LIEF_DEBUG("LC_SYMTAB.nb_symbols: {:#08x} -> {:#x}",
|
|
symbol_command.numberof_symbols(), symtab.nsyms);
|
|
linkedit_.write(raw_nlist_table);
|
|
}
|
|
/*
|
|
* Two Level Hints
|
|
*/
|
|
if (auto* two = binary_->two_level_hints()) {
|
|
build<T>(*two);
|
|
}
|
|
/*
|
|
* Indirect symbol table
|
|
*/
|
|
if (dynsym != nullptr) {
|
|
LIEF_DEBUG("LC_DYSYMTAB.indirectsymoff: {:#08x} -> {:#x}",
|
|
dynsym->indirect_symbol_offset(),
|
|
linkedit_offset_ + linkedit_.size());
|
|
dynsym->indirect_symbol_offset(linkedit_offset_ + linkedit_.size());
|
|
size_t count = 0;
|
|
for (Symbol* sym : dynsym->indirect_symbols_) {
|
|
if (sym->category() == Symbol::CATEGORY::INDIRECT_ABS) {
|
|
linkedit_.write(details::INDIRECT_SYMBOL_ABS);
|
|
++count;
|
|
continue;
|
|
}
|
|
|
|
if (sym->category() == Symbol::CATEGORY::INDIRECT_LOCAL) {
|
|
linkedit_.write(details::INDIRECT_SYMBOL_LOCAL);
|
|
++count;
|
|
continue;
|
|
}
|
|
|
|
if (sym->category() == Symbol::CATEGORY::INDIRECT_ABS_LOCAL) {
|
|
linkedit_.write(details::INDIRECT_SYMBOL_LOCAL |
|
|
details::INDIRECT_SYMBOL_ABS);
|
|
++count;
|
|
continue;
|
|
}
|
|
|
|
if (auto it_idx = indirect_symbols.find(sym);
|
|
it_idx != indirect_symbols.end())
|
|
{
|
|
linkedit_.write(it_idx->second);
|
|
++count;
|
|
} else {
|
|
LIEF_ERR("Can't find the symbol index");
|
|
}
|
|
}
|
|
LIEF_DEBUG("LC_DYSYMTAB.nindirectsyms: {:#08x} -> {:#x}",
|
|
dynsym->nb_indirect_symbols(), count);
|
|
dynsym->nb_indirect_symbols(count);
|
|
}
|
|
|
|
symtab.stroff = linkedit_offset_ + linkedit_.size();
|
|
symtab.strsize = strtab.size();
|
|
LIEF_DEBUG("LC_SYMTAB.strtab.offset: {:#08x} -> {:#x}",
|
|
symbol_command.strings_offset(), symtab.stroff);
|
|
LIEF_DEBUG("LC_SYMTAB.strtab.size: {:#08x} -> {:#x}",
|
|
symbol_command.strings_size(), symtab.strsize);
|
|
|
|
linkedit_.write(strtab);
|
|
|
|
symtab.cmd = static_cast<uint32_t>(symbol_command.command());
|
|
symtab.cmdsize = static_cast<uint32_t>(symbol_command.size());
|
|
|
|
symbol_command.original_data_.clear();
|
|
symbol_command.original_data_.resize(sizeof(details::symtab_command));
|
|
|
|
swap_endian_if_needed(symtab);
|
|
std::memcpy(symbol_command.original_data_.data(), &symtab,
|
|
sizeof(details::symtab_command));
|
|
|
|
return ok();
|
|
}
|
|
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(DynamicSymbolCommand& symbol_command) {
|
|
details::dysymtab_command rawcmd{};
|
|
|
|
rawcmd.cmd = static_cast<uint32_t>(symbol_command.command());
|
|
rawcmd.cmdsize = static_cast<uint32_t>(symbol_command.size());
|
|
|
|
rawcmd.ilocalsym = static_cast<uint32_t>(symbol_command.idx_local_symbol());
|
|
rawcmd.nlocalsym = static_cast<uint32_t>(symbol_command.nb_local_symbols());
|
|
|
|
rawcmd.iextdefsym =
|
|
static_cast<uint32_t>(symbol_command.idx_external_define_symbol());
|
|
rawcmd.nextdefsym =
|
|
static_cast<uint32_t>(symbol_command.nb_external_define_symbols());
|
|
|
|
rawcmd.iundefsym = static_cast<uint32_t>(symbol_command.idx_undefined_symbol());
|
|
rawcmd.nundefsym = static_cast<uint32_t>(symbol_command.nb_undefined_symbols());
|
|
|
|
rawcmd.indirectsymoff =
|
|
static_cast<uint32_t>(symbol_command.indirect_symbol_offset());
|
|
rawcmd.nindirectsyms =
|
|
static_cast<uint32_t>(symbol_command.nb_indirect_symbols());
|
|
|
|
rawcmd.tocoff = static_cast<uint32_t>(symbol_command.toc_offset());
|
|
rawcmd.ntoc = static_cast<uint32_t>(symbol_command.nb_toc());
|
|
rawcmd.modtaboff = static_cast<uint32_t>(symbol_command.module_table_offset());
|
|
rawcmd.nmodtab = static_cast<uint32_t>(symbol_command.nb_module_table());
|
|
rawcmd.extrefsymoff =
|
|
static_cast<uint32_t>(symbol_command.external_reference_symbol_offset());
|
|
rawcmd.nextrefsyms =
|
|
static_cast<uint32_t>(symbol_command.nb_external_reference_symbols());
|
|
rawcmd.extreloff =
|
|
static_cast<uint32_t>(symbol_command.external_relocation_offset());
|
|
rawcmd.nextrel = static_cast<uint32_t>(symbol_command.nb_external_relocations());
|
|
rawcmd.locreloff =
|
|
static_cast<uint32_t>(symbol_command.local_relocation_offset());
|
|
rawcmd.nlocrel = static_cast<uint32_t>(symbol_command.nb_local_relocations());
|
|
|
|
symbol_command.original_data_.clear();
|
|
symbol_command.original_data_.resize(sizeof(details::dysymtab_command));
|
|
swap_endian_if_needed(rawcmd);
|
|
memcpy(symbol_command.original_data_.data(), &rawcmd,
|
|
sizeof(details::dysymtab_command));
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(DataInCode& datacode) {
|
|
LIEF_DEBUG("Build '{}'", to_string(datacode.command()));
|
|
|
|
details::linkedit_data_command raw_cmd{};
|
|
|
|
span<const uint8_t> raw_content = datacode.content();
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(datacode.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(datacode.size());
|
|
raw_cmd.dataoff = linkedit_.size();
|
|
|
|
for (const DataCodeEntry& entry : datacode.entries()) {
|
|
details::data_in_code_entry e{};
|
|
e.offset = entry.offset();
|
|
e.length = entry.length();
|
|
e.kind = static_cast<decltype(e.kind)>(entry.type());
|
|
linkedit_.write(e);
|
|
}
|
|
raw_cmd.datasize = linkedit_.size() - raw_cmd.dataoff;
|
|
raw_cmd.dataoff += linkedit_offset_;
|
|
|
|
LIEF_DEBUG("LC_DATA_IN_CODE.offset: {:#08x} -> {:#x}", datacode.data_offset(),
|
|
raw_cmd.dataoff);
|
|
LIEF_DEBUG("LC_DATA_IN_CODE.size: {:#08x} -> {:#x}", datacode.data_size(),
|
|
raw_content.size());
|
|
|
|
datacode.size_ = sizeof(details::linkedit_data_command);
|
|
datacode.original_data_.clear();
|
|
datacode.original_data_.resize(datacode.size_);
|
|
|
|
swap_endian_if_needed(raw_cmd);
|
|
memcpy(datacode.original_data_.data(), &raw_cmd,
|
|
sizeof(details::linkedit_data_command));
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(CodeSignature& code_signature) {
|
|
LIEF_DEBUG("Build '{}'", to_string(code_signature.command()));
|
|
|
|
details::linkedit_data_command raw_cmd{};
|
|
|
|
span<const uint8_t> sp = code_signature.content();
|
|
|
|
/*
|
|
* The code signature Payload requires to be aligned on 16-bits
|
|
*/
|
|
linkedit_.align(16);
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(code_signature.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(code_signature.size());
|
|
raw_cmd.dataoff = linkedit_offset_ + linkedit_.size();
|
|
raw_cmd.datasize = sp.size();
|
|
|
|
LIEF_DEBUG("LC_CODE_SIGNATURE.offset: {:#08x} -> {:#x}",
|
|
code_signature.data_offset(), raw_cmd.dataoff);
|
|
LIEF_DEBUG("LC_CODE_SIGNATURE.size: {:#08x} -> {:#x}",
|
|
code_signature.data_size(), raw_cmd.datasize);
|
|
linkedit_.write(sp.data(), sp.size());
|
|
|
|
code_signature.size_ = sizeof(details::linkedit_data_command);
|
|
code_signature.original_data_.clear();
|
|
code_signature.original_data_.resize(code_signature.size_);
|
|
|
|
swap_endian_if_needed(raw_cmd);
|
|
memcpy(code_signature.original_data_.data(), &raw_cmd,
|
|
sizeof(details::linkedit_data_command));
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(SegmentSplitInfo& ssi) {
|
|
LIEF_DEBUG("Build '{}'", to_string(ssi.command()));
|
|
|
|
details::linkedit_data_command raw_cmd{};
|
|
|
|
span<const uint8_t> raw_content = ssi.content();
|
|
|
|
LIEF_DEBUG("LC_SEGMENT_SPLIT_INFO.offset: {:#08x} -> {:#x}", ssi.data_offset(),
|
|
linkedit_offset_ + linkedit_.size());
|
|
LIEF_DEBUG("LC_SEGMENT_SPLIT_INFO.size: {:#08x} -> {:#x}", ssi.data_size(),
|
|
raw_content.size());
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(ssi.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(ssi.size());
|
|
raw_cmd.dataoff = linkedit_offset_ + linkedit_.size();
|
|
raw_cmd.datasize = raw_content.size();
|
|
|
|
linkedit_.write(raw_content.data(), raw_content.size());
|
|
|
|
ssi.size_ = sizeof(details::linkedit_data_command);
|
|
ssi.original_data_.clear();
|
|
ssi.original_data_.resize(ssi.size_);
|
|
|
|
swap_endian_if_needed(raw_cmd);
|
|
memcpy(ssi.original_data_.data(), &raw_cmd,
|
|
sizeof(details::linkedit_data_command));
|
|
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(SubFramework& sf) {
|
|
LIEF_DEBUG("Build '{}'", to_string(sf.command()));
|
|
details::sub_framework_command raw_cmd{};
|
|
|
|
const uint32_t original_size = sf.original_data_.size();
|
|
|
|
const uint32_t raw_size =
|
|
sizeof(details::sub_framework_command) + sf.umbrella().size() + 1;
|
|
const auto size_needed =
|
|
std::max<uint32_t>(align(raw_size, sizeof(typename T::uint)), original_size);
|
|
|
|
const uint32_t padding = size_needed - raw_size;
|
|
|
|
if (sf.original_data_.size() < size_needed || sf.size() < size_needed) {
|
|
LIEF_WARN("Not enough spaces to rebuild '{}'. Size required: {:#x} vs {:#x}",
|
|
sf.umbrella(), size_needed, sf.original_data_.size());
|
|
}
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(sf.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(size_needed);
|
|
raw_cmd.umbrella = static_cast<uint32_t>(sizeof(details::sub_framework_command));
|
|
|
|
sf.size_ = size_needed;
|
|
sf.original_data_.clear();
|
|
|
|
// Write Header
|
|
swap_endian_if_needed(raw_cmd);
|
|
std::move(reinterpret_cast<uint8_t*>(&raw_cmd),
|
|
reinterpret_cast<uint8_t*>(&raw_cmd) + sizeof(raw_cmd),
|
|
std::back_inserter(sf.original_data_));
|
|
|
|
// Write String
|
|
std::string_view um = sf.umbrella();
|
|
std::move(um.begin(), um.end(), std::back_inserter(sf.original_data_));
|
|
sf.original_data_.push_back(0);
|
|
sf.original_data_.insert(sf.original_data_.end(), padding, 0);
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(SubClient& sc) {
|
|
LIEF_DEBUG("Build '{}'", to_string(sc.command()));
|
|
details::sub_client_command raw_cmd{};
|
|
|
|
const uint32_t original_size = sc.original_data_.size();
|
|
|
|
const uint32_t raw_size =
|
|
sizeof(details::sub_client_command) + sc.client().size() + 1;
|
|
const uint32_t size_needed =
|
|
std::max<uint32_t>(align(raw_size, sizeof(typename T::uint)), original_size);
|
|
const uint32_t padding = size_needed - raw_size;
|
|
|
|
if (sc.original_data_.size() < size_needed || sc.size() < size_needed) {
|
|
LIEF_WARN("Not enough spaces to rebuild '{}'. Size required: {:#x} vs {:#x}",
|
|
sc.client(), size_needed, sc.original_data_.size());
|
|
}
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(sc.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(size_needed);
|
|
raw_cmd.client = static_cast<uint32_t>(sizeof(details::sub_client_command));
|
|
|
|
sc.size_ = size_needed;
|
|
sc.original_data_.clear();
|
|
|
|
// Write Header
|
|
swap_endian_if_needed(raw_cmd);
|
|
std::move(reinterpret_cast<uint8_t*>(&raw_cmd),
|
|
reinterpret_cast<uint8_t*>(&raw_cmd) + sizeof(raw_cmd),
|
|
std::back_inserter(sc.original_data_));
|
|
|
|
// Write String
|
|
std::string_view um = sc.client();
|
|
std::move(um.begin(), um.end(), std::back_inserter(sc.original_data_));
|
|
sc.original_data_.push_back(0);
|
|
sc.original_data_.insert(sc.original_data_.end(), padding, 0);
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(DyldEnvironment& de) {
|
|
details::dylinker_command raw_cmd{};
|
|
|
|
const uint32_t original_size = de.original_data_.size();
|
|
|
|
const uint32_t raw_size =
|
|
sizeof(details::dylinker_command) + de.value().size() + 1;
|
|
const uint32_t size_needed =
|
|
std::max<uint32_t>(align(raw_size, sizeof(typename T::uint)), original_size);
|
|
const uint32_t padding = size_needed - raw_size;
|
|
|
|
if (de.original_data_.size() < size_needed || de.size() < size_needed) {
|
|
LIEF_WARN("Not enough spaces to rebuild {}. Size required: {:#x} vs {:#x}",
|
|
de.value(), de.original_data_.size(), size_needed);
|
|
}
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(de.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(size_needed);
|
|
raw_cmd.name = static_cast<uint32_t>(sizeof(details::dylinker_command));
|
|
|
|
de.size_ = size_needed;
|
|
de.original_data_.clear();
|
|
|
|
// Write Header
|
|
swap_endian_if_needed(raw_cmd);
|
|
std::move(reinterpret_cast<uint8_t*>(&raw_cmd),
|
|
reinterpret_cast<uint8_t*>(&raw_cmd) + sizeof(raw_cmd),
|
|
std::back_inserter(de.original_data_));
|
|
|
|
// Write String
|
|
std::string_view value = de.value();
|
|
std::move(value.begin(), value.end(), std::back_inserter(de.original_data_));
|
|
|
|
de.original_data_.push_back(0);
|
|
de.original_data_.insert(de.original_data_.end(), padding, 0);
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(ThreadCommand& tc) {
|
|
details::thread_command raw_cmd{};
|
|
|
|
const span<const uint8_t> state = tc.state();
|
|
|
|
const uint32_t raw_size = sizeof(details::thread_command) + state.size();
|
|
const uint32_t size_needed = align(raw_size, sizeof(typename T::uint));
|
|
const uint32_t padding = size_needed - raw_size;
|
|
|
|
if (tc.original_data_.size() < size_needed || tc.size() < size_needed) {
|
|
LIEF_WARN("Not enough spaces to rebuild 'ThreadCommand'. Size required: {:#x} "
|
|
"vs {:#x}",
|
|
tc.original_data_.size(), size_needed);
|
|
}
|
|
|
|
const uint32_t state_size_needed = tc.count() * sizeof(uint32_t);
|
|
if (state.size() < state_size_needed) {
|
|
|
|
LIEF_WARN("Not enough spaces to rebuild 'ThreadCommand'. Size required: {:#x} "
|
|
"vs {:#x}",
|
|
state.size(), state_size_needed);
|
|
}
|
|
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(tc.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(size_needed);
|
|
raw_cmd.flavor = static_cast<uint32_t>(tc.flavor());
|
|
raw_cmd.count = static_cast<uint32_t>(tc.count());
|
|
|
|
tc.size_ = size_needed;
|
|
tc.original_data_.clear();
|
|
|
|
// Write Header
|
|
swap_endian_if_needed(raw_cmd);
|
|
std::move(reinterpret_cast<uint8_t*>(&raw_cmd),
|
|
reinterpret_cast<uint8_t*>(&raw_cmd) + sizeof(raw_cmd),
|
|
std::back_inserter(tc.original_data_));
|
|
|
|
// Write state
|
|
std::move(state.begin(), state.end(), std::back_inserter(tc.original_data_));
|
|
|
|
tc.original_data_.insert(tc.original_data_.end(), padding, 0);
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(EncryptionInfo& info) {
|
|
details::encryption_info_command raw_cmd{};
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(info.command());
|
|
raw_cmd.cmdsize = info.size();
|
|
raw_cmd.cryptoff = info.crypt_offset();
|
|
raw_cmd.cryptsize = info.crypt_size();
|
|
raw_cmd.cryptid = info.crypt_id();
|
|
|
|
std::fill(info.original_data_.begin(), info.original_data_.end(), 0);
|
|
|
|
swap_endian_if_needed(raw_cmd);
|
|
std::copy(reinterpret_cast<uint8_t*>(&raw_cmd),
|
|
reinterpret_cast<uint8_t*>(&raw_cmd) + sizeof(raw_cmd),
|
|
reinterpret_cast<uint8_t*>(info.original_data_.data()));
|
|
|
|
return ok();
|
|
}
|
|
|
|
template<typename T>
|
|
ok_error_t Builder::update_fixups(DyldChainedFixups& command) {
|
|
for (const auto& seg_info : command.chained_starts_in_segments()) {
|
|
for (const std::unique_ptr<Relocation>& reloc : seg_info.segment.relocations_)
|
|
{
|
|
if (!RelocationFixup::classof(*reloc)) {
|
|
LIEF_WARN("Weird: a relocation in {} is not RelocationFixup",
|
|
seg_info.segment.name());
|
|
continue;
|
|
}
|
|
const auto& fixup = static_cast<const RelocationFixup&>(*reloc);
|
|
span<uint8_t> sdata = seg_info.segment.content();
|
|
const uint64_t seg_off = seg_info.segment.file_offset();
|
|
|
|
LIEF_DEBUG("{:#012x}: {:#012x} Offset: {:#x}", fixup.address(),
|
|
fixup.target(), fixup.offset_);
|
|
const uint64_t rel_offset = fixup.offset_ - seg_off;
|
|
switch (fixup.rtypes_) {
|
|
case RelocationFixup::REBASE_TYPES::ARM64E_AUTH_REBASE:
|
|
{
|
|
auto& raw_fixup =
|
|
*reinterpret_cast<details::dyld_chained_ptr_arm64e*>(sdata.data() +
|
|
rel_offset);
|
|
raw_fixup.auth_rebase = *fixup.arm64_auth_rebase_;
|
|
break;
|
|
}
|
|
|
|
case RelocationFixup::REBASE_TYPES::ARM64E_REBASE:
|
|
{
|
|
auto& raw_fixup =
|
|
*reinterpret_cast<details::dyld_chained_ptr_arm64e*>(sdata.data() +
|
|
rel_offset);
|
|
raw_fixup.rebase = *fixup.arm64_rebase_;
|
|
break;
|
|
}
|
|
|
|
case RelocationFixup::REBASE_TYPES::PTR64_REBASE:
|
|
{
|
|
auto& raw_fixup =
|
|
*reinterpret_cast<details::dyld_chained_ptr_generic64*>(
|
|
sdata.data() + rel_offset
|
|
);
|
|
raw_fixup.rebase = *fixup.p64_rebase_;
|
|
break;
|
|
}
|
|
|
|
case RelocationFixup::REBASE_TYPES::PTR32_REBASE:
|
|
{
|
|
auto& raw_fixup =
|
|
*reinterpret_cast<details::dyld_chained_ptr_generic32*>(
|
|
sdata.data() + rel_offset
|
|
);
|
|
raw_fixup.rebase = *fixup.p32_rebase_;
|
|
break;
|
|
}
|
|
|
|
case RelocationFixup::REBASE_TYPES::SEGMENTED:
|
|
{
|
|
[[maybe_unused]] auto& raw_fixup = *reinterpret_cast<
|
|
details::dyld_chained_ptr_arm64e_segmented_rebase*
|
|
>(sdata.data() + rel_offset);
|
|
LIEF_ERR(
|
|
"dyld_chained_ptr_arm64e_segmented_rebase is not supported ({})",
|
|
__LINE__
|
|
);
|
|
break;
|
|
}
|
|
|
|
case RelocationFixup::REBASE_TYPES::AUTH_SEGMENTED:
|
|
{
|
|
[[maybe_unused]] auto& raw_fixup = *reinterpret_cast<
|
|
details::dyld_chained_ptr_arm64e_auth_segmented_rebase*
|
|
>(sdata.data() + rel_offset);
|
|
LIEF_ERR("dyld_chained_ptr_arm64e_auth_segmented_rebase is not "
|
|
"supported ({})",
|
|
__LINE__);
|
|
break;
|
|
}
|
|
|
|
case RelocationFixup::REBASE_TYPES::UNKNOWN:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ok();
|
|
}
|
|
|
|
|
|
template<typename T>
|
|
ok_error_t Builder::build(DyldChainedFixups& fixups) {
|
|
using pin_t = typename T::uint;
|
|
/*
|
|
* Note(romain): Most of the logic that constructs this command
|
|
* is located in ld64/src/ld/LinkEdit.hpp -
|
|
* ChainedInfoAtom<A>::encode() The following code is highly inspired by the
|
|
* Apple ld64 code
|
|
*/
|
|
LIEF_DEBUG("[->] Writing DyldChainedFixups");
|
|
|
|
// First, we have to re-write the fixups
|
|
if (!update_fixups<T>(fixups)) {
|
|
LIEF_WARN("Error while re-writing chained fixups");
|
|
}
|
|
|
|
vector_iostream lnk_data;
|
|
|
|
details::dyld_chained_fixups_header header{};
|
|
header.fixups_version = 0;
|
|
header.starts_offset = align(sizeof(details::dyld_chained_fixups_header), 8);
|
|
header.imports_offset = 0;
|
|
header.symbols_offset = 0;
|
|
header.imports_count = fixups.internal_bindings_.size();
|
|
header.imports_format = static_cast<uint32_t>(fixups.imports_format());
|
|
header.symbols_format = 0;
|
|
|
|
lnk_data.reserve(fixups.data_size());
|
|
lnk_data.write(header);
|
|
lnk_data.align(8);
|
|
|
|
const size_t segs_header_off = lnk_data.size();
|
|
if (fixups.starts_offset() > 0 && segs_header_off != fixups.starts_offset()) {
|
|
LIEF_INFO("segs_header_off may be incorrect");
|
|
}
|
|
auto starts_in_segment = fixups.chained_starts_in_segments();
|
|
lnk_data.write<uint32_t>(starts_in_segment.size());
|
|
const size_t segs_info_off = lnk_data.size();
|
|
for (size_t i = 0; i < starts_in_segment.size(); ++i) {
|
|
// Write empty offsets that will be filled later
|
|
lnk_data.write<uint32_t>(0);
|
|
}
|
|
size_t seg_idx = 0;
|
|
uint64_t text_start_address = 0;
|
|
uint64_t max_rebase_address = 0;
|
|
|
|
for (const auto& seg_info : starts_in_segment) {
|
|
if (seg_info.segment.name() == "__TEXT") {
|
|
text_start_address = seg_info.segment.virtual_address();
|
|
} else if (seg_info.segment.name() == "__LINKEDIT") {
|
|
uint64_t base_address = text_start_address;
|
|
if (seg_info.pointer_format == DYLD_CHAINED_PTR_FORMAT::PTR_32 &&
|
|
text_start_address == 0x4000)
|
|
{
|
|
base_address = 0;
|
|
}
|
|
max_rebase_address =
|
|
align(seg_info.segment.virtual_address() - base_address, 0x00100000);
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
|
|
for (const DyldChainedFixups::chained_starts_in_segment& seg_info :
|
|
starts_in_segment)
|
|
{
|
|
if (seg_info.page_count() == 0) {
|
|
++seg_idx;
|
|
continue;
|
|
}
|
|
uint16_t start_bytes_per_page = sizeof(uint16_t);
|
|
if (seg_info.pointer_format == DYLD_CHAINED_PTR_FORMAT::PTR_32) {
|
|
// TODO(romain): Would be worth implementing this case as it seems only
|
|
// used on x86 dyld?
|
|
LIEF_ERR("DYLD_CHAINED_PTR_FORMAT::PTR_32 is not supported yet");
|
|
return make_error_code(lief_errors::not_supported);
|
|
}
|
|
details::dyld_chained_starts_in_segment seg{};
|
|
const size_t page_count = seg_info.page_count();
|
|
seg.size = sizeof(details::dyld_chained_starts_in_segment) +
|
|
page_count * start_bytes_per_page;
|
|
seg.page_size = seg_info.page_size;
|
|
seg.pointer_format = static_cast<uint16_t>(seg_info.pointer_format);
|
|
seg.segment_offset = seg_info.segment.virtual_address() - text_start_address;
|
|
seg.page_count = page_count;
|
|
seg.max_valid_pointer =
|
|
seg_info.pointer_format == DYLD_CHAINED_PTR_FORMAT::PTR_32 ?
|
|
max_rebase_address :
|
|
0;
|
|
|
|
// According to the linker documentation, dyld_chained_starts_in_segment
|
|
// must be 64-bit aligned.
|
|
lnk_data.align(8);
|
|
auto* seg_info_offsets =
|
|
reinterpret_cast<uint32_t*>(lnk_data.raw().data() + segs_info_off);
|
|
seg_info_offsets[seg_idx] = lnk_data.size() - segs_header_off;
|
|
LIEF_DEBUG("{:#08x} seg_info_offsets[{}] = {:#018x}",
|
|
segs_info_off + sizeof(uint32_t) * seg_idx, seg_idx,
|
|
seg_info_offsets[seg_idx]);
|
|
lnk_data.write(seg);
|
|
for (uint16_t off : seg_info.page_start) {
|
|
lnk_data.write(off);
|
|
}
|
|
// Skip DYLD_CHAINED_PTR_32
|
|
// {
|
|
// ...
|
|
// }
|
|
++seg_idx;
|
|
}
|
|
|
|
|
|
// Now build the imports and symbol table
|
|
vector_iostream string_pool;
|
|
|
|
vector_iostream imports;
|
|
vector_iostream imports_addend;
|
|
vector_iostream imports_addend64;
|
|
|
|
std::unordered_map<std::string_view, size_t> offset_name_map;
|
|
string_pool.write<uint8_t>(0);
|
|
size_t offset_counter = string_pool.tellp();
|
|
std::vector<std::string> string_table_optimized = optimize(
|
|
fixups.internal_bindings_,
|
|
[](const std::unique_ptr<ChainedBindingInfoList>& bnd) {
|
|
if (const Symbol* s = bnd->symbol()) {
|
|
return s->name();
|
|
}
|
|
return std::string_view();
|
|
},
|
|
offset_counter, &offset_name_map
|
|
);
|
|
string_pool.reserve(string_table_optimized.size() * 10);
|
|
for (std::string_view name : string_table_optimized) {
|
|
string_pool.write(name);
|
|
}
|
|
|
|
const DYLD_CHAINED_FORMAT fmt = fixups.imports_format();
|
|
const size_t nb_bindings = fixups.internal_bindings_.size();
|
|
switch (fmt) {
|
|
case DYLD_CHAINED_FORMAT::IMPORT:
|
|
imports.reserve(nb_bindings * sizeof(details::dyld_chained_import));
|
|
break;
|
|
case DYLD_CHAINED_FORMAT::IMPORT_ADDEND:
|
|
imports.reserve(nb_bindings * sizeof(details::dyld_chained_import_addend));
|
|
break;
|
|
case DYLD_CHAINED_FORMAT::IMPORT_ADDEND64:
|
|
imports.reserve(nb_bindings * sizeof(details::dyld_chained_import_addend64));
|
|
break;
|
|
}
|
|
|
|
for (const std::unique_ptr<ChainedBindingInfoList>& info :
|
|
fixups.internal_bindings_)
|
|
{
|
|
uint32_t name_offset = 0;
|
|
|
|
const std::string& name = info->symbol()->name();
|
|
if (auto it_name_off = offset_name_map.find(name);
|
|
it_name_off != offset_name_map.end())
|
|
{
|
|
name_offset = it_name_off->second;
|
|
} else {
|
|
LIEF_WARN("Can't find symbol: '{}'", name);
|
|
name_offset = 0;
|
|
}
|
|
switch (fmt) {
|
|
case DYLD_CHAINED_FORMAT::IMPORT:
|
|
{
|
|
details::dyld_chained_import import{};
|
|
import.lib_ordinal = info->library_ordinal();
|
|
import.weak_import = info->is_weak_import();
|
|
import.name_offset = name_offset;
|
|
imports.write(import);
|
|
break;
|
|
}
|
|
case DYLD_CHAINED_FORMAT::IMPORT_ADDEND:
|
|
{
|
|
details::dyld_chained_import_addend import{};
|
|
import.lib_ordinal = info->library_ordinal();
|
|
import.weak_import = info->is_weak_import();
|
|
import.name_offset = name_offset;
|
|
import.addend = info->addend();
|
|
imports_addend.write(import);
|
|
break;
|
|
}
|
|
case DYLD_CHAINED_FORMAT::IMPORT_ADDEND64:
|
|
{
|
|
details::dyld_chained_import_addend64 import{};
|
|
import.lib_ordinal = info->library_ordinal();
|
|
import.weak_import = info->is_weak_import();
|
|
import.name_offset = name_offset;
|
|
import.addend = info->addend();
|
|
imports_addend64.write(import);
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (ChainedBindingInfo* binding : info->elements_) {
|
|
const uint64_t rel_offset =
|
|
binding->offset_ - binding->segment()->file_offset();
|
|
uint8_t* data_ptr = binding->segment_->content().data() + rel_offset;
|
|
LIEF_DEBUG(
|
|
"Write binding (offset={:#012x}): {:#018x} {} in {} offset={:#012x}",
|
|
binding->offset_, binding->address(), binding->symbol()->name(),
|
|
binding->segment_->name(), binding->segment()->file_offset() + rel_offset
|
|
);
|
|
|
|
// Rewrite the raw chained binding
|
|
switch (binding->btypes_) {
|
|
case ChainedBindingInfo::BIND_TYPES::ARM64E_BIND:
|
|
{
|
|
auto& raw_bind =
|
|
*reinterpret_cast<details::dyld_chained_ptr_arm64e*>(data_ptr);
|
|
raw_bind.bind = *binding->arm64_bind_;
|
|
break;
|
|
}
|
|
case ChainedBindingInfo::BIND_TYPES::ARM64E_AUTH_BIND:
|
|
{
|
|
auto& raw_bind =
|
|
*reinterpret_cast<details::dyld_chained_ptr_arm64e*>(data_ptr);
|
|
raw_bind.auth_bind = *binding->arm64_auth_bind_;
|
|
break;
|
|
}
|
|
case ChainedBindingInfo::BIND_TYPES::ARM64E_BIND24:
|
|
{
|
|
auto& raw_bind =
|
|
*reinterpret_cast<details::dyld_chained_ptr_arm64e*>(data_ptr);
|
|
raw_bind.bind24 = *binding->arm64_bind24_;
|
|
break;
|
|
}
|
|
case ChainedBindingInfo::BIND_TYPES::ARM64E_AUTH_BIND24:
|
|
{
|
|
auto& raw_bind =
|
|
*reinterpret_cast<details::dyld_chained_ptr_arm64e*>(data_ptr);
|
|
raw_bind.auth_bind24 = *binding->arm64_auth_bind24_;
|
|
break;
|
|
}
|
|
case ChainedBindingInfo::BIND_TYPES::PTR64_BIND:
|
|
{
|
|
auto& raw_bind =
|
|
*reinterpret_cast<details::dyld_chained_ptr_generic64*>(data_ptr);
|
|
raw_bind.bind = *binding->p64_bind_;
|
|
break;
|
|
}
|
|
case ChainedBindingInfo::BIND_TYPES::PTR32_BIND:
|
|
{
|
|
auto& raw_bind =
|
|
*reinterpret_cast<details::dyld_chained_ptr_generic32*>(data_ptr);
|
|
raw_bind.bind = *binding->p32_bind_;
|
|
break;
|
|
}
|
|
case ChainedBindingInfo::BIND_TYPES::UNKNOWN: break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Re-access the header to update the offsets
|
|
// NOTE/WARN: We need to 'reset' the pointer as the underlying buffer
|
|
// (std::vector) might be relocated.
|
|
auto* hdr = reinterpret_cast<details::dyld_chained_fixups_header*>(
|
|
lnk_data.raw().data()
|
|
);
|
|
switch (fmt) {
|
|
case DYLD_CHAINED_FORMAT::IMPORT:
|
|
{
|
|
lnk_data.align(4);
|
|
hdr = reinterpret_cast<details::dyld_chained_fixups_header*>(
|
|
lnk_data.raw().data()
|
|
);
|
|
hdr->imports_offset = lnk_data.size();
|
|
lnk_data.write(imports);
|
|
break;
|
|
}
|
|
case DYLD_CHAINED_FORMAT::IMPORT_ADDEND:
|
|
{
|
|
lnk_data.align(4);
|
|
hdr = reinterpret_cast<details::dyld_chained_fixups_header*>(
|
|
lnk_data.raw().data()
|
|
);
|
|
hdr->imports_offset = lnk_data.size();
|
|
lnk_data.write(imports_addend);
|
|
break;
|
|
}
|
|
case DYLD_CHAINED_FORMAT::IMPORT_ADDEND64:
|
|
{
|
|
lnk_data.align(8);
|
|
hdr = reinterpret_cast<details::dyld_chained_fixups_header*>(
|
|
lnk_data.raw().data()
|
|
);
|
|
hdr->imports_offset = lnk_data.size();
|
|
lnk_data.write(imports_addend64);
|
|
break;
|
|
}
|
|
}
|
|
|
|
hdr = reinterpret_cast<details::dyld_chained_fixups_header*>(
|
|
lnk_data.raw().data()
|
|
);
|
|
hdr->symbols_offset = lnk_data.size();
|
|
lnk_data.write(string_pool.raw()).align(sizeof(pin_t));
|
|
|
|
const std::vector<uint8_t>& raw = lnk_data.raw();
|
|
LIEF_DEBUG("__chainfixups.old_size: {:#08x}", fixups.data_size());
|
|
LIEF_DEBUG("__chainfixups.new_size: {:#08x}", raw.size());
|
|
|
|
if (fixups.data_size() > 0 && fixups.data_size() < raw.size()) {
|
|
LIEF_WARN("New chained fixups size is larger than the original one");
|
|
}
|
|
|
|
LIEF_DEBUG("LC_DYLD_CHAINED_FIXUPS.offset: {:#08x} -> {:#x}",
|
|
fixups.data_offset(), linkedit_offset_ + linkedit_.size());
|
|
LIEF_DEBUG("LC_DYLD_CHAINED_FIXUPS.size: {:#08x} -> {:#x}", fixups.data_size(),
|
|
lnk_data.size());
|
|
|
|
// Write back the 'linkedit' structure
|
|
details::linkedit_data_command raw_cmd{};
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(fixups.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(fixups.size());
|
|
raw_cmd.dataoff = linkedit_offset_ + linkedit_.size();
|
|
raw_cmd.datasize = lnk_data.size();
|
|
|
|
linkedit_.write(lnk_data.raw());
|
|
|
|
fixups.size_ = sizeof(details::linkedit_data_command);
|
|
fixups.original_data_.clear();
|
|
fixups.original_data_.resize(fixups.size_);
|
|
|
|
swap_endian_if_needed(raw_cmd);
|
|
memcpy(fixups.original_data_.data(), &raw_cmd,
|
|
sizeof(details::linkedit_data_command));
|
|
return ok();
|
|
}
|
|
|
|
template<typename T>
|
|
ok_error_t Builder::build(DyldExportsTrie& exports) {
|
|
using pin_t = typename T::uint;
|
|
std::vector<uint8_t> raw = create_trie(exports.export_info_, sizeof(pin_t));
|
|
|
|
if (exports.data_size() > 0 && raw.size() > exports.content_.size()) {
|
|
const uint64_t delta = raw.size() - exports.content_.size();
|
|
LIEF_INFO("The export trie is larger than the original "
|
|
"LC_DYLD_EXPORTS_TRIE (+{:#x} bytes)",
|
|
delta);
|
|
}
|
|
|
|
LIEF_DEBUG("LC_DYLD_EXPORTS_TRIE.offset: {:#08x} -> {:#x}",
|
|
exports.data_offset(), linkedit_offset_ + linkedit_.size());
|
|
LIEF_DEBUG("LC_DYLD_EXPORTS_TRIE.size: {:#08x} -> {:#x}", exports.data_size(),
|
|
raw.size());
|
|
|
|
// Write back the 'linkedit' structure
|
|
details::linkedit_data_command raw_cmd{};
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(exports.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(exports.size());
|
|
raw_cmd.dataoff = linkedit_offset_ + linkedit_.size();
|
|
raw_cmd.datasize = raw.size();
|
|
|
|
linkedit_.write(raw);
|
|
|
|
exports.size_ = sizeof(details::linkedit_data_command);
|
|
exports.original_data_.clear();
|
|
exports.original_data_.resize(exports.size_);
|
|
|
|
swap_endian_if_needed(raw_cmd);
|
|
memcpy(exports.original_data_.data(), &raw_cmd,
|
|
sizeof(details::linkedit_data_command));
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(BuildVersion& bv) {
|
|
details::build_version_command raw_cmd{};
|
|
|
|
const BuildVersion::tools_list_t& tools = bv.tools();
|
|
|
|
const uint32_t raw_size = sizeof(details::build_version_command) +
|
|
tools.size() * sizeof(details::build_tool_version);
|
|
const uint32_t size_needed = align(raw_size, sizeof(typename T::uint));
|
|
const uint32_t padding = size_needed - raw_size;
|
|
|
|
if (bv.original_data_.size() < size_needed || bv.size() < size_needed) {
|
|
LIEF_WARN("Not enough spaces to rebuild 'BuildVersion'. Size required: {:#x} "
|
|
"vs {:#x}",
|
|
bv.original_data_.size(), size_needed);
|
|
}
|
|
|
|
const BuildVersion::version_t& minos = bv.minos();
|
|
const BuildVersion::version_t& sdk = bv.sdk();
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(bv.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(size_needed);
|
|
|
|
raw_cmd.minos = static_cast<uint32_t>(minos[0] << 16 | minos[1] << 8 | minos[2]);
|
|
raw_cmd.sdk = static_cast<uint32_t>(sdk[0] << 16 | sdk[1] << 8 | sdk[2]);
|
|
raw_cmd.platform = static_cast<uint32_t>(bv.platform());
|
|
raw_cmd.ntools = tools.size();
|
|
// raw_cmd.name = static_cast<uint32_t>(sizeof(build_version_command));
|
|
std::vector<uint8_t> raw_tools(
|
|
raw_cmd.ntools * sizeof(details::build_tool_version), 0
|
|
);
|
|
auto* tools_array =
|
|
reinterpret_cast<details::build_tool_version*>(raw_tools.data());
|
|
for (size_t i = 0; i < tools.size(); ++i) {
|
|
BuildToolVersion::version_t version = tools[i].version();
|
|
tools_array[i].tool = static_cast<uint32_t>(tools[i].tool());
|
|
tools_array[i].version =
|
|
static_cast<uint32_t>(version[0] << 16 | version[1] << 8 | version[2]);
|
|
swap_endian_if_needed(tools_array[i]);
|
|
}
|
|
|
|
bv.size_ = size_needed;
|
|
bv.original_data_.clear();
|
|
|
|
// Write Header
|
|
swap_endian_if_needed(raw_cmd);
|
|
std::move(reinterpret_cast<uint8_t*>(&raw_cmd),
|
|
reinterpret_cast<uint8_t*>(&raw_cmd) + sizeof(raw_cmd),
|
|
std::back_inserter(bv.original_data_));
|
|
|
|
std::move(raw_tools.begin(), raw_tools.end(),
|
|
std::back_inserter(bv.original_data_));
|
|
|
|
bv.original_data_.insert(bv.original_data_.end(), padding, 0);
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(CodeSignatureDir& sig) {
|
|
details::linkedit_data_command raw_cmd{};
|
|
|
|
span<const uint8_t> sp = sig.content();
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(sig.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(sig.size());
|
|
raw_cmd.dataoff = linkedit_offset_ + linkedit_.size();
|
|
raw_cmd.datasize = sp.size();
|
|
|
|
LIEF_DEBUG("LC_DYLIB_CODE_SIGN_DRS.offset: {:#08x} -> {:#x}", sig.data_offset(),
|
|
raw_cmd.dataoff);
|
|
LIEF_DEBUG("LC_DYLIB_CODE_SIGN_DRS.size: {:#08x} -> {:#x}", sig.data_size(),
|
|
raw_cmd.datasize);
|
|
|
|
linkedit_.write(sp.data(), sp.size());
|
|
|
|
sig.size_ = sizeof(details::linkedit_data_command);
|
|
sig.original_data_.clear();
|
|
sig.original_data_.resize(sig.size_);
|
|
|
|
swap_endian_if_needed(raw_cmd);
|
|
memcpy(sig.original_data_.data(), &raw_cmd,
|
|
sizeof(details::linkedit_data_command));
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(LinkerOptHint& opt) {
|
|
details::linkedit_data_command raw_cmd{};
|
|
|
|
span<const uint8_t> sp = opt.content();
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(opt.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(opt.size());
|
|
raw_cmd.dataoff = linkedit_offset_ + linkedit_.size();
|
|
raw_cmd.datasize = sp.size();
|
|
|
|
LIEF_DEBUG("LC_LINKER_OPTIMIZATION_HINT.offset: {:#08x} -> {:#x}",
|
|
opt.data_offset(), raw_cmd.dataoff);
|
|
LIEF_DEBUG("LC_LINKER_OPTIMIZATION_HINT.size: {:#08x} -> {:#x}",
|
|
opt.data_size(), raw_cmd.datasize);
|
|
|
|
linkedit_.write(sp.data(), sp.size());
|
|
|
|
opt.size_ = sizeof(details::linkedit_data_command);
|
|
opt.original_data_.clear();
|
|
opt.original_data_.resize(opt.size_);
|
|
|
|
swap_endian_if_needed(raw_cmd);
|
|
memcpy(opt.original_data_.data(), &raw_cmd,
|
|
sizeof(details::linkedit_data_command));
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(AtomInfo& atom) {
|
|
details::linkedit_data_command raw_cmd{};
|
|
|
|
span<const uint8_t> sp = atom.content();
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(atom.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(atom.size());
|
|
raw_cmd.dataoff = linkedit_offset_ + linkedit_.size();
|
|
raw_cmd.datasize = sp.size();
|
|
|
|
LIEF_DEBUG("LC_ATOM_INFO.offset: {:#08x} -> {:#x}", atom.data_offset(),
|
|
raw_cmd.dataoff);
|
|
LIEF_DEBUG("LC_ATOM_INFO.size: {:#08x} -> {:#x}", atom.data_size(),
|
|
raw_cmd.datasize);
|
|
|
|
linkedit_.write(sp.data(), sp.size());
|
|
|
|
atom.size_ = sizeof(details::linkedit_data_command);
|
|
atom.original_data_.clear();
|
|
atom.original_data_.resize(atom.size_);
|
|
|
|
swap_endian_if_needed(raw_cmd);
|
|
memcpy(atom.original_data_.data(), &raw_cmd,
|
|
sizeof(details::linkedit_data_command));
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(TwoLevelHints& two) {
|
|
LIEF_DEBUG("Build '{}'", to_string(two.command()));
|
|
details::twolevel_hints_command raw_cmd{};
|
|
const auto it_hints = two.hints();
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(two.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(two.size());
|
|
|
|
raw_cmd.offset = linkedit_offset_ + linkedit_.size();
|
|
raw_cmd.nhints = it_hints.size();
|
|
|
|
LIEF_DEBUG("LC_TWOLEVEL_HINTS.offset: {:#08x} -> {:#x}", two.offset(),
|
|
raw_cmd.offset);
|
|
LIEF_DEBUG("LC_TWOLEVEL_HINTS.nhints: {:#08x} -> {:#x}", two.original_nb_hints(),
|
|
raw_cmd.nhints);
|
|
|
|
for (uint32_t value : it_hints) {
|
|
linkedit_.write(value);
|
|
}
|
|
|
|
two.size_ = sizeof(details::twolevel_hints_command);
|
|
two.original_data_.clear();
|
|
two.original_data_.resize(two.size_);
|
|
|
|
swap_endian_if_needed(raw_cmd);
|
|
memcpy(two.original_data_.data(), &raw_cmd,
|
|
sizeof(details::twolevel_hints_command));
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(FunctionVariants& func_variants) {
|
|
LIEF_DEBUG("Build '{}'", to_string(func_variants.command()));
|
|
details::linkedit_data_command raw_cmd{};
|
|
raw_cmd.dataoff = linkedit_.size();
|
|
|
|
// Reconstruct the payload following this layout:
|
|
//
|
|
// uint32_t tableCount
|
|
// uint32_t tableOffsets[tableCount] // payload-relative, points to 'kind'
|
|
// <runtime tables>
|
|
//
|
|
// where each runtime table is:
|
|
//
|
|
// uint32_t kind
|
|
// uint32_t count
|
|
// runtime_table_entry_t entries[count] // 8 bytes each (2 * uint32_t)
|
|
auto runtime_tables = func_variants.runtime_table();
|
|
const size_t nb_tables = runtime_tables.size();
|
|
|
|
linkedit_.write((uint32_t)nb_tables);
|
|
|
|
// The tables are stored right after the (tableCount + tableOffsets[]) header.
|
|
size_t table_offset = sizeof(uint32_t) * (1 + nb_tables);
|
|
for (const FunctionVariants::RuntimeTable& table : runtime_tables) {
|
|
linkedit_.write((uint32_t)table_offset);
|
|
// Each table is a 'kind' + 'count' header followed by its entries.
|
|
table_offset +=
|
|
2 * sizeof(uint32_t) +
|
|
table.entries().size() * sizeof(details::runtime_table_entry_t);
|
|
}
|
|
|
|
for (const FunctionVariants::RuntimeTable& table : runtime_tables) {
|
|
(linkedit_)
|
|
.write((uint32_t)table.kind())
|
|
.write((uint32_t)table.entries().size());
|
|
|
|
for (const FunctionVariants::RuntimeTableEntry& entry : table.entries()) {
|
|
details::runtime_table_entry_t raw_entry{};
|
|
raw_entry.impl = entry.impl();
|
|
raw_entry.another_table = entry.another_table();
|
|
|
|
span<const uint8_t> flag_bit_nums = entry.flag_bit_nums();
|
|
std::copy(flag_bit_nums.begin(), flag_bit_nums.end(),
|
|
std::begin(raw_entry.flag_bit_nums));
|
|
|
|
linkedit_.write(raw_entry);
|
|
}
|
|
}
|
|
|
|
// The payload is padded to the natural pointer size (as emitted by the linker)
|
|
linkedit_.align(sizeof(typename T::uint));
|
|
|
|
raw_cmd.cmd = (uint32_t)func_variants.command();
|
|
raw_cmd.cmdsize = (uint32_t)func_variants.size();
|
|
raw_cmd.datasize = linkedit_.size() - raw_cmd.dataoff;
|
|
raw_cmd.dataoff += linkedit_offset_;
|
|
|
|
LIEF_DEBUG("LC_FUNCTION_VARIANTS.offset: {:#08x} -> {:#x}",
|
|
func_variants.data_offset(), raw_cmd.dataoff);
|
|
LIEF_DEBUG("LC_FUNCTION_VARIANTS.size: {:#08x} -> {:#x}",
|
|
func_variants.data_size(), raw_cmd.datasize);
|
|
|
|
func_variants.size_ = sizeof(details::linkedit_data_command);
|
|
func_variants.original_data_.clear();
|
|
func_variants.original_data_.resize(func_variants.size_);
|
|
swap_endian_if_needed(raw_cmd);
|
|
memcpy(func_variants.original_data_.data(), &raw_cmd,
|
|
sizeof(details::linkedit_data_command));
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(FunctionVariantFixups& func_variant_fixups) {
|
|
LIEF_DEBUG("Build '{}'", to_string(func_variant_fixups.command()));
|
|
details::linkedit_data_command raw_cmd{};
|
|
raw_cmd.dataoff = linkedit_.size();
|
|
|
|
for (const FunctionVariantFixups::Fixup& fixup : func_variant_fixups.fixups()) {
|
|
details::function_variant_fixup_t raw_fixup{};
|
|
raw_fixup.seg_offset = fixup.seg_offset();
|
|
raw_fixup.seg_index =
|
|
fixup.segment() != nullptr ? fixup.segment()->index() : fixup.seg_index();
|
|
raw_fixup.variant_index = fixup.variant_index();
|
|
raw_fixup.pac_auth = fixup.pac_auth();
|
|
raw_fixup.pac_address = fixup.pac_address();
|
|
raw_fixup.pac_key = fixup.pac_key();
|
|
raw_fixup.pac_diversity = fixup.pac_diversity();
|
|
linkedit_.write(raw_fixup);
|
|
}
|
|
|
|
linkedit_.align(sizeof(typename T::uint));
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(func_variant_fixups.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(func_variant_fixups.size());
|
|
raw_cmd.datasize = linkedit_.size() - raw_cmd.dataoff;
|
|
raw_cmd.dataoff += linkedit_offset_;
|
|
|
|
|
|
LIEF_DEBUG("LC_FUNCTION_VARIANT_FIXUPS.offset: {:#08x} -> {:#x}",
|
|
func_variant_fixups.data_offset(), raw_cmd.dataoff);
|
|
LIEF_DEBUG("LC_FUNCTION_VARIANT_FIXUPS.size: {:#08x} -> {:#x}",
|
|
func_variant_fixups.data_size(), raw_cmd.datasize);
|
|
|
|
func_variant_fixups.size_ = sizeof(details::linkedit_data_command);
|
|
func_variant_fixups.original_data_.clear();
|
|
func_variant_fixups.original_data_.resize(func_variant_fixups.size_);
|
|
swap_endian_if_needed(raw_cmd);
|
|
memcpy(func_variant_fixups.original_data_.data(), &raw_cmd,
|
|
sizeof(details::linkedit_data_command));
|
|
return ok();
|
|
}
|
|
|
|
template<class T>
|
|
ok_error_t Builder::build(LazyLoadDylibInfo& cmd) {
|
|
LIEF_DEBUG("Build '{}'", to_string(cmd.command()));
|
|
|
|
// TODO(romain): Currently, fixup addresses are not regenerated as fixup chains.
|
|
// This can cause an issue when a Binary::shift operation targets some of
|
|
// these fixup
|
|
|
|
details::linkedit_data_command raw_cmd{};
|
|
raw_cmd.dataoff = linkedit_.size();
|
|
|
|
cmd.serialize(linkedit_);
|
|
linkedit_.align(sizeof(typename T::uint));
|
|
|
|
raw_cmd.cmd = static_cast<uint32_t>(cmd.command());
|
|
raw_cmd.cmdsize = static_cast<uint32_t>(cmd.size());
|
|
raw_cmd.datasize = linkedit_.size() - raw_cmd.dataoff;
|
|
raw_cmd.dataoff += linkedit_offset_;
|
|
|
|
LIEF_DEBUG("LC_LAZY_LOAD_DYLIB_INFO.offset: {:#08x} -> {:#x}", cmd.data_offset(),
|
|
raw_cmd.dataoff);
|
|
LIEF_DEBUG("LC_LAZY_LOAD_DYLIB_INFO.size: {:#08x} -> {:#x}", cmd.data_size(),
|
|
raw_cmd.datasize);
|
|
|
|
cmd.size_ = sizeof(details::linkedit_data_command);
|
|
cmd.original_data_.clear();
|
|
cmd.original_data_.resize(cmd.size_);
|
|
swap_endian_if_needed(raw_cmd);
|
|
memcpy(cmd.original_data_.data(), &raw_cmd,
|
|
sizeof(details::linkedit_data_command));
|
|
return ok();
|
|
}
|
|
|
|
template<class MACHO_T>
|
|
ok_error_t Builder::build_header() {
|
|
using header_t = typename MACHO_T::header;
|
|
|
|
header_t header{};
|
|
|
|
const Header& binary_header = binary_->header();
|
|
|
|
header.magic = static_cast<uint32_t>(binary_header.magic());
|
|
header.cputype = static_cast<uint32_t>(binary_header.cpu_type());
|
|
header.cpusubtype = static_cast<uint32_t>(binary_header.cpu_subtype());
|
|
header.filetype = static_cast<uint32_t>(binary_header.file_type());
|
|
header.ncmds = static_cast<uint32_t>(binary_header.nb_cmds());
|
|
header.sizeofcmds = static_cast<uint32_t>(binary_header.sizeof_cmds());
|
|
header.flags = static_cast<uint32_t>(binary_header.flags());
|
|
|
|
if constexpr (std::is_same_v<header_t, details::mach_header_64>) {
|
|
header.reserved = static_cast<uint32_t>(binary_header.reserved());
|
|
}
|
|
|
|
LIEF_DEBUG("Writing header at: 0 (size: {:#06x})", sizeof(header));
|
|
|
|
raw_.seekp(0).write(header);
|
|
|
|
return ok();
|
|
}
|
|
}
|