diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0cc7a91..6284981 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,30 +1,22 @@ -name: Build +name: Run Gradle on PRs on: pull_request: push: - branches: - - main + workflow_dispatch: jobs: gradle: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 + - uses: actions/checkout@v6 + - uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '17' - - name: Install Ghidra - run: | - curl -L "$GHIDRA_URL" -o ghidra.zip - mkdir ~/ghidra - unzip ghidra.zip -d ~/ghidra - rm ghidra.zip - env: - GHIDRA_URL: https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_11.1_build/ghidra_11.1_PUBLIC_20240607.zip + java-version: '21' + - uses: antoniovazquezblanco/setup-ghidra@v2.0.16 + with: + version: '12.0' - name: Execute Gradle build - run: ./gradlew - env: - GHIDRA_INSTALL_DIR: /home/runner/ghidra/ghidra_11.1_PUBLIC/ + run: ./gradlew -PGHIDRA_INSTALL_DIR=${{ env.GHIDRA_INSTALL_DIR }} - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') diff --git a/build.gradle b/build.gradle index 93ce401..864bfc2 100644 --- a/build.gradle +++ b/build.gradle @@ -36,12 +36,9 @@ def DISTRIBUTION_DIR = file("dist") def PATH_IN_ZIP = "${project.name}" def getGitHash = { - def stdout = new ByteArrayOutputStream() - exec { + return providers.exec { commandLine 'git', 'rev-parse', '--short', 'HEAD' - standardOutput = stdout - } - return stdout.toString().trim() + }.standardOutput.asText.get().trim() } wrapper { @@ -55,23 +52,24 @@ repositories { configurations { localDeps + implementation.extendsFrom localDeps } dependencies { implementation fileTree(dir: ghidraInstallDir + '/Ghidra/Processors', include: "**/*.jar") - localDeps group: 'org.lz4', name: 'lz4-java', version: '1.5.1' - implementation configurations.localDeps + localDeps 'org.lz4:lz4-java:1.5.1' } buildExtension { - baseName "${project.name}-${project.version}-${getGitHash()}-Ghidra_${ghidra_version}".replace(' ', '_') - extension 'zip' - destinationDir DISTRIBUTION_DIR - version '' + archiveBaseName.set("${project.name}-${project.version}-${getGitHash()}-Ghidra_${ghidra_version}".replace(' ', '_')) + archiveExtension.set('zip') + destinationDirectory.set(DISTRIBUTION_DIR) + + archiveVersion.set('') // Make sure that we don't try to copy the same file with the same path into the // zip (this can happen!) - duplicatesStrategy 'exclude' + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // This filtered property file copy must appear before the general // copy to ensure that it is prefered over the unmodified file @@ -119,13 +117,13 @@ buildExtension { into PATH_IN_ZIP } - doLast { - println "\nCreated " + baseName + "." + extension + " in " + destinationDir + doLast { + println "\nCreated " + archiveBaseName.get() + "." + archiveExtension.get() + " in " + destinationDirectory.get().asFile } } -tasks.withType(Copy).all { duplicatesStrategy 'exclude' } +tasks.withType(Copy).all { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } jar { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE } diff --git a/gradle.properties b/gradle.properties index d12cae7..6177143 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.9.1 \ No newline at end of file +version=0.9.2 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ae04661..ac57dd1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/cafeloader/Cafe_ElfRelocationHandler.java b/src/main/java/cafeloader/Cafe_ElfRelocationHandler.java index 5bdb278..7389afb 100644 --- a/src/main/java/cafeloader/Cafe_ElfRelocationHandler.java +++ b/src/main/java/cafeloader/Cafe_ElfRelocationHandler.java @@ -1,161 +1,205 @@ package cafeloader; import ghidra.app.util.bin.format.elf.*; -import ghidra.app.util.bin.format.elf.relocation.ElfRelocationContext; -import ghidra.app.util.bin.format.elf.relocation.ElfRelocationHandler; +import ghidra.app.util.bin.format.elf.relocation.*; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.MemoryAccessException; import ghidra.util.Msg; import ghidra.util.exception.InvalidInputException; -import ghidra.util.exception.NotFoundException; import ghidra.program.model.symbol.ExternalLocation; import ghidra.program.model.symbol.RefType; import ghidra.program.model.symbol.SourceType; import ghidra.program.model.reloc.Relocation.Status; import ghidra.program.model.reloc.RelocationResult; -public class Cafe_ElfRelocationHandler extends ElfRelocationHandler { - @Override - public boolean canRelocate(ElfHeader elf) { - return elf instanceof RplHeader; - } - - @Override - public RelocationResult relocate(ElfRelocationContext elfRelocationContext, ElfRelocation relocation, - Address relocationAddress) throws MemoryAccessException, NotFoundException { - int type = relocation.getType(); - if (type == Cafe_ElfRelocationConstants.R_PPC_NONE) { - return RelocationResult.SKIPPED; - } - - ElfHeader elf = elfRelocationContext.getElfHeader(); - Program program = elfRelocationContext.getProgram(); - Memory memory = program.getMemory(); - int symbolIndex = relocation.getSymbolIndex(); - int addend = (int) relocation.getAddend(); - int offset = (int) relocationAddress.getOffset(); - ElfSymbol sym = elfRelocationContext.getSymbol(symbolIndex); - int symbolValue = (int) elfRelocationContext.getSymbolValue(sym); - int oldValue = memory.getInt(relocationAddress); - int newValue = 0; - - int byteLength = 4; // most relocations affect 4-bytes (change if different) - - /* - * If the symbol is in a SHT_RPL_IMPORTS section then we must add a memory - * reference because it will be too far away for the actual relocation to - * be valid itself. - */ - - if (sym.getSectionHeaderIndex() > 0) { - ElfSectionHeader symbolSection = elf.getSections()[sym.getSectionHeaderIndex()]; - if (symbolSection.getType() == Cafe_ElfExtension.SHT_RPL_IMPORTS.value) { - String symbolSectionName = symbolSection.getNameAsString(); - boolean isDataImport = false; - if (symbolSectionName.startsWith(".dimport_")) { - program.getReferenceManager().addMemoryReference(relocationAddress, - elfRelocationContext.getSymbolAddress(sym), RefType.DATA, SourceType.IMPORTED, 0); - isDataImport = true; - } else if (symbolSectionName.startsWith(".fimport_")) { - program.getReferenceManager().addMemoryReference(relocationAddress, - elfRelocationContext.getSymbolAddress(sym), RefType.UNCONDITIONAL_CALL, SourceType.IMPORTED, 0); - } - - String rplName = symbolSectionName.split("import_")[1]; - if (!rplName.endsWith(".rpl")) { - rplName += ".rpl"; - } - - ExternalLocation location = program.getExternalManager().getUniqueExternalLocation(rplName, sym.getNameAsString()); - if (location != null) { - try { - if (isDataImport) { - program.getReferenceManager().addExternalReference(relocationAddress, 1, - location, SourceType.IMPORTED, RefType.DATA); - } else { - program.getReferenceManager().addExternalReference(relocationAddress, 1, - location, SourceType.IMPORTED, RefType.UNCONDITIONAL_CALL); - } - } catch (InvalidInputException e) { - Msg.warn(this, "addExternalReference failed with " + e); - } - } else { - Msg.warn(this, "Failed to find location for " + sym.getNameAsString()); - } - } - } - - switch (type) { - case Cafe_ElfRelocationConstants.R_PPC_ADDR32: - newValue = symbolValue + addend; - memory.setInt(relocationAddress, newValue); - break; - case Cafe_ElfRelocationConstants.R_PPC_ADDR16_LO: - newValue = symbolValue + addend; - memory.setShort(relocationAddress, (short) newValue); - byteLength = 2; - break; - case Cafe_ElfRelocationConstants.R_PPC_ADDR16_HI: - newValue = (symbolValue + addend) >> 16; - memory.setShort(relocationAddress, (short) newValue); - byteLength = 2; - break; - case Cafe_ElfRelocationConstants.R_PPC_ADDR16_HA: - newValue = (symbolValue + addend + 0x8000) >> 16; - memory.setShort(relocationAddress, (short) newValue); - byteLength = 2; - break; - case Cafe_ElfRelocationConstants.R_PPC_REL24: - newValue = (symbolValue + addend - offset) >> 2; - newValue = ((newValue << 2) & Cafe_ElfRelocationConstants.PPC_LOW24); - newValue = (oldValue & ~Cafe_ElfRelocationConstants.PPC_LOW24) | newValue; - memory.setInt(relocationAddress, newValue); - break; - case Cafe_ElfRelocationConstants.R_PPC_REL14: - newValue = (symbolValue + addend - offset) >> 2; - newValue = (oldValue & ~Cafe_ElfRelocationConstants.PPC_LOW14) | - ((newValue << 2) & Cafe_ElfRelocationConstants.PPC_LOW14); - memory.setInt(relocationAddress, newValue); - break; - case Cafe_ElfRelocationConstants.R_PPC_GHS_REL16_HA: - newValue = (symbolValue + addend - offset + 0x8000) >> 16; - memory.setShort(relocationAddress, (short) newValue); - byteLength = 2; - break; - case Cafe_ElfRelocationConstants.R_PPC_GHS_REL16_HI: - newValue = (symbolValue + addend - offset) >> 16; - memory.setShort(relocationAddress, (short) newValue); - byteLength = 2; - break; - case Cafe_ElfRelocationConstants.R_PPC_GHS_REL16_LO: - newValue = (symbolValue + addend - offset); - memory.setShort(relocationAddress, (short) newValue); - byteLength = 2; - break; - case Cafe_ElfRelocationConstants.R_PPC_DTPREL32: - newValue = symbolValue + addend; - memory.setInt(relocationAddress, newValue); - break; - case Cafe_ElfRelocationConstants.R_PPC_DTPMOD32: - // TODO: Do we need a tlsModuleIndex? - // *virt_cast(target) = tlsModuleIndex; - memory.setInt(relocationAddress, 0); - break; - case Cafe_ElfRelocationConstants.R_PPC_EMB_SDA21: - // TODO: SDA relocations require sda / sda2 base from SHT_RPL_FILEINFO section - case Cafe_ElfRelocationConstants.R_PPC_EMB_RELSDA: - case Cafe_ElfRelocationConstants.R_PPC_DIAB_SDA21_LO: - case Cafe_ElfRelocationConstants.R_PPC_DIAB_SDA21_HI: - case Cafe_ElfRelocationConstants.R_PPC_DIAB_SDA21_HA: - case Cafe_ElfRelocationConstants.R_PPC_DIAB_RELSDA_LO: - case Cafe_ElfRelocationConstants.R_PPC_DIAB_RELSDA_HI: - case Cafe_ElfRelocationConstants.R_PPC_DIAB_RELSDA_HA: - default: - return RelocationResult.UNSUPPORTED; - } - - return new RelocationResult(Status.APPLIED, byteLength); - } -} + +public class Cafe_ElfRelocationHandler extends AbstractElfRelocationHandler> { + + public enum CafeRelocationType implements ElfRelocationType { + R_PPC_NONE(Cafe_ElfRelocationConstants.R_PPC_NONE), + R_PPC_ADDR32(Cafe_ElfRelocationConstants.R_PPC_ADDR32), + R_PPC_ADDR16_LO(Cafe_ElfRelocationConstants.R_PPC_ADDR16_LO), + R_PPC_ADDR16_HI(Cafe_ElfRelocationConstants.R_PPC_ADDR16_HI), + R_PPC_ADDR16_HA(Cafe_ElfRelocationConstants.R_PPC_ADDR16_HA), + R_PPC_REL24(Cafe_ElfRelocationConstants.R_PPC_REL24), + R_PPC_REL14(Cafe_ElfRelocationConstants.R_PPC_REL14), + R_PPC_DTPMOD32(Cafe_ElfRelocationConstants.R_PPC_DTPMOD32), + R_PPC_DTPREL32(Cafe_ElfRelocationConstants.R_PPC_DTPREL32), + R_PPC_EMB_SDA21(Cafe_ElfRelocationConstants.R_PPC_EMB_SDA21), + R_PPC_EMB_RELSDA(Cafe_ElfRelocationConstants.R_PPC_EMB_RELSDA), + R_PPC_DIAB_SDA21_LO(Cafe_ElfRelocationConstants.R_PPC_DIAB_SDA21_LO), + R_PPC_DIAB_SDA21_HI(Cafe_ElfRelocationConstants.R_PPC_DIAB_SDA21_HI), + R_PPC_DIAB_SDA21_HA(Cafe_ElfRelocationConstants.R_PPC_DIAB_SDA21_HA), + R_PPC_DIAB_RELSDA_LO(Cafe_ElfRelocationConstants.R_PPC_DIAB_RELSDA_LO), + R_PPC_DIAB_RELSDA_HI(Cafe_ElfRelocationConstants.R_PPC_DIAB_RELSDA_HI), + R_PPC_DIAB_RELSDA_HA(Cafe_ElfRelocationConstants.R_PPC_DIAB_RELSDA_HA), + R_PPC_GHS_REL16_HA(Cafe_ElfRelocationConstants.R_PPC_GHS_REL16_HA), + R_PPC_GHS_REL16_HI(Cafe_ElfRelocationConstants.R_PPC_GHS_REL16_HI), + R_PPC_GHS_REL16_LO(Cafe_ElfRelocationConstants.R_PPC_GHS_REL16_LO); + + + public final int typeId; + + private CafeRelocationType(int typeId) { + this.typeId = typeId; + } + + @Override + public int typeId() { + return typeId; + } + } + + public Cafe_ElfRelocationHandler() { + super(CafeRelocationType.class); + } + + @Override + public boolean canRelocate(ElfHeader elf) { + return elf instanceof RplHeader; + } + + @Override + protected RelocationResult relocate(ElfRelocationContext elfRelocationContext, + ElfRelocation relocation, + CafeRelocationType relo, + Address relocationAddress, + ElfSymbol sym, + Address symbolAddr, + long _symbolValue, + String symbolName) throws MemoryAccessException { + + int type = relocation.getType(); + if (type == Cafe_ElfRelocationConstants.R_PPC_NONE) { + return RelocationResult.SKIPPED; + } + + ElfHeader elf = elfRelocationContext.getElfHeader(); + Program program = elfRelocationContext.getProgram(); + Memory memory = program.getMemory(); + int addend = (int) relocation.getAddend(); + int symbolValue = (int) elfRelocationContext.getSymbolValue(sym); + int offset = (int) relocationAddress.getOffset(); + int oldValue = memory.getInt(relocationAddress); + long newValue = 0; + + int byteLength = 4; // most relocations affect 4-bytes (change if different) + + /* + * If the symbol is in a SHT_RPL_IMPORTS section then we must add a memory + * reference because it will be too far away for the actual relocation to + * be valid itself. + */ + + if (sym.getSectionHeaderIndex() > 0) { + ElfSectionHeader symbolSection = elf.getSections()[sym.getSectionHeaderIndex()]; + if (symbolSection.getType() == Cafe_ElfExtension.SHT_RPL_IMPORTS.value) { + String symbolSectionName = symbolSection.getNameAsString(); + boolean isDataImport = false; + if (symbolSectionName.startsWith(".dimport_")) { + program.getReferenceManager().addMemoryReference(relocationAddress, + elfRelocationContext.getSymbolAddress(sym), RefType.DATA, SourceType.IMPORTED, 0); + isDataImport = true; + } else if (symbolSectionName.startsWith(".fimport_")) { + program.getReferenceManager().addMemoryReference(relocationAddress, + elfRelocationContext.getSymbolAddress(sym), RefType.UNCONDITIONAL_CALL, SourceType.IMPORTED, 0); + } + + String rplName = symbolSectionName.split("import_")[1]; + if (!rplName.endsWith(".rpl")) { + rplName += ".rpl"; + } + + ExternalLocation location = program.getExternalManager().getUniqueExternalLocation(rplName, sym.getNameAsString()); + if (location != null) { + try { + if (isDataImport) { + program.getReferenceManager().addExternalReference(relocationAddress, 1, + location, SourceType.IMPORTED, RefType.DATA); + } else { + program.getReferenceManager().addExternalReference(relocationAddress, 1, + location, SourceType.IMPORTED, RefType.UNCONDITIONAL_CALL); + } + } catch (InvalidInputException e) { + Msg.warn(this, "addExternalReference failed with " + e); + } + } else { + Msg.warn(this, "Failed to find location for " + sym.getNameAsString()); + } + } + } + + switch (relo) { + case CafeRelocationType.R_PPC_ADDR32: + newValue = symbolValue + addend; + memory.setInt(relocationAddress, (int) newValue); + break; + case CafeRelocationType.R_PPC_ADDR16_LO: + newValue = symbolValue + addend; + memory.setShort(relocationAddress, (short) newValue); + byteLength = 2; + break; + case CafeRelocationType.R_PPC_ADDR16_HI: + newValue = (symbolValue + addend) >> 16; + memory.setShort(relocationAddress, (short) newValue); + byteLength = 2; + break; + case CafeRelocationType.R_PPC_ADDR16_HA: + newValue = (symbolValue + addend + 0x8000) >> 16; + memory.setShort(relocationAddress, (short) newValue); + byteLength = 2; + break; + case CafeRelocationType.R_PPC_REL24: + newValue = (symbolValue + addend - offset) >> 2; + newValue = ((newValue << 2) & Cafe_ElfRelocationConstants.PPC_LOW24); + newValue = (oldValue & ~Cafe_ElfRelocationConstants.PPC_LOW24) | newValue; + memory.setInt(relocationAddress, (int) newValue); + break; + case CafeRelocationType.R_PPC_REL14: + newValue = (symbolValue + addend - offset) >> 2; + newValue = (oldValue & ~Cafe_ElfRelocationConstants.PPC_LOW14) | + ((newValue << 2) & Cafe_ElfRelocationConstants.PPC_LOW14); + memory.setInt(relocationAddress, (int) newValue); + break; + case CafeRelocationType.R_PPC_GHS_REL16_HA: + newValue = (symbolValue + addend - offset + 0x8000) >> 16; + memory.setShort(relocationAddress, (short) newValue); + byteLength = 2; + break; + case CafeRelocationType.R_PPC_GHS_REL16_HI: + newValue = (symbolValue + addend - offset) >> 16; + memory.setShort(relocationAddress, (short) newValue); + byteLength = 2; + break; + case CafeRelocationType.R_PPC_GHS_REL16_LO: + newValue = (symbolValue + addend - offset); + memory.setShort(relocationAddress, (short) newValue); + byteLength = 2; + break; + case CafeRelocationType.R_PPC_DTPREL32: + newValue = symbolValue + addend; + memory.setInt(relocationAddress, (int) newValue); + break; + case CafeRelocationType.R_PPC_DTPMOD32: + // TODO: Do we need a tlsModuleIndex? + // *virt_cast(target) = tlsModuleIndex; + memory.setInt(relocationAddress, 0); + break; + case CafeRelocationType.R_PPC_EMB_SDA21: + // TODO: SDA relocations require sda / sda2 base from SHT_RPL_FILEINFO section + case CafeRelocationType.R_PPC_EMB_RELSDA: + case CafeRelocationType.R_PPC_DIAB_SDA21_LO: + case CafeRelocationType.R_PPC_DIAB_SDA21_HI: + case CafeRelocationType.R_PPC_DIAB_SDA21_HA: + case CafeRelocationType.R_PPC_DIAB_RELSDA_LO: + case CafeRelocationType.R_PPC_DIAB_RELSDA_HI: + case CafeRelocationType.R_PPC_DIAB_RELSDA_HA: + default: + return RelocationResult.UNSUPPORTED; + } + + return new RelocationResult(Status.APPLIED, byteLength); + } +} \ No newline at end of file diff --git a/src/main/java/ghidra/app/util/opinion/CafeLoader.java b/src/main/java/ghidra/app/util/opinion/CafeLoader.java index 5ee4aea..c446db1 100644 --- a/src/main/java/ghidra/app/util/opinion/CafeLoader.java +++ b/src/main/java/ghidra/app/util/opinion/CafeLoader.java @@ -21,61 +21,66 @@ import ghidra.util.task.TaskMonitor; public class CafeLoader extends ElfLoader { - @Override - public Collection findSupportedLoadSpecs(ByteProvider provider) throws IOException { - byte[] header = new byte[] { 0x7F, 0x45, 0x4C, 0x46, 0x01, 0x02, 0x01, (byte) 0xCA, (byte) 0xFE }; - List loadSpecs = new ArrayList<>(); - - if (Arrays.equals(provider.readBytes(0, header.length), header)) { - List results = QueryOpinionService.query(getName(), "wiiu", null); - boolean hasGekkoProcessor = false; - - for (QueryResult result : results) { - if (result.pair.languageID.getIdAsString().contains("Gekko")) { - hasGekkoProcessor = true; - } - } - - for (QueryResult result : results) { - if (result.pair.languageID.getIdAsString().contains("Gekko")) { - loadSpecs.add(new LoadSpec(this, 0, new QueryResult(result.pair, true))); - } else { - loadSpecs.add(new LoadSpec(this, 0, new QueryResult(result.pair, hasGekkoProcessor ? false : result.preferred))); - } - } - - if (loadSpecs.isEmpty()) { - loadSpecs.add(new LoadSpec(this, 0, new LanguageCompilerSpecPair("PowerPC:BE:32:default", "default"), true)); - } - } - - return loadSpecs; - } - - @Override - public String getName() { - return "Wii U / CafeOS Binary (RPX/RPL)"; - } - - @Override - public LoaderTier getTier() { - return LoaderTier.SPECIALIZED_TARGET_LOADER; - } - - @Override - public int getTierPriority() { - return 0; - } - - @Override - public void load(ByteProvider provider, LoadSpec loadSpec, List