GP-5815 Added ELF Loader GOT allocation support for AARCH64 in support

of object module loading.
This commit is contained in:
ghidra1 2025-07-11 16:17:19 -04:00
parent 8c56fc8e04
commit 438725bafd
5 changed files with 473 additions and 290 deletions

View file

@ -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();
}
}

View file

@ -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;
}
}
}

View file

@ -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

View file

@ -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);

View file

@ -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();
}
}