346 lines
13 KiB
C++
Executable File
346 lines
13 KiB
C++
Executable File
/**********
|
|
* Copyright (c) 2004 Greg Parker. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY GREG PARKER ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
**********/
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <stdint.h>
|
|
|
|
#include "elf.h"
|
|
#include "got.h"
|
|
#include "swap.h"
|
|
#include "image.h"
|
|
#include "symbol.h"
|
|
#include "complain.h"
|
|
#include "relocation.h"
|
|
#include "stringtable.h"
|
|
|
|
#include "section.h"
|
|
|
|
|
|
Section::Section(Image& newImage)
|
|
: mImage(newImage)
|
|
{ }
|
|
|
|
Section::Section(Image &newImage, string newName, uint32_t newType, uint32_t newFlags, uint32_t newVMAddr, uint8_t *newContents, uint32_t newSize)
|
|
: mName(newName),
|
|
mType(newType),
|
|
mFlags(newFlags),
|
|
mVMaddr(newVMAddr),
|
|
mAlign(4),
|
|
mSize(newSize),
|
|
mContents(newContents),
|
|
mLink(0),
|
|
mInfo(0),
|
|
mEntrySize(0),
|
|
mImage(newImage),
|
|
mBaseSymbol(NULL)
|
|
{ }
|
|
|
|
void Section::read(Elf32_Shdr *shdr)
|
|
{
|
|
if (mImage.strtab()) {
|
|
mName = (*mImage.strtab())[swap32(shdr->sh_name)];
|
|
} else {
|
|
mName = ".shstrtab"; // this is the section name strtab itself
|
|
}
|
|
mType = swap32(shdr->sh_type);
|
|
mFlags = swap32(shdr->sh_flags);
|
|
mVMaddr = swap32(shdr->sh_addr);
|
|
mAlign = swap32(shdr->sh_addralign);
|
|
mSize = swap32(shdr->sh_size);
|
|
mContents = mImage.contents() + swap32(shdr->sh_offset);
|
|
// relocations will be read later
|
|
mLink = 0;
|
|
mInfo = 0;
|
|
mEntrySize = swap32(shdr->sh_entsize);
|
|
mBaseSymbol = NULL;
|
|
}
|
|
|
|
void Section::reserveThumbThunk(const Relocation &r)
|
|
{
|
|
Thunk query(r.symbol(), r.addend());
|
|
vector<Thunk>::iterator t;
|
|
// fixme slow, should use hash_map
|
|
t = find(mThumbThunks.begin(), mThumbThunks.end(), query);
|
|
if (t == mThumbThunks.end()) mThumbThunks.push_back(query);
|
|
}
|
|
|
|
void Section::reserveARMThunk(const Relocation &r)
|
|
{
|
|
Thunk query(r.symbol(), r.addend());
|
|
vector<Thunk>::iterator t;
|
|
// fixme slow, should use hash_map
|
|
t = find(mARMThunks.begin(), mARMThunks.end(), query);
|
|
if (t == mARMThunks.end()) mARMThunks.push_back(query);
|
|
}
|
|
|
|
void Section::reserveThunkFromThumb(const Relocation &r, uint16_t insn1,
|
|
uint16_t insn2)
|
|
{
|
|
uint16_t opcode1 = insn1 >> 11;
|
|
uint16_t opcode2 = insn2 >> 11;
|
|
if (opcode1 == 0x001e && opcode2 == 0x001f) {
|
|
// BL: Thumb->Thumb branch, use Thumb thunk
|
|
reserveThumbThunk(r);
|
|
} else if (opcode1 == 0x001e && opcode2 == 0x001d) {
|
|
// BLX: Thumb->ARM branch, use ARM thunk
|
|
reserveARMThunk(r);
|
|
} else {
|
|
unimplemented("unexpected R_ARM_THM_PC22 relocation for Thumb insns "
|
|
"0x%x, 0x%x", insn1, insn2);
|
|
}
|
|
}
|
|
|
|
void Section::reserveThunkFromARM(const Relocation &r, uint32_t insn)
|
|
{
|
|
uint32_t cond = (insn & 0xf0000000) >> 28;
|
|
uint32_t opcode = (insn & 0x0e000000) >> 25;
|
|
if (opcode == 0x05) {
|
|
if (cond == 0x0f) {
|
|
// BLX: ARM->Thumb branch, use Thumb thunk
|
|
reserveThumbThunk(r);
|
|
} else {
|
|
// Bcc, BLcc: ARM->ARM branch, use ARM thunk
|
|
reserveARMThunk(r);
|
|
}
|
|
} else {
|
|
// unknown instruction
|
|
unimplemented("unexpected R_ARM_PC24 relocation for ARM insn 0x%x",
|
|
insn);
|
|
}
|
|
}
|
|
|
|
void Section::applyRelocations(Elf32_Shdr *rhdr)
|
|
{
|
|
Elf32_Rel *rel = (Elf32_Rel *)
|
|
(mImage.contents() + swap32(rhdr->sh_offset));
|
|
Elf32_Rel *relEnd = (Elf32_Rel *)
|
|
((uint8_t *)rel + swap32(rhdr->sh_size));
|
|
|
|
for ( ; rel < relEnd; ++rel) {
|
|
Relocation r(rel, mImage.symtab(), *this);
|
|
const Section *symSection = r.symbol()->section();
|
|
if (!symSection) unimplemented("relocation relative to absolute or common symbol");
|
|
|
|
uint32_t symoffset = symSection->vmaddr();
|
|
Symbol *sym = symSection->baseSymbol();
|
|
if (symSection->isLib())
|
|
{
|
|
sym = r.symbol();
|
|
symoffset += sym->offset();
|
|
}
|
|
|
|
switch (r.type()) {
|
|
case R_ARM_PC24:
|
|
// Linker stored the offset from here to a symbol in a branch insn.
|
|
// This needs no additional relocation if here and the symbol
|
|
// are in the same section.
|
|
if (this == symSection) {
|
|
// Source and dest are in the same section - nothing to do
|
|
} else {
|
|
// Source and dest are in different sections, which may slide
|
|
// relative to each other - create a relocation and a thunk
|
|
int32_t offset = peek32(r.offset()) & 0x00ffffff; // SIGNED!!
|
|
// sign-extend and convert to byte offset from start of insn
|
|
offset = ((offset << 8) >> 8) * 4 + 8;
|
|
Relocation r2 = Relocation(R_ARM_PC24, r.offset(),
|
|
sym,
|
|
vmaddr() + r.offset() +
|
|
offset - symoffset);
|
|
mRelocations.push_back(r2);
|
|
reserveThunkFromARM(r2, peek32(r.offset()));
|
|
}
|
|
break;
|
|
|
|
case R_ARM_THM_PC22:
|
|
// Like R_ARM_PC24, except each of the next two Thumb
|
|
// instructions contains half of a 22-bit offset.
|
|
if (this == symSection) {
|
|
// Source and dest are in the same section - nothing to do
|
|
} else {
|
|
// Source and dest are in different sections, which may slide
|
|
// relative to each other - create a relocation and a thunk
|
|
int32_t offset_hi = peek16(r.offset()) & 0x07ff;
|
|
int32_t offset_lo = peek16(r.offset()+2) & 0x07ff;
|
|
int32_t offset = (offset_hi << 11) | offset_lo;
|
|
// sign-extend and convert to byte offset from start of insn
|
|
offset = ((offset << 10) >> 10) * 2 + 4;
|
|
Relocation r2 = Relocation(R_ARM_THM_PC22, r.offset(),
|
|
sym,
|
|
vmaddr() + r.offset() +
|
|
offset - symoffset);
|
|
|
|
mRelocations.push_back(r2);
|
|
reserveThunkFromThumb(r2, peek16(r.offset()),
|
|
peek16(r.offset()+2));
|
|
}
|
|
break;
|
|
|
|
case R_ARM_GOTOFF:
|
|
// Linker stored the offset from the GOT to a target symbol.
|
|
// This needs no additional relocation if the symbol is in r/w mem.
|
|
if (!symSection->isReadOnly()) {
|
|
// Target symbol is in a r/w section - nothing to do
|
|
// At runtime, the GOT and all r/w sections will be
|
|
// in the same places relative to each other, so
|
|
// linker-generated GOT offsets will be correct.
|
|
} else {
|
|
// Target symbol is in a r/o section - create relocation
|
|
// At runtime, the GOT and the r/o sections will move
|
|
// relative to each other.
|
|
mRelocations.push_back
|
|
(Relocation(R_ARM_GOTOFF, r.offset(),
|
|
symSection->baseSymbol(),
|
|
peek32(r.offset()) - (symSection->vmaddr() - mImage.got().vmaddr())));
|
|
}
|
|
break;
|
|
|
|
case R_ARM_GOT32: {
|
|
// Linker stored the absolute address of a symbol in the GOT.
|
|
// Find the GOT entry containing the symbol, and create a
|
|
// relocation that will change the *GOT entry* at runtime.
|
|
// These new relocations aren't actually created until
|
|
// GOT::buildRelocations() is called.
|
|
uint32_t sym_vmaddr = r.symbol()->vmaddr(thumb);
|
|
GOT& got = mImage.got();
|
|
unsigned int g;
|
|
for (g = 0; g < got.entries().size(); g++) {
|
|
if (got.entries()[g] == sym_vmaddr) {
|
|
if (got.symbols()[g] == NULL) {
|
|
got.symbols()[g] = r.symbol();
|
|
} else if (got.symbols()[g] != r.symbol()) {
|
|
error("single GOT entry has multiple R_ARM_GOT32 uses");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (g == got.entries().size()) {
|
|
error("R_ARM_GOT32 relocation has no associated GOT entry (symbol %s, symbol vmaddr 0x%x, section %s", r.symbol()->name().c_str(), sym_vmaddr, symSection->name().c_str());
|
|
}
|
|
break;
|
|
}
|
|
case R_ARM_ABS32:
|
|
// Linker stored an absolute address at r.offset() in this section
|
|
// Subtract symbol section's linker-determined vmaddr and relocate
|
|
// at runtime using symbol section's base symbol.
|
|
mRelocations.push_back
|
|
(Relocation(R_ARM_ABS32, r.offset(), sym,
|
|
peek32(r.offset()) - symoffset));
|
|
break;
|
|
|
|
case R_ARM_REL32:
|
|
// Linker stored the offset between here and the symbol.
|
|
// If here and the symbol are in different sections, they
|
|
// may move relative to each other at runtime.
|
|
if (this == symSection) {
|
|
// Source and dest are in the same section - nothing to do
|
|
}
|
|
else {
|
|
// Source and dest are in different sections, which may slide
|
|
// relative to each other - create a relocation
|
|
mRelocations.push_back
|
|
(Relocation(R_ARM_REL32, r.offset(),
|
|
sym,
|
|
vmaddr() + r.offset() + peek32(r.offset()) - symoffset));
|
|
}
|
|
break;
|
|
|
|
case R_ARM_GOTPC:
|
|
// Like R_ARM_REL32, but the symbol is the start of the GOT.
|
|
// Replace with an ordinary R_ARM_REL32 relocation that uses
|
|
// the GOT's section symbol instead of _GLOBAL_OFFSET_TABLE_.
|
|
if (isReadOnly()) {
|
|
mRelocations.push_back
|
|
(Relocation(R_ARM_REL32, r.offset(),
|
|
mImage.got().baseSymbol(),
|
|
vmaddr() + r.offset() + peek32(r.offset()) - mImage.got().vmaddr()));
|
|
} else {
|
|
// read/write sections do not move relative to the GOT.
|
|
}
|
|
break;
|
|
|
|
case R_ARM_PLT32:
|
|
if (this != symSection)
|
|
warning("ignoring R_ARM_PLT32 relocation (symbol '%s')",
|
|
r.symbol()->name().c_str());
|
|
break;
|
|
|
|
default:
|
|
unimplemented("relocation type %d", r.type());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Section::isLib(void) const { return strncmp(mName.c_str(),".lib",4)==0; }
|
|
bool Section::isReadOnly(void) const { return ! (mFlags & SHF_WRITE); }
|
|
const string& Section::name(void) const { return mName; }
|
|
uint32_t Section::vmaddr(void) const { return mVMaddr; }
|
|
uint32_t Section::type(void) const { return mType; }
|
|
|
|
Symbol *Section::baseSymbol(void) const
|
|
{
|
|
if (!mBaseSymbol) {
|
|
mBaseSymbol = new Symbol(mName, this, 0, STB_WEAK, STT_SECTION);
|
|
}
|
|
return mBaseSymbol;
|
|
}
|
|
|
|
void Section::setLink(uint32_t newLink) { mLink = newLink; }
|
|
void Section::setInfo(uint32_t newInfo) { mInfo = newInfo; }
|
|
void Section::setEntrySize(uint32_t newEntrySize) { mEntrySize = newEntrySize; }
|
|
|
|
|
|
void Section::emit(Elf32_Shdr *shdr, uint8_t *&buf, uint32_t& bufLen)
|
|
{
|
|
uint32_t offset = bufLen;
|
|
|
|
|
|
// add blank space for relocation thunks
|
|
mThunkSize = 0;
|
|
mThunkSize += mThumbThunks.size() * 16; // Thumb thunks 16 bytes each
|
|
mThunkSize += mARMThunks.size() * 8; // ARM thunks 8 bytes each
|
|
if (mThunkSize && (mSize % 4)) mThunkSize += 4 - (mSize%4); // thunks are 4-byte aligned
|
|
mSize += mThunkSize;
|
|
|
|
// write shdr before buf gets reallocated (shdr points into buf)
|
|
shdr->sh_name = swap32(mImage.strtab()->indexOf(mName));
|
|
shdr->sh_type = swap32(mType);
|
|
shdr->sh_flags = swap32(mFlags);
|
|
shdr->sh_addr = swap32(mVMaddr);
|
|
shdr->sh_offset = swap32(offset);
|
|
shdr->sh_size = swap32(mSize);
|
|
shdr->sh_link = swap32(mLink);
|
|
shdr->sh_info = swap32(mInfo);
|
|
shdr->sh_addralign = swap32(mAlign);
|
|
shdr->sh_entsize = swap32(mEntrySize);
|
|
|
|
if (mType != SHT_NOBITS) {
|
|
bufLen += mSize;
|
|
buf = (uint8_t *)realloc(buf, bufLen);
|
|
memcpy(buf+offset, mContents, mSize - mThunkSize);
|
|
memset(buf+offset+mSize-mThunkSize, 0, mThunkSize);
|
|
}
|
|
}
|