diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutHeader.java index 87f22f2323..06c145021e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutHeader.java @@ -18,14 +18,9 @@ package ghidra.app.util.bin.format.unixaout; import java.io.IOException; -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.ByteProvider; -import ghidra.app.util.bin.StructConverter; +import ghidra.app.util.bin.*; import ghidra.program.model.address.Address; -import ghidra.program.model.data.CategoryPath; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.Structure; -import ghidra.program.model.data.StructureDataType; +import ghidra.program.model.data.*; import ghidra.program.model.listing.Listing; import ghidra.program.model.listing.Program; import ghidra.program.model.util.CodeUnitInsertionException; @@ -74,168 +69,165 @@ public class UnixAoutHeader implements StructConverter { // The Linux implementation of a.out appears to start the .text content at // file offset 0x400 (rather than immediately after the 0x20 bytes of header - // data). It's possible that there exist Linux a.out executabes with other + // data). It's possible that there exist Linux a.out executabLes with other // (unintended?) header sizes caused by a mixture of 32- and 64-bit integers // being padded out in the struct. The intended size is eight 32-bit words // (32 bytes total.) - private static final int sizeOfExecHeader = 0x20; - private static final int sizeOfLongExecHeader = 0x400; + private static final int SIZE_OF_EXEC_HEADER = 0x20; + private static final int SIZE_OF_LONG_EXEC_HEADER = 0x400; /** - * Interprets binary data as an exec header from a UNIX-style a.out executable, - * and validates the contained fields. + * Interprets binary data as an exec header from a UNIX-style a.out executable, and validates + * the contained fields. * - * @param provider Source of header binary data - * @param isLittleEndian Flag indicating whether to interpret the data as - * little-endian. - * @throws IOException + * @param provider Source of header binary data + * @param isLittleEndian Flag indicating whether to interpret the data as little-endian. + * @throws IOException if an IO-related error occurred */ public UnixAoutHeader(ByteProvider provider, boolean isLittleEndian) throws IOException { - this.reader = new BinaryReader(provider, isLittleEndian); + reader = new BinaryReader(provider, isLittleEndian); - this.a_magic = reader.readNextUnsignedInt(); - this.a_text = reader.readNextUnsignedInt(); - this.a_data = reader.readNextUnsignedInt(); - this.a_bss = reader.readNextUnsignedInt(); - this.a_syms = reader.readNextUnsignedInt(); - this.a_entry = reader.readNextUnsignedInt(); - this.a_trsize = reader.readNextUnsignedInt(); - this.a_drsize = reader.readNextUnsignedInt(); - this.binarySize = reader.length(); + a_magic = reader.readNextUnsignedInt(); + a_text = reader.readNextUnsignedInt(); + a_data = reader.readNextUnsignedInt(); + a_bss = reader.readNextUnsignedInt(); + a_syms = reader.readNextUnsignedInt(); + a_entry = reader.readNextUnsignedInt(); + a_trsize = reader.readNextUnsignedInt(); + a_drsize = reader.readNextUnsignedInt(); + binarySize = reader.length(); - checkExecutableType(); + setExecutableType(a_magic); - // NOTE: In NetBSD/i386 examples of a.out, the "new-style" 32-bit a_magic/midmag - // word - // is written in big-endian regardless of the data endianness in the rest of the - // file. - if ((this.exeType == AoutType.UNKNOWN) && isLittleEndian) { - this.a_magic = Integer.reverseBytes((int) this.a_magic); - checkExecutableType(); + // NOTE: In NetBSD/i386 examples of a.out, the "new-style" 32-bit a_magic/midmag word is + // written in big-endian regardless of the data endianness in the rest of the file. + if ((exeType == AoutType.UNKNOWN) && isLittleEndian) { + a_magic = Integer.reverseBytes((int) a_magic); + setExecutableType(a_magic); } checkMachineTypeValidity(isLittleEndian); - determineTextOffset(reader, isLittleEndian); + determineTextOffset(); - this.datOffset = this.txtOffset + this.a_text; - this.txtRelOffset = this.datOffset + this.a_data; - this.datRelOffset = this.txtRelOffset + this.a_trsize; - this.symOffset = this.datRelOffset + this.a_drsize; - this.strOffset = this.symOffset + this.a_syms; + datOffset = txtOffset + a_text; + txtRelOffset = datOffset + a_data; + datRelOffset = txtRelOffset + a_trsize; + symOffset = datRelOffset + a_drsize; + strOffset = symOffset + a_syms; - this.strSize = 0; - if (this.strOffset != 0 && (this.strOffset + 4) <= binarySize) { - this.strSize = reader.readUnsignedInt(this.strOffset); + strSize = 0; + if (strOffset != 0 && (strOffset + 4) <= binarySize) { + strSize = reader.readUnsignedInt(strOffset); } determineTextAddr(); - this.txtEndAddr = this.txtAddr + this.a_text; - this.datAddr = (this.exeType == AoutType.OMAGIC) ? this.txtEndAddr : segmentRound(this.txtEndAddr); - this.bssAddr = this.datAddr + this.a_data; + txtEndAddr = txtAddr + a_text; + datAddr = (exeType == AoutType.OMAGIC) ? txtEndAddr : segmentRound(txtEndAddr); + bssAddr = datAddr + a_data; } public BinaryReader getReader() { - return this.reader; + return reader; } /** - * Returns the processor/language specified by this header. + * {@return the processor/language specified by this header.} */ public String getLanguageSpec() { - return this.languageSpec; + return languageSpec; } /** - * Returns the compiler used by this executable. This is left as 'default' for - * all machine types other than i386, where it is assumed to be gcc. + * {@return the compiler used by this executable. This is left as 'default' for + * all machine types other than i386, where it is assumed to be gcc.} */ public String getCompilerSpec() { - return this.compilerSpec; + return compilerSpec; } /** - * Returns the enumerated type of executable contained in this A.out file. + * {@return the enumerated type of executable contained in this A.out file.} */ public AoutType getExecutableType() { - return this.exeType; + return exeType; } /** - * Returns an indication of whether this header's fields are all valid; this - * includes the machine type, executable type, and section offsets. + * {@return an indication of whether this header's fields are all valid; this + * includes the machine type, executable type, and section offsets.} */ public boolean isValid() { return isMachineTypeValid() && - (this.exeType != AoutType.UNKNOWN) && - areOffsetsValid(); + (exeType != AoutType.UNKNOWN) && + areOffsetsValid(); } public long getTextSize() { - return this.a_text; + return a_text; } public long getDataSize() { - return this.a_data; + return a_data; } public long getBssSize() { - return this.a_bss; + return a_bss; } public long getSymSize() { - return this.a_syms; + return a_syms; } public long getStrSize() { - return this.strSize; + return strSize; } public long getEntryPoint() { - return this.a_entry; + return a_entry; } public long getTextRelocSize() { - return this.a_trsize; + return a_trsize; } public long getDataRelocSize() { - return this.a_drsize; + return a_drsize; } public long getTextOffset() { - return this.txtOffset; + return txtOffset; } public long getDataOffset() { - return this.datOffset; + return datOffset; } public long getTextRelocOffset() { - return this.txtRelOffset; + return txtRelOffset; } public long getDataRelocOffset() { - return this.datRelOffset; + return datRelOffset; } public long getSymOffset() { - return this.symOffset; + return symOffset; } public long getStrOffset() { - return this.strOffset; + return strOffset; } public long getTextAddr() { - return this.txtAddr; + return txtAddr; } public long getDataAddr() { - return this.datAddr; + return datAddr; } public long getBssAddr() { - return this.bssAddr; + return bssAddr; } /** @@ -244,9 +236,9 @@ public class UnixAoutHeader implements StructConverter { */ private void checkMachineTypeValidity(boolean readingAsLittleEndian) { - this.machineTypeValid = true; - this.pageSize = 4096; - final short machtype = (short) ((this.a_magic >> 16) & 0xFF); + machineTypeValid = true; + pageSize = 4096; + final short machtype = (short) ((a_magic >> 16) & 0xFF); final String readEndianness = readingAsLittleEndian ? "LE" : "BE"; switch (machtype) { @@ -254,108 +246,108 @@ public class UnixAoutHeader implements StructConverter { * Motorola 68K family */ case UnixAoutMachineType.M_68010: - this.languageSpec = "68000:BE:32:MC68010"; + languageSpec = "68000:BE:32:MC68010"; break; case UnixAoutMachineType.M_68020: - this.languageSpec = "68000:BE:32:MC68020"; + languageSpec = "68000:BE:32:MC68020"; break; case UnixAoutMachineType.M_M68K_NETBSD: - this.pageSize = 8192; + pageSize = 8192; case UnixAoutMachineType.M_M68K4K_NETBSD: - this.isNetBSD = true; - this.languageSpec = "68000:BE:32:default"; + isNetBSD = true; + languageSpec = "68000:BE:32:default"; break; /** * SPARC family */ case UnixAoutMachineType.M_SPARC_NETBSD: - this.isNetBSD = true; + isNetBSD = true; case UnixAoutMachineType.M_SPARC: case UnixAoutMachineType.M_SPARCLET: - this.isSparc = true; - this.pageSize = 8192; - this.languageSpec = "sparc:BE:32:default"; + isSparc = true; + pageSize = 8192; + languageSpec = "sparc:BE:32:default"; break; case UnixAoutMachineType.M_SPARC64_NETBSD: - this.isNetBSD = true; - this.isSparc = true; - this.languageSpec = "sparc:BE:64:default"; + isNetBSD = true; + isSparc = true; + languageSpec = "sparc:BE:64:default"; break; /** * MIPS family */ case UnixAoutMachineType.M_PMAX_NETBSD: - this.isNetBSD = true; + isNetBSD = true; case UnixAoutMachineType.M_MIPS1: case UnixAoutMachineType.M_MIPS2: case UnixAoutMachineType.M_R3000: - this.languageSpec = "MIPS:LE:32:default"; + languageSpec = "MIPS:LE:32:default"; break; case UnixAoutMachineType.M_MIPS: - this.languageSpec = "MIPS:BE:32:default"; + languageSpec = "MIPS:BE:32:default"; break; /** * National Semiconductor NS32000 family */ case UnixAoutMachineType.M_532_NETBSD: - this.isNetBSD = true; + isNetBSD = true; case UnixAoutMachineType.M_NS32032: case UnixAoutMachineType.M_NS32532: - this.languageSpec = "UNKNOWN:LE:32:default"; + languageSpec = "UNKNOWN:LE:32:default"; break; /** * x86 family */ case UnixAoutMachineType.M_386_NETBSD: - this.isNetBSD = true; + isNetBSD = true; case UnixAoutMachineType.M_386: case UnixAoutMachineType.M_386_DYNIX: - this.compilerSpec = "gcc"; - this.languageSpec = "x86:LE:32:default"; + compilerSpec = "gcc"; + languageSpec = "x86:LE:32:default"; break; case UnixAoutMachineType.M_X86_64_NETBSD: - this.compilerSpec = "gcc"; - this.languageSpec = "x86:LE:64:default"; + compilerSpec = "gcc"; + languageSpec = "x86:LE:64:default"; break; /** * ARM family */ case UnixAoutMachineType.M_ARM6_NETBSD: - this.isNetBSD = true; + isNetBSD = true; case UnixAoutMachineType.M_ARM: - this.languageSpec = "ARM:" + readEndianness + ":32:default"; + languageSpec = "ARM:" + readEndianness + ":32:default"; break; case UnixAoutMachineType.M_AARCH64: - this.languageSpec = "AARCH64:" + readEndianness + ":64:default"; + languageSpec = "AARCH64:" + readEndianness + ":64:default"; break; /** * RISC family */ case UnixAoutMachineType.M_OR1K: - this.languageSpec = "UNKNOWN:BE:32:default"; + languageSpec = "UNKNOWN:BE:32:default"; break; case UnixAoutMachineType.M_RISCV: - this.languageSpec = "RISCV:LE:32:default"; + languageSpec = "RISCV:LE:32:default"; break; case UnixAoutMachineType.M_HPPA_OPENBSD: - this.languageSpec = "pa-risc:BE:32:default"; + languageSpec = "pa-risc:BE:32:default"; break; /** * PowerPC family */ case UnixAoutMachineType.M_POWERPC_NETBSD: - this.isNetBSD = true; - this.languageSpec = "PowerPC:" + readEndianness + ":32:default"; + isNetBSD = true; + languageSpec = "PowerPC:" + readEndianness + ":32:default"; break; case UnixAoutMachineType.M_POWERPC64: - this.languageSpec = "PowerPC:" + readEndianness + ":64:default"; + languageSpec = "PowerPC:" + readEndianness + ":64:default"; break; /** @@ -366,52 +358,52 @@ public class UnixAoutHeader implements StructConverter { */ case UnixAoutMachineType.M_SH3: case UnixAoutMachineType.M_SH5_32: - this.languageSpec = "SuperH:BE:32:default"; + languageSpec = "SuperH:BE:32:default"; break; case UnixAoutMachineType.M_SH5_64: - this.languageSpec = "SuperH:BE:64:default"; + languageSpec = "SuperH:BE:64:default"; break; /** * VAX family */ case UnixAoutMachineType.M_VAX_NETBSD: - this.pageSize = 512; + pageSize = 512; case UnixAoutMachineType.M_VAX4K_NETBSD: - this.isNetBSD = true; - this.languageSpec = "UNKNOWN:LE:32:default"; + isNetBSD = true; + languageSpec = "UNKNOWN:LE:32:default"; break; /** * Other */ case UnixAoutMachineType.M_CRIS: - this.languageSpec = "UNKNOWN:LE:32:default"; + languageSpec = "UNKNOWN:LE:32:default"; break; case UnixAoutMachineType.M_ALPHA_NETBSD: - this.isNetBSD = true; + isNetBSD = true; case UnixAoutMachineType.M_IA64: - this.languageSpec = "UNKNOWN:" + readEndianness + ":64:default"; + languageSpec = "UNKNOWN:" + readEndianness + ":64:default"; break; case UnixAoutMachineType.M_29K: case UnixAoutMachineType.M_88K_OPENBSD: - this.languageSpec = "UNKNOWN:" + readEndianness + ":32:default"; + languageSpec = "UNKNOWN:" + readEndianness + ":32:default"; break; case UnixAoutMachineType.M_UNKNOWN: - this.languageSpec = "UNKNOWN:" + readEndianness + ":32:default"; + languageSpec = "UNKNOWN:" + readEndianness + ":32:default"; break; default: - this.machineTypeValid = false; + machineTypeValid = false; } // Check that the detected architecture's endianness matches the endianness // with which we're reading the file; if there's a mismatch, clear the // machineTypeValid flag because this was evidently a false reading. - if (this.machineTypeValid) { - String[] languageTokens = this.languageSpec.split(":"); + if (machineTypeValid) { + String[] languageTokens = languageSpec.split(":"); if ((languageTokens.length < 2) || - !languageTokens[1].equalsIgnoreCase(readEndianness)) { - this.machineTypeValid = false; + !languageTokens[1].equalsIgnoreCase(readEndianness)) { + machineTypeValid = false; } } } @@ -421,35 +413,23 @@ public class UnixAoutHeader implements StructConverter { * ID. */ private boolean isMachineTypeValid() { - return this.machineTypeValid; + return machineTypeValid; } /** - * Returns a flag indicating whether this header contains a representation of a - * valid executable type. + * Sets the executable type based on the given magic + * + * @param magic The magic */ - private void checkExecutableType() { - final short exetypeMagic = (short) (this.a_magic & 0xFFFF); - - switch (exetypeMagic) { - case 0x111: // 0421: core file - this.exeType = AoutType.CMAGIC; - break; - case 0x108: // 0410: pure executable - this.exeType = AoutType.NMAGIC; - break; - case 0x107: // 0407: object file or impure executable - this.exeType = AoutType.OMAGIC; - break; - case 0x0CC: // 0314: demand-paged exe w/ header in .text - this.exeType = AoutType.QMAGIC; - break; - case 0x10B: // 0413: demand-paged executable - this.exeType = AoutType.ZMAGIC; - break; - default: - this.exeType = AoutType.UNKNOWN; - } + private void setExecutableType(long magic) { + exeType = switch ((short) (magic & 0xFFFF)) { + case 0x111 -> AoutType.CMAGIC; // 0421: core file + case 0x108 -> AoutType.NMAGIC; // 0410: pure executable + case 0x107 -> AoutType.OMAGIC; // 0407: object file or impure executable + case 0x0CC -> AoutType.QMAGIC; // 0314: demand-paged exe w/ header in .text + case 0x10B -> AoutType.ZMAGIC; // 0413: demand-paged executable + default -> AoutType.UNKNOWN; + }; } /** @@ -465,46 +445,51 @@ public class UnixAoutHeader implements StructConverter { * the a_magic word even when the file contains code for a little-endian * processor. */ - private void determineTextOffset(BinaryReader reader, boolean isLittleEndian) { + private void determineTextOffset() { boolean isLinuxStyle = false; - final long fixedContentSize = this.a_text + this.a_data + this.a_syms + this.a_trsize + this.a_drsize; + final long fixedContentSize = a_text + a_data + a_syms + a_trsize + a_drsize; // If the file is large enough to read at least one word beyond a long-style // header // of 0x400 bytes plus all the sections whose sizes are specified in the // header... - if (reader.isValidIndex(sizeOfLongExecHeader + fixedContentSize)) { + if (reader.isValidIndex(SIZE_OF_LONG_EXEC_HEADER + fixedContentSize)) { try { // The word that immediately follows the symbol table will contain the size of // the string table. - final long stringTableLength = reader.readUnsignedInt(sizeOfLongExecHeader + fixedContentSize); - final long longHeaderExpectedFileSize = sizeOfLongExecHeader + fixedContentSize + stringTableLength; + final long stringTableLength = + reader.readUnsignedInt(SIZE_OF_LONG_EXEC_HEADER + fixedContentSize); + final long longHeaderExpectedFileSize = + SIZE_OF_LONG_EXEC_HEADER + fixedContentSize + stringTableLength; // If the size of the file exactly matches what we'd expect if the .text content // starts at offset 0x400 rather than 0, this implies that the a.out is a // Linux-style binary. - if (this.binarySize == longHeaderExpectedFileSize) { + if (binarySize == longHeaderExpectedFileSize) { isLinuxStyle = true; } - } catch (IOException e) { + } + catch (IOException e) { e.printStackTrace(); } } - if (isLinuxStyle && (this.exeType == AoutType.ZMAGIC)) { + if (isLinuxStyle && (exeType == AoutType.ZMAGIC)) { // Linux ZMAGICs don't start the .text content until 0x400 - this.txtOffset = sizeOfLongExecHeader; + txtOffset = SIZE_OF_LONG_EXEC_HEADER; - } else if ((this.exeType == AoutType.QMAGIC) || - (this.exeType == AoutType.ZMAGIC)) { + } + else if ((exeType == AoutType.QMAGIC) || + (exeType == AoutType.ZMAGIC)) { // ZMAGIC for other platforms (as well as QMAGIC) include the file header itself // in the .text content - this.txtOffset = 0; + txtOffset = 0; - } else { + } + else { // Otherwise, the .text content starts immediately after the 0x20-byte header - this.txtOffset = sizeOfExecHeader; + txtOffset = SIZE_OF_EXEC_HEADER; } } @@ -514,15 +499,9 @@ public class UnixAoutHeader implements StructConverter { * base address of the .text segment when loaded. */ private void determineTextAddr() { - - if ((this.isSparc && (this.exeType == AoutType.NMAGIC)) || - (this.isNetBSD) || - (this.exeType == AoutType.QMAGIC)) { - this.txtAddr = this.pageSize; - - } else { - this.txtAddr = 0; - } + txtAddr = (isSparc && exeType == AoutType.NMAGIC) || isNetBSD || exeType == AoutType.QMAGIC + ? pageSize + : 0; } /** @@ -534,11 +513,11 @@ public class UnixAoutHeader implements StructConverter { // doesn't exist, its offset will be computed to be beyond the end of // the file. The string table is also not given an explicit size in // the header. - boolean status = ((this.a_text == 0) || (this.txtOffset < this.binarySize) && - ((this.a_data == 0) || (this.datOffset < this.binarySize)) && - ((this.a_trsize == 0) || (this.txtRelOffset < this.binarySize)) && - ((this.a_drsize == 0) || (this.datRelOffset < this.binarySize)) && - ((this.a_syms == 0) || (this.symOffset < this.binarySize))); + boolean status = ((a_text == 0) || (txtOffset < binarySize) && + ((a_data == 0) || (datOffset < binarySize)) && + ((a_trsize == 0) || (txtRelOffset < binarySize)) && + ((a_drsize == 0) || (datRelOffset < binarySize)) && + ((a_syms == 0) || (symOffset < binarySize))); return status; } @@ -546,28 +525,27 @@ public class UnixAoutHeader implements StructConverter { * Rounds the provided address up to the next page boundary. */ private long segmentRound(long addr) { - final long mask = this.pageSize - 1; + final long mask = pageSize - 1; long rounded = ((addr + mask) & ~mask); return rounded; } @Override public DataType toDataType() throws DuplicateNameException, IOException { - String dtName = "exec"; - Structure struct = new StructureDataType(new CategoryPath("/AOUT"), dtName, 0); - struct.add(DWORD, "a_midmag", null); - struct.add(DWORD, "a_text", null); - struct.add(DWORD, "a_data", null); - struct.add(DWORD, "a_bss", null); - struct.add(DWORD, "a_syms", null); - struct.add(DWORD, "a_entry", null); - struct.add(DWORD, "a_trsize", null); - struct.add(DWORD, "a_drsize", null); - + Structure struct = new StructureDataType(new CategoryPath("/AOUT"), "exec", 0); + struct.add(DWORD, "a_midmag", "magic (network byte order)"); + struct.add(DWORD, "a_text", "the size of the text segment in bytes"); + struct.add(DWORD, "a_data", "the size of the data segment in bytes"); + struct.add(DWORD, "a_bss", "the number of bytes in the bss segment"); + struct.add(DWORD, "a_syms", "the size in bytes of the symbol table section"); + struct.add(DWORD, "a_entry", "the address of the entry point"); + struct.add(DWORD, "a_trsize", "the size in bytes of the text relocation table"); + struct.add(DWORD, "a_drsize", "the size in bytes of the data relocation table"); return struct; } - public void markup(Program program, Address headerAddress) throws CodeUnitInsertionException, DuplicateNameException, IOException { + public void markup(Program program, Address headerAddress) + throws CodeUnitInsertionException, DuplicateNameException, IOException { Listing listing = program.getListing(); listing.createData(headerAddress, toDataType()); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutMachineType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutMachineType.java index 102f8e861e..15c69bae3a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutMachineType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutMachineType.java @@ -18,25 +18,17 @@ package ghidra.app.util.bin.format.unixaout; public class UnixAoutMachineType { - // These values come from a combination of sources, including NetBSD's - // aout_mids.h - // and the GNU BFD Library's libaout.h. + // These values come from a combination of sources, including NetBSD's aout_mids.h and the GNU + // BFD Library's libaout.h. // - // Note: some a.out header files list a few HP values (for the 300 Series, 800 - // Series, etc.) - // and these values exceed a full eight-bit count. Occasionally, this is - // accounted for by - // extending the Machine ID field of the a_magic word two bits higher, leaving - // only six bits - // in the MSB for other flags. This may not be correct, because those high-value - // HP machine - // IDs probably only appear in HP UX binaries, which use a different format. - // (This format is - // still named "a.out", but has a completely different header and internal - // organization.) - // The 10-bit Machine ID field would also interfere with flags used by VxWorks, - // NetBSD, and - // probably others. + // Note: some a.out header files list a few HP values (for the 300 Series, 800 Series, etc.) + // and these values exceed a full eight-bit count. Occasionally, this is accounted for by + // extending the Machine ID field of the a_magic word two bits higher, leaving only six bits in + // the MSB for other flags. This may not be correct, because those high-value HP machine IDs + // probably only appear in HP UX binaries, which use a different format. (This format is still + // named "a.out", but has a completely different header and internal organization.) The 10-bit + // Machine ID field would also interfere with flags used by VxWorks, NetBSD, and probably + // others. public final static short M_UNKNOWN = 0x00; public final static short M_68010 = 0x01; @@ -78,10 +70,9 @@ public class UnixAoutMachineType { public final static short M_RISCV = 0xb9; // RISC-V public final static short M_CRIS = 0xff; // Axis ETRAX CRIS - /** - * Machine IDs that should only appear in the incompatible HP UX a.out format: - * HP300 (68020+68881): 0x12c - * HP200/300 : 0x20c - * HP800 : 0x20b - */ + // Machine IDs that should only appear in the incompatible HP UX a.out format: + // + // HP300 (68020+68881): 0x12c + // HP200/300 : 0x20c + // HP800 : 0x20b } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutRelocation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutRelocation.java index 91f46cb9a5..d8cc4926e0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutRelocation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutRelocation.java @@ -37,8 +37,8 @@ public class UnixAoutRelocation { /** * * @param address First of the two words in the table entry (a 32-bit address) - * @param flags Second of the two words in the table entry (containing several - * bitfields) + * @param flags Second of the two words in the table entry (containing several bitfields) + * @param bigEndian True if big endian; otherwise, false */ public UnixAoutRelocation(long address, long flags, boolean bigEndian) { this.address = (0xFFFFFFFF & address); @@ -53,7 +53,8 @@ public class UnixAoutRelocation { this.jmpTable = ((flags & 0x4) != 0); this.relative = ((flags & 0x2) != 0); this.copy = ((flags & 0x1) != 0); - } else { + } + else { this.symbolNum = (int) (flags & 0x00FFFFFF); this.flags = (byte) ((flags & 0xFF000000) >> 24); this.pcRelativeAddressing = ((this.flags & 0x01) != 0); @@ -67,17 +68,16 @@ public class UnixAoutRelocation { } public String getSymbolName(UnixAoutSymbolTable symtab) { - if (extern == true && symbolNum < symtab.size()) { + if (extern && symbolNum < symtab.size()) { return symtab.get(symbolNum).name; - } else if (extern == false) { - switch (symbolNum) { - case 4: - return UnixAoutProgramLoader.dot_text; - case 6: - return UnixAoutProgramLoader.dot_data; - case 8: - return UnixAoutProgramLoader.dot_bss; - } + } + else if (!extern) { + return switch (symbolNum) { + case 4 -> UnixAoutProgramLoader.dot_text; + case 6 -> UnixAoutProgramLoader.dot_data; + case 8 -> UnixAoutProgramLoader.dot_bss; + default -> null; + }; } return null; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutRelocationTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutRelocationTable.java index a782345075..613da8cf15 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutRelocationTable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutRelocationTable.java @@ -16,24 +16,14 @@ package ghidra.app.util.bin.format.unixaout; import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; +import java.util.*; import org.apache.commons.lang3.StringUtils; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.StructConverter; -import ghidra.program.model.data.ArrayDataType; -import ghidra.program.model.data.CategoryPath; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.InvalidDataTypeException; -import ghidra.program.model.data.Structure; -import ghidra.program.model.data.StructureDataType; -import ghidra.program.model.listing.CodeUnit; -import ghidra.program.model.listing.Data; -import ghidra.program.model.listing.Listing; -import ghidra.program.model.listing.Program; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.*; import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.util.exception.DuplicateNameException; @@ -45,8 +35,8 @@ public class UnixAoutRelocationTable implements Iterable, St private final List relocations; private final UnixAoutSymbolTable symtab; - public UnixAoutRelocationTable(BinaryReader reader, long fileOffset, long fileSize, UnixAoutSymbolTable symtab) - throws IOException { + public UnixAoutRelocationTable(BinaryReader reader, long fileOffset, long fileSize, + UnixAoutSymbolTable symtab) throws IOException { this.fileSize = fileSize; this.relocations = new ArrayList<>(); this.symtab = symtab; @@ -58,7 +48,8 @@ public class UnixAoutRelocationTable implements Iterable, St long address = reader.readNextUnsignedInt(); long flags = reader.readNextUnsignedInt(); - UnixAoutRelocation relocation = new UnixAoutRelocation(address, flags, reader.isBigEndian()); + UnixAoutRelocation relocation = + new UnixAoutRelocation(address, flags, reader.isBigEndian()); relocations.add(relocation); } } @@ -83,7 +74,8 @@ public class UnixAoutRelocationTable implements Iterable, St struct.addBitField(BYTE, 1, "r_jmptable", null); struct.addBitField(BYTE, 1, "r_relative", null); struct.addBitField(BYTE, 1, "r_copy", null); - } catch (InvalidDataTypeException e) { + } + catch (InvalidDataTypeException e) { throw new RuntimeException(e); } @@ -101,7 +93,7 @@ public class UnixAoutRelocationTable implements Iterable, St if (!StringUtils.isBlank(name)) { Data structData = array.getComponent(idx); - structData.setComment(CodeUnit.EOL_COMMENT, name); + structData.setComment(CommentType.EOL, name); } idx++; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutStringTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutStringTable.java index 9678b30af6..2709811cae 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutStringTable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutStringTable.java @@ -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. @@ -21,9 +21,7 @@ import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.StructConverter; import ghidra.program.model.address.Address; import ghidra.program.model.data.TerminatedStringDataType; -import ghidra.program.model.listing.Data; -import ghidra.program.model.listing.Listing; -import ghidra.program.model.listing.Program; +import ghidra.program.model.listing.*; import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.util.CodeUnitInsertionException; @@ -42,7 +40,8 @@ public class UnixAoutStringTable { } try { return reader.readUtf8String(fileOffset + stringOffset).trim(); - } catch (IOException e) { + } + catch (IOException e) { // FIXME } return null; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutSymbol.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutSymbol.java index fdb8c2db6f..1d31a58da7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutSymbol.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutSymbol.java @@ -38,55 +38,29 @@ public class UnixAoutSymbol { public long value; public boolean isExt; - public UnixAoutSymbol(long nameStringOffset, byte typeByte, byte otherByte, - short desc, long value) { + public UnixAoutSymbol(long nameStringOffset, byte typeByte, byte otherByte, short desc, + long value) { this.nameStringOffset = nameStringOffset; this.otherByte = otherByte; this.desc = desc; this.value = value; this.isExt = (typeByte & 1) == 1; - switch (typeByte & 0xfe) { - case 0: - type = SymbolType.N_UNDF; - break; - case 2: - type = SymbolType.N_ABS; - break; - case 4: - type = SymbolType.N_TEXT; - break; - case 6: - type = SymbolType.N_DATA; - break; - case 8: - type = SymbolType.N_BSS; - break; - case 10: - type = SymbolType.N_INDR; - break; - default: - if ((typeByte & 0xfe) >= 0x20) { - type = SymbolType.N_STAB; - } else { - type = SymbolType.UNKNOWN; - } - break; - } + this.type = switch (typeByte & 0xfe) { + case 0 -> SymbolType.N_UNDF; + case 2 -> SymbolType.N_ABS; + case 4 -> SymbolType.N_TEXT; + case 6 -> SymbolType.N_DATA; + case 8 -> SymbolType.N_BSS; + case 10 -> SymbolType.N_INDR; + default -> (typeByte & 0xfe) >= 0x20 ? SymbolType.N_STAB : SymbolType.UNKNOWN; + }; - switch (otherByte & 0x0f) { - case 1: - kind = SymbolKind.AUX_OBJECT; - break; - case 2: - kind = SymbolKind.AUX_FUNC; - break; - case 3: - kind = SymbolKind.AUX_LABEL; - break; - default: - kind = SymbolKind.UNKNOWN; - break; - } + this.kind = switch (otherByte & 0x0f) { + case 1 -> SymbolKind.AUX_OBJECT; + case 2 -> SymbolKind.AUX_FUNC; + case 3 -> SymbolKind.AUX_LABEL; + default -> SymbolKind.UNKNOWN; + }; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutSymbolTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutSymbolTable.java index e2a0d664e1..fcea31e9a3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutSymbolTable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/unixaout/UnixAoutSymbolTable.java @@ -16,9 +16,7 @@ package ghidra.app.util.bin.format.unixaout; import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; +import java.util.*; import org.apache.commons.lang3.StringUtils; @@ -26,15 +24,8 @@ import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.StructConverter; import ghidra.app.util.importer.MessageLog; import ghidra.app.util.opinion.UnixAoutProgramLoader; -import ghidra.program.model.data.ArrayDataType; -import ghidra.program.model.data.CategoryPath; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.Structure; -import ghidra.program.model.data.StructureDataType; -import ghidra.program.model.listing.CodeUnit; -import ghidra.program.model.listing.Data; -import ghidra.program.model.listing.Listing; -import ghidra.program.model.listing.Program; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.*; import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.util.exception.DuplicateNameException; @@ -45,8 +36,8 @@ public class UnixAoutSymbolTable implements Iterable, StructConv private final long fileSize; private List symbols; - public UnixAoutSymbolTable(BinaryReader reader, long fileOffset, long fileSize, UnixAoutStringTable strtab, MessageLog log) - throws IOException { + public UnixAoutSymbolTable(BinaryReader reader, long fileOffset, long fileSize, + UnixAoutStringTable strtab, MessageLog log) throws IOException { this.fileSize = fileSize; this.symbols = new ArrayList<>(); @@ -63,7 +54,8 @@ public class UnixAoutSymbolTable implements Iterable, StructConv UnixAoutSymbol symbol = new UnixAoutSymbol(strOffset, typeByte, otherByte, desc, value); if (symbol.type == UnixAoutSymbol.SymbolType.UNKNOWN) { - log.appendMsg(UnixAoutProgramLoader.dot_symtab, String.format("Unknown symbol type 0x%02x at symbol index %d", typeByte, idx)); + log.appendMsg(UnixAoutProgramLoader.dot_symtab, + String.format("Unknown symbol type 0x%02x at symbol index %d", typeByte, idx)); } symbols.add(symbol); @@ -93,15 +85,16 @@ public class UnixAoutSymbolTable implements Iterable, StructConv return new ArrayDataType(struct, (int) (fileSize / ENTRY_SIZE), ENTRY_SIZE); } - public UnixAoutSymbol get(int symbolNum) { - return symbols.get(symbolNum); - } + public UnixAoutSymbol get(int symbolNum) { + return symbols.get(symbolNum); + } - public long size() { - return symbols.size(); - } + public long size() { + return symbols.size(); + } - public void markup(Program program, MemoryBlock block) throws CodeUnitInsertionException, DuplicateNameException, IOException { + public void markup(Program program, MemoryBlock block) + throws CodeUnitInsertionException, DuplicateNameException, IOException { Listing listing = program.getListing(); Data array = listing.createData(block.getStart(), toDataType()); @@ -111,7 +104,7 @@ public class UnixAoutSymbolTable implements Iterable, StructConv Data structData = array.getComponent(idx); if (structData != null) { - structData.setComment(CodeUnit.EOL_COMMENT, symbol.name); + structData.setComment(CommentType.EOL, symbol.name); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/UnixAoutLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/UnixAoutLoader.java index 86e1ad1d8a..24f6cb80e3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/UnixAoutLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/UnixAoutLoader.java @@ -16,9 +16,7 @@ package ghidra.app.util.opinion; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; +import java.util.*; import ghidra.app.util.Option; import ghidra.app.util.OptionException; @@ -26,9 +24,7 @@ import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.format.unixaout.UnixAoutHeader; import ghidra.app.util.importer.MessageLog; import ghidra.framework.model.DomainObject; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressFactory; -import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.address.*; import ghidra.program.model.lang.LanguageCompilerSpecPair; import ghidra.program.model.listing.Program; import ghidra.util.exception.CancelledException; @@ -36,46 +32,56 @@ import ghidra.util.task.TaskMonitor; /** * A {@link Loader} for processing UNIX-style A.out executables - * - * This style was also used by UNIX-like systems such as SunOS, BSD, and - * VxWorks, as well as some early distributions of Linux. Although there do - * exist implementations of A.out with 64-bit and GNU extensions, this loader - * does not currently support them. + *

+ * This style was also used by UNIX-like systems such as SunOS, BSD, and VxWorks, as well as some + * early distributions of Linux. Although there do exist implementations of A.out with 64-bit and \ + * GNU extensions, this loader does not currently support them. * * @see OSDev.org A.out - * @see FreeBSD - * manpage + * @see FreeBSD manpage */ public class UnixAoutLoader extends AbstractProgramWrapperLoader { + + public final static String UNIX_AOUT_NAME = "UNIX A.out"; + public static final String OPTION_NAME_BASE_ADDR = "Base Address"; @Override - public String getName() { - return "UNIX A.out executable"; + public Collection findSupportedLoadSpecs(ByteProvider provider) throws IOException { + List loadSpecs = new ArrayList<>(); + + // Attempt to parse the header as both little- and big-endian. + // It is likely that only one of these will produce sensible values. + UnixAoutHeader hdrBE = new UnixAoutHeader(provider, false); + UnixAoutHeader hdrLE = new UnixAoutHeader(provider, true); + boolean beValid = false; + + if (hdrBE.isValid()) { + final String lang = hdrBE.getLanguageSpec(); + final String comp = hdrBE.getCompilerSpec(); + loadSpecs.add(new LoadSpec(this, 0, new LanguageCompilerSpecPair(lang, comp), true)); + beValid = true; + } + if (hdrLE.isValid()) { + final String lang = hdrLE.getLanguageSpec(); + final String comp = hdrLE.getCompilerSpec(); + loadSpecs + .add(new LoadSpec(this, 0, new LanguageCompilerSpecPair(lang, comp), !beValid)); + } + + return loadSpecs; } - /** - * Retrieves the Address offset given in the "Base Address" option. - * Returns 0 if the option could not be found or contains an invalid value. - */ - private long getBaseAddrOffset(List