blob: ce32d717a0e752f72a42f81d3e374c0f74874d3e [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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 "arch/arm/final_relocations_arm.h"
#include "compiled_method.h"
#include "driver/compiler_driver.h"
#include "oat_writer.h"
#include "dex/compiler_ir.h"
namespace art {
void FinalEntrypointRelocationSetArm::Apply(uint8_t* code, const OatWriter* writer,
uint32_t address) const {
uint32_t island_offset = writer->GetCurrentTrampolineIslandOffset();
const bool kDebugPrint = false;
for (auto& reloc : relocations_) {
switch (reloc.type_) {
case kRelocationCall: {
// Fetch the instruction. This is two 16 bit words. We can't do a 32 bit load
// because it's not guaranteed to be 4-byte aligned.
uint32_t inst = static_cast<uint32_t>(
*reinterpret_cast<uint16_t*>(code + reloc.code_offset_) << 16
| *reinterpret_cast<uint16_t*>(code + reloc.code_offset_ + 2));
// Remove the current offset from the instruction. This is there
// to prevent deduplication from wrongly handling this instruction.
inst &= ~0x7ff; // Bottom 11 bits.
// Check that we are trying to relocate a Thumb2 BL instruction.
CHECK_EQ(inst, 0xf000d000);
uint32_t pc = address + reloc.code_offset_ + 4; // Thumb PC is instruction + 4
// The trampoline target is to a table starting at the island. Each trampoline
// entry is 4 bytes long.
uint32_t target = island_offset + static_cast<uint32_t>(reloc.value_) * 4;
int32_t delta = target - pc;
if (kDebugPrint) {
LOG(INFO) << "applying final relocation for island " << island_offset;
LOG(INFO) << "pc: " << std::hex << pc << ", target: " << target <<
", reloc.value: " << reloc.value_ << ", delta: " << delta;
}
// Max range for a Thumb2 BL is 16MB. All calls will be to a lower address.
const int32_t kMaxRange = -16 * static_cast<int32_t>(MB);
CHECK_LT(delta, 0);
CHECK_GT(delta, kMaxRange);
// Modify the instruction using the T1 BL instruction format.
// This is equivalent of a R_ARM_THM_CALL ELF relocation.
delta >>= 1; // Low bit is implicit.
uint32_t signbit = (delta >> 31) & 0x1;
uint32_t i1 = (delta >> 22) & 0x1;
uint32_t i2 = (delta >> 21) & 0x1;
uint32_t imm10 = (delta >> 11) & 0x03ff;
uint32_t imm11 = delta & 0x07ff;
uint32_t j1 = (i1 ^ signbit) ? 0 : 1;
uint32_t j2 = (i2 ^ signbit) ? 0 : 1;
uint32_t value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) |
imm11;
// Blit the value into the instruction.
inst |= value;
// Write the instruction back. High 16 bits first, little endian format.
uint32_t offset = reloc.code_offset_;
code[offset+0] = (inst >> 16) & 0xff;
code[offset+1] = (inst >> 24) & 0xff;
code[offset+2] = (inst >> 0) & 0xff;
code[offset+3] = (inst >> 8) & 0xff;
break;
}
default:
LOG(FATAL) << "Unknown entrypoint relocation type " << reloc.type_;
}
}
}
} // namespace art