Merge remote-tracking branch

'origin/GP-3091_ghidra_ElfPowerPC64_ELFv1Fixes' (Closes #570)
This commit is contained in:
Ryan Kurtz
2025-07-25 19:07:17 +00:00
7 changed files with 375 additions and 176 deletions
@@ -216,9 +216,13 @@ public class ClearFlowAndRepairCmd extends BackgroundCommand<Program> {
monitor.checkCancelled(); monitor.checkCancelled();
Symbol[] syms = symTable.getSymbols(addr); Symbol[] syms = symTable.getSymbols(addr);
for (Symbol sym : syms) { for (Symbol sym : syms) {
// TODO: GP-5872 This code is suspect - should it be restricted to LABELS only.
// Why would we remove a non-default function that had references?
if (sym.getSource() == SourceType.DEFAULT) { if (sym.getSource() == SourceType.DEFAULT) {
break; break;
} }
// TODO: GP-5872 If one of many labels at a location has a direct reference why
// would we bail when we may have already removed a few that did not.
if (sym.hasReferences()) { if (sym.hasReferences()) {
continue; continue;
} }
@@ -718,6 +722,11 @@ public class ClearFlowAndRepairCmd extends BackgroundCommand<Program> {
if (source == SourceType.USER_DEFINED || source == SourceType.IMPORTED) { if (source == SourceType.USER_DEFINED || source == SourceType.IMPORTED) {
continue; // keep imported or user-defined function continue; // keep imported or user-defined function
} }
// TODO: GP-5872 Clearing thunks explicitly created by loader or pattern
// generally have default SourceType and may not have references
// to them. We need to prevent these thunks from getting cleared.
// PowerPC64 ELF extension was forced to rename thunks it created to
// avoid this where the thunk rename has its own set of issues.
} }
if (clearOffcut && !destAddrs.contains(blockAddr)) { if (clearOffcut && !destAddrs.contains(blockAddr)) {
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -167,8 +167,7 @@ public class ElfDefaultGotPltMarkup {
// PLT head is unknown. If the binary has not placed external symbols within the PLT // PLT head is unknown. If the binary has not placed external symbols within the PLT
// processing and disassembly of the PLT may be skipped. // processing and disassembly of the PLT may be skipped.
long pltgot = elf.adjustAddressForPrelink( long pltgot = elf.adjustAddressForPrelink(dynamicTable.getDynamicValue(pltGotType));
dynamicTable.getDynamicValue(pltGotType));
Address gotStart = defaultSpace.getAddress(pltgot + imageBaseAdj); Address gotStart = defaultSpace.getAddress(pltgot + imageBaseAdj);
ElfRelocation[] relocations = relocationTable.getRelocations(); ElfRelocation[] relocations = relocationTable.getRelocations();
@@ -354,7 +353,7 @@ public class ElfDefaultGotPltMarkup {
* Mark-up all GOT entries as pointers within the memory range gotStart to * Mark-up all GOT entries as pointers within the memory range gotStart to
* gotEnd. * gotEnd.
* @param gotStart address for start of GOT * @param gotStart address for start of GOT
* @param gotEnd address for end of GOT * @param gotEnd address for end of GOT (inclusive)
* @param monitor task monitor * @param monitor task monitor
* @throws CancelledException thrown if task cancelled * @throws CancelledException thrown if task cancelled
*/ */
@@ -402,21 +401,17 @@ public class ElfDefaultGotPltMarkup {
try { try {
int pointerSize = program.getDataTypeManager().getDataOrganization().getPointerSize(); int pointerSize = program.getDataTypeManager().getDataOrganization().getPointerSize();
long gotSizeRemaining = gotEnd.subtract(gotStart) + 1;
long entryOffset = 0;
Address newImageBase = null; Address newImageBase = null;
Address nextGotAddr = gotStart; while (gotSizeRemaining >= pointerSize) {
while (gotEnd.subtract(nextGotAddr) >= pointerSize) { Address nextGotAddr = gotStart.addNoWrap(entryOffset);
data = createPointer(nextGotAddr, true); data = createPointer(nextGotAddr, true);
if (data == null) { if (data == null) {
break; break;
} }
gotSizeRemaining -= pointerSize;
try { entryOffset += pointerSize;
nextGotAddr = data.getMaxAddress().add(1);
}
catch (AddressOutOfBoundsException e) {
break; // no more room
}
newImageBase = UglyImageBaseCheck(data, newImageBase); newImageBase = UglyImageBaseCheck(data, newImageBase);
} }
if (newImageBase != null) { if (newImageBase != null) {
@@ -573,12 +568,6 @@ public class ElfDefaultGotPltMarkup {
} }
int pointerSize = program.getDataTypeManager().getDataOrganization().getPointerSize(); int pointerSize = program.getDataTypeManager().getDataOrganization().getPointerSize();
Pointer pointer = PointerDataType.dataType.clone(program.getDataTypeManager()); Pointer pointer = PointerDataType.dataType.clone(program.getDataTypeManager());
if (elf.is32Bit() && pointerSize != 4) {
pointer = Pointer32DataType.dataType;
}
else if (elf.is64Bit() && pointerSize != 8) {
pointer = Pointer64DataType.dataType;
}
Data data = listing.getDataAt(addr); Data data = listing.getDataAt(addr);
if (data == null || !pointer.isEquivalent(data.getDataType())) { if (data == null || !pointer.isEquivalent(data.getDataType())) {
if (data != null) { if (data != null) {
@@ -623,7 +612,10 @@ public class ElfDefaultGotPltMarkup {
Program program = pointerData.getProgram(); Program program = pointerData.getProgram();
Memory memory = program.getMemory(); Memory memory = program.getMemory();
Address refAddr = (Address) pointerData.getValue(); Address refAddr = (Address) pointerData.getValue();
if (memory.contains(refAddr)) { if (refAddr == null) {
return false;
}
else if (memory.contains(refAddr)) {
return true; return true;
} }
Symbol primary = program.getSymbolTable().getPrimarySymbol(refAddr); Symbol primary = program.getSymbolTable().getPrimarySymbol(refAddr);
@@ -20,6 +20,8 @@ import static ghidra.program.util.FunctionChangeRecord.FunctionChangeType.*;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import javax.help.UnsupportedOperationException;
import db.DBRecord; import db.DBRecord;
import ghidra.program.database.*; import ghidra.program.database.*;
import ghidra.program.database.data.DataTypeManagerDB; import ghidra.program.database.data.DataTypeManagerDB;
@@ -140,6 +142,9 @@ public class FunctionDB extends DatabaseObject implements Function {
@Override @Override
public void setThunkedFunction(Function referencedFunction) { public void setThunkedFunction(Function referencedFunction) {
if (isExternal()) {
throw new UnsupportedOperationException("External functions may not be a thunk");
}
if ((referencedFunction != null) && !(referencedFunction instanceof FunctionDB)) { if ((referencedFunction != null) && !(referencedFunction instanceof FunctionDB)) {
throw new IllegalArgumentException("FunctionDB expected for referenced function"); throw new IllegalArgumentException("FunctionDB expected for referenced function");
} }
@@ -702,6 +702,7 @@ public interface Function extends Namespace {
* @throws IllegalArgumentException if an attempt is made to thunk a function or another * @throws IllegalArgumentException if an attempt is made to thunk a function or another
* thunk which would result in a loop back to this function or if this function is an external * thunk which would result in a loop back to this function or if this function is an external
* function, or specified function is from a different program instance. * function, or specified function is from a different program instance.
* @throws UnsupportedOperationException if this method is invoked on an external function.
*/ */
public void setThunkedFunction(Function thunkedFunction) throws IllegalArgumentException; public void setThunkedFunction(Function thunkedFunction) throws IllegalArgumentException;
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -113,6 +113,8 @@ public interface FunctionManager extends ManagerDB {
* @return new function or null if one or more functions overlap the specified body address set. * @return new function or null if one or more functions overlap the specified body address set.
* @throws OverlappingFunctionException if the address set of the body overlaps an existing * @throws OverlappingFunctionException if the address set of the body overlaps an existing
* function * function
* @throws UnsupportedOperationException if this method is invoked on an external entryPoint
* address.
*/ */
public Function createThunkFunction(String name, Namespace nameSpace, Address entryPoint, public Function createThunkFunction(String name, Namespace nameSpace, Address entryPoint,
AddressSetView body, Function thunkedFunction, SourceType source) AddressSetView body, Function thunkedFunction, SourceType source)
@@ -25,7 +25,6 @@ import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationResult; import ghidra.program.model.reloc.RelocationResult;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolUtilities; import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.util.*;
public class PowerPC64_ElfRelocationHandler public class PowerPC64_ElfRelocationHandler
extends AbstractElfRelocationHandler<PowerPC64_ElfRelocationType, ElfRelocationContext<?>> { extends AbstractElfRelocationHandler<PowerPC64_ElfRelocationType, ElfRelocationContext<?>> {
@@ -46,7 +45,7 @@ public class PowerPC64_ElfRelocationHandler
@Override @Override
public boolean canRelocate(ElfHeader elf) { public boolean canRelocate(ElfHeader elf) {
return elf.e_machine() == ElfConstants.EM_PPC64 && elf.is64Bit(); return elf.e_machine() == ElfConstants.EM_PPC64;
} }
@Override @Override
@@ -62,7 +61,7 @@ public class PowerPC64_ElfRelocationHandler
Program program = elfRelocationContext.getProgram(); Program program = elfRelocationContext.getProgram();
Memory memory = program.getMemory(); Memory memory = program.getMemory();
// NOTE: Based upon glibc source it appears that PowerPC only uses RELA relocations // NOTE: Based upon glibc source it appears that PowerPC only uses RELA relocations
long addend = relocation.getAddend(); long addend = relocation.getAddend();
long offset = relocationAddress.getOffset(); long offset = relocationAddress.getOffset();
@@ -98,7 +97,7 @@ public class PowerPC64_ElfRelocationHandler
break; break;
default: default:
} }
// Handle relative relocations that do not require symbolAddr or symbolValue // Handle relative relocations that do not require symbolAddr or symbolValue
switch (type) { switch (type) {
@@ -106,32 +105,32 @@ public class PowerPC64_ElfRelocationHandler
long value64 = elfRelocationContext.getImageBaseWordAdjustmentOffset() + addend; long value64 = elfRelocationContext.getImageBaseWordAdjustmentOffset() + addend;
memory.setLong(relocationAddress, value64); memory.setLong(relocationAddress, value64);
return new RelocationResult(Status.APPLIED, 8); return new RelocationResult(Status.APPLIED, 8);
case R_PPC64_TOC: case R_PPC64_TOC:
memory.setLong(relocationAddress, toc); memory.setLong(relocationAddress, toc);
return new RelocationResult(Status.APPLIED, 8); return new RelocationResult(Status.APPLIED, 8);
case R_PPC64_COPY: case R_PPC64_COPY:
markAsUnsupportedCopy(program, relocationAddress, type, symbolName, symbolIndex, markAsUnsupportedCopy(program, relocationAddress, type, symbolName, symbolIndex,
sym.getSize(), elfRelocationContext.getLog()); sym.getSize(), elfRelocationContext.getLog());
return RelocationResult.UNSUPPORTED; return RelocationResult.UNSUPPORTED;
default: default:
break; break;
} }
// Check for unresolved symbolAddr and symbolValue required by remaining relocation types handled below // Check for unresolved symbolAddr and symbolValue required by remaining relocation types handled below
if (handleUnresolvedSymbol(elfRelocationContext, relocation, relocationAddress)) { if (handleUnresolvedSymbol(elfRelocationContext, relocation, relocationAddress)) {
return RelocationResult.FAILURE; return RelocationResult.FAILURE;
} }
int oldValue = memory.getInt(relocationAddress); int oldValue = memory.getInt(relocationAddress);
int newValue = 0; int newValue = 0;
int byteLength = 4; // most relocations affect 4-bytes (change if different) int byteLength = 4; // most relocations affect 4-bytes (change if different)
switch (type) { switch (type) {
case R_PPC64_ADDR32: case R_PPC64_ADDR32:
newValue = (int) (symbolValue + addend); newValue = (int) (symbolValue + addend);
memory.setInt(relocationAddress, newValue); memory.setInt(relocationAddress, newValue);
@@ -194,10 +193,6 @@ public class PowerPC64_ElfRelocationHandler
memory.setInt(relocationAddress, newValue); memory.setInt(relocationAddress, newValue);
break; break;
case R_PPC64_REL24: case R_PPC64_REL24:
// attempt to handle Object module case where referenced symbol resides within .opd
symbolValue = fixupOPDSymbolValue(elfRelocationContext, sym);
newValue = (int) ((symbolValue + addend - offset) >> 2); newValue = (int) ((symbolValue + addend - offset) >> 2);
newValue = ((newValue << 2) & PPC64_LOW24); newValue = ((newValue << 2) & PPC64_LOW24);
newValue = (oldValue & ~PPC64_LOW24) | newValue; newValue = (oldValue & ~PPC64_LOW24) | newValue;
@@ -230,7 +225,7 @@ public class PowerPC64_ElfRelocationHandler
} }
if (PowerPC64_ElfExtension if (PowerPC64_ElfExtension
.getPpc64ABIVersion(elfRelocationContext.getElfHeader()) == 1) { .getPpc64ElfABIVersion(elfRelocationContext.getElfHeader()) == 1) {
// ABI ELFv1 (used by big-endian PPC64) expected to copy full function descriptor // ABI ELFv1 (used by big-endian PPC64) expected to copy full function descriptor
// into .got.plt section where symbolAddr refers to function descriptor // into .got.plt section where symbolAddr refers to function descriptor
// Copy function descriptor data // Copy function descriptor data
@@ -273,38 +268,4 @@ public class PowerPC64_ElfRelocationHandler
return new RelocationResult(Status.APPLIED, byteLength); return new RelocationResult(Status.APPLIED, byteLength);
} }
/**
* This method generates a symbol value with possible substitution for those
* symbols residing within the .opd to refer to the real function instead.
* Care must be taken not to invoke this method for relocations which may be
* applied to call stubs. It is also important that relocations have already
* been applied to the .opd section since we will be using its data for
* locating the real function.
* @param elfRelocationContext ELF relocation context
* @param sym ELF relocation symbol
* @return symbol value
* @throws MemoryAccessException if memory access error occurs
*/
private long fixupOPDSymbolValue(ElfRelocationContext<?> elfRelocationContext, ElfSymbol sym)
throws MemoryAccessException {
Address addr = elfRelocationContext.getSymbolAddress(sym);
if (addr == null) {
return 0;
}
Program program = elfRelocationContext.getProgram();
MemoryBlock block = program.getMemory().getBlock(addr);
if (block == null || !".opd".equals(block.getName())) {
return addr.getOffset();
}
// .opd symbols will get moved to the real function by the extension (see processFunctionDescriptors)
// Call stubs should always use the .opd symbol value and not the function address so we can - this
// distinction can only be made using the relocation type.
byte[] bytes = new byte[8];
block.getBytes(addr, bytes);
boolean bigEndian = elfRelocationContext.getElfHeader().isBigEndian();
DataConverter dataConverter =
bigEndian ? BigEndianDataConverter.INSTANCE : LittleEndianDataConverter.INSTANCE;
return dataConverter.getLong(bytes);
}
} }