GP-6976 handle .cold symbols in ELF files

This commit is contained in:
James
2026-07-22 09:23:05 -04:00
parent d70f969e8e
commit 221ad21bed
15 changed files with 217 additions and 90 deletions

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,8 +15,7 @@
*/
package ghidra.app.cmd.analysis;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import ghidra.app.cmd.disassemble.SetFlowOverrideCmd;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
@@ -24,6 +23,7 @@ import ghidra.framework.cmd.BackgroundCommand;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.AddressSetPropertyMap;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@@ -72,6 +72,10 @@ public class SharedReturnAnalysisCmd extends BackgroundCommand<Program> {
processFunctionJumpReferences(program, entry, monitor);
}
AddressSetPropertyMap coldMap =
program.getAddressSetPropertyMap(Program.COLD_ENTRY_MAP_NAME);
Set<Address> conditionalJumpTargets = new HashSet<>();
Set<Address> onlyUnconditionalJumpTargets = new HashSet<>();
if (assumeContiguousFunctions) {
// assume if checkAllJumpReferences then set is much more than new function starts
@@ -116,6 +120,45 @@ public class SharedReturnAnalysisCmd extends BackgroundCommand<Program> {
continue; // can't handle flows between different spaces/overlays
}
// skip .cold targets
if (coldMap != null) {
if (coldMap.contains(destAddr)) {
continue;
}
}
if (!considerConditionalBranches) {
if (conditionalJumpTargets.contains(destAddr)) {
// destAddr is a conditional jump target, skip
continue;
}
if (!onlyUnconditionalJumpTargets.contains(destAddr)) {
//haven't checked the types of flow refs to destAddr yet
ReferenceIterator refIter =
program.getReferenceManager().getReferencesTo(destAddr);
boolean conditionalRef = false;
while (refIter.hasNext()) {
Reference ref = refIter.next();
RefType refType = ref.getReferenceType();
if (!refType.isFlow()) {
continue;
}
if (refType.isConditional()) {
conditionalRef = true;
break;
}
}
if (conditionalRef) {
// found a conditional jump, record destAddr and skip
conditionalJumpTargets.add(destAddr);
continue;
}
// no conditional flow refs to destAddr
onlyUnconditionalJumpTargets.add(destAddr);
}
}
// Reset cached functions if we transition to a different space/overlay
if (functionAfterSrc != null &&
functionAfterSrc.getAddressSpace() != srcAddr.getAddressSpace()) {

View File

@@ -404,7 +404,7 @@ public class EntryPointAnalyzer extends AbstractAnalyzer {
}
private void disassembleCodeMapMarkers(Program program, TaskMonitor monitor) {
AddressSetPropertyMap codeProp = program.getAddressSetPropertyMap("CodeMap");
AddressSetPropertyMap codeProp = program.getAddressSetPropertyMap(Program.CODE_MAP_NAME);
if (codeProp != null) {
Set<Address> codeSet = new HashSet<>();
AddressIterator aiter = codeProp.getAddresses();

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.
@@ -33,7 +33,7 @@ import ghidra.util.task.TaskMonitor;
* associated branching instruction flow to a CALL-RETURN
*/
public class SharedReturnAnalyzer extends AbstractAnalyzer {
private static final String NAME = "Shared Return Calls";
protected static final String DESCRIPTION =
"Converts branches to calls, followed by an immediate return, when the destination is a function. " +
@@ -59,7 +59,8 @@ public class SharedReturnAnalyzer extends AbstractAnalyzer {
private final static boolean OPTION_DEFAULT_CONSIDER_CONDITIONAL_BRANCHES_ENABLED = false;
private boolean assumeContiguousFunctions = OPTION_DEFAULT_ASSUME_CONTIGUOUS_FUNCTIONS_ENABLED;
private boolean considerConditionalBranches = OPTION_DEFAULT_CONSIDER_CONDITIONAL_BRANCHES_ENABLED;
private boolean considerConditionalBranches =
OPTION_DEFAULT_CONSIDER_CONDITIONAL_BRANCHES_ENABLED;
public SharedReturnAnalyzer() {
this(NAME, DESCRIPTION, AnalyzerType.FUNCTION_ANALYZER);
@@ -91,11 +92,12 @@ public class SharedReturnAnalyzer extends AbstractAnalyzer {
if (GoRttiMapper.isGolangProgram(program)) {
sharedReturnEnabled = false;
}
// If the language (in the .pspec file) overrides this setting, use that value
boolean contiguousFunctionsEnabled = language.getPropertyAsBoolean(
GhidraLanguagePropertyKeys.ENABLE_ASSUME_CONTIGUOUS_FUNCTIONS_ONLY, assumeContiguousFunctions);
GhidraLanguagePropertyKeys.ENABLE_ASSUME_CONTIGUOUS_FUNCTIONS_ONLY,
assumeContiguousFunctions);
assumeContiguousFunctions = contiguousFunctionsEnabled;
return sharedReturnEnabled;
@@ -121,7 +123,8 @@ public class SharedReturnAnalyzer extends AbstractAnalyzer {
assumeContiguousFunctions = options.getBoolean(OPTION_NAME_ASSUME_CONTIGUOUS_FUNCTIONS,
assumeContiguousFunctions);
considerConditionalBranches = options.getBoolean(OPTION_NAME_CONSIDER_CONDITIONAL_BRANCHES_FUNCTIONS,
considerConditionalBranches =
options.getBoolean(OPTION_NAME_CONSIDER_CONDITIONAL_BRANCHES_FUNCTIONS,
considerConditionalBranches);
}

View File

@@ -18,7 +18,7 @@ package ghidra.app.plugin.exceptionhandlers.gcc.structures.ehFrame;
import ghidra.app.cmd.comments.SetCommentCmd;
import ghidra.app.cmd.data.CreateArrayCmd;
import ghidra.app.cmd.data.CreateDataCmd;
import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.plugin.exceptionhandlers.gcc.*;
import ghidra.app.plugin.exceptionhandlers.gcc.sections.CieSource;
import ghidra.app.plugin.exceptionhandlers.gcc.sections.DebugFrameSection;
@@ -469,9 +469,12 @@ public class FrameDescriptionEntry extends GccAnalysisClass {
region.setIPRange(addrRange);
try {
/* Create a function at the pcBegin Addr address */
CreateFunctionCmd createFuncCmd = new CreateFunctionCmd(pcBeginAddr);
createFuncCmd.applyTo(program);
/* pcBeginAddr is not necessarily a function start, but it is code */
/* schedule disassembly if there is not already a function defined there */
if (program.getListing().getFunctionAt(pcBeginAddr) == null) {
AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(program);
analysisMgr.disassemble(new AddressSet(pcBeginAddr));
}
}
catch (AddressOutOfBoundsException e) {
throw new ExceptionHandlerFrameException(

View File

@@ -17,6 +17,7 @@ package ghidra.app.util.bin.format.elf;
import ghidra.app.util.bin.format.MemoryLoadable;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.AbstractProgramLoader;
import ghidra.app.util.opinion.ElfLoaderOptionsFactory;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
@@ -78,7 +79,10 @@ public interface ElfLoadHelper {
* The analyzers will pick this up and disassemble the code.
* @param address code memory address to be marked
*/
void markAsCode(Address address);
default void markAsCode(Address address) {
Program program = getProgram();
AbstractProgramLoader.markProperty(program, address, Program.CODE_MAP_NAME);
}
/**
* Create a one-byte function, so that when the code is analyzed,

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.
@@ -16,6 +16,8 @@
package ghidra.app.util.bin.format.elf;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
@@ -91,6 +93,10 @@ public class ElfSymbol {
/**Not preemptible, not exported*/
public static final byte STV_PROTECTED = 3;
/** Pattern for recognized .cold symbol names in ELF files **/
private static final Pattern COLD_ELF_SYMBOL_PATTERN =
Pattern.compile(".+\\.cold((\\.[0-9]+)?)");
private ElfSymbolTable symbolTable;
private final int symbolTableIndex;
@@ -317,11 +323,36 @@ public class ElfSymbol {
}
/**
* Returns true if this symbol defines a function.
* @return true if this symbol defines a function
* Returns true if this symbol has type {@link ElfSymbol#STT_FUNC}.
* <br>
* Note: compare to {@link ElfSymbol#isFunction(boolean)}
* @return true if symbol type is {@code STT_FUNC}
*/
public boolean isFunction() {
return getType() == STT_FUNC;
return isFunction(false);
}
/**
* If {@code entryPointsOnly} is false, this method tests whether the symbol's type is
* {@link ElfSymbol#STT_FUNC}. If {@code entryPointsOnly} is true, this method tests whether
* the symbol's type is {@link ElfSymbol#STT_FUNC} and whether the symbol represents an actual
* function entry point (currently by checking whether the symbol name ends in {@code .cold}
* or {@code .cold.N})
* @param entryPointsOnly if true, restrict to function entry points
* @return true if the symbol is a (possibly restricted) function symbol.
*/
public boolean isFunction(boolean entryPointsOnly) {
if (getType() != STT_FUNC) {
// not a function symbol
return false;
}
if (!entryPointsOnly) {
// only care about whether it is a function symbol
return true;
}
// check the symbol name
Matcher matcher = COLD_ELF_SYMBOL_PATTERN.matcher(getNameAsString());
return !matcher.matches();
}
/**
@@ -504,4 +535,14 @@ public class ElfSymbol {
Integer.toHexString(Short.toUnsignedInt(st_shndx));
}
/**
* Tests whether {@code name} is the name of a "cold" Elf symbol.
* @param name string to test
* @return true if cold, false otherwise
*/
public static boolean isColdSymbolName(String name) {
Matcher matcher = COLD_ELF_SYMBOL_PATTERN.matcher(name);
return matcher.matches();
}
}

View File

@@ -36,6 +36,7 @@ import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.AddressSetPropertyMap;
import ghidra.program.util.DefaultLanguageService;
import ghidra.program.util.GhidraProgramUtilities;
import ghidra.util.MD5Utilities;
@@ -348,6 +349,27 @@ public abstract class AbstractProgramLoader implements Loader {
prog.setExecutableSHA256(sha256);
}
/**
* Mark an address in the given property map. If the map does not exist it will be created.
* @param program program
* @param address address to mark
* @param propertyMapName name of property map
*/
public static void markProperty(Program program, Address address, String propertyMapName) {
AddressSetPropertyMap codeProp = program.getAddressSetPropertyMap(propertyMapName);
if (codeProp == null) {
try {
codeProp = program.createAddressSetPropertyMap(propertyMapName);
}
catch (DuplicateNameException e) {
codeProp = program.getAddressSetPropertyMap(propertyMapName);
}
}
if (codeProp != null) {
codeProp.add(address, address);
}
}
private String getProgramNameFromSourceData(ByteProvider provider, String domainFileName) {
FSRL fsrl = provider.getFSRL();
if (fsrl != null) {

View File

@@ -50,7 +50,6 @@ import ghidra.program.model.reloc.*;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.AddressSetPropertyMap;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.*;
import ghidra.util.datastruct.*;
@@ -613,13 +612,13 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
set.delete(startAddr, block.getEnd());
}
}
catch (MemoryBlockException | LockException | NotFoundException e) {
catch (MemoryBlockException | LockException e) {
throw new AssertException(e); // unexpected
}
}
private MemoryBlock setReadOnlyBlockRange(MemoryBlock block, AddressRange range)
throws MemoryBlockException, LockException, NotFoundException {
throws MemoryBlockException, LockException {
if (!block.isWrite()) {
return block;
}
@@ -2102,7 +2101,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
}
try {
boolean isPrimary = (elfSymbol.getType() == ElfSymbol.STT_FUNC) ||
boolean isPrimary = elfSymbol.isFunction() ||
(elfSymbol.getType() == ElfSymbol.STT_OBJECT) || (elfSymbol.getSize() != 0);
// don't displace existing primary unless symbol is a function or object symbol
if (name.contains("@")) {
@@ -2127,8 +2126,8 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
program.getSymbolTable().addExternalEntryPoint(address);
}
if (elfSymbol.getType() == ElfSymbol.STT_FUNC) {
Function existingFunction = program.getFunctionManager().getFunctionAt(address);
Function existingFunction = program.getFunctionManager().getFunctionAt(address);
if (elfSymbol.isFunction(true)) {
if (existingFunction == null) {
Function f = createOneByteFunction(null, address, false);
if (f != null) {
@@ -2146,6 +2145,11 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
}
}
}
else if (elfSymbol.isFunction(false) && existingFunction == null) {
AbstractProgramLoader.markProperty(program, address, Program.CODE_MAP_NAME);
AbstractProgramLoader.markProperty(program, address,
Program.COLD_ENTRY_MAP_NAME);
}
}
catch (DuplicateNameException e) {
throw new RuntimeException("Unexpected Exception", e);
@@ -2153,7 +2157,6 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
}
}
@Override
public void setElfSymbolAddress(ElfSymbol elfSymbol, Address address) {
symbolMap.put(elfSymbol, address);
@@ -2164,25 +2167,6 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
return symbolMap.get(elfSymbol);
}
@Override
public void markAsCode(Address address) {
// TODO: this should be in a common place, so all importers can communicate that something
// is code or data.
AddressSetPropertyMap codeProp = program.getAddressSetPropertyMap("CodeMap");
if (codeProp == null) {
try {
codeProp = program.createAddressSetPropertyMap("CodeMap");
}
catch (DuplicateNameException e) {
codeProp = program.getAddressSetPropertyMap("CodeMap");
}
}
if (codeProp != null) {
codeProp.add(address, address);
}
}
@Override
public Function createOneByteFunction(String name, Address address, boolean isEntry) {

View File

@@ -50,7 +50,6 @@ import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationTable;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.AddressSetPropertyMap;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.Msg;
import ghidra.util.exception.*;
@@ -495,32 +494,6 @@ public class PeLoader extends AbstractPeDebugLoader {
}
}
/**
* Mark this location as code in the CodeMap. The analyzers will pick this up and disassemble
* the code.
*
* TODO: this should be in a common place, so all importers can communicate that something is
* code or data.
*
* @param program The program to mark up.
* @param address The location.
*/
private void markAsCode(Program program, Address address) {
AddressSetPropertyMap codeProp = program.getAddressSetPropertyMap("CodeMap");
if (codeProp == null) {
try {
codeProp = program.createAddressSetPropertyMap("CodeMap");
}
catch (DuplicateNameException e) {
codeProp = program.getAddressSetPropertyMap("CodeMap");
}
}
if (codeProp != null) {
codeProp.add(address, address);
}
}
private void processExports(OptionalHeader optionalHeader, Program program, TaskMonitor monitor,
MessageLog log) {
@@ -809,7 +782,7 @@ public class PeLoader extends AbstractPeDebugLoader {
if (entry > 0) {
try {
symTable.createLabel(entryAddr, "__x86_CIL_", SourceType.IMPORTED);
markAsCode(prog, entryAddr);
markProperty(prog, entryAddr, Program.CODE_MAP_NAME);
symTable.addExternalEntryPoint(entryAddr);
}
catch (InvalidInputException e) {
@@ -825,7 +798,7 @@ public class PeLoader extends AbstractPeDebugLoader {
try {
// mark up entry (either Native or IL)
symTable.createLabel(entryAddr, "entry", SourceType.IMPORTED);
markAsCode(prog, entryAddr);
markProperty(prog, entryAddr, Program.CODE_MAP_NAME);
}
catch (InvalidInputException e) {
// ignore

View File

@@ -29,6 +29,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
import generic.jar.ResourceFile;
import generic.json.Json;
import ghidra.app.util.SymbolPathParser;
import ghidra.app.util.bin.format.elf.ElfSymbol;
import ghidra.app.util.demangler.*;
import ghidra.framework.Application;
import ghidra.program.model.lang.CompilerSpec;
@@ -451,8 +452,12 @@ public class GnuDemanglerParser {
private DemangledObjectBuilder getSpecializedBuilder(String demangled) {
// first check for .cold clone labels
if (ElfSymbol.isColdSymbolName(mangledSource)) {
return new ColdLabelHandler(demangled);
}
//
// Note: we check for the 'special handlers' first, since they are more specific than
// Note: we check for the 'special handlers' next, since they are more specific than
// the other handlers here. Checking for the operator handler first can produce
// errors, since some 'special handler' strings actually contain 'operator'
// signatures. In those cases, the operator handler will incorrectly match on the
@@ -2567,4 +2572,17 @@ public class GnuDemanglerParser {
}
}
private class ColdLabelHandler extends DemangledObjectBuilder {
ColdLabelHandler(String demangled) {
super(demangled);
}
@Override
DemangledObject build() {
return new DemangledLabel(mangledSource, demangledSource, demangled);
}
}
}

View File

@@ -17,6 +17,7 @@ package ghidra.app.util.demangler;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.List;
import org.junit.Before;
@@ -2386,6 +2387,32 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
signature);
}
@Test
public void testColdLabel1() throws IOException {
String mangled = "_Z6calleei.cold";
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
assertNotNull(object);
assertType(object, DemangledLabel.class);
assertEquals("callee(int) [clone .cold]", object.getRawDemangled());
assertEquals("callee(int)_[clone_.cold]", object.getName());
}
@Test
public void testColdLabel2() throws IOException {
String mangled = "_ZN6ghidra15ContextInternal9FreeArrayaSERKS1_.cold";
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
assertNotNull(object);
assertType(object, DemangledLabel.class);
assertEquals(
"ghidra::ContextInternal::FreeArray::operator=(ghidra::ContextInternal::FreeArray const&) [clone .cold]",
object.getRawDemangled());
assertEquals(
"ghidra::ContextInternal::FreeArray::operator=(ghidra::ContextInternal::FreeArray_const&)_[clone_.cold]",
object.getName());
}
private void assertType(Demangled o, Class<?> c) {
assertTrue("Wrong demangled type. \nExpected " + c + "; \nfound " + o.getClass(),
c.isInstance(o));

View File

@@ -82,6 +82,11 @@ public interface Program extends DataTypeManagerDomainObject, ProgramArchitectur
/** The maximum number of operands for any assembly language */
public final static int MAX_OPERANDS = 16;
/** Name of code property map **/
public static final String CODE_MAP_NAME = "CodeMap";
/** Name of cold entry point addresses property map **/
public static final String COLD_ENTRY_MAP_NAME = "ColdEntries";
/**
* Get the listing object.
* @return the Listing interface to the listing object.

View File

@@ -126,8 +126,9 @@ public class ARM_ElfExtension extends ElfExtension {
}
functionAddress = functionAddress.previous(); // align address
try {
program.getProgramContext().setValue(tmodeRegister, functionAddress,
functionAddress, BigInteger.ONE);
program.getProgramContext()
.setValue(tmodeRegister, functionAddress,
functionAddress, BigInteger.ONE);
}
catch (ContextChangeException e) {
// ignore since should not be instructions at time of import
@@ -165,8 +166,9 @@ public class ARM_ElfExtension extends ElfExtension {
}
else if ("$t".equals(symName) || symName.startsWith("$t.")) {
// is thumb mode
program.getProgramContext().setValue(tmodeRegister, address, address,
BigInteger.valueOf(1));
program.getProgramContext()
.setValue(tmodeRegister, address, address,
BigInteger.valueOf(1));
elfLoadHelper.markAsCode(address);
// do not retain $t symbols in program due to potential function/thunk naming interference
@@ -175,8 +177,9 @@ public class ARM_ElfExtension extends ElfExtension {
}
else if ("$a".equals(symName) || symName.startsWith("$a.")) {
// is arm mode
program.getProgramContext().setValue(tmodeRegister, address, address,
BigInteger.valueOf(0));
program.getProgramContext()
.setValue(tmodeRegister, address, address,
BigInteger.valueOf(0));
elfLoadHelper.markAsCode(address);
// do not retain $a symbols in program due to potential function/thunk naming interference
@@ -194,12 +197,13 @@ public class ARM_ElfExtension extends ElfExtension {
elfLoadHelper.setElfSymbolAddress(elfSymbol, address);
return null;
}
if (elfSymbol.getType() == ElfSymbol.STT_FUNC) {
if (elfSymbol.isFunction()) {
long symVal = address.getOffset();
if ((symVal & 1) != 0 && tmodeRegister != null) {
address = address.previous();
program.getProgramContext().setValue(tmodeRegister, address, address,
BigInteger.valueOf(1));
program.getProgramContext()
.setValue(tmodeRegister, address, address,
BigInteger.valueOf(1));
}
}
}

View File

@@ -393,7 +393,7 @@ public class MIPS_ElfExtension extends ElfExtension {
return address;
}
if (elfSymbol.getType() == ElfSymbol.STT_FUNC) {
if (elfSymbol.isFunction()) {
Program program = elfLoadHelper.getProgram();

View File

@@ -704,7 +704,7 @@ public class PowerPC64_ElfExtension extends ElfExtension {
ElfHeader elfHeader = elfLoadHelper.getElfHeader();
// Check for V2 ABI
if (isExternal || elfSymbol.getType() != ElfSymbol.STT_FUNC ||
if (isExternal || !elfSymbol.isFunction() ||
getPpc64ElfABIVersion(elfHeader) != 2) {
return address;
}