GP-2041 Added ELF ARM PC Bias import option for relocation processing

This commit is contained in:
ghidra1 2022-06-14 17:42:31 -04:00
parent 91e5259018
commit 8e00f4faa4
7 changed files with 162 additions and 14 deletions

View File

@ -37,6 +37,15 @@ public interface ElfLoadHelper {
*/
Program getProgram();
/**
* Get an import processing option value
* @param <T> class of option value (e.g., String, Boolean, etc.)
* @param optionName option name
* @param defaultValue default option value which also establishes expected value type
* @return option value
*/
<T> T getOption(String optionName, T defaultValue);
/**
* Get ELF Header object
* @return ELF Header object

View File

@ -18,9 +18,9 @@ package ghidra.app.util.bin.format.elf.extend;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import ghidra.app.util.Option;
import ghidra.app.util.bin.format.MemoryLoadable;
import ghidra.app.util.bin.format.elf.*;
import ghidra.program.model.address.Address;
@ -512,4 +512,13 @@ public class ElfLoadAdapter {
return null;
}
/**
* Add extension-specific load options
* @param elf ELF header
* @param options list to which load options may be added
*/
public void addLoadOptions(ElfHeader elf, List<Option> options) {
// no additional options
}
}

View File

@ -22,6 +22,8 @@ import ghidra.app.util.OptionUtils;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.elf.ElfException;
import ghidra.app.util.bin.format.elf.ElfHeader;
import ghidra.app.util.bin.format.elf.extend.ElfExtensionFactory;
import ghidra.app.util.bin.format.elf.extend.ElfLoadAdapter;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.util.NumericUtilities;
@ -87,6 +89,12 @@ public class ElfLoaderOptionsFactory {
options.add(
new Option(RESOLVE_EXTERNAL_SYMBOLS_OPTION_NAME, RESOLVE_EXTERNAL_SYMBOLS_DEFAULT,
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-resolveExternalSymbols"));
ElfLoadAdapter extensionAdapter = ElfExtensionFactory.getLoadAdapter(elf);
if (extensionAdapter != null) {
extensionAdapter.addLoadOptions(elf, options);
}
}
private static boolean includeDataImageBaseOption(ElfHeader elf, Language language) {

View File

@ -25,8 +25,7 @@ import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
import org.apache.commons.lang3.StringUtils;
import ghidra.app.cmd.label.SetLabelPrimaryCmd;
import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.Option;
import ghidra.app.util.*;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.MemoryLoadable;
import ghidra.app.util.bin.format.elf.*;
@ -94,6 +93,11 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
listing = program.getListing();
}
@Override
public <T> T getOption(String optionName, T defaultValue) {
return OptionUtils.getOption(optionName, options, defaultValue);
}
@Override
public ElfHeader getElfHeader() {
return elf;

View File

@ -15,19 +15,33 @@
*/
package ghidra.app.util.bin.format.elf.extend;
import java.io.IOException;
import java.math.BigInteger;
import java.util.List;
import ghidra.app.util.Option;
import ghidra.app.util.bin.format.elf.*;
import ghidra.app.util.opinion.Loader;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.ContextChangeException;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public class ARM_ElfExtension extends ElfExtension {
/**
* ARM PC Bias option affecting all relative relocations.
* If PC Bias is already accounted for within relocation addend this option should be specified
* as false, otherwise true (default).
*/
public static final String APPLY_PC_BIAS_TO_RELATIVE_RELOCATIONS_OPTION_NAME =
"Apply PC Bias to relative relocations";
public static final boolean APPLY_PC_BIAS_TO_RELATIVE_RELOCATIONS_DEFAULT = false; // reflects binutils
// Elf Program Header Extensions
public static final ElfProgramHeaderType PT_ARM_EXIDX =
new ElfProgramHeaderType(0x70000000, "PT_ARM_EXIDX", "Frame unwind information");
@ -62,6 +76,31 @@ public class ARM_ElfExtension extends ElfExtension {
return "_ARM";
}
@Override
public void addLoadOptions(ElfHeader elf, List<Option> options) {
// If PC Bias option disabled addend assumed to includes PC Bias,
// if enabled PC Bias must be factored in explicitly during relocation processing
boolean enablePcBiasOption = false;
try {
elf.parse(); // ensure ELF is fully parsed to query section data
// Enable PC Bias use if Green Hills (GHS) detected
ElfSectionHeader section = elf.getSection(".ghsinfo");
if (section != null) {
enablePcBiasOption = true;
}
}
catch (IOException e) {
Msg.warn(this, "Failed to fully parse ELF headers to formulate ARM import options");
}
options.add(new Option(APPLY_PC_BIAS_TO_RELATIVE_RELOCATIONS_OPTION_NAME,
enablePcBiasOption, Boolean.class,
Loader.COMMAND_LINE_ARG_PREFIX + "-applyArmElfRelocPCBias"));
}
@Override
public void processElf(ElfLoadHelper elfLoadHelper, TaskMonitor monitor)
throws CancelledException {

View File

@ -0,0 +1,67 @@
/* ###
* 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.app.util.bin.format.elf.extend.ARM_ElfExtension;
import ghidra.program.model.address.Address;
class ARM_ElfRelocationContext extends ElfRelocationContext {
private final boolean applyPcBiasToRelativeRelocations;
protected ARM_ElfRelocationContext(ElfRelocationHandler handler, ElfLoadHelper loadHelper,
ElfRelocationTable relocationTable,
Map<ElfSymbol, Address> symbolMap) {
super(handler, loadHelper, relocationTable, symbolMap);
applyPcBiasToRelativeRelocations =
loadHelper.getOption(ARM_ElfExtension.APPLY_PC_BIAS_TO_RELATIVE_RELOCATIONS_OPTION_NAME,
ARM_ElfExtension.APPLY_PC_BIAS_TO_RELATIVE_RELOCATIONS_DEFAULT);
}
/**
* Get the appropriate PC Bias value which should be applied to the computed relocation value.
* This method and related option is intended as a work around for differences in how tool-chain
* and associated loaders handle the PC Bias and if they factor it into the addend or not.
* Within Ghidra, the default is to assume the PC Bias is not factored into the relocation addend
* with the {@link ARM_ElfExtension#APPLY_PC_BIAS_TO_RELATIVE_RELOCATIONS_OPTION_NAME} option
* being true.
* <p>
* Example as to how this PC Bias value factors into relocation value computation:
* <pre>
* value = (symbolValue + addend) - (relocAddr + pcBias)
* </pre>
* Within the Sleigh language this bias may be reflected by:
* <pre>
* ARM:
* (inst_start + 8) or (inst_next + 4)
* Thumb (either 16-bit or 32-bit forms):
* (inst_start + 4)
* </pre>
* @param isThumb true if Thumb instruction, false if ARM
* @return PC Bias value to be applied
*/
int getPcBias(boolean isThumb) {
if (applyPcBiasToRelativeRelocations) {
return isThumb ? 4 : 8;
}
return 0;
}
}

View File

@ -15,6 +15,8 @@
*/
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;
import ghidra.program.model.listing.Function;
@ -24,6 +26,12 @@ import ghidra.util.exception.NotFoundException;
public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
@Override
public ARM_ElfRelocationContext createRelocationContext(ElfLoadHelper loadHelper,
ElfRelocationTable relocationTable, Map<ElfSymbol, Address> symbolMap) {
return new ARM_ElfRelocationContext(this, loadHelper, relocationTable, symbolMap);
}
@Override
public boolean canRelocate(ElfHeader elf) {
return elf.e_machine() == ElfConstants.EM_ARM;
@ -35,14 +43,17 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
}
@Override
public void relocate(ElfRelocationContext elfRelocationContext, ElfRelocation relocation,
public void relocate(ElfRelocationContext context, ElfRelocation relocation,
Address relocationAddress) throws MemoryAccessException, NotFoundException {
ElfHeader elf = elfRelocationContext.getElfHeader();
if (elf.e_machine() != ElfConstants.EM_ARM) {
ElfHeader elf = context.getElfHeader();
if (elf.e_machine() != ElfConstants.EM_ARM ||
!(context instanceof ARM_ElfRelocationContext)) {
return;
}
ARM_ElfRelocationContext elfRelocationContext = (ARM_ElfRelocationContext) context;
Program program = elfRelocationContext.getProgram();
Memory memory = program.getMemory();
@ -75,7 +86,9 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
if (elfRelocationContext.extractAddend()) {
addend = (oldValue << 8 >> 6); // extract addend and sign-extend with *4 factor
}
newValue = (int) (symbolValue - offset + addend);
newValue = (int) (symbolValue + addend);
newValue -= (offset + elfRelocationContext.getPcBias(false));
// if this a BLX instruction, must set bit24 to identify half-word
if ((oldValue & 0xf0000000) == 0xf0000000) {
newValue = (oldValue & 0xfe000000) | (((newValue >> 1) & 1) << 24) |
@ -132,7 +145,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
case ARM_ElfRelocationConstants.R_ARM_LDR_PC_G0: { // Target class: ARM Instruction
int oldValue = memory.getInt(relocationAddress, instructionBigEndian);
newValue = (int) (symbolValue + addend);
newValue -= (offset + 8); // PC relative, PC will be 8 bytes after inst start
newValue -= (offset + elfRelocationContext.getPcBias(false));
newValue = (oldValue & 0xff7ff000) | ((~(newValue >> 31) & 1) << 23) |
((newValue >> 2) & 0xfff);
memory.setInt(relocationAddress, newValue, instructionBigEndian);
@ -303,8 +316,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
case ARM_ElfRelocationConstants.R_ARM_GOT_PLT32:
int oldValue = memory.getInt(relocationAddress, instructionBigEndian);
newValue = (int) (symbolValue + addend);
newValue -= (offset + 8); // PC relative, PC will be 8 bytes past inst start
newValue -= (offset + elfRelocationContext.getPcBias(false));
// is this a BLX instruction, must put the lower half word in bit24
// TODO: this might not appear on a BLX, but just in case
@ -404,7 +416,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
if (type == ARM_ElfRelocationConstants.R_ARM_THM_MOVW_PREL_NC ||
type == ARM_ElfRelocationConstants.R_ARM_THM_MOVT_PREL) {
value -= (offset + 4); // PC relative, PC will be 4 bytes past inst start
value -= (offset + elfRelocationContext.getPcBias(true));
}
if (type == ARM_ElfRelocationConstants.R_ARM_THM_MOVT_ABS ||
type == ARM_ElfRelocationConstants.R_ARM_THM_MOVT_PREL ||
@ -574,7 +586,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
addend = (oldValue << 21 >> 20); // extract addend and sign-extend with *2 factor
}
newValue = (int) (symbolValue + addend);
newValue -= offset; // PC relative
newValue -= (offset + elfRelocationContext.getPcBias(true)); // PC relative
newValue = (oldValue & 0x0000f800) | ((newValue >> 1) & 0x000007ff);
memory.setShort(relocationAddress, (short) newValue, instructionBigEndian);
break;
@ -585,7 +597,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
addend = (oldValue << 24 >> 23); // extract addend and sign-extend with *2 factor
}
newValue = (int) (symbolValue + addend);
newValue -= offset; // PC relative
newValue -= (offset + elfRelocationContext.getPcBias(true)); // PC relative
newValue = (oldValue & 0x0000ff00) | ((newValue >> 1) & 0x000000ff);
memory.setShort(relocationAddress, (short) newValue, instructionBigEndian);
break;