GP-6347: SourceLanguage modules and new extension points

This commit is contained in:
Ryan Kurtz
2026-05-17 19:48:41 -04:00
parent c9671f2a49
commit 3a479c9f7e
258 changed files with 2513 additions and 1292 deletions
@@ -236,7 +236,6 @@ data/typeinfo/golang/patchverdiffs/go1.24.4.json.diff||GHIDRA||||END|
data/typeinfo/golang/patchverdiffs/go1.24.5.json.diff||GHIDRA||||END|
data/typeinfo/golang/patchverdiffs/go1.24.6.json.diff||GHIDRA||||END|
data/typeinfo/mac_10.9/mac_osx.gdt||GHIDRA||||END|
data/typeinfo/rust/rust-common.gdt||GHIDRA||||END|
data/typeinfo/win32/msvcrt/clsids.txt||GHIDRA||reviewed||END|
data/typeinfo/win32/msvcrt/guids.txt||GHIDRA||reviewed||END|
data/typeinfo/win32/msvcrt/iids.txt||GHIDRA||||END|
@@ -5,6 +5,9 @@ FieldFactory
FieldMouseHandler
StringHandler
Loader
SourceLanguage
SourceLanguageDataArchive
SourceLanguageSpecExtension
TableRowMapper
ScriptProvider
Recognizer
@@ -0,0 +1,49 @@
/* ###
* 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.
*/
//Removes a source language from the program
//@category Program
import java.util.*;
import ghidra.app.script.GhidraScript;
import ghidra.app.util.sourcelanguage.SourceLanguageID;
public class RemoveSourceLanguage extends GhidraScript {
@Override
public void run() throws Exception {
if (currentProgram == null) {
return;
}
Set<SourceLanguageID> idSet = currentProgram.getSourceLanguageIDs();
if (idSet.isEmpty()) {
println("No source languages found in " + currentProgram);
}
Set<SourceLanguageID> selected = new HashSet<>(askChoices("Remove Source Language(s)",
"Select source languages to remove from the program", new ArrayList<>(idSet)));
// Remove from the "Source Language" program property
idSet.removeIf(selected::contains);
currentProgram.setSourceLanguageIDs(idSet);
// Future: Remove associated spec extensions if possible
// Future: Remove associated data archives if possible
}
}
@@ -27,6 +27,7 @@ import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility;
import ghidra.app.services.*;
import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.sourcelanguage.SourceLanguageService;
import ghidra.framework.Application;
import ghidra.framework.model.*;
import ghidra.framework.options.OptionType;
@@ -194,6 +195,38 @@ public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer {
}
}
}
// Add source language data archives
result.addAll(getSourceLanguageDTMs(program, log, monitor));
return result;
}
private List<DataTypeManager> getSourceLanguageDTMs(Program program, MessageLog log,
TaskMonitor monitor) {
List<DataTypeManager> result = new ArrayList<>();
for (ResourceFile file : SourceLanguageService.getDataArchives(program,
program.getSourceLanguageIDs(), log, monitor)) {
if (monitor.isCancelled()) {
break;
}
try {
DataTypeManager dtm = dtmService.openArchive(file, false);
result.add(dtm);
}
catch (Exception e) {
Throwable cause = e.getCause();
if (cause instanceof VersionException) {
log.appendMsg("Apply Data Archives",
"Unable to open archive %s: %s".formatted(file, cause.toString()));
}
else {
String msg = Objects.requireNonNullElse(e.getMessage(), e.toString());
log.appendMsg("Apply Data Archives",
"Unexpected Error opening archive %s: %s".formatted(file, msg));
}
}
}
return result;
}
@@ -0,0 +1,102 @@
/* ###
* 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.plugin.core.analysis;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import ghidra.app.services.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.sourcelanguage.SourceLanguageID;
import ghidra.app.util.sourcelanguage.SourceLanguageService;
import ghidra.framework.options.Options;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Adds/updates source language-specific support to the program
*/
public class SourceLanguageAnalyzer extends AbstractAnalyzer {
private final static String NAME = "Source Language Support";
private final static String DESCRIPTION =
"Adds/updates source language-specific support to the program";
private static String OPTION_NAME_SPEC_EXTENSIONS = "Add specification extensions";
private static String OPTION_DESC_SPEC_EXTENSIONS =
"Add any source language-specific specification extensions to the program.";
private static boolean OPTION_DEFAULT_SPEC_EXTENSIONS = true;
private AtomicBoolean analysisStarted = new AtomicBoolean(false);
private boolean addSpecExtensions = OPTION_DEFAULT_SPEC_EXTENSIONS;
/**
* Creates a new {@link SourceLanguageAnalyzer}
*/
public SourceLanguageAnalyzer() {
super(NAME, DESCRIPTION, AnalyzerType.BYTE_ANALYZER);
setPriority(AnalysisPriority.FORMAT_ANALYSIS.before().before().before().before().before());
setDefaultEnablement(true);
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {
// Enforce that the below code only executes once per analysis session
if (analysisStarted.getAndSet(true)) {
return true;
}
// Look for new source languages in the program
Set<SourceLanguageID> ids = SourceLanguageService.find(program, log, monitor);
// Update the "Source Languages" program property
program.setSourceLanguageIDs(ids);
// Optionally add source language spec extensions
if (addSpecExtensions) {
if (program.hasExclusiveAccess()) {
SourceLanguageService.addSpecExtensions(program, ids, log, monitor);
}
else {
log.appendMsg(
NAME + ": Cannot add spec extensions without exclusive access to program.");
}
}
return true;
}
@Override
public void registerOptions(Options options, Program program) {
options.registerOption(OPTION_NAME_SPEC_EXTENSIONS, OPTION_DEFAULT_SPEC_EXTENSIONS, null,
OPTION_DESC_SPEC_EXTENSIONS);
}
@Override
public void optionsChanged(Options options, Program program) {
addSpecExtensions =
options.getBoolean(OPTION_NAME_SPEC_EXTENSIONS, OPTION_DEFAULT_SPEC_EXTENSIONS);
}
@Override
public void analysisEnded(Program program) {
analysisStarted.set(false);
}
}
@@ -1,150 +0,0 @@
/* ###
* 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.plugin.core.analysis.rust;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicBoolean;
import org.xml.sax.SAXException;
import generic.jar.ResourceFile;
import ghidra.app.plugin.processors.sleigh.SleighException;
import ghidra.app.util.bin.format.elf.info.ElfComment;
import ghidra.app.util.opinion.ElfLoader;
import ghidra.framework.Application;
import ghidra.framework.store.LockException;
import ghidra.program.database.SpecExtension;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.Processor;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.util.Msg;
import ghidra.util.bytesearch.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import ghidra.xml.XmlParseException;
/**
* Rust utility functions
*/
public class RustUtilities {
private static final java.util.regex.Pattern ELF_COMMENT_REGEX =
java.util.regex.Pattern.compile("^rustc version .*$");
/**
* Checks if the specified program contains Rust stuff, either by metadata or by searching
* the specified {@link MemoryBlock} for byte pattern signatures.
* <p>
* This may be used by loaders to determine if a program was compiled with rust.
* If the program is determined to be rust, then the compiler property is set to
* {@link RustConstants#RUST_COMPILER}.
*
* @param program The {@link Program}
* @param block The {@link MemoryBlock} to scan for Rust signatures
* @param monitor The monitor
* @return True if the given {@link MemoryBlock} is not null and contains a Rust signature;
* otherwise, false
* @throws IOException if there was an IO-related error
* @throws CancelledException if the user cancelled the operation
*/
public static boolean isRust(Program program, MemoryBlock block, TaskMonitor monitor)
throws IOException, CancelledException {
if ( ElfLoader.isElf(program)) {
// ELF binaries can contain a ".comment" section that records the toolchains that
// produced the binary. Search this first as its quick and easy.
ElfComment elfComments = ElfComment.fromProgram(program);
if ( elfComments != null ) {
for(String s : elfComments.getCommentStrings() ) {
if (ELF_COMMENT_REGEX.matcher(s).matches()) {
return true;
}
}
}
}
if (block == null) {
return false;
}
// Use a MemoryBytePatternSearch for more efficient byte searching over a list of potential
// byte signatures. The below action sets our supplied boolean to true on a match, which we
// can later query and use as a return value for this method.
GenericMatchAction<AtomicBoolean> action =
new GenericMatchAction<AtomicBoolean>(new AtomicBoolean()) {
@Override
public void apply(Program prog, Address addr, Match match) {
getMatchValue().set(true);
}
};
MemoryBytePatternSearcher searcher = new MemoryBytePatternSearcher("Rust signatures");
for (byte[] sig : RustConstants.RUST_SIGNATURES) {
searcher.addPattern(new GenericByteSequencePattern<AtomicBoolean>(sig, action));
}
searcher.search(program, new AddressSet(block.getAddressRange()), monitor);
return action.getMatchValue().get();
}
/**
* Returns true if the given program has earlier been tagged as having a Rust compiler by
* the loader.
*
* @param program {@link Program}
* @return boolean true if program's compiler property includes rust
*/
public static boolean isRustProgram(Program program) {
String name = program.getCompiler();
return name != null && name.contains(RustConstants.RUST_COMPILER);
}
public static int addExtensions(Program program, TaskMonitor monitor, String subPath)
throws IOException {
Language language = program.getLanguageCompilerSpecPair().getLanguage();
Processor processor = language.getProcessor();
int bitSize = language.getLanguageDescription().getSize();
String spath = RustConstants.RUST_EXTENSIONS_PATH + subPath + bitSize;
ResourceFile module = Application.getModuleDataSubDirectory(processor.toString(), spath);
int extensionCount = 0;
ResourceFile[] files = module.listFiles();
if (files != null) {
for (ResourceFile file : files) {
InputStream stream = file.getInputStream();
byte[] bytes = stream.readAllBytes();
String xml = new String(bytes);
try {
SpecExtension extension = new SpecExtension(program);
extension.addReplaceCompilerSpecExtension(xml, monitor);
extensionCount++;
}
catch (SleighException | SAXException | XmlParseException | LockException e) {
Msg.error(RustUtilities.class,
"Failed to load Rust cspec extension: " + file.getAbsolutePath(), e);
}
}
}
return extensionCount;
}
}
@@ -18,7 +18,6 @@ package ghidra.app.plugin.core.datamgr.util;
import java.util.*;
import generic.jar.ResourceFile;
import ghidra.app.plugin.core.analysis.rust.RustConstants;
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
import ghidra.app.util.opinion.*;
import ghidra.framework.Application;
@@ -143,10 +142,6 @@ public class DataTypeArchiveUtility {
list.add("generic_clib");
}
if (program.getCompiler().contains(RustConstants.RUST_COMPILER)) {
list.add("rust-common");
}
return list;
}
@@ -23,7 +23,6 @@ import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.elf.*;
import ghidra.app.util.bin.format.golang.GoConstants;
import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper;
import ghidra.app.util.bin.format.swift.SwiftUtils;
import ghidra.framework.model.DomainObject;
import ghidra.framework.model.ProjectData;
import ghidra.framework.options.Options;
@@ -206,9 +205,6 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
List<String> sectionNames = Arrays.stream(elf.getSections())
.map(ElfSectionHeader::getNameAsString)
.toList();
if (SwiftUtils.isSwift(sectionNames)) {
return SwiftUtils.SWIFT_COMPILER;
}
if (GoRttiMapper.hasGolangSections(sectionNames)) {
return GoConstants.GOLANG_CSPEC_NAME;
}
@@ -25,8 +25,6 @@ import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
import org.apache.commons.lang3.StringUtils;
import ghidra.app.cmd.label.SetLabelPrimaryCmd;
import ghidra.app.plugin.core.analysis.rust.RustConstants;
import ghidra.app.plugin.core.analysis.rust.RustUtilities;
import ghidra.app.util.*;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.MemoryLoadable;
@@ -188,8 +186,6 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
markupElfInfoProducers(monitor);
setCompiler(monitor);
success = true;
}
finally {
@@ -2464,22 +2460,6 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
}
}
private void setCompiler(TaskMonitor monitor) throws CancelledException {
// Check for Rust
try {
if (RustUtilities.isRust(program, memory.getBlock(ElfSectionHeaderConstants.dot_rodata),
monitor)) {
program.setCompiler(RustConstants.RUST_COMPILER);
int extensionCount = RustUtilities.addExtensions(program, monitor,
RustConstants.RUST_EXTENSIONS_UNIX);
log.appendMsg("Installed " + extensionCount + " Rust cspec extensions");
}
}
catch (IOException e) {
log.appendMsg("Rust error: " + e.getMessage());
}
}
private void markupHashTable(TaskMonitor monitor) {
ElfDynamicTable dynamicTable = elf.getDynamicTable();
@@ -28,7 +28,6 @@ import ghidra.app.util.bin.format.macho.*;
import ghidra.app.util.bin.format.macho.commands.*;
import ghidra.app.util.bin.format.macho.dyld.DyldArchitecture;
import ghidra.app.util.bin.format.macho.dyld.DyldCacheHeader;
import ghidra.app.util.bin.format.swift.SwiftUtils;
import ghidra.app.util.bin.format.ubi.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.formats.gfilesystem.*;
@@ -543,9 +542,6 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
.flatMap(seg -> seg.getSections().stream())
.map(section -> section.getSectionName())
.toList();
if (SwiftUtils.isSwift(sectionNames)) {
return SwiftUtils.SWIFT_COMPILER;
}
if (GoRttiMapper.hasGolangSections(sectionNames)) {
return GoConstants.GOLANG_CSPEC_NAME;
}
@@ -30,7 +30,8 @@ import ghidra.app.util.importer.MessageLog;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.util.exception.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
@@ -173,12 +174,6 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
return machoInfoList;
}
@Override
protected void renameObjMsgSendRtpSymbol()
throws DuplicateNameException, InvalidInputException {
// Do nothing. This is not applicable for a PRELINK Mach-O.
}
@Override
protected void markupChainedFixups(MachHeader header, List<Address> fixups)
throws CancelledException {
@@ -15,14 +15,11 @@
*/
package ghidra.app.util.opinion;
import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
import org.apache.commons.collections4.map.LazySortedMap;
import ghidra.app.plugin.core.analysis.rust.RustConstants;
import ghidra.app.plugin.core.analysis.rust.RustUtilities;
import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.RelocationException;
@@ -40,8 +37,6 @@ import ghidra.app.util.bin.format.macho.dyld.DyldChainedPtr.DyldChainType;
import ghidra.app.util.bin.format.macho.dyld.DyldFixup;
import ghidra.app.util.bin.format.macho.relocation.*;
import ghidra.app.util.bin.format.macho.threadcommand.ThreadCommand;
import ghidra.app.util.bin.format.objc.ObjcUtils;
import ghidra.app.util.bin.format.objc.objc1.Objc1Constants;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options;
import ghidra.program.database.function.OverlappingFunctionException;
@@ -173,9 +168,7 @@ public class MachoProgramBuilder {
}
// Perform additional actions
renameObjMsgSendRtpSymbol();
fixupProgramTree(null); // should be done last to account for new memory blocks
setCompiler(provider.getName());
}
/**
@@ -1808,53 +1801,6 @@ public class MachoProgramBuilder {
}
}
protected void setCompiler(String source) throws CancelledException {
if (ObjcUtils.isObjc(program)) {
try {
program.setCompiler(ObjcUtils.OBJC_COMPILER);
int count = ObjcUtils.addExtensions(program, monitor);
if (count > 0) {
log.appendMsg("%s: installed %d objc SpecExtensions".formatted(source, count));
}
}
catch (IOException e) {
log.appendMsg("%s: objc error - %s".formatted(source, e.getMessage()));
}
return;
}
try {
Section section =
machoHeader.getSection(SegmentNames.SEG_TEXT, SectionNames.TEXT_CONST);
if (section != null && RustUtilities.isRust(program,
memory.getBlock(space.getAddress(section.getAddress())), monitor)) {
program.setCompiler(RustConstants.RUST_COMPILER);
int count = RustUtilities.addExtensions(program, monitor,
RustConstants.RUST_EXTENSIONS_UNIX);
if (count > 0) {
log.appendMsg("%s: installed %d rust SpecExtensions".formatted(source, count));
}
}
}
catch (IOException e) {
log.appendMsg("%s: Rust error - %s".formatted(source, e.getMessage()));
}
}
protected void renameObjMsgSendRtpSymbol()
throws DuplicateNameException, InvalidInputException {
Address address = space.getAddress(Objc1Constants.OBJ_MSGSEND_RTP);
Symbol symbol = program.getSymbolTable().getPrimarySymbol(address);
if (symbol != null && symbol.isDynamic()) {
symbol.setName(Objc1Constants.OBJC_MSG_SEND_RTP_NAME, SourceType.IMPORTED);
}
else {
program.getSymbolTable()
.createLabel(address, Objc1Constants.OBJC_MSG_SEND_RTP_NAME,
SourceType.IMPORTED);
}
}
/**
* Associates the given {@link Symbol} with the correct external {@link Library} (fixing
* the {@code <EXTERNAL>} association)
@@ -23,8 +23,6 @@ import org.apache.commons.io.FilenameUtils;
import com.google.common.primitives.Bytes;
import ghidra.app.plugin.core.analysis.rust.RustConstants;
import ghidra.app.plugin.core.analysis.rust.RustUtilities;
import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.Option;
import ghidra.app.util.bin.BinaryReader;
@@ -39,7 +37,6 @@ import ghidra.app.util.bin.format.pe.ImageCor20Header.ImageCor20Flags;
import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout;
import ghidra.app.util.bin.format.pe.debug.DebugCOFFSymbol;
import ghidra.app.util.bin.format.pe.debug.DebugDirectoryParser;
import ghidra.app.util.bin.format.swift.SwiftUtils;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.model.DomainObject;
import ghidra.framework.options.Options;
@@ -925,9 +922,7 @@ public class PeLoader extends AbstractPeDebugLoader {
BorlandCpp("borland:c++", "borlandcpp"),
BorlandUnk("borland:unknown", "borlandcpp"),
CLI("cli", "cli"),
Rustc(RustConstants.RUST_COMPILER, RustConstants.RUST_COMPILER),
GOLANG("golang", "golang"),
Swift(SwiftUtils.SWIFT_COMPILER, SwiftUtils.SWIFT_COMPILER),
Unknown("unknown", "unknown"),
// The following values represent the presence of ambiguous indicators
@@ -981,34 +976,6 @@ public class PeLoader extends AbstractPeDebugLoader {
DOSHeader dh = pe.getDOSHeader();
// Check for Rust. Program object is required, which may be null.
try {
if (program != null && RustUtilities.isRust(program,
program.getMemory().getBlock(".rdata"), monitor)) {
try {
int extensionCount = RustUtilities.addExtensions(program, monitor,
RustConstants.RUST_EXTENSIONS_WINDOWS);
log.appendMsg("Installed " + extensionCount + " Rust cspec extensions");
}
catch (IOException e) {
log.appendMsg("Rust error: " + e.getMessage());
}
return CompilerEnum.Rustc;
}
}
catch (CancelledException e) {
// Move on
}
// Check for Swift
List<String> sectionNames =
Arrays.stream(pe.getNTHeader().getFileHeader().getSectionHeaders())
.map(section -> section.getName())
.toList();
if (SwiftUtils.isSwift(sectionNames)) {
return CompilerEnum.Swift;
}
// Check for managed code (.NET)
if (pe.getNTHeader().getOptionalHeader().isCLI()) {
return CompilerEnum.CLI;
+3
View File
@@ -0,0 +1,3 @@
# Objective-C
The module provides improved support for binaries written in the Objective-C programming language.
+25
View File
@@ -0,0 +1,25 @@
/* ###
* 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.
*/
apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle"
apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
apply plugin: 'eclipse'
eclipse.project.name = 'Features Objective-C'
dependencies {
api project(":Base")
api project(":Decompiler")
}
@@ -0,0 +1,12 @@
##VERSION: 2.0
Module.manifest||GHIDRA||||END|
README.md||GHIDRA||||END|
data/extensions.json||GHIDRA||||END|
data/extensions/aarch64/chkstk_darwin_fixup.xml||GHIDRA||||END|
data/extensions/aarch64/objc_getProperty_fixup.xml||GHIDRA||||END|
data/extensions/aarch64/objc_load_fixup.xml||GHIDRA||||END|
data/extensions/aarch64/objc_msgSend_stub.xml||GHIDRA||||END|
data/extensions/aarch64/objc_release_fixup.xml||GHIDRA||||END|
data/extensions/aarch64/objc_retain_fixup.xml||GHIDRA||||END|
data/extensions/aarch64/objc_setProperty_fixup.xml||GHIDRA||||END|
data/extensions/aarch64/objc_store_fixup.xml||GHIDRA||||END|
@@ -0,0 +1,8 @@
[
{
"processor": "AARCH64",
"endian": "little",
"size": "64",
"directory": "extensions/aarch64"
}
]
@@ -32,6 +32,7 @@ import ghidra.app.util.bin.format.objc.objc2.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.MachoLoader;
import ghidra.app.util.opinion.MachoProgramBuilder;
import ghidra.app.util.sourcelanguage.ObjcSourceLanguageSpecExtension;
import ghidra.framework.options.OptionType;
import ghidra.framework.options.Options;
import ghidra.program.model.address.*;
@@ -275,8 +276,9 @@ public class ObjcMessageAnalyzer extends AbstractAnalyzer {
String cc = CompilerSpec.CALLING_CONVENTION_unknown;
if (isStub) {
if (program.getDataTypeManager()
.getCallingConvention(ObjcUtils.OBJC_MSGSEND_STUBS_CC) != null) {
cc = ObjcUtils.OBJC_MSGSEND_STUBS_CC;
.getCallingConvention(
ObjcSourceLanguageSpecExtension.OBJC_MSGSEND_STUBS) != null) {
cc = ObjcSourceLanguageSpecExtension.OBJC_MSGSEND_STUBS;
}
}
@@ -998,7 +1000,8 @@ public class ObjcMessageAnalyzer extends AbstractAnalyzer {
if (args.length >= 2 && args[1].getDataType().equals(dataTypes.sel)) {
signature.setArguments(ArrayUtils.remove(args, 1));
}
signature.setCallingConvention(ObjcUtils.OBJC_MSGSEND_STUBS_CC);
signature.setCallingConvention(
ObjcSourceLanguageSpecExtension.OBJC_MSGSEND_STUBS);
HighFunctionDBUtil.writeOverride(funcMgr.getFunctionContaining(fromAddress),
fromAddress, signature);
}
@@ -15,32 +15,25 @@
*/
package ghidra.app.util.bin.format.objc;
import java.io.*;
import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
import org.xml.sax.SAXException;
import generic.jar.ResourceFile;
import ghidra.app.cmd.data.CreateDataCmd;
import ghidra.app.cmd.disassemble.DisassembleCommand;
import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.cmd.register.SetRegisterCmd;
import ghidra.app.plugin.processors.sleigh.SleighException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.objc.objc2.Objc2Constants;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.Application;
import ghidra.framework.cmd.BackgroundCommand;
import ghidra.framework.cmd.Command;
import ghidra.framework.store.LockException;
import ghidra.program.database.SpecExtension;
import ghidra.program.database.SpecExtension.DocInfo;
import ghidra.program.database.symbol.ClassSymbol;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.data.DataUtilities.ClearDataMode;
import ghidra.program.model.lang.*;
import ghidra.program.model.lang.Processor;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
@@ -51,24 +44,12 @@ import ghidra.util.Msg;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
import ghidra.xml.XmlParseException;
/**
* Objective-C utilities
*/
public final class ObjcUtils {
/**
* The Objective-C compiler name, used by {@link Program#setCompiler(String)}
*/
public static final String OBJC_COMPILER = "objc";
/**
* The Objective-C {@code _objc_msgSend} stub calling convention name (added with processor
* extension)
*/
public static final String OBJC_MSGSEND_STUBS_CC = "__objc_msgSend_stub";
/**
* String that prefixes Objective-C class symbols
*/
@@ -446,56 +427,4 @@ public final class ObjcUtils {
}
return name;
}
/**
* Adds Objective-C processor extensions to the {@link Program}, which include:
* <ul>
* <li>A special calling convention used by objc_msgSend stubs</li>
* <li>Call fixups to clear out a lot of Objective-C Automatic Reference Counting (ARC) clutter</li>
* </ul>
*
* @param program The {@link Program} to add the extensions to
* @param monitor A cancelable task monitor
* @return The number of extensions successfully added
* @throws IOException if an IO-related error occurred
* @see <a href="https://doi.org/10.1109/STATIC66697.2025.00005">Heros in Action: Analyzing Objective-C Binaries through Decompilation and IFDS</a>
* @see <a href="https://youtu.be/ojXI7Gio8Pg?si=zcAaZ2KGeBFcAabn">RE//verse 2025: Langs Beyond The C</a>
*/
public static int addExtensions(Program program, TaskMonitor monitor) throws IOException {
Language language = program.getLanguageCompilerSpecPair().getLanguage();
Processor processor = language.getProcessor();
String spath = "extensions/" + OBJC_COMPILER;
int extensionCount = 0;
try {
ResourceFile module =
Application.getModuleDataSubDirectory(processor.toString(), spath);
ResourceFile[] files = module.listFiles();
if (files != null) {
for (ResourceFile file : files) {
InputStream stream = file.getInputStream();
byte[] bytes = stream.readAllBytes();
String xml = new String(bytes);
try {
SpecExtension extension = new SpecExtension(program);
DocInfo docInfo = extension.testExtensionDocument(xml);
if (SpecExtension.getCompilerSpecExtension(program, docInfo) == null) {
extension.addReplaceCompilerSpecExtension(xml, monitor);
extensionCount++;
}
}
catch (SleighException | SAXException | XmlParseException | LockException e) {
Msg.error(ObjcUtils.class,
"Failed to load Objective-C cspec extension: " + file, e);
}
}
}
}
catch (FileNotFoundException e) {
// fall thru
}
return extensionCount;
}
}

Some files were not shown because too many files have changed in this diff Show More