mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 01:39:21 +02:00
GP-5815 Added ELF Loader GOT allocation support for AARCH64 in support
of object module loading.
This commit is contained in:
parent
8c56fc8e04
commit
438725bafd
5 changed files with 473 additions and 290 deletions
|
@ -0,0 +1,294 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.elf.relocation;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.MemoryBlockUtils;
|
||||
import ghidra.app.util.bin.format.elf.*;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.PointerDataType;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.exception.NotFoundException;
|
||||
|
||||
/**
|
||||
* <code>ElfGotRelocationContext</code> provides ability to generate a
|
||||
* Global Offset Table (GOT) to facilitate GOT related relocations encountered within
|
||||
* object modules.
|
||||
* <P>
|
||||
* NOTE: This should be considered experimental and has its limitations with GOT size and
|
||||
* placement which could behave improperly for large binaries.
|
||||
*
|
||||
* @param <H> ELF relocation handler class
|
||||
*/
|
||||
public abstract class ElfGotRelocationContext<H extends ElfRelocationHandler>
|
||||
extends ElfRelocationContext<H> {
|
||||
|
||||
private AddressRange allocatedGotLimits;
|
||||
private Address allocatedGotAddress;
|
||||
private Address lastAllocatedGotEntryAddress;
|
||||
private Address nextAllocatedGotEntryAddress;
|
||||
|
||||
private Map<Long, Address> gotMap;
|
||||
|
||||
ElfGotRelocationContext(H handler, ElfLoadHelper loadHelper,
|
||||
Map<ElfSymbol, Address> symbolMap) {
|
||||
super(handler, loadHelper, symbolMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSymbolValue(ElfSymbol symbol) {
|
||||
long symbolValue = super.getSymbolValue(symbol);
|
||||
if (symbolValue == 0 && ElfConstants.GOT_SYMBOL_NAME.equals(symbol.getNameAsString())) {
|
||||
Address gotAddr = symbolMap.get(symbol);
|
||||
if (gotAddr == null) {
|
||||
gotAddr = allocateGot();
|
||||
}
|
||||
if (gotAddr != null) {
|
||||
return gotAddr.getOffset();
|
||||
}
|
||||
}
|
||||
return symbolValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getGOTValue() throws NotFoundException {
|
||||
try {
|
||||
return super.getGOTValue();
|
||||
}
|
||||
catch (NotFoundException e) {
|
||||
Address gotAddr = allocateGot();
|
||||
if (gotAddr != null) {
|
||||
return gotAddr.getOffset();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private ElfSymbol findGotElfSymbol() {
|
||||
for (ElfSymbolTable st : getElfHeader().getSymbolTables()) {
|
||||
for (ElfSymbol s : st.getSymbols()) {
|
||||
if (ElfConstants.GOT_SYMBOL_NAME.equals(s.getNameAsString())) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private int computeRequiredGotSize() {
|
||||
// NOTE: GOT allocation calculation assumes all GOT entries correspond to a specific
|
||||
// symbol and not a computed offset. This assumption may need to be revised based upon
|
||||
// uses of getGotEntryAddress method
|
||||
Set<Long> uniqueSymbolValues = new HashSet<>();
|
||||
for (ElfRelocationTable rt : getElfHeader().getRelocationTables()) {
|
||||
ElfSymbolTable st = rt.getAssociatedSymbolTable();
|
||||
if (st == null) {
|
||||
continue;
|
||||
}
|
||||
for (ElfRelocation r : rt.getRelocations()) {
|
||||
int symbolIndex = r.getSymbolIndex();
|
||||
if (!requiresGotEntry(r) || symbolIndex == 0) {
|
||||
continue;
|
||||
}
|
||||
ElfSymbol elfSymbol = st.getSymbol(symbolIndex);
|
||||
if (elfSymbol == null) {
|
||||
continue;
|
||||
}
|
||||
long symbolValue = getSymbolValue(elfSymbol);
|
||||
if (!uniqueSymbolValues.add(symbolValue)) {
|
||||
System.out.println("Duplicate sym value 0x" + Long.toHexString(symbolValue) +
|
||||
" for " + elfSymbol.getNameAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return Math.max(8, uniqueSymbolValues.size() * 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return true if the specified relocation type requires a GOT entry.}
|
||||
* <P>
|
||||
* NOTE: It is very important that all relocations types which invoke
|
||||
* {@link #getGotEntryAddress(ElfSymbol)} result in this method returning true. Failure to
|
||||
* do so could result in an under allocation of the GOT memory block.
|
||||
* @param r relocation type
|
||||
*/
|
||||
protected abstract boolean requiresGotEntry(ElfRelocation r);
|
||||
|
||||
private Address allocateGot() {
|
||||
|
||||
if (allocatedGotAddress != null) {
|
||||
if (allocatedGotAddress == Address.NO_ADDRESS) {
|
||||
return null;
|
||||
}
|
||||
return allocatedGotAddress;
|
||||
}
|
||||
|
||||
allocatedGotAddress = Address.NO_ADDRESS;
|
||||
nextAllocatedGotEntryAddress = Address.NO_ADDRESS;
|
||||
|
||||
ElfSymbol gotElfSymbol = findGotElfSymbol();
|
||||
|
||||
if (gotElfSymbol == null && !getElfHeader().isRelocatable()) {
|
||||
loadHelper
|
||||
.log("GOT allocatiom failed. " + ElfConstants.GOT_SYMBOL_NAME + " not defined");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (gotElfSymbol != null && gotElfSymbol.getValue() != 0) {
|
||||
loadHelper.log(
|
||||
"GOT allocatiom failed. " + ElfConstants.GOT_SYMBOL_NAME + " already defined");
|
||||
return null;
|
||||
}
|
||||
|
||||
int alignment = getLoadAdapter().getLinkageBlockAlignment();
|
||||
int gotSize = computeRequiredGotSize();
|
||||
allocatedGotLimits = getLoadHelper().allocateLinkageBlock(alignment, gotSize,
|
||||
ElfRelocationHandler.GOT_BLOCK_NAME);
|
||||
if (allocatedGotLimits != null &&
|
||||
allocatedGotLimits.getMinAddress().getOffset() < Integer.MAX_VALUE) {
|
||||
// NOTE: GOT must fall within first 32-bit segment
|
||||
if (gotElfSymbol != null) {
|
||||
// remember where GOT was allocated
|
||||
try {
|
||||
loadHelper.createSymbol(allocatedGotLimits.getMinAddress(),
|
||||
ElfConstants.GOT_SYMBOL_NAME, true, false, null);
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
throw new AssertionError("Unexpected exception", e);
|
||||
}
|
||||
symbolMap.put(gotElfSymbol, allocatedGotLimits.getMinAddress());
|
||||
}
|
||||
allocatedGotAddress = allocatedGotLimits.getMinAddress();
|
||||
nextAllocatedGotEntryAddress = allocatedGotAddress;
|
||||
gotMap = new HashMap<>();
|
||||
loadHelper.log("Created " + ElfRelocationHandler.GOT_BLOCK_NAME +
|
||||
" block required for GOT relocation processing");
|
||||
return allocatedGotAddress;
|
||||
}
|
||||
|
||||
loadHelper.log("Failed to allocate GOT block required for relocation processing");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate the next section GOT entry location. If GOT has not been allocated an attempt
|
||||
* will be made to create one. If allocated gotMap will also be established.
|
||||
* @return Address of GOT entry or {@link Address#NO_ADDRESS} if unable to allocate.
|
||||
*/
|
||||
private Address getNextAllocatedGotEntryAddress() {
|
||||
if (nextAllocatedGotEntryAddress == null) {
|
||||
if (allocateGot() == null) {
|
||||
return Address.NO_ADDRESS; // failed to allocate got
|
||||
}
|
||||
}
|
||||
|
||||
Address addr = nextAllocatedGotEntryAddress;
|
||||
if (addr == Address.NO_ADDRESS) {
|
||||
return Address.NO_ADDRESS; // insufficient space in got
|
||||
}
|
||||
|
||||
try {
|
||||
// verify that entry fits in got
|
||||
int pointerSize = loadHelper.getProgram().getDefaultPointerSize();
|
||||
Address lastAddr = nextAllocatedGotEntryAddress.addNoWrap(pointerSize - 1);
|
||||
if (allocatedGotLimits.contains(lastAddr)) {
|
||||
// entry fits in got - update and return entry address
|
||||
lastAllocatedGotEntryAddress = lastAddr;
|
||||
nextAllocatedGotEntryAddress = lastAllocatedGotEntryAddress.addNoWrap(1);
|
||||
if (!allocatedGotLimits.contains(nextAllocatedGotEntryAddress)) {
|
||||
// allocated got space fully consumed
|
||||
nextAllocatedGotEntryAddress = Address.NO_ADDRESS;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// insufficient space in got - fail future allocation attempts
|
||||
nextAllocatedGotEntryAddress = Address.NO_ADDRESS;
|
||||
return Address.NO_ADDRESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or allocate a GOT entry for the specified symbolValue.
|
||||
* NOTE: This is restricted to object modules only which do not of a GOT.
|
||||
* @param elfSymbol ELF symbol
|
||||
* @return GOT entry address or null if unable to allocate
|
||||
*/
|
||||
public Address getGotEntryAddress(ElfSymbol elfSymbol) {
|
||||
long symbolValue = getSymbolValue(elfSymbol);
|
||||
Address addr = null;
|
||||
if (gotMap != null) {
|
||||
addr = gotMap.get(symbolValue);
|
||||
}
|
||||
if (addr == null) {
|
||||
addr = getNextAllocatedGotEntryAddress();
|
||||
if (gotMap != null) {
|
||||
gotMap.put(symbolValue, addr);
|
||||
}
|
||||
}
|
||||
return addr == Address.NO_ADDRESS ? null : addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the section GOT table to a new %got memory block
|
||||
*/
|
||||
private void createGot() {
|
||||
if (lastAllocatedGotEntryAddress == null) {
|
||||
return;
|
||||
}
|
||||
int size = (int) lastAllocatedGotEntryAddress.subtract(allocatedGotAddress) + 1;
|
||||
try {
|
||||
MemoryBlock block = MemoryBlockUtils.createInitializedBlock(program, false,
|
||||
ElfRelocationHandler.GOT_BLOCK_NAME, allocatedGotAddress, size,
|
||||
"NOTE: This block is artificial and allows ELF Relocations to work correctly",
|
||||
"Elf Loader", true, false, false, loadHelper.getLog());
|
||||
|
||||
// Mark block as an artificial fabrication
|
||||
block.setArtificial(true);
|
||||
|
||||
DataConverter converter =
|
||||
program.getMemory().isBigEndian() ? BigEndianDataConverter.INSTANCE
|
||||
: LittleEndianDataConverter.INSTANCE;
|
||||
for (long symbolValue : gotMap.keySet()) {
|
||||
Address addr = gotMap.get(symbolValue);
|
||||
byte[] bytes = converter.getBytes(symbolValue); // 8-byte pointer value
|
||||
block.putBytes(addr, bytes);
|
||||
loadHelper.createData(addr, PointerDataType.dataType);
|
||||
}
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
String msg = "Failed to create GOT at " + allocatedGotAddress;
|
||||
loadHelper.log(msg);
|
||||
Msg.error(this, msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
|
||||
// Generate the object module GOT table if required
|
||||
createGot();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.elf.relocation;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.app.util.bin.format.elf.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
/**
|
||||
* <code>AARCH64_ElfRelocationContext</code> provides ability to generate a
|
||||
* Global Offset Table (GOT) to facilitate GOT related relocations encountered within
|
||||
* object modules.
|
||||
*/
|
||||
class AARCH64_ElfRelocationContext extends ElfGotRelocationContext<AARCH64_ElfRelocationHandler> {
|
||||
|
||||
AARCH64_ElfRelocationContext(AARCH64_ElfRelocationHandler handler, ElfLoadHelper loadHelper,
|
||||
Map<ElfSymbol, Address> symbolMap) {
|
||||
super(handler, loadHelper, symbolMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean requiresGotEntry(ElfRelocation r) {
|
||||
|
||||
AARCH64_ElfRelocationType type = handler.getRelocationType(r.getType());
|
||||
if (type == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
|
||||
// NOTE: There are many more relocation types that require a GOT allocation.
|
||||
//@formatter:off
|
||||
|
||||
//case R_AARCH64_P32_GOT_LD_PREL19:
|
||||
case R_AARCH64_P32_ADR_GOT_PAGE:
|
||||
case R_AARCH64_P32_LD32_GOT_LO12_NC:
|
||||
//case R_AARCH64_P32_LD32_GOTPAGE_LO14:
|
||||
case R_AARCH64_ADR_GOT_PAGE:
|
||||
case R_AARCH64_LD64_GOT_LO12_NC:
|
||||
return true;
|
||||
|
||||
//@formatter:on
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package ghidra.app.util.bin.format.elf.relocation;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ghidra.app.util.bin.format.elf.*;
|
||||
|
@ -25,8 +27,8 @@ import ghidra.program.model.mem.*;
|
|||
import ghidra.program.model.reloc.Relocation.Status;
|
||||
import ghidra.program.model.reloc.RelocationResult;
|
||||
|
||||
public class AARCH64_ElfRelocationHandler
|
||||
extends AbstractElfRelocationHandler<AARCH64_ElfRelocationType, ElfRelocationContext<?>> {
|
||||
public class AARCH64_ElfRelocationHandler extends
|
||||
AbstractElfRelocationHandler<AARCH64_ElfRelocationType, AARCH64_ElfRelocationContext> {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -46,11 +48,17 @@ public class AARCH64_ElfRelocationHandler
|
|||
}
|
||||
|
||||
@Override
|
||||
protected RelocationResult relocate(ElfRelocationContext<?> elfRelocationContext,
|
||||
public AARCH64_ElfRelocationContext createRelocationContext(ElfLoadHelper loadHelper,
|
||||
Map<ElfSymbol, Address> symbolMap) {
|
||||
return new AARCH64_ElfRelocationContext(this, loadHelper, symbolMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RelocationResult relocate(AARCH64_ElfRelocationContext elfRelocationContext,
|
||||
ElfRelocation relocation, AARCH64_ElfRelocationType type, Address relocationAddress,
|
||||
ElfSymbol sym, Address symbolAddr, long symbolValue, String symbolName)
|
||||
throws MemoryAccessException {
|
||||
|
||||
|
||||
Program program = elfRelocationContext.getProgram();
|
||||
Memory memory = program.getMemory();
|
||||
boolean isBigEndianInstructions =
|
||||
|
@ -58,13 +66,13 @@ public class AARCH64_ElfRelocationHandler
|
|||
|
||||
long addend = relocation.getAddend(); // will be 0 for REL case
|
||||
long offset = relocationAddress.getOffset();
|
||||
|
||||
|
||||
int symbolIndex = relocation.getSymbolIndex();
|
||||
boolean is64bit = true;
|
||||
boolean overflowCheck = true; // *_NC type relocations specify "no overflow check"
|
||||
long newValue = 0;
|
||||
int byteLength = 4; // most relocations affect 4-bytes (change if different)
|
||||
|
||||
|
||||
// Handle relative relocations that do not require symbolAddr or symbolValue
|
||||
switch (type) {
|
||||
case R_AARCH64_P32_RELATIVE:
|
||||
|
@ -84,7 +92,7 @@ public class AARCH64_ElfRelocationHandler
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// Check for unresolved symbolAddr and symbolValue required by remaining relocation types handled below
|
||||
if (handleUnresolvedSymbol(elfRelocationContext, relocation, relocationAddress)) {
|
||||
return RelocationResult.FAILURE;
|
||||
|
@ -308,8 +316,7 @@ public class AARCH64_ElfRelocationHandler
|
|||
|
||||
// LD/ST64: (S+A) & 0xff8
|
||||
case R_AARCH64_LDST64_ABS_LO12_NC:
|
||||
case R_AARCH64_P32_LDST64_ABS_LO12_NC:
|
||||
case R_AARCH64_LD64_GOT_LO12_NC: {
|
||||
case R_AARCH64_P32_LDST64_ABS_LO12_NC: {
|
||||
int oldValue = memory.getInt(relocationAddress, isBigEndianInstructions);
|
||||
newValue = (int) ((symbolValue + addend) & 0xff8) >> 3;
|
||||
|
||||
|
@ -330,6 +337,53 @@ public class AARCH64_ElfRelocationHandler
|
|||
break;
|
||||
}
|
||||
|
||||
// ADRP: Page(G(GDAT(S)))-Page(P)
|
||||
// Page of GOT entry address for S minus page of relocation address
|
||||
// Set the immediate value of an ADRP to bits [32:12] of X
|
||||
case R_AARCH64_ADR_GOT_PAGE:
|
||||
case R_AARCH64_P32_ADR_GOT_PAGE: {
|
||||
int oldValue = memory.getInt(relocationAddress, isBigEndianInstructions);
|
||||
Address symbolGotAddress = elfRelocationContext.getGotEntryAddress(sym);
|
||||
if (symbolGotAddress == null) {
|
||||
markAsError(program, relocationAddress, type, symbolName, symbolIndex,
|
||||
"GOT allocation failure", elfRelocationContext.getLog());
|
||||
return RelocationResult.FAILURE;
|
||||
}
|
||||
newValue = page(symbolGotAddress.getOffset()) - page(relocationAddress.getOffset());
|
||||
newValue = (newValue & 0x1fffff000L) >>> 12;
|
||||
int loBits = (int) newValue & 0x3;
|
||||
int hiBits = (int) newValue >>> 2;
|
||||
|
||||
newValue = (oldValue & 0x9f00001fL) | (loBits << 29) | (hiBits << 5);
|
||||
memory.setInt(relocationAddress, (int) newValue, isBigEndianInstructions);
|
||||
break;
|
||||
}
|
||||
|
||||
// LD/ST: G(GDAT(S))
|
||||
case R_AARCH64_P32_LD32_GOT_LO12_NC: // Set the LD/ST immediate field to bits [11:2] of X
|
||||
case R_AARCH64_LD64_GOT_LO12_NC: // Set the LD/ST immediate field to bits [11:3] of X
|
||||
{
|
||||
int oldValue = memory.getInt(relocationAddress, isBigEndianInstructions);
|
||||
Address symbolGotAddress = elfRelocationContext.getGotEntryAddress(sym);
|
||||
if (symbolGotAddress == null) {
|
||||
markAsError(program, relocationAddress, type, symbolName, symbolIndex,
|
||||
"GOT allocation failure", elfRelocationContext.getLog());
|
||||
return RelocationResult.FAILURE;
|
||||
}
|
||||
int lo12bits = (int) (symbolGotAddress.getOffset() & 0xfff);
|
||||
if (type == AARCH64_ElfRelocationType.R_AARCH64_P32_LD32_GOT_LO12_NC) {
|
||||
lo12bits &= ~0x3;
|
||||
}
|
||||
else {
|
||||
lo12bits &= ~0x7;
|
||||
}
|
||||
|
||||
// Bits [21:10] — imm12: 12-bit unsigned immediate offset
|
||||
newValue = (oldValue & 0xffc003ffL) | (lo12bits << 10);
|
||||
memory.setInt(relocationAddress, (int) newValue, isBigEndianInstructions);
|
||||
break;
|
||||
}
|
||||
|
||||
case R_AARCH64_P32_GLOB_DAT:
|
||||
is64bit = false;
|
||||
case R_AARCH64_GLOB_DAT: {
|
||||
|
@ -379,6 +433,10 @@ public class AARCH64_ElfRelocationHandler
|
|||
return new RelocationResult(Status.APPLIED, byteLength);
|
||||
}
|
||||
|
||||
private long page(long offset) {
|
||||
return offset & ~0xfff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new value in memory
|
||||
* @param memory memory
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -17,6 +17,17 @@ package ghidra.app.util.bin.format.elf.relocation;
|
|||
|
||||
public enum AARCH64_ElfRelocationType implements ElfRelocationType {
|
||||
|
||||
// Comment macros:
|
||||
// S - symbol address
|
||||
// A - addend
|
||||
// P - relocation address
|
||||
// X - result of a relocation operation
|
||||
// PG(expr) = Page(expr) = expr & ~0xFFF
|
||||
// G(expr) = Address of GOT entry corresponding to expr
|
||||
// GDAT(expr) = GOT entry for expression expr value
|
||||
//
|
||||
// Reference: https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst
|
||||
|
||||
R_AARCH64_NONE(0),
|
||||
R_AARCH64_P32_ABS32(1), // .word: (S+A)
|
||||
R_AARCH64_P32_ABS16(2), // .half: (S+A)
|
||||
|
@ -40,10 +51,10 @@ public enum AARCH64_ElfRelocationType implements ElfRelocationType {
|
|||
R_AARCH64_P32_JUMP26(20), // B: ((S+A-P) >> 2) & 0x3ffffff.
|
||||
R_AARCH64_P32_CALL26(21), // BL: ((S+A-P) >> 2) & 0x3ffffff.
|
||||
|
||||
R_AARCH64_P32_GOT_LD_PREL19(25),
|
||||
R_AARCH64_P32_ADR_GOT_PAGE(26),
|
||||
R_AARCH64_P32_LD32_GOT_LO12_NC(27),
|
||||
R_AARCH64_P32_LD32_GOTPAGE_LO14(28),
|
||||
R_AARCH64_P32_GOT_LD_PREL19(25), // G(GDAT(S))- P
|
||||
R_AARCH64_P32_ADR_GOT_PAGE(26), // ADRP: Page(G(GDAT(S)))-Page(P)
|
||||
R_AARCH64_P32_LD32_GOT_LO12_NC(27), // LD/ST: G(GDAT(S))
|
||||
R_AARCH64_P32_LD32_GOTPAGE_LO14(28), // LD/ST: G(GDAT(S))-Page(GOT)
|
||||
|
||||
R_AARCH64_P32_TLSGD_ADR_PREL21(80),
|
||||
R_AARCH64_P32_TLSGD_ADR_PAGE21(81),
|
||||
|
@ -82,9 +93,9 @@ public enum AARCH64_ElfRelocationType implements ElfRelocationType {
|
|||
R_AARCH64_P32_TLSDESC_ADD_LO12_NC(126),
|
||||
R_AARCH64_P32_TLSDESC_CALL(127),
|
||||
|
||||
R_AARCH64_P32_COPY(180), // Copy symbol at runtime.
|
||||
R_AARCH64_P32_GLOB_DAT(181), // Create GOT entry.
|
||||
R_AARCH64_P32_JUMP_SLOT(182), // Create PLT entry.
|
||||
R_AARCH64_P32_COPY(180), // Copy symbol value at runtime.
|
||||
R_AARCH64_P32_GLOB_DAT(181), // Relocate GOT entry.
|
||||
R_AARCH64_P32_JUMP_SLOT(182), // Relocate PLT entry.
|
||||
|
||||
// Adjust by program base.
|
||||
R_AARCH64_P32_RELATIVE(183),
|
||||
|
@ -127,28 +138,28 @@ public enum AARCH64_ElfRelocationType implements ElfRelocationType {
|
|||
R_AARCH64_LDST16_ABS_LO12_NC(284), // LD/ST16: (S+A) & 0xffe
|
||||
R_AARCH64_LDST32_ABS_LO12_NC(285), // LD/ST32: (S+A) & 0xffc
|
||||
R_AARCH64_LDST64_ABS_LO12_NC(286), // LD/ST64: (S+A) & 0xff8
|
||||
R_AARCH64_MOVW_PREL_G0(287),
|
||||
R_AARCH64_MOVW_PREL_G0_NC(288),
|
||||
R_AARCH64_MOVW_PREL_G1(289),
|
||||
R_AARCH64_MOVW_PREL_G1_NC(290),
|
||||
R_AARCH64_MOVW_PREL_G2(291),
|
||||
R_AARCH64_MOVW_PREL_G2_NC(292),
|
||||
R_AARCH64_MOVW_PREL_G3(293),
|
||||
R_AARCH64_MOVW_PREL_G0(287), // S+A-P
|
||||
R_AARCH64_MOVW_PREL_G0_NC(288), // S+A-P
|
||||
R_AARCH64_MOVW_PREL_G1(289), // S+A-P
|
||||
R_AARCH64_MOVW_PREL_G1_NC(290), // S+A-P
|
||||
R_AARCH64_MOVW_PREL_G2(291), // S+A-P
|
||||
R_AARCH64_MOVW_PREL_G2_NC(292), // S+A-P
|
||||
R_AARCH64_MOVW_PREL_G3(293), // S+A-P
|
||||
R_AARCH64_LDST128_ABS_LO12_NC(299), // LD/ST128: (S+A) & 0xff0
|
||||
R_AARCH64_MOVW_GOTOFF_G0(300),
|
||||
R_AARCH64_MOVW_GOTOFF_G0_NC(301),
|
||||
R_AARCH64_MOVW_GOTOFF_G1(302),
|
||||
R_AARCH64_MOVW_GOTOFF_G1_NC(303),
|
||||
R_AARCH64_MOVW_GOTOFF_G2(304),
|
||||
R_AARCH64_MOVW_GOTOFF_G2_NC(305),
|
||||
R_AARCH64_MOVW_GOTOFF_G3(306),
|
||||
R_AARCH64_GOTREL64(307),
|
||||
R_AARCH64_GOTREL32(308),
|
||||
R_AARCH64_GOT_LD_PREL19(309),
|
||||
R_AARCH64_LD64_GOTOFF_LO15(310),
|
||||
R_AARCH64_ADR_GOT_PAGE(311),
|
||||
R_AARCH64_LD64_GOT_LO12_NC(312),
|
||||
R_AARCH64_LD64_GOTPAGE_LO15(313),
|
||||
R_AARCH64_MOVW_GOTOFF_G0(300), // MOV[NZ]: G(GDAT(S)) -GOT
|
||||
R_AARCH64_MOVW_GOTOFF_G0_NC(301), // MOVK: G(GDAT(S)) -GOT
|
||||
R_AARCH64_MOVW_GOTOFF_G1(302), // MOV[NZ]: G(GDAT(S)) -GOT
|
||||
R_AARCH64_MOVW_GOTOFF_G1_NC(303), // MOVK: G(GDAT(S)) -GOT
|
||||
R_AARCH64_MOVW_GOTOFF_G2(304), // MOV[NZ]: G(GDAT(S)) -GOT
|
||||
R_AARCH64_MOVW_GOTOFF_G2_NC(305), // MOVK: G(GDAT(S)) -GOT
|
||||
R_AARCH64_MOVW_GOTOFF_G3(306), // MOV[NZ]: G(GDAT(S)) -GOT
|
||||
R_AARCH64_GOTREL64(307), // S+A-GOT
|
||||
R_AARCH64_GOTREL32(308), // S+A-GOT
|
||||
R_AARCH64_GOT_LD_PREL19(309), // G(GDAT(S))- P
|
||||
R_AARCH64_LD64_GOTOFF_LO15(310), // LD/ST: G(GDAT(S))- GOT
|
||||
R_AARCH64_ADR_GOT_PAGE(311), // ADRP: Page(G(GDAT(S)))-Page(P)
|
||||
R_AARCH64_LD64_GOT_LO12_NC(312), // LD/ST: G(GDAT(S))
|
||||
R_AARCH64_LD64_GOTPAGE_LO15(313), // LD/ST: G(GDAT(S))-Page(GOT)
|
||||
|
||||
R_AARCH64_TLSGD_ADR_PREL21(512),
|
||||
R_AARCH64_TLSGD_ADR_PAGE21(513),
|
||||
|
@ -226,9 +237,9 @@ public enum AARCH64_ElfRelocationType implements ElfRelocationType {
|
|||
R_AARCH64_TLS_DTPMOD64(1028),
|
||||
R_AARCH64_TLS_DTPREL64(1029),
|
||||
R_AARCH64_TLS_TPREL64(1030),
|
||||
R_AARCH64_TLS_DTPMOD(1028),
|
||||
R_AARCH64_TLS_DTPREL(1029),
|
||||
R_AARCH64_TLS_TPREL(1030),
|
||||
R_AARCH64_TLS_DTPMOD(1028), // aka R_AARCH64_TLS_DTPMOD64
|
||||
R_AARCH64_TLS_DTPREL(1029), // aka R_AARCH64_TLS_DTPREL64
|
||||
R_AARCH64_TLS_TPREL(1030), // aka R_AARCH64_TLS_TPREL64
|
||||
R_AARCH64_TLSDESC(1031),
|
||||
R_AARCH64_IRELATIVE(1032);
|
||||
|
||||
|
|
|
@ -15,31 +15,17 @@
|
|||
*/
|
||||
package ghidra.app.util.bin.format.elf.relocation;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.app.util.MemoryBlockUtils;
|
||||
import ghidra.app.util.bin.format.elf.*;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.PointerDataType;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.exception.NotFoundException;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
/**
|
||||
* <code>X86_64_ElfRelocationContext</code> provides ability to generate a
|
||||
* Global Offset Table (GOT) to facilitate GOT related relocations encountered within
|
||||
* object modules.
|
||||
*/
|
||||
class X86_64_ElfRelocationContext extends ElfRelocationContext<X86_64_ElfRelocationHandler> {
|
||||
|
||||
private AddressRange allocatedGotLimits;
|
||||
private Address allocatedGotAddress;
|
||||
private Address lastAllocatedGotEntryAddress;
|
||||
private Address nextAllocatedGotEntryAddress;
|
||||
|
||||
private Map<Long, Address> gotMap;
|
||||
class X86_64_ElfRelocationContext extends ElfGotRelocationContext<X86_64_ElfRelocationHandler> {
|
||||
|
||||
X86_64_ElfRelocationContext(X86_64_ElfRelocationHandler handler, ElfLoadHelper loadHelper,
|
||||
Map<ElfSymbol, Address> symbolMap) {
|
||||
|
@ -47,75 +33,7 @@ class X86_64_ElfRelocationContext extends ElfRelocationContext<X86_64_ElfRelocat
|
|||
}
|
||||
|
||||
@Override
|
||||
public long getSymbolValue(ElfSymbol symbol) {
|
||||
long symbolValue = super.getSymbolValue(symbol);
|
||||
if (symbolValue == 0 && ElfConstants.GOT_SYMBOL_NAME.equals(symbol.getNameAsString())) {
|
||||
Address gotAddr = symbolMap.get(symbol);
|
||||
if (gotAddr == null) {
|
||||
gotAddr = allocateGot();
|
||||
}
|
||||
if (gotAddr != null) {
|
||||
return gotAddr.getOffset();
|
||||
}
|
||||
}
|
||||
return symbolValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getGOTValue() throws NotFoundException {
|
||||
try {
|
||||
return super.getGOTValue();
|
||||
}
|
||||
catch (NotFoundException e) {
|
||||
Address gotAddr = allocateGot();
|
||||
if (gotAddr != null) {
|
||||
return gotAddr.getOffset();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private ElfSymbol findGotElfSymbol() {
|
||||
for (ElfSymbolTable st : getElfHeader().getSymbolTables()) {
|
||||
for (ElfSymbol s : st.getSymbols()) {
|
||||
if (ElfConstants.GOT_SYMBOL_NAME.equals(s.getNameAsString())) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private int computeRequiredGotSize() {
|
||||
// NOTE: GOT allocation calculation assumes all GOT entries correspond to a specific
|
||||
// symbol and not a computed offset. This assumption may need to be revised based upon
|
||||
// uses of getGotEntryAddress method
|
||||
Set<Long> uniqueSymbolValues = new HashSet<>();
|
||||
for (ElfRelocationTable rt : getElfHeader().getRelocationTables()) {
|
||||
ElfSymbolTable st = rt.getAssociatedSymbolTable();
|
||||
if (st == null) {
|
||||
continue;
|
||||
}
|
||||
for (ElfRelocation r : rt.getRelocations()) {
|
||||
int symbolIndex = r.getSymbolIndex();
|
||||
if (!requiresGotEntry(r) || symbolIndex == 0) {
|
||||
continue;
|
||||
}
|
||||
ElfSymbol elfSymbol = st.getSymbol(symbolIndex);
|
||||
if (elfSymbol == null) {
|
||||
continue;
|
||||
}
|
||||
long symbolValue = getSymbolValue(elfSymbol);
|
||||
if (!uniqueSymbolValues.add(symbolValue)) {
|
||||
System.out.println("Duplicate sym value 0x" + Long.toHexString(symbolValue) +
|
||||
" for " + elfSymbol.getNameAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return Math.max(8, uniqueSymbolValues.size() * 8);
|
||||
}
|
||||
|
||||
private boolean requiresGotEntry(ElfRelocation r) {
|
||||
protected boolean requiresGotEntry(ElfRelocation r) {
|
||||
|
||||
X86_64_ElfRelocationType type = handler.getRelocationType(r.getType());
|
||||
if (type == null) {
|
||||
|
@ -140,164 +58,4 @@ class X86_64_ElfRelocationContext extends ElfRelocationContext<X86_64_ElfRelocat
|
|||
}
|
||||
}
|
||||
|
||||
private Address allocateGot() {
|
||||
|
||||
if (allocatedGotAddress != null) {
|
||||
if (allocatedGotAddress == Address.NO_ADDRESS) {
|
||||
return null;
|
||||
}
|
||||
return allocatedGotAddress;
|
||||
}
|
||||
|
||||
allocatedGotAddress = Address.NO_ADDRESS;
|
||||
nextAllocatedGotEntryAddress = Address.NO_ADDRESS;
|
||||
|
||||
ElfSymbol gotElfSymbol = findGotElfSymbol();
|
||||
|
||||
if (gotElfSymbol == null && !getElfHeader().isRelocatable()) {
|
||||
loadHelper
|
||||
.log("GOT allocatiom failed. " + ElfConstants.GOT_SYMBOL_NAME + " not defined");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (gotElfSymbol != null && gotElfSymbol.getValue() != 0) {
|
||||
loadHelper
|
||||
.log("GOT allocatiom failed. " + ElfConstants.GOT_SYMBOL_NAME + " already defined");
|
||||
return null;
|
||||
}
|
||||
|
||||
int alignment = getLoadAdapter().getLinkageBlockAlignment();
|
||||
int gotSize = computeRequiredGotSize();
|
||||
allocatedGotLimits = getLoadHelper().allocateLinkageBlock(alignment, gotSize,
|
||||
ElfRelocationHandler.GOT_BLOCK_NAME);
|
||||
if (allocatedGotLimits != null &&
|
||||
allocatedGotLimits.getMinAddress().getOffset() < Integer.MAX_VALUE) {
|
||||
// NOTE: GOT must fall within first 32-bit segment
|
||||
if (gotElfSymbol != null) {
|
||||
// remember where GOT was allocated
|
||||
try {
|
||||
loadHelper.createSymbol(allocatedGotLimits.getMinAddress(),
|
||||
ElfConstants.GOT_SYMBOL_NAME, true, false, null);
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
throw new AssertionError("Unexpected exception", e);
|
||||
}
|
||||
symbolMap.put(gotElfSymbol, allocatedGotLimits.getMinAddress());
|
||||
}
|
||||
allocatedGotAddress = allocatedGotLimits.getMinAddress();
|
||||
nextAllocatedGotEntryAddress = allocatedGotAddress;
|
||||
gotMap = new HashMap<>();
|
||||
loadHelper.log("Created " + ElfRelocationHandler.GOT_BLOCK_NAME +
|
||||
" block required for GOT relocation processing");
|
||||
return allocatedGotAddress;
|
||||
}
|
||||
|
||||
loadHelper.log("Failed to allocate GOT block required for relocation processing");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate the next section GOT entry location. If GOT has not been allocated an attempt
|
||||
* will be made to create one. If allocated gotMap will also be established.
|
||||
* @return Address of GOT entry or {@link Address#NO_ADDRESS} if unable to allocate.
|
||||
*/
|
||||
private Address getNextAllocatedGotEntryAddress() {
|
||||
if (nextAllocatedGotEntryAddress == null) {
|
||||
if (allocateGot() == null) {
|
||||
return Address.NO_ADDRESS; // failed to allocate got
|
||||
}
|
||||
}
|
||||
|
||||
Address addr = nextAllocatedGotEntryAddress;
|
||||
if (addr == Address.NO_ADDRESS) {
|
||||
return Address.NO_ADDRESS; // insufficient space in got
|
||||
}
|
||||
|
||||
try {
|
||||
// verify that entry fits in got
|
||||
int pointerSize = loadHelper.getProgram().getDefaultPointerSize();
|
||||
Address lastAddr = nextAllocatedGotEntryAddress.addNoWrap(pointerSize - 1);
|
||||
if (allocatedGotLimits.contains(lastAddr)) {
|
||||
// entry fits in got - update and return entry address
|
||||
lastAllocatedGotEntryAddress = lastAddr;
|
||||
nextAllocatedGotEntryAddress = lastAllocatedGotEntryAddress.addNoWrap(1);
|
||||
if (!allocatedGotLimits.contains(nextAllocatedGotEntryAddress)) {
|
||||
// allocated got space fully consumed
|
||||
nextAllocatedGotEntryAddress = Address.NO_ADDRESS;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// insufficient space in got - fail future allocation attempts
|
||||
nextAllocatedGotEntryAddress = Address.NO_ADDRESS;
|
||||
return Address.NO_ADDRESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or allocate a GOT entry for the specified symbolValue.
|
||||
* NOTE: This is restricted to object modules only which do not of a GOT.
|
||||
* @param elfSymbol ELF symbol
|
||||
* @return GOT entry address or null if unable to allocate
|
||||
*/
|
||||
public Address getGotEntryAddress(ElfSymbol elfSymbol) {
|
||||
long symbolValue = getSymbolValue(elfSymbol);
|
||||
Address addr = null;
|
||||
if (gotMap != null) {
|
||||
addr = gotMap.get(symbolValue);
|
||||
}
|
||||
if (addr == null) {
|
||||
addr = getNextAllocatedGotEntryAddress();
|
||||
if (gotMap != null) {
|
||||
gotMap.put(symbolValue, addr);
|
||||
}
|
||||
}
|
||||
return addr == Address.NO_ADDRESS ? null : addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the section GOT table to a new %got memory block
|
||||
*/
|
||||
private void createGot() {
|
||||
if (lastAllocatedGotEntryAddress == null) {
|
||||
return;
|
||||
}
|
||||
int size = (int) lastAllocatedGotEntryAddress.subtract(allocatedGotAddress) + 1;
|
||||
try {
|
||||
MemoryBlock block = MemoryBlockUtils.createInitializedBlock(program, false,
|
||||
ElfRelocationHandler.GOT_BLOCK_NAME, allocatedGotAddress, size,
|
||||
"NOTE: This block is artificial and allows ELF Relocations to work correctly",
|
||||
"Elf Loader", true, false, false, loadHelper.getLog());
|
||||
|
||||
// Mark block as an artificial fabrication
|
||||
block.setArtificial(true);
|
||||
|
||||
DataConverter converter =
|
||||
program.getMemory().isBigEndian() ? BigEndianDataConverter.INSTANCE
|
||||
: LittleEndianDataConverter.INSTANCE;
|
||||
for (long symbolValue : gotMap.keySet()) {
|
||||
Address addr = gotMap.get(symbolValue);
|
||||
byte[] bytes = converter.getBytes(symbolValue); // 8-byte pointer value
|
||||
block.putBytes(addr, bytes);
|
||||
loadHelper.createData(addr, PointerDataType.dataType);
|
||||
}
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
String msg = "Failed to create GOT at " + allocatedGotAddress;
|
||||
loadHelper.log(msg);
|
||||
Msg.error(this, msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
|
||||
// Generate the object module GOT table if required
|
||||
createGot();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue