blob: 77d73b9f13b8bd9030c0b3c775ba36097d5cc962 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "courgette/assembly_program.h"
#include "base/callback.h"
#include "base/logging.h"
#include "courgette/courgette.h"
#include "courgette/encoded_program.h"
namespace courgette {
namespace {
// Sets the current address for the emitting instructions.
class OriginInstruction : public Instruction {
public:
explicit OriginInstruction(RVA rva) : Instruction(ORIGIN, 0), rva_(rva) {}
RVA origin_rva() const { return rva_; }
private:
RVA rva_;
};
// Emits an entire PE base relocation table.
class PeRelocsInstruction : public Instruction {
public:
PeRelocsInstruction() : Instruction(MAKEPERELOCS) {}
};
// Emits an ELF relocation table.
class ElfRelocsInstruction : public Instruction {
public:
ElfRelocsInstruction() : Instruction(MAKEELFRELOCS) {}
};
// Emits an ELF ARM relocation table.
class ElfARMRelocsInstruction : public Instruction {
public:
ElfARMRelocsInstruction() : Instruction(MAKEELFARMRELOCS) {}
};
// Emits a single byte.
class ByteInstruction : public Instruction {
public:
explicit ByteInstruction(uint8_t value) : Instruction(DEFBYTE, value) {}
uint8_t byte_value() const { return info_; }
};
// Emits a single byte.
class BytesInstruction : public Instruction {
public:
BytesInstruction(const uint8_t* values, size_t len)
: Instruction(DEFBYTES, 0), values_(values), len_(len) {}
const uint8_t* byte_values() const { return values_; }
size_t len() const { return len_; }
private:
const uint8_t* values_;
size_t len_;
};
// A ABS32 to REL32 instruction emits a reference to a label's address.
class InstructionWithLabel : public Instruction {
public:
InstructionWithLabel(OP op, Label* label)
: Instruction(op, 0), label_(label) {
if (label == NULL) NOTREACHED();
}
Label* label() const { return label_; }
protected:
Label* label_;
};
// An ARM REL32 instruction emits a reference to a label's address and
// a specially-compressed ARM op.
class InstructionWithLabelARM : public InstructionWithLabel {
public:
InstructionWithLabelARM(OP op,
uint16_t compressed_op,
Label* label,
const uint8_t* arm_op,
uint16_t op_size)
: InstructionWithLabel(op, label),
compressed_op_(compressed_op),
arm_op_(arm_op),
op_size_(op_size) {
if (label == NULL) NOTREACHED();
}
uint16_t compressed_op() const { return compressed_op_; }
const uint8_t* arm_op() const { return arm_op_; }
uint16_t op_size() const { return op_size_; }
private:
uint16_t compressed_op_;
const uint8_t* arm_op_;
uint16_t op_size_;
};
/******** InstructionCountReceptor ********/
// An InstructionReceptor that counts space occupied by emitted instructions.
class InstructionCountReceptor : public InstructionReceptor {
public:
InstructionCountReceptor() = default;
size_t size() const { return size_; }
// InstructionReceptor:
// TODO(huangs): 2016/11: Populate these with size_ += ...
CheckBool EmitPeRelocs() override { return true; }
CheckBool EmitElfRelocation() override { return true; }
CheckBool EmitElfARMRelocation() override { return true; }
CheckBool EmitOrigin(RVA rva) override { return true; }
CheckBool EmitSingleByte(uint8_t byte) override { return true; }
CheckBool EmitMultipleBytes(const uint8_t* bytes, size_t len) override {
return true;
}
CheckBool EmitRel32(Label* label) override { return true; }
CheckBool EmitRel32ARM(uint16_t op,
Label* label,
const uint8_t* arm_op,
uint16_t op_size) override {
return true;
}
CheckBool EmitAbs32(Label* label) override { return true; }
CheckBool EmitAbs64(Label* label) override { return true; }
private:
size_t size_ = 0;
DISALLOW_COPY_AND_ASSIGN(InstructionCountReceptor);
};
/******** InstructionStoreReceptor ********/
// An InstructionReceptor that stores emitted instructions.
class InstructionStoreReceptor : public InstructionReceptor {
public:
explicit InstructionStoreReceptor(AssemblyProgram* program)
: program_(program) {
CHECK(program_);
}
// TODO(huangs): 2016/11: Add Reserve().
// InstructionReceptor:
// TODO(huangs): 2016/11: Replace stub with implementation.
CheckBool EmitPeRelocs() override { return program_->EmitPeRelocs(); }
CheckBool EmitElfRelocation() override {
return program_->EmitElfRelocation();
}
CheckBool EmitElfARMRelocation() override {
return program_->EmitElfARMRelocation();
}
CheckBool EmitOrigin(RVA rva) override { return program_->EmitOrigin(rva); }
CheckBool EmitSingleByte(uint8_t byte) override {
return program_->EmitSingleByte(byte);
}
CheckBool EmitMultipleBytes(const uint8_t* bytes, size_t len) override {
return program_->EmitMultipleBytes(bytes, len);
}
CheckBool EmitRel32(Label* label) override {
return program_->EmitRel32(label);
}
CheckBool EmitRel32ARM(uint16_t op,
Label* label,
const uint8_t* arm_op,
uint16_t op_size) override {
return program_->EmitRel32ARM(op, label, arm_op, op_size);
}
CheckBool EmitAbs32(Label* label) override {
return program_->EmitAbs32(label);
}
CheckBool EmitAbs64(Label* label) override {
return program_->EmitAbs64(label);
}
private:
AssemblyProgram* program_;
DISALLOW_COPY_AND_ASSIGN(InstructionStoreReceptor);
};
} // namespace
/******** AssemblyProgram ********/
AssemblyProgram::AssemblyProgram(ExecutableType kind, uint64_t image_base)
: kind_(kind), image_base_(image_base) {}
AssemblyProgram::~AssemblyProgram() {
for (size_t i = 0; i < instructions_.size(); ++i) {
Instruction* instruction = instructions_[i];
if (instruction->op() != DEFBYTE) // Owned by byte_instruction_cache_.
UncheckedDelete(instruction);
}
if (byte_instruction_cache_.get()) {
for (size_t i = 0; i < 256; ++i)
UncheckedDelete(byte_instruction_cache_[i]);
}
}
CheckBool AssemblyProgram::EmitPeRelocs() {
return Emit(ScopedInstruction(UncheckedNew<PeRelocsInstruction>()));
}
CheckBool AssemblyProgram::EmitElfRelocation() {
return Emit(ScopedInstruction(UncheckedNew<ElfRelocsInstruction>()));
}
CheckBool AssemblyProgram::EmitElfARMRelocation() {
return Emit(ScopedInstruction(UncheckedNew<ElfARMRelocsInstruction>()));
}
CheckBool AssemblyProgram::EmitOrigin(RVA rva) {
return Emit(ScopedInstruction(UncheckedNew<OriginInstruction>(rva)));
}
CheckBool AssemblyProgram::EmitSingleByte(uint8_t byte) {
return EmitShared(GetByteInstruction(byte));
}
CheckBool AssemblyProgram::EmitMultipleBytes(const uint8_t* bytes, size_t len) {
return Emit(ScopedInstruction(UncheckedNew<BytesInstruction>(bytes, len)));
}
CheckBool AssemblyProgram::EmitRel32(Label* label) {
return Emit(
ScopedInstruction(UncheckedNew<InstructionWithLabel>(REL32, label)));
}
CheckBool AssemblyProgram::EmitRel32ARM(uint16_t op,
Label* label,
const uint8_t* arm_op,
uint16_t op_size) {
return Emit(ScopedInstruction(UncheckedNew<InstructionWithLabelARM>(
REL32ARM, op, label, arm_op, op_size)));
}
CheckBool AssemblyProgram::EmitAbs32(Label* label) {
return Emit(
ScopedInstruction(UncheckedNew<InstructionWithLabel>(ABS32, label)));
}
CheckBool AssemblyProgram::EmitAbs64(Label* label) {
return Emit(
ScopedInstruction(UncheckedNew<InstructionWithLabel>(ABS64, label)));
}
void AssemblyProgram::PrecomputeLabels(RvaVisitor* abs32_visitor,
RvaVisitor* rel32_visitor) {
abs32_label_manager_.Read(abs32_visitor);
rel32_label_manager_.Read(rel32_visitor);
TrimLabels();
}
// Chosen empirically to give the best reduction in payload size for
// an update from daisy_3701.98.0 to daisy_4206.0.0.
const int AssemblyProgram::kLabelLowerLimit = 5;
void AssemblyProgram::TrimLabels() {
// For now only trim for ARM binaries.
if (kind() != EXE_ELF_32_ARM)
return;
int lower_limit = kLabelLowerLimit;
VLOG(1) << "TrimLabels: threshold " << lower_limit;
rel32_label_manager_.RemoveUnderusedLabels(lower_limit);
}
void AssemblyProgram::UnassignIndexes() {
abs32_label_manager_.UnassignIndexes();
rel32_label_manager_.UnassignIndexes();
}
void AssemblyProgram::DefaultAssignIndexes() {
abs32_label_manager_.DefaultAssignIndexes();
rel32_label_manager_.DefaultAssignIndexes();
}
void AssemblyProgram::AssignRemainingIndexes() {
abs32_label_manager_.AssignRemainingIndexes();
rel32_label_manager_.AssignRemainingIndexes();
}
Label* AssemblyProgram::FindAbs32Label(RVA rva) {
return abs32_label_manager_.Find(rva);
}
Label* AssemblyProgram::FindRel32Label(RVA rva) {
return rel32_label_manager_.Find(rva);
}
void AssemblyProgram::HandleInstructionLabels(
const AssemblyProgram::LabelHandlerMap& handler_map) const {
for (const Instruction* instruction : instructions_) {
LabelHandlerMap::const_iterator it = handler_map.find(instruction->op());
if (it != handler_map.end()) {
it->second.Run(
static_cast<const InstructionWithLabel*>(instruction)->label());
}
}
}
CheckBool AssemblyProgram::GenerateInstructions(
const InstructionGenerator& gen) {
// Pass 1: Count the space needed to store instructions.
InstructionCountReceptor count_receptor;
if (!gen.Run(this, &count_receptor))
return false;
// Pass 2: Emit all instructions to preallocated buffer (uses Phase 1 count).
InstructionStoreReceptor store_receptor(this);
// TODO(huangs): 2016/11: Pass |count_receptor_->size()| to |store_receptor_|
// to reserve space for raw data.
return gen.Run(this, &store_receptor);
}
CheckBool AssemblyProgram::Emit(ScopedInstruction instruction) {
if (!instruction || !instructions_.push_back(instruction.get()))
return false;
// Ownership successfully passed to instructions_.
ignore_result(instruction.release());
return true;
}
CheckBool AssemblyProgram::EmitShared(Instruction* instruction) {
DCHECK(!instruction || instruction->op() == DEFBYTE);
return instruction && instructions_.push_back(instruction);
}
std::unique_ptr<EncodedProgram> AssemblyProgram::Encode() const {
std::unique_ptr<EncodedProgram> encoded(new EncodedProgram());
encoded->set_image_base(image_base_);
if (!encoded->ImportLabels(abs32_label_manager_, rel32_label_manager_))
return nullptr;
for (size_t i = 0; i < instructions_.size(); ++i) {
Instruction* instruction = instructions_[i];
switch (instruction->op()) {
case ORIGIN: {
OriginInstruction* org = static_cast<OriginInstruction*>(instruction);
if (!encoded->AddOrigin(org->origin_rva()))
return nullptr;
break;
}
case DEFBYTE: {
uint8_t b = static_cast<ByteInstruction*>(instruction)->byte_value();
if (!encoded->AddCopy(1, &b))
return nullptr;
break;
}
case DEFBYTES: {
const uint8_t* byte_values =
static_cast<BytesInstruction*>(instruction)->byte_values();
size_t len = static_cast<BytesInstruction*>(instruction)->len();
if (!encoded->AddCopy(len, byte_values))
return nullptr;
break;
}
case REL32: {
Label* label = static_cast<InstructionWithLabel*>(instruction)->label();
if (!encoded->AddRel32(label->index_))
return nullptr;
break;
}
case REL32ARM: {
Label* label =
static_cast<InstructionWithLabelARM*>(instruction)->label();
uint16_t compressed_op =
static_cast<InstructionWithLabelARM*>(instruction)->compressed_op();
if (!encoded->AddRel32ARM(compressed_op, label->index_))
return nullptr;
break;
}
case ABS32: {
Label* label = static_cast<InstructionWithLabel*>(instruction)->label();
if (!encoded->AddAbs32(label->index_))
return nullptr;
break;
}
case ABS64: {
Label* label = static_cast<InstructionWithLabel*>(instruction)->label();
if (!encoded->AddAbs64(label->index_))
return nullptr;
break;
}
case MAKEPERELOCS: {
if (!encoded->AddPeMakeRelocs(kind_))
return nullptr;
break;
}
case MAKEELFRELOCS: {
if (!encoded->AddElfMakeRelocs())
return nullptr;
break;
}
case MAKEELFARMRELOCS: {
if (!encoded->AddElfARMMakeRelocs())
return nullptr;
break;
}
default: {
NOTREACHED() << "Unknown Insn OP kind";
}
}
}
return encoded;
}
Instruction* AssemblyProgram::GetByteInstruction(uint8_t byte) {
if (!byte_instruction_cache_) {
Instruction** ram = nullptr;
if (!base::UncheckedMalloc(sizeof(Instruction*) * 256,
reinterpret_cast<void**>(&ram))) {
return nullptr;
}
byte_instruction_cache_.reset(ram);
for (int i = 0; i < 256; ++i) {
byte_instruction_cache_[i] =
UncheckedNew<ByteInstruction>(static_cast<uint8_t>(i));
if (!byte_instruction_cache_[i]) {
for (int j = 0; j < i; ++j)
UncheckedDelete(byte_instruction_cache_[j]);
byte_instruction_cache_.reset();
return nullptr;
}
}
}
return byte_instruction_cache_[byte];
}
////////////////////////////////////////////////////////////////////////////////
Status Encode(const AssemblyProgram& program,
std::unique_ptr<EncodedProgram>* output) {
// Explicitly release any memory associated with the output before encoding.
output->reset();
*output = program.Encode();
return (*output) ? C_OK : C_GENERAL_ERROR;
}
} // namespace courgette