// Copyright 2014 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 "run_length_encoder.h" #include #include "debug.h" #include "elf_traits.h" namespace relocation_packer { namespace { // Generate a vector of deltas between the r_offset fields of adjacent // relative relocations. void GetDeltas(const std::vector& relocations, std::vector* deltas) { CHECK(relocations.size() >= 2); for (size_t i = 0; i < relocations.size() - 1; ++i) { const ELF::Rel* first = &relocations[i]; CHECK(ELF_R_TYPE(first->r_info) == ELF::kRelativeRelocationCode); const ELF::Rel* second = &relocations[i + 1]; CHECK(ELF_R_TYPE(second->r_info) == ELF::kRelativeRelocationCode); // Requires that offsets are 'strictly increasing'. The packing // algorithm fails if this does not hold. CHECK(second->r_offset > first->r_offset); deltas->push_back(second->r_offset - first->r_offset); } } // Condense a set of r_offset deltas into a run-length encoded packing. // Represented as count-delta pairs, where count is the run length and // delta the common difference between adjacent r_offsets. void Condense(const std::vector& deltas, std::vector* packed) { CHECK(!deltas.empty()); size_t count = 0; ELF::Addr current = deltas[0]; // Identify spans of identically valued deltas. for (size_t i = 0; i < deltas.size(); ++i) { const ELF::Addr delta = deltas[i]; if (delta == current) { count++; } else { // We reached the end of a span of identically valued deltas. packed->push_back(count); packed->push_back(current); current = delta; count = 1; } } // Write the final span. packed->push_back(count); packed->push_back(current); } // Uncondense a set of r_offset deltas from a run-length encoded packing. // The initial address for uncondensing, the start index for the first // condensed slot in packed, and the count of pairs are provided. void Uncondense(ELF::Addr addr, const std::vector& packed, size_t start_index, size_t end_index, std::vector* relocations) { // The first relocation is just one created from the initial address. ELF::Rel initial; initial.r_offset = addr; initial.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); relocations->push_back(initial); // Read each count and delta pair, beginning at the start index and // finishing at the end index. for (size_t i = start_index; i < end_index; i += 2) { size_t count = packed[i]; const ELF::Addr delta = packed[i + 1]; CHECK(count > 0 && delta > 0); // Generate relocations for this count and delta pair. while (count) { addr += delta; ELF::Rel relocation; relocation.r_offset = addr; relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); relocations->push_back(relocation); count--; } } } } // namespace // Encode relative relocations into a run-length encoded (packed) // representation. void RelocationRunLengthCodec::Encode(const std::vector& relocations, std::vector* packed) { // If we have zero or one relocation only then there is no packing // possible; a run-length encoding needs a run. if (relocations.size() < 2) return; std::vector deltas; GetDeltas(relocations, &deltas); // Reserve space for the element count. packed->push_back(0); // Initialize the packed data with the first offset, then follow up with // the condensed deltas vector. packed->push_back(relocations[0].r_offset); Condense(deltas, packed); // Fill in the packed pair count. packed->at(0) = (packed->size() - 2) >> 1; } // Decode relative relocations from a run-length encoded (packed) // representation. void RelocationRunLengthCodec::Decode(const std::vector& packed, std::vector* relocations) { // We need at least one packed pair after the packed pair count and start // address to be able to unpack. if (packed.size() < 4) return; // Ensure that the packed data offers enough pairs. There may be zero // padding on it that we ignore. CHECK(packed[0] <= (packed.size() - 2) >> 1); // The first packed vector element is the pairs count and the second the // initial address. Start uncondensing pairs at the third, and finish // at the end of the pairs data. const size_t pairs_count = packed[0]; const ELF::Addr addr = packed[1]; Uncondense(addr, packed, 2, 2 + (pairs_count << 1), relocations); } } // namespace relocation_packer