/* 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 <memory>
#include <algorithm>
#include "spdlog/fmt/fmt.h"
#include "LIEF/Visitor.hpp"
#include "LIEF/BinaryStream/SpanStream.hpp"
#include "LIEF/MachO/Section.hpp"
#include "LIEF/MachO/SegmentCommand.hpp"
#include "LIEF/MachO/Relocation.hpp"
#include "MachO/Structures.hpp"
namespace LIEF::MachO {
SegmentCommand::SegmentCommand() = default;
SegmentCommand::~SegmentCommand() = default;
SegmentCommand::SegmentCommand(std::string name, content_t content) :
name_(std::move(name)),
data_(std::move(content)) {}
SegmentCommand::SegmentCommand(std::string name) :
name_(std::move(name)) {}
SegmentCommand& SegmentCommand::operator=(SegmentCommand other) {
swap(other);
return *this;
}
SegmentCommand::SegmentCommand(const SegmentCommand& other) :
LoadCommand{other},
name_{other.name_},
virtual_address_{other.virtual_address_},
virtual_size_{other.virtual_size_},
file_offset_{other.file_offset_},
file_size_{other.file_size_},
max_protection_{other.max_protection_},
init_protection_{other.init_protection_},
nb_sections_{other.nb_sections_},
flags_{other.flags_},
data_{other.data_} {
for (const std::unique_ptr<Section>& section : other.sections_) {
auto new_section = section->clone();
new_section->segment_ = this;
new_section->segment_name_ = name();
sections_.push_back(std::move(new_section));
}
// TODO:
// for (Relocation* relocation : other.relocations_) {
// Relocation* new_relocation = relocation->clone();
// //relocations_.push_back(new_relocation);
//}
}
SegmentCommand::SegmentCommand(const details::segment_command_32& seg) :
LoadCommand{LoadCommand::TYPE::SEGMENT, seg.cmdsize},
name_{seg.segname, sizeof(seg.segname)},
virtual_address_{seg.vmaddr},
virtual_size_{seg.vmsize},
file_offset_{seg.fileoff},
file_size_{seg.filesize},
max_protection_{seg.maxprot},
init_protection_{seg.initprot},
nb_sections_{seg.nsects},
flags_{seg.flags} {
name_ = std::string{name_.c_str()};
}
SegmentCommand::SegmentCommand(const details::segment_command_64& seg) :
LoadCommand{LoadCommand::TYPE::SEGMENT_64, seg.cmdsize},
name_{seg.segname, sizeof(seg.segname)},
virtual_address_{seg.vmaddr},
virtual_size_{seg.vmsize},
file_offset_{seg.fileoff},
file_size_{seg.filesize},
max_protection_{seg.maxprot},
init_protection_{seg.initprot},
nb_sections_{seg.nsects},
flags_{seg.flags} {
name_ = std::string{name_.c_str()};
}
void SegmentCommand::swap(SegmentCommand& other) noexcept {
LoadCommand::swap(other);
std::swap(virtual_address_, other.virtual_address_);
std::swap(virtual_size_, other.virtual_size_);
std::swap(file_offset_, other.file_offset_);
std::swap(file_size_, other.file_size_);
std::swap(max_protection_, other.max_protection_);
std::swap(init_protection_, other.init_protection_);
std::swap(nb_sections_, other.nb_sections_);
std::swap(flags_, other.flags_);
std::swap(data_, other.data_);
std::swap(sections_, other.sections_);
std::swap(relocations_, other.relocations_);
// std::swap(dyld_, other.dyld_);
}
void SegmentCommand::content(SegmentCommand::content_t data) {
update_data([data = std::move(data)](std::vector<uint8_t>& inner_data) mutable {
// If the size is the same, we avoid the std::move that would dangle every
// span/iterator referencing this segment
if (data.size() == inner_data.size()) {
std::copy(data.begin(), data.end(), inner_data.begin());
} else {
inner_data = std::move(data);
}
});
}
void SegmentCommand::remove_all_sections() {
numberof_sections(0);
sections_.clear();
}
Section& SegmentCommand::add_section(const Section& section) {
auto new_section = section.clone();
new_section->segment_ = this;
new_section->segment_name_ = name();
new_section->size(section.content().size());
new_section->offset(file_offset() + file_size());
if (section.virtual_address() == 0) {
new_section->virtual_address(virtual_address() + new_section->offset());
}
file_size(file_size() + new_section->size());
const size_t relative_offset = new_section->offset() - file_offset();
span<const uint8_t> content = section.content();
update_data([](std::vector<uint8_t>& inner_data, size_t w,
size_t s) { inner_data.resize(w + s); },
relative_offset, content.size());
std::copy(content.begin(), content.end(), data_.begin() + relative_offset);
file_size(data_.size());
sections_.push_back(std::move(new_section));
numberof_sections(numberof_sections() + 1);
return *sections_.back();
}
bool SegmentCommand::has(const Section& section) const {
auto it = std::find_if(sections_.begin(), sections_.end(),
[§ion](const std::unique_ptr<Section>& sec) {
return *sec == section;
});
return it != sections_.end();
}
bool SegmentCommand::has_section(const std::string& section_name) const {
auto it = std::find_if(sections_.begin(), sections_.end(),
[§ion_name](const std::unique_ptr<Section>& sec) {
return sec->name() == section_name;
});
return it != sections_.end();
}
void SegmentCommand::content_resize(size_t size) {
update_data([size](std::vector<uint8_t>& inner_data) {
if (inner_data.size() >= size) {
return;
}
inner_data.resize(size, 0);
});
}
void SegmentCommand::content_insert(size_t where, size_t size) {
update_data(
[](std::vector<uint8_t>& inner_data, size_t w, size_t s) {
if (s == 0) {
return;
}
if (w < inner_data.size()) {
inner_data.insert(inner_data.begin() + w, s, 0);
} else {
inner_data.resize(inner_data.size() + w + s, 0);
}
},
where, size
);
}
const Section* SegmentCommand::get_section(const std::string& name) const {
const auto it = std::find_if(sections_.begin(), sections_.end(),
[&name](const std::unique_ptr<Section>& sec) {
return sec->name() == name;
});
if (it == sections_.end()) {
return nullptr;
}
return it->get();
}
Section* SegmentCommand::get_section(const std::string& name) {
return const_cast<Section*>(
static_cast<const SegmentCommand*>(this)->get_section(name)
);
}
std::unique_ptr<SpanStream> SegmentCommand::stream() const {
return std::make_unique<SpanStream>(content());
}
void SegmentCommand::accept(Visitor& visitor) const {
visitor.visit(*this);
}
std::ostream& SegmentCommand::print(std::ostream& os) const {
LoadCommand::print(os) << '\n';
os << fmt::format(
"name={}, vaddr={:#08x}, vsize={:#06x} "
"offset={:#08x}, size={}, max protection={}, init protection={} "
"flags={}",
name(), virtual_address(), virtual_size(), file_offset(), file_size(),
max_protection(), init_protection(), flags()
);
return os;
}
void SegmentCommand::update_data(const SegmentCommand::update_fnc_t& f) {
f(data_);
}
void SegmentCommand::update_data(const SegmentCommand::update_fnc_ws_t& f,
size_t where, size_t size) {
f(data_, where, size);
}
const char* to_string(SegmentCommand::FLAGS flag) {
switch (flag) {
case SegmentCommand::FLAGS::HIGHVM: return "HIGHVM";
case SegmentCommand::FLAGS::FVMLIB: return "FVMLIB";
case SegmentCommand::FLAGS::NORELOC: return "NORELOC";
case SegmentCommand::FLAGS::PROTECTED_VERSION_1: return "PROTECTED_VERSION_1";
case SegmentCommand::FLAGS::READ_ONLY: return "READ_ONLY";
}
return "UNKNOWN";
}
const char* to_string(SegmentCommand::VM_PROTECTIONS protection) {
switch (protection) {
case SegmentCommand::VM_PROTECTIONS::READ: return "R";
case SegmentCommand::VM_PROTECTIONS::WRITE: return "W";
case SegmentCommand::VM_PROTECTIONS::EXECUTE: return "X";
}
return "UNKNOWN";
}
}