From 356ea446c7e95f0be086000bf8851ca881107c2d Mon Sep 17 00:00:00 2001 From: ghidra1 Date: Tue, 29 Sep 2020 10:22:46 -0400 Subject: [PATCH] GP-49 external disassembly field can now switch based upon context (implemented for ARM/Thumb) --- GPL/GnuDisassembler/src/gdis/c/disasm_1.c | 222 ++++++++++-------- .../disassemble/ExternalDisassembler.java | 2 + .../ExternalDisassemblyFieldFactory.java | 5 +- .../disassemble/GNUExternalDisassembler.java | 220 +++++++++++++++-- Ghidra/Processors/ARM/certification.manifest | 1 + Ghidra/Processors/ARM/data/languages/ARM.gdis | 41 ++++ .../Processors/ARM/data/languages/ARM.ldefs | 11 + Ghidra/Processors/x86/certification.manifest | 1 + .../Processors/x86/data/languages/x86-16.gdis | 3 + .../Processors/x86/data/languages/x86.ldefs | 4 + 10 files changed, 390 insertions(+), 120 deletions(-) create mode 100644 Ghidra/Processors/ARM/data/languages/ARM.gdis create mode 100644 Ghidra/Processors/x86/data/languages/x86-16.gdis diff --git a/GPL/GnuDisassembler/src/gdis/c/disasm_1.c b/GPL/GnuDisassembler/src/gdis/c/disasm_1.c index 8926449ee4..5274062917 100644 --- a/GPL/GnuDisassembler/src/gdis/c/disasm_1.c +++ b/GPL/GnuDisassembler/src/gdis/c/disasm_1.c @@ -21,7 +21,7 @@ void listSupportedArchMachTargets(void) const char** targetList; const char** archList; int i, j; - + targetList = bfd_target_list(); if(targetList != NULL){ for(i=0, j=0; targetList[i] !=0; i++){ @@ -29,7 +29,7 @@ void listSupportedArchMachTargets(void) } } printf("\ndone with targetList.\n"); - + archList = bfd_arch_list(); if(archList != NULL){ for(i=0, j=0; archList[i] !=0; i++){ @@ -44,29 +44,29 @@ void listSupportedArchMachTargets(void) /* sprintf to a "stream". */ int objdump_sprintf (SFILE *f, const char *format, ...) { - + int i; size_t n; va_list args; - + va_start (args, format); n = vsnprintf (f->buffer + f->pos, BUFF_SIZE, format, args); strncat(disassembled_buffer, f->buffer, n); va_end (args); - + return n; } void configureDisassembleInfo(bfd* abfd, - disassemble_info* info, - enum bfd_architecture arch, - unsigned long mach, - enum bfd_endian end) + disassemble_info* info, + enum bfd_architecture arch, + unsigned long mach, + enum bfd_endian end) { memset(sfile.buffer, 0x00, BUFF_SIZE); - + INIT_DISASSEMBLE_INFO(*info, stdout, objdump_sprintf); info->arch = (enum bfd_architecture) arch; info->mach = mach; @@ -79,24 +79,24 @@ void configureDisassembleInfo(bfd* abfd, } disassembler_ftype configureBfd(bfd* abfd, - enum bfd_architecture arch, - unsigned long mach, - enum bfd_endian endian, - disassemble_info* DI, - disassembler_ftype* disassemble_fn) + enum bfd_architecture arch, + unsigned long mach, + enum bfd_endian endian, + disassemble_info* DI, + disassembler_ftype* disassemble_fn) { struct bfd_target *xvec; - + abfd->flags |= EXEC_P; - - + + // set up xvec byteorder. xvec = (struct bfd_target *) malloc (sizeof (struct bfd_target)); memset(xvec, 0x00, sizeof (struct bfd_target)); memcpy (xvec, abfd->xvec, sizeof (struct bfd_target)); xvec->byteorder = endian; abfd->xvec = xvec; - + configureDisassembleInfo(abfd, DI, arch, mach, endian); if(endian == BFD_ENDIAN_BIG){ bfd_big_endian(abfd); @@ -106,29 +106,29 @@ disassembler_ftype configureBfd(bfd* abfd, bfd_little_endian(abfd); DI->display_endian = DI->endian = BFD_ENDIAN_LITTLE; } - + /* bfd_error_type err = bfd_get_error(); printf("bfd_error_msg: %s.\n", bfd_errmsg(err)); - */ - + */ + /* Use libopcodes to locate a suitable disassembler. */ *disassemble_fn = NULL; *disassemble_fn = disassembler (arch, endian == BFD_ENDIAN_BIG, mach, abfd); if (!*disassemble_fn){ printf("can't disassemble for arch 0x%08X, mach 0x%08lX\n", arch, mach); exit(1); - } - - return *disassemble_fn; + } + + return *disassemble_fn; } int disassemble_buffer( disassembler_ftype disassemble_fn, - disassemble_info *info, - int* offset, - PDIS_INFO pDisInfo) + disassemble_info *info, + int* offset, + PDIS_INFO pDisInfo) { int i, j, size = 0; int len = 0; @@ -136,7 +136,7 @@ int disassemble_buffer( disassembler_ftype disassemble_fn, while ( *offset < info->buffer_length ) { /* call the libopcodes disassembler */ memset(pDisInfo->disassemblyString, 0x00, MAX_DIS_STRING); - + /* set the insn_info_valid bit to 0, as explained in BFD's * include/dis-asm.h. The bit will then be set to tell us * whether the decoder supports "extra" information about the @@ -147,7 +147,7 @@ int disassemble_buffer( disassembler_ftype disassemble_fn, size = (*disassemble_fn)(info->buffer_vma + *offset, info); /* -- analyze disassembled instruction here -- */ /* -- print any symbol names as labels here -- */ - + /* save off corresponding hex bytes */ for ( j= 0,i = 0; i < 8; i++, j+=3) { if ( i < size ){ @@ -155,7 +155,7 @@ int disassemble_buffer( disassembler_ftype disassemble_fn, pDisInfo->bytesBufferBin[i] = info->buffer[*offset + i]; } } - + /* add the augmented information to our disassembly info struct */ pDisInfo->count = size; pDisInfo->insn_info_valid = info->insn_info_valid; @@ -167,44 +167,44 @@ int disassemble_buffer( disassembler_ftype disassemble_fn, strcat(&(pDisInfo->disassemblyString[0]), disassembled_buffer); memset(disassembled_buffer, 0x00, BUFF_SIZE); - + if(size != 0){ *offset += size; /* advance position in buffer */ goto END; } } -END: + END: return size; } void processBuffer(unsigned char* buff, - int buff_len, - bfd_vma buff_vma, - disassembler_ftype disassemble_fn, - struct disassemble_info* DI) + int buff_len, + bfd_vma buff_vma, + disassembler_ftype disassemble_fn, + struct disassemble_info* DI) { int bytesConsumed = -1; int offset = 0; int numDisassemblies = 0; int i; - + DI->buffer = buff; /* buffer of bytes to disassemble */ DI->buffer_length = buff_len; /* size of buffer */ DI->buffer_vma = buff_vma; /* base RVA of buffer */ - + memset(disassemblyInfoBuffer, 0x00, sizeof(DIS_INFO)*MAX_NUM_ENTRIES); - + while((buff_len - offset) > 0 && bytesConsumed != 0 && numDisassemblies < MAX_NUM_ENTRIES){ bytesConsumed = disassemble_buffer( disassemble_fn, DI, &offset, &(disassemblyInfoBuffer[numDisassemblies++])); } for (i = 0; i < numDisassemblies; i++) { - printf("%s\nInfo: %d,%d,%d,%d,%d\n", disassemblyInfoBuffer[i].disassemblyString, - disassemblyInfoBuffer[i].count, - disassemblyInfoBuffer[i].insn_info_valid, - disassemblyInfoBuffer[i].branch_delay_insns, - disassemblyInfoBuffer[i].data_size, - disassemblyInfoBuffer[i].insn_type); + printf("%s\nInfo: %d,%d,%d,%d,%d\n", disassemblyInfoBuffer[i].disassemblyString, + disassemblyInfoBuffer[i].count, + disassemblyInfoBuffer[i].insn_info_valid, + disassemblyInfoBuffer[i].branch_delay_insns, + disassemblyInfoBuffer[i].data_size, + disassemblyInfoBuffer[i].insn_type); } } @@ -224,7 +224,7 @@ int main(int argc, char* argv[]){ char elf_file_location[MAX_ELF_FILE_PATH_LEN]; char arch_str[256]; char mach_str[256]; - + if ( argc < 8) { fprintf(stderr, "Usage: %s target-str, arch, mach, disassembly base-addr (for rel offsets instrs), full-path to Little and Big Elfs, big/little ascii-byte-string up to %d chars\n", argv[0], MAX_ASCII_CHAR_BYTE_STRING); listSupportedArchMachTargets(); @@ -247,16 +247,16 @@ int main(int argc, char* argv[]){ mach = 0x00000000; arch = (enum bfd_architecture) 0x00; offset = 0x00000000; - + sscanf(argv[2], "%128s", arch_str); sscanf(argv[3], "%18lX", &mach); sscanf(argv[4], "%10X", &end); sscanf(argv[5], "%18lX", &offset); - + // if arch starts with 0x, then parse a number // else lookup the string in the table to get the arch, ignore the mach if (arch_str[0] == '0' && arch_str[1] == 'x') { - sscanf(arch_str, "%10X", &arch); + sscanf(arch_str, "%10X", &arch); } else { const char** archList = bfd_arch_list(); const bfd_arch_info_type* ait; @@ -271,17 +271,17 @@ int main(int argc, char* argv[]){ archList++; } if (ait == NULL) { - printf("Couldn't find arch %s\n", arch_str); - return(-1); + printf("Couldn't find arch %s\n", arch_str); + return(-1); } } - - + + endian = (enum bfd_endian) end; /* open a correct type of file to fill in most of the required data. */ - + // printf("Arch is: 0x%08X, Machine is: 0x%08lX Endian is: 0x%02X.\n", arch, mach, endian); - + memset(elf_file_location, 0x00, MAX_ELF_FILE_PATH_LEN); strncpy(elf_file_location, argv[6], MAX_ELF_FILE_PATH_LEN-sizeof(LITTLE_ELF_FILE)-2); // actual file name and nulls @@ -294,59 +294,66 @@ int main(int argc, char* argv[]){ stdin_mode = 1; // use STDIN } - unsigned char byteBuffer[BYTE_BUFFER_SIZE]; - char byteStringBuffer[(BYTE_BUFFER_SIZE*2)]; - - char addressStringBuffer[128]; - if (endian == BFD_ENDIAN_BIG){ strcat(elf_file_location, BIG_ELF_FILE); } else { strcat(elf_file_location, LITTLE_ELF_FILE); } - + + bfd_init(); + while (stdin_mode) { - + + //bfd_init(); // convert user input AsciiHex to Binary data for processing char tmp[3]; unsigned int byteValue; tmp[0] = tmp[1] = tmp[2] = 0x00; + char disassemblerOptions[128] = {0}; + unsigned char byteBuffer[BYTE_BUFFER_SIZE] = {0}; + char byteStringBuffer[(BYTE_BUFFER_SIZE*2)] = {0}; + char addressStringBuffer[128] = {0}; + char bytesAndOptionsBuffer[BYTE_BUFFER_SIZE*2 + sizeof(disassemblerOptions)] = {0}; if (stdin_mode == 1) { // use stdin // read in the address if (fgets(addressStringBuffer, sizeof(addressStringBuffer), stdin)) { - - //fprintf(stderr, "read: %s\n", addressStringBuffer); - //char *p = strchr(addressStringBuffer, '\n'); - //if (p) { - // *p = '\0'; - //} - sscanf(addressStringBuffer, "%18lX", &offset); } - //getchar(); - // read in the ASCII hex string from stdin - if (fgets(byteStringBuffer, sizeof(byteStringBuffer), stdin)) { - - //fprintf(stderr, "read: %s\n", byteStringBuffer); - // remove trailing newline - char *p = strchr(byteStringBuffer, '\n'); + //Read in the rest of the input. There are two possible styles: + //"old": input is just the bytes to disassemble + //"new": input = bytes"*" + //for compatibility reasons, handle both styles + if (fgets(bytesAndOptionsBuffer, sizeof(bytesAndOptionsBuffer), stdin)) { + char *p = strchr(bytesAndOptionsBuffer, '*'); if (p) { - *p = '\0'; + strncpy(byteStringBuffer,bytesAndOptionsBuffer,(int) (p - bytesAndOptionsBuffer)); + strncpy(disassemblerOptions,p+1,sizeof(disassemblerOptions)); + p = strchr(disassemblerOptions,'\n'); + if (p){ + *p = '\0'; + } + } + else { + //no "*" in the string, so no disassembly options + //replace newline with null terminator + strncpy(byteStringBuffer, bytesAndOptionsBuffer, sizeof(byteStringBuffer)); + p = strchr(byteStringBuffer,'\n'); + if (p){ + *p = '\0'; + } } - //if (strcmp(byteStringBuffer, "EOF") == 0) { - //return 0; // terminate on EOF string - //} - } else { - fprintf(stderr, "exiting, no ASCII hex found\n"); - return 0; // finished! #TODO } - - } else { + else { + fprintf(stderr, "exiting, no ASCII hex found\n"); + return 0; // finished! #TODO + } + } + else { if(strlen(byteString) > BYTE_BUFFER_SIZE*2) { - fprintf(stderr, "Max ascii string size is %d you provided: %lu chars. Exiting.\n", BYTE_BUFFER_SIZE*2, - strlen(byteString)); + fprintf(stderr, "Max ascii string size is %d you provided: %lu chars. Exiting.\n", BYTE_BUFFER_SIZE*2, + strlen(byteString)); exit(-1); } strncpy(byteStringBuffer, byteString, BYTE_BUFFER_SIZE*2); @@ -378,9 +385,9 @@ int main(int argc, char* argv[]){ for(j=0; j < BYTE_BUFFER_SIZE; j++){ printf("0x%02X ", byteBuffer[j]); } - */ - - bfd_init( ); + */ + + //bfd_init( ); target = argv[1]; bfd_set_default_target(target); @@ -392,7 +399,7 @@ int main(int argc, char* argv[]){ bfdfile = bfd_openr(elf_file_location, target ); if ( ! bfdfile ) { printf("Error opening BIG ELF file: %s\n", elf_file_location); - bfd_perror( "Error on bfdfile" ); + bfd_perror( "Error on bfdfile" ); return(3); } } @@ -401,34 +408,37 @@ int main(int argc, char* argv[]){ if ( ! bfdfile ) { printf("Error opening LITTLE ELF file: %s\n", elf_file_location); // bfdfile = bfd_openr(elf_file_location, target ); - bfd_perror( "Error on bfdfile" ); + bfd_perror( "Error on bfdfile" ); return(3); } } - + memset((void*) &DI, 0x00, sizeof(struct disassemble_info)); disassemble_fn = NULL; - + // important set up! //--------------------------------------- ai.arch = arch; ai.mach = mach; bfd_set_arch_info(bfdfile, &ai); //--------------------------------------- - + /* bfd_error_type err = bfd_get_error(); printf("bfd_error_msg: %s.\n", bfd_errmsg(err)); - */ - + */ + + /*if (disassemblerOptions[0] != '\0'){ + DI.disassembler_options = disassemblerOptions; + }*/ configureBfd(bfdfile, arch, mach, endian, &DI, &disassemble_fn); /* err = bfd_get_error(); printf("bfd_error_msg: %s.\n", bfd_errmsg(err)); - */ - + */ + if (disassemble_fn == NULL){ fprintf(stderr, "Error: disassemble_fn is NULL. Nothing I can do.\n"); exit(1); @@ -437,21 +447,25 @@ int main(int argc, char* argv[]){ /* printf("the disassemble_fn func pointer is: 0x%08X.\n", disassemble_fn); printf("We can try to disassemble for this arch/mach. calling disassemble_init_for_target().\n"); - */ + */ + disassemble_init_for_target(&DI); + if (disassemblerOptions[0] != '\0'){ + DI.disassembler_options = disassemblerOptions; + } // go diassemble the buffer and build up the result in a accumulator string buffer. - processBuffer(byteBuffer, size >> 1, offset, disassemble_fn, &DI); // + processBuffer(byteBuffer, size >> 1, offset, disassemble_fn, &DI); // } free((void*)bfdfile->xvec); bfd_close(bfdfile); - + printf("EOF\n"); fflush(stdout); } // while loop on lines of stdin - + return 0; } diff --git a/Ghidra/Extensions/SleighDevTools/src/main/java/ghidra/app/util/disassemble/ExternalDisassembler.java b/Ghidra/Extensions/SleighDevTools/src/main/java/ghidra/app/util/disassemble/ExternalDisassembler.java index 0629e68642..912a8e160b 100644 --- a/Ghidra/Extensions/SleighDevTools/src/main/java/ghidra/app/util/disassemble/ExternalDisassembler.java +++ b/Ghidra/Extensions/SleighDevTools/src/main/java/ghidra/app/util/disassemble/ExternalDisassembler.java @@ -23,6 +23,8 @@ public interface ExternalDisassembler extends ExtensionPoint { public String getDisassembly(CodeUnit cu) throws Exception; + public String getDisassemblyDisplayPrefix(CodeUnit cu) throws Exception; + public String getDisassemblyOfBytes(Language language, boolean isBigEndian, long address, byte[] byteString) throws Exception; diff --git a/Ghidra/Extensions/SleighDevTools/src/main/java/ghidra/app/util/disassemble/ExternalDisassemblyFieldFactory.java b/Ghidra/Extensions/SleighDevTools/src/main/java/ghidra/app/util/disassemble/ExternalDisassemblyFieldFactory.java index 1a17a3abf7..89f3407db2 100644 --- a/Ghidra/Extensions/SleighDevTools/src/main/java/ghidra/app/util/disassemble/ExternalDisassemblyFieldFactory.java +++ b/Ghidra/Extensions/SleighDevTools/src/main/java/ghidra/app/util/disassemble/ExternalDisassemblyFieldFactory.java @@ -150,7 +150,10 @@ public class ExternalDisassemblyFieldFactory extends FieldFactory { if (disassembly == null) { return null; } - + String prefix = disassembler.getDisassemblyDisplayPrefix(cu); + if (prefix != null) { + disassembly = prefix + " " + disassembly; + } return disassembly; } diff --git a/Ghidra/Extensions/SleighDevTools/src/main/java/ghidra/app/util/disassemble/GNUExternalDisassembler.java b/Ghidra/Extensions/SleighDevTools/src/main/java/ghidra/app/util/disassemble/GNUExternalDisassembler.java index f71797c991..7fb8a4605b 100644 --- a/Ghidra/Extensions/SleighDevTools/src/main/java/ghidra/app/util/disassemble/GNUExternalDisassembler.java +++ b/Ghidra/Extensions/SleighDevTools/src/main/java/ghidra/app/util/disassemble/GNUExternalDisassembler.java @@ -18,19 +18,23 @@ package ghidra.app.util.disassemble; import java.io.*; import java.util.*; +import org.apache.commons.lang3.StringUtils; +import org.jdom.*; +import org.jdom.input.SAXBuilder; + import generic.jar.ResourceFile; import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.MemoryByteProvider; import ghidra.framework.*; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressOutOfBoundsException; -import ghidra.program.model.lang.Language; -import ghidra.program.model.lang.LanguageID; -import ghidra.program.model.listing.CodeUnit; -import ghidra.program.model.listing.Program; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.*; import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.MemoryAccessException; import ghidra.util.Msg; +import ghidra.util.xml.XmlUtilities; +import util.CollectionUtils; public class GNUExternalDisassembler implements ExternalDisassembler { @@ -39,6 +43,7 @@ public class GNUExternalDisassembler implements ExternalDisassembler { // magic values for gdis that direct it to read bytes from stdin private static final String READ_FROM_STDIN_PARAMETER = "stdin"; private static final String SEPARATOR_CHARACTER = "\n"; + private static final String OPTIONS_SEPARATOR = "*"; private static final String ADDRESS_OUT_OF_BOUNDS = "is out of bounds."; private static final String ENDING_STRING = "EOF"; private static final int NUM_BYTES = 32; @@ -48,6 +53,8 @@ public class GNUExternalDisassembler implements ExternalDisassembler { private static final String GDIS_EXE = Platform.CURRENT_PLATFORM.getOperatingSystem() == OperatingSystem.WINDOWS ? "gdis.exe" : "gdis"; + private static final String EMPTY_DISASSEMBLER_OPTIONS = ""; + private static final String GDIS_OPTIONS_FILENAME_PROPERTY = "gdis.disassembler.options.file"; private static HashMap languageGdisMap; private static File defaultGdisExecFile; @@ -82,6 +89,21 @@ public class GNUExternalDisassembler implements ExternalDisassembler { return gdisConfig != null && gdisConfig.architecture != UNSUPPORTED; } + @Override + public String getDisassemblyDisplayPrefix(CodeUnit cu) throws Exception { + GdisConfig gdisConfig = checkLanguage(cu.getProgram().getLanguage()); + if (gdisConfig == null || gdisConfig.architecture == UNSUPPORTED) { + return null; + } + Register contextRegister = gdisConfig.getContextRegister(); + if (contextRegister == null) { + return null; + } + long value = getContextRegisterValue(cu, contextRegister); + String option = gdisConfig.getDisplayPrefixMap().get(value); + return option; + } + private static void reportMultipleMappings(Language language) { List externalNames = language.getLanguageDescription().getExternalNames("gnu"); if (externalNames != null && externalNames.size() > 1) { @@ -89,8 +111,9 @@ public class GNUExternalDisassembler implements ExternalDisassembler { StringBuilder sb = new StringBuilder(); boolean prependSeparator = false; for (String name : externalNames) { - if (prependSeparator) + if (prependSeparator) { sb.append(", "); + } sb.append(name); prependSeparator = true; } @@ -110,11 +133,17 @@ public class GNUExternalDisassembler implements ExternalDisassembler { String machineId; File gdisExecFile; boolean usingDefault; + Register contextRegister; + Map valueToOptionString; + Map valueToDisplayPrefix; + Language lang; + String globalDisassemblerOptions; GdisConfig(Language language, boolean isBigEndian) { this.languageId = language.getLanguageID().toString(); this.isBigEndian = isBigEndian; + this.lang = language; List architectures = language.getLanguageDescription().getExternalNames("gnu"); //get first non-null @@ -143,12 +172,123 @@ public class GNUExternalDisassembler implements ExternalDisassembler { gdisExecFile = defaultGdisExecFile; usingDefault = true; } + + List gDisOptionsFile = + language.getLanguageDescription().getExternalNames(GDIS_OPTIONS_FILENAME_PROPERTY); + if (!CollectionUtils.isBlank(gDisOptionsFile)) { + try { + parseGdisOptionsFile(gDisOptionsFile.get(0)); + } + catch (IOException e) { + Msg.error(this, "Error reading gdis options file " + e.getMessage()); + contextRegister = null; + valueToOptionString = null; + valueToDisplayPrefix = null; + } + } } GdisConfig(Language lang) { this(lang, lang.isBigEndian()); } + private void parseGdisOptionsFile(String fileName) throws IOException { + LanguageDescription desc = lang.getLanguageDescription(); + if (!(desc instanceof SleighLanguageDescription)) { + throw new IOException("Not a Sleigh Language: " + lang.getLanguageID()); + } + + SleighLanguageDescription sld = (SleighLanguageDescription) desc; + ResourceFile defsFile = sld.getDefsFile(); + ResourceFile parentFile = defsFile.getParentFile(); + ResourceFile gdisOpts = new ResourceFile(parentFile, fileName); + SAXBuilder sax = XmlUtilities.createSecureSAXBuilder(false, false); + try (InputStream fis = gdisOpts.getInputStream()) { + Document doc = sax.build(fis); + Element rootElem = doc.getRootElement(); + Element globalElement = rootElem.getChild("global"); + if (globalElement != null) { + globalDisassemblerOptions = globalElement.getAttributeValue("optstring"); + } + Element contextRegisterElement = rootElem.getChild("context_register"); + if (contextRegisterElement == null) { + //no context_register element found in the xml file + //this is not necessarily an error - might only be a global optstring + //global optstring has already been parsed, so we're done + if (globalElement != null) { + Msg.info(this, + "no context register element in " + gdisOpts.getAbsolutePath()); + return; + } + //no context register element or global element, error + throw new JDOMException( + "No context_register element or global element in gdis options file"); + } + if (contextRegisterElement.getContentSize() == 0) { + throw new JDOMException("No context register name provided."); + } + String contextRegisterName = contextRegisterElement.getContent(0).getValue(); + contextRegister = lang.getRegister(contextRegisterName); + if (contextRegister == null) { + //the context register named in the xml file does not exist in the sleigh language + //this is an error + throw new JDOMException("Unknown context register " + contextRegisterName + + " for language " + lang.getLanguageID().getIdAsString()); + } + valueToOptionString = new HashMap<>(); + valueToDisplayPrefix = new HashMap<>(); + Element options = rootElem.getChild("options"); + List optList = options.getChildren("option"); + for (Element opt : optList) { + Long value = Long.decode(opt.getAttributeValue("value")); + String optString = opt.getAttributeValue("optstring"); + valueToOptionString.put(value, optString); + String displayPrefix = opt.getAttributeValue("display_prefix"); + valueToDisplayPrefix.put(value, displayPrefix); + } + + } + catch (JDOMException e) { + Msg.error(this, "Error reading " + fileName + ": " + e.getMessage()); + contextRegister = null; + valueToOptionString = null; + valueToDisplayPrefix = null; + } + } + + /** + * Returns the global disassembler options + * @return global option string, or {@code null} if the + * global is unspecified. + */ + public String getGlobalDisassemblerOptions() { + return globalDisassemblerOptions; + } + + /** + * Return the context register determine by the gdis options file + * @return the context register + */ + public Register getContextRegister() { + return contextRegister; + } + + /** + * Returns the map from context register values to gdis disassembler options + * @return map values->options + */ + public Map getOptionsMap() { + return valueToOptionString; + } + + /** + * Returns the map from context register values to disassembly display prefixes + * @return map values->prefixes + */ + public Map getDisplayPrefixMap() { + return valueToDisplayPrefix; + } + @Override public boolean equals(Object obj) { if (!(obj instanceof GdisConfig)) { @@ -260,7 +400,7 @@ public class GNUExternalDisassembler implements ExternalDisassembler { String bytes = getBytes(byteProvider, blockSize); - return runDisassembler(gdisConfig, address, bytes); + return runDisassembler(gdisConfig, address, bytes, EMPTY_DISASSEMBLER_OPTIONS); } public List getBlockDisassembly(Program program, Address addr, @@ -272,8 +412,8 @@ public class GNUExternalDisassembler implements ExternalDisassembler { int blockSize = pow2(blockSizeFactor); Address blockAddr = addr.getNewAddress(addr.getOffset() & -blockSize); // block - // aligned - // address + // aligned + // address return getBlockDisassembly(program.getLanguage(), blockAddr, blockSizeFactor, new MemoryByteProvider(program.getMemory(), blockAddr)); @@ -301,7 +441,37 @@ public class GNUExternalDisassembler implements ExternalDisassembler { return ""; } - List disassembly = runDisassembler(gdisConfig, address, bytes); + String disOptions = EMPTY_DISASSEMBLER_OPTIONS; + String globalOptions = gdisConfig.getGlobalDisassemblerOptions(); + boolean hasGlobal = false; + if (globalOptions != null) { + hasGlobal = true; + //set the disOptions to the global options here in case there are global + //options but no options for context register values + disOptions = globalOptions; + } + Register contextRegister = gdisConfig.getContextRegister(); + if (contextRegister != null) { + long value = getContextRegisterValue(cu, contextRegister); + String contextRegisterValueOption = gdisConfig.getOptionsMap().get(value); + if (contextRegisterValueOption == null) { + Msg.warn(this, "No option for value " + value + " of context register " + + contextRegister.getName()); + } + else { + //need to put a comma between the global options and the options + //for this context register value + if (hasGlobal) { + disOptions = globalOptions + "," + contextRegisterValueOption; + } + //no global options, just send the options for this context register + else { + disOptions = contextRegisterValueOption; + } + } + } + List disassembly = + runDisassembler(gdisConfig, address, bytes, disOptions); if (disassembly == null || disassembly.size() == 0 || disassembly.get(0) == null) { return "(bad)"; @@ -324,7 +494,7 @@ public class GNUExternalDisassembler implements ExternalDisassembler { String address = "0x" + Long.toHexString(addressOffset); List disassembly = - runDisassembler(gdisConfig, address, bytesString); + runDisassembler(gdisConfig, address, bytesString, EMPTY_DISASSEMBLER_OPTIONS); if (disassembly == null || disassembly.isEmpty() || disassembly.get(0) == null) { return "(bad)"; @@ -332,14 +502,26 @@ public class GNUExternalDisassembler implements ExternalDisassembler { return disassembly.get(0).toString(); } + private long getContextRegisterValue(CodeUnit cu, Register contextRegister) { + ProgramContext context = cu.getProgram().getProgramContext(); + RegisterValue value = context.getRegisterValue(contextRegister, cu.getAddress()); + if (value != null) { + return value.getUnsignedValue().longValue(); + } + //we tried, return 0 to match SLEIGH default + return 0; + } + private String converBytesToString(byte[] bytes) { String byteString = null; for (byte thisByte : bytes) { String thisByteString = Integer.toHexString(thisByte); - if (thisByteString.length() == 1) + if (thisByteString.length() == 1) { thisByteString = "0" + thisByteString; // pad single digits - if (thisByteString.length() > 2) + } + if (thisByteString.length() > 2) { thisByteString = thisByteString.substring(thisByteString.length() - 2); + } // append this byte's hex string to the larger word length string byteString = byteString + thisByteString; } @@ -402,7 +584,7 @@ public class GNUExternalDisassembler implements ExternalDisassembler { } private List runDisassembler(GdisConfig gdisConfig, - String addrString, String bytes) throws IOException { + String addrString, String bytes, String disassemblerOptions) throws IOException { // if this is the first time running the disassembler process, or a // parameter has changed (notably, not the address--we pass that in @@ -414,8 +596,9 @@ public class GNUExternalDisassembler implements ExternalDisassembler { if (disassemblerProcess == null || !sameConfig) { - if (!setupDisassembler(gdisConfig)) + if (!setupDisassembler(gdisConfig)) { return null; + } outputWriter = new OutputStreamWriter(disassemblerProcess.getOutputStream()); @@ -432,7 +615,14 @@ public class GNUExternalDisassembler implements ExternalDisassembler { return null; // if process previously died return nothing - quickly } - String disassemblyRequest = addrString + SEPARATOR_CHARACTER + bytes + SEPARATOR_CHARACTER; + String disassemblyRequest = addrString + SEPARATOR_CHARACTER + bytes; + if (StringUtils.isEmpty(disassemblerOptions)) { + disassemblyRequest += '\n'; + } + else { + disassemblyRequest += OPTIONS_SEPARATOR + disassemblerOptions + SEPARATOR_CHARACTER; + } + try { outputWriter.write(disassemblyRequest); outputWriter.flush(); diff --git a/Ghidra/Processors/ARM/certification.manifest b/Ghidra/Processors/ARM/certification.manifest index fb30db5de9..7c50fa9da9 100644 --- a/Ghidra/Processors/ARM/certification.manifest +++ b/Ghidra/Processors/ARM/certification.manifest @@ -3,6 +3,7 @@ Module.manifest||GHIDRA||||END| build.gradle||GHIDRA||||END| data/languages/ARM.cspec||GHIDRA||||END| data/languages/ARM.dwarf||GHIDRA||||END| +data/languages/ARM.gdis||GHIDRA||||END| data/languages/ARM.ldefs||GHIDRA||||END| data/languages/ARM.opinion||GHIDRA||||END| data/languages/ARM.sinc||GHIDRA||||END| diff --git a/Ghidra/Processors/ARM/data/languages/ARM.gdis b/Ghidra/Processors/ARM/data/languages/ARM.gdis new file mode 100644 index 0000000000..33429f85c3 --- /dev/null +++ b/Ghidra/Processors/ARM/data/languages/ARM.gdis @@ -0,0 +1,41 @@ + + + + TMode + + + + diff --git a/Ghidra/Processors/ARM/data/languages/ARM.ldefs b/Ghidra/Processors/ARM/data/languages/ARM.ldefs index 929560c95a..0ccef7185d 100644 --- a/Ghidra/Processors/ARM/data/languages/ARM.ldefs +++ b/Ghidra/Processors/ARM/data/languages/ARM.ldefs @@ -14,6 +14,7 @@ + @@ -31,6 +32,7 @@ + @@ -63,6 +65,7 @@ Generic ARM/Thumb v8 big endian + @@ -96,6 +99,7 @@ + @@ -128,6 +132,7 @@ Generic ARM/Thumb v7 big endian + @@ -174,6 +179,7 @@ Generic ARM/Thumb v6 little endian + @@ -192,6 +198,7 @@ Generic ARM/Thumb v6 big endian + @@ -210,6 +217,7 @@ Generic ARM/Thumb v5 little endian (T-variant) + @@ -226,6 +234,7 @@ Generic ARM/Thumb v5 big endian (T-variant) + @@ -274,6 +283,7 @@ Generic ARM/Thumb v4 little endian (T-variant) + @@ -290,6 +300,7 @@ Generic ARM/Thumb v4 big endian (T-variant) + diff --git a/Ghidra/Processors/x86/certification.manifest b/Ghidra/Processors/x86/certification.manifest index 66f695fb6e..521374d29f 100644 --- a/Ghidra/Processors/x86/certification.manifest +++ b/Ghidra/Processors/x86/certification.manifest @@ -30,6 +30,7 @@ data/languages/sha.sinc||GHIDRA||||END| data/languages/smx.sinc||GHIDRA||||END| data/languages/x86-16-real.pspec||GHIDRA||||END| data/languages/x86-16.cspec||GHIDRA||||END| +data/languages/x86-16.gdis||GHIDRA||||END| data/languages/x86-16.pspec||GHIDRA||||END| data/languages/x86-64-gcc.cspec||GHIDRA||||END| data/languages/x86-64-win.cspec||GHIDRA||||END| diff --git a/Ghidra/Processors/x86/data/languages/x86-16.gdis b/Ghidra/Processors/x86/data/languages/x86-16.gdis new file mode 100644 index 0000000000..2efbfd6196 --- /dev/null +++ b/Ghidra/Processors/x86/data/languages/x86-16.gdis @@ -0,0 +1,3 @@ + + + diff --git a/Ghidra/Processors/x86/data/languages/x86.ldefs b/Ghidra/Processors/x86/data/languages/x86.ldefs index c225a01aca..201683ea35 100644 --- a/Ghidra/Processors/x86/data/languages/x86.ldefs +++ b/Ghidra/Processors/x86/data/languages/x86.ldefs @@ -58,6 +58,8 @@ + + Intel/AMD 16-bit x86 Protected Mode + +