diff --git a/Ghidra/Features/Base/ghidra_scripts/DWARFSetExternalDebugFilesLocationPrescript.java b/Ghidra/Features/Base/ghidra_scripts/DWARFSetExternalDebugFilesLocationPrescript.java index dbd6b6f34c..dcde006cc2 100644 --- a/Ghidra/Features/Base/ghidra_scripts/DWARFSetExternalDebugFilesLocationPrescript.java +++ b/Ghidra/Features/Base/ghidra_scripts/DWARFSetExternalDebugFilesLocationPrescript.java @@ -26,7 +26,7 @@ import java.util.ArrayList; import java.util.List; import ghidra.app.script.GhidraScript; -import ghidra.app.util.bin.format.dwarf4.external.*; +import ghidra.app.util.bin.format.dwarf.external.*; import ghidra.util.Msg; public class DWARFSetExternalDebugFilesLocationPrescript extends GhidraScript { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DWARFAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DWARFAnalyzer.java index 6ae117201b..3b7ff08aed 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DWARFAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DWARFAnalyzer.java @@ -18,9 +18,9 @@ package ghidra.app.plugin.core.analysis; import java.io.IOException; import ghidra.app.services.*; -import ghidra.app.util.bin.format.dwarf4.next.*; -import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.DWARFSectionProvider; -import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.DWARFSectionProviderFactory; +import ghidra.app.util.bin.format.dwarf.*; +import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProvider; +import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProviderFactory; import ghidra.app.util.importer.MessageLog; import ghidra.framework.options.Options; import ghidra.program.model.address.AddressSetView; @@ -94,15 +94,15 @@ public class DWARFAnalyzer extends AbstractAnalyzer { try { try (DWARFProgram prog = new DWARFProgram(program, importOptions, monitor, dsp)) { if (prog.getRegisterMappings() == null && importOptions.isImportFuncs()) { - log.appendMsg( - "No DWARF to Ghidra register mappings found for this program's language [%s], function information may be incorrect / incomplete." + log.appendMsg("No DWARF to Ghidra register mappings found for this program's " + + "language [%s], function information may be incorrect / incomplete." .formatted(program.getLanguageID().getIdAsString())); } prog.init(monitor); - DWARFParser dp = new DWARFParser(prog, monitor); - DWARFImportSummary parseResults = dp.parse(); - parseResults.logSummaryResults(); + DWARFImporter importer = new DWARFImporter(prog, monitor); + DWARFImportSummary importResults = importer.performImport(); + importResults.logSummaryResults(); } Options propList = program.getOptions(Program.PROGRAM_INFO); propList.setBoolean(DWARF_LOADED_OPTION_NAME, true); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/LEB128Info.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/LEB128Info.java index 903a5a7b97..fb0820de88 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/LEB128Info.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/LEB128Info.java @@ -17,9 +17,7 @@ package ghidra.app.util.bin; import java.io.IOException; -import ghidra.program.model.address.Address; import ghidra.program.model.data.LEB128; -import ghidra.program.model.listing.Program; /** * Class to hold result of reading a {@link LEB128} value, along with size and position metadata. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DIEAggregate.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DIEAggregate.java similarity index 54% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DIEAggregate.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DIEAggregate.java index 3db7f23d73..94470288aa 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DIEAggregate.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DIEAggregate.java @@ -13,23 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4; +package ghidra.app.util.bin.format.dwarf; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.*; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.*; +import static ghidra.app.util.bin.format.dwarf.DWARFTag.*; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; import java.io.IOException; import java.util.*; import org.apache.commons.lang3.ArrayUtils; -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.dwarf4.attribs.*; -import ghidra.app.util.bin.format.dwarf4.encoding.*; -import ghidra.app.util.bin.format.dwarf4.expression.*; -import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram; +import ghidra.app.util.bin.format.dwarf.attribs.*; +import ghidra.app.util.bin.format.dwarf.expression.*; import ghidra.util.Msg; -import ghidra.util.NumericUtilities; /** * DIEAggregate groups related {@link DebugInfoEntry} records together in a single interface @@ -52,8 +48,6 @@ public class DIEAggregate { */ private static final int MAX_FRAGMENT_COUNT = 20; - public static final int[] REF_ATTRS = { DW_AT_abstract_origin, DW_AT_specification }; - /** * A list of {@link DebugInfoEntry DIEs} that make up this DWARF program element, with * the 'head'-most listed first, followed by earlier less specified DIEs, ending with @@ -119,8 +113,8 @@ public class DIEAggregate { * Used when a DIEA is composed of a head DIE with a different TAG type than the rest of * the DIEs. (ie. a dw_tag_call_site -> dw_tag_sub DIEA) * - * @param source - * @return + * @param source {@link DIEAggregate} containing fragments + * @return {@link DIEAggregate} with the fragments of the source, skipping the first */ public static DIEAggregate createSkipHead(DIEAggregate source) { if (source.fragments.length == 1) { @@ -137,8 +131,8 @@ public class DIEAggregate { * Mainly useful early in the {@link DWARFCompilationUnit}'s bootstrapping process * when it needs to read values from DIEs. *

- * @param die - * @return + * @param die {@link DebugInfoEntry} + * @return {@link DIEAggregate} containing a single DIE */ public static DIEAggregate createSingle(DebugInfoEntry die) { DIEAggregate result = new DIEAggregate(new DebugInfoEntry[] { die }); @@ -149,7 +143,8 @@ public class DIEAggregate { /** * Private ctor to force use of the static factory methods {@link #createFromHead(DebugInfoEntry)} * and {@link #createSingle(DebugInfoEntry)}. - * @param die + * + * @param fragments array of DIEs that make this aggregate */ private DIEAggregate(DebugInfoEntry[] fragments) { this.fragments = fragments; @@ -164,7 +159,7 @@ public class DIEAggregate { * call {@link #flipFragments()} after the build phase to reverse the order of the * DIE fragments list so that querying for attribute values will return the correct values. * - * @param newDIE + * @param newDIE {@link DebugInfoEntry} to add */ private void addFragment(DebugInfoEntry newDIE) { DebugInfoEntry[] tmp = new DebugInfoEntry[fragments.length + 1]; @@ -215,13 +210,13 @@ public class DIEAggregate { /** * Returns {@link #getOffset()} as a hex string. - * @return + * @return string hex offset of the head DIE */ public String getHexOffset() { return Long.toHexString(getHeadFragment().getOffset()); } - public int getTag() { + public DWARFTag getTag() { return getHeadFragment().getTag(); } @@ -235,7 +230,7 @@ public class DIEAggregate { /** * Returns the last {@link DebugInfoEntry DIE} fragment, ie. the decl DIE. - * @return + * @return last DIE of this aggregate */ public DebugInfoEntry getLastFragment() { return fragments[fragments.length - 1]; @@ -244,7 +239,7 @@ public class DIEAggregate { /** * Returns the first {@link DebugInfoEntry DIE} fragment, ie. the spec or abstract_origin * DIE. - * @return + * @return first DIE of this aggregate */ public DebugInfoEntry getHeadFragment() { return fragments[0]; @@ -272,27 +267,18 @@ public class DIEAggregate { * This value matches the nesting value shown when dumping DWARF * info using 'readelf'. * - * @return + * @return depth of this instance, from the root of its head DIE fragment, with 0 indicating + * that this instance was already the root of the compUnit */ public int getDepth() { return getProgram().getParentDepth(getHeadFragment().getIndex()); } - private AttrInfo findAttribute(int attribute) { + private FoundAttribute findAttribute(DWARFAttribute attribute) { for (DebugInfoEntry die : fragments) { - DWARFAttributeValue[] dieAttrValues = die.getAttributes(); - DWARFAttributeSpecification[] attrDefs = die.getAbbreviation().getAttributes(); - for (int i = 0; i < attrDefs.length; i++) { - DWARFAttributeSpecification attrDef = attrDefs[i]; - if (attrDef.getAttribute() == attribute) { - DWARFAttributeValue attrVal = dieAttrValues[i]; - DWARFForm form = attrDef.getAttributeForm(); - if (attrVal instanceof DWARFIndirectAttribute) { - form = ((DWARFIndirectAttribute) attrVal).getForm(); - attrVal = ((DWARFIndirectAttribute) attrVal).getValue(); - } - return new AttrInfo(attrVal, die, form); - } + DWARFAttributeValue attrVal = die.findAttribute(attribute); + if (attrVal != null) { + return new FoundAttribute(attrVal, die); } } return null; @@ -302,14 +288,14 @@ public class DIEAggregate { * Return an attribute that is present in this {@link DIEAggregate}, or in any of its * direct children (of a specific type) * - * @param + * @param attribute value type * @param attribute the attribute to find * @param childTag the type of children to search * @param clazz type of the attribute to return * @return attribute value, or null if not found */ - public T findAttributeInChildren(int attribute, int childTag, - Class clazz) { + public T findAttributeInChildren(DWARFAttribute attribute, + DWARFTag childTag, Class clazz) { T attributeValue = getAttribute(attribute, clazz); if (attributeValue != null) { return attributeValue; @@ -325,7 +311,7 @@ public class DIEAggregate { } /** - * Finds a {@link DWARFAttributeValue attribute} with a matching {@link DWARFAttribute} type + * Finds a {@link DWARFAttributeValue attribute} with a matching {@link DWARFAttribute} id. *

* Returns null if the attribute does not exist or is wrong java class type. *

@@ -335,14 +321,27 @@ public class DIEAggregate { * * @param attribute See {@link DWARFAttribute} * @param clazz must be derived from {@link DWARFAttributeValue} - * @return + * @return DWARFAttributeValue or subclass as specified by the clazz, or null if not found */ - public T getAttribute(int attribute, Class clazz) { - AttrInfo attrInfo = findAttribute(attribute); + public T getAttribute(DWARFAttribute attribute, + Class clazz) { + FoundAttribute attrInfo = findAttribute(attribute); return attrInfo != null ? attrInfo.getValue(clazz) : null; } - public DWARFAttributeValue getAttribute(int attribute) { + /** + * Finds a {@link DWARFAttributeValue attribute} with a matching {@link DWARFAttribute} id. + *

+ * Returns null if the attribute does not exist. + *

+ * Attributes are searched for in each fragment in this aggregate, starting with the + * 'head' fragment, progressing toward the 'decl' fragment. + *

+ * + * @param attribute See {@link DWARFAttribute} + * @return DWARFAttributeValue, or null if not found + */ + public DWARFAttributeValue getAttribute(DWARFAttribute attribute) { return getAttribute(attribute, DWARFAttributeValue.class); } @@ -350,11 +349,11 @@ public class DIEAggregate { * Returns the value of the requested attribute, or -defaultValue- if the * attribute is missing. * - * @param attribute - * @param defaultValue - * @return + * @param attribute {@link DWARFAttribute} id + * @param defaultValue value to return if attribute is not present + * @return long value, or the defaultValue if attribute not present */ - public long getLong(int attribute, long defaultValue) { + public long getLong(DWARFAttribute attribute, long defaultValue) { DWARFNumericAttribute attr = getAttribute(attribute, DWARFNumericAttribute.class); return (attr != null) ? attr.getValue() : defaultValue; } @@ -363,11 +362,11 @@ public class DIEAggregate { * Returns the boolean value of the requested attribute, or -defaultValue- if * the attribute is missing or not the correct type. *

- * @param attribute - * @param defaultValue - * @return + * @param attribute {@link DWARFAttribute} id + * @param defaultValue value to return if attribute is not present + * @return boolean value, or the defaultValue if attribute is not present */ - public boolean getBool(int attribute, boolean defaultValue) { + public boolean getBool(DWARFAttribute attribute, boolean defaultValue) { DWARFBooleanAttribute val = getAttribute(attribute, DWARFBooleanAttribute.class); return (val != null) ? val.getValue() : defaultValue; } @@ -376,23 +375,26 @@ public class DIEAggregate { * Returns the string value of the requested attribute, or -defaultValue- if * the attribute is missing or not the correct type. *

- * @param attribute - * @param defaultValue - * @return + * @param attribute {@link DWARFAttribute} id + * @param defaultValue value to return if attribute is not present + * @return String value, or the defaultValue if attribute is not present */ - public String getString(int attribute, String defaultValue) { - DWARFStringAttribute attr = getAttribute(attribute, DWARFStringAttribute.class); - return (attr != null) ? attr.getValue(getProgram().getDebugStrings()) : defaultValue; + public String getString(DWARFAttribute attribute, String defaultValue) { + FoundAttribute attrInfo = findAttribute(attribute); + if (attrInfo == null || !(attrInfo.attr instanceof DWARFStringAttribute strAttr)) { + return defaultValue; + } + return strAttr.getValue(attrInfo.die.getCompilationUnit()); } /** * Returns the string value of the {@link DWARFAttribute#DW_AT_name dw_at_name} attribute, * or null if it is missing. *

- * @return + * @return name of this DIE aggregate, or null if missing */ public String getName() { - return getString(DWARFAttribute.DW_AT_name, null); + return getString(DW_AT_name, null); } /** @@ -403,30 +405,23 @@ public class DIEAggregate { * the dwarf information (ie. a value with the high bit set is not treated as signed). *

* The -defaultValue- parameter can accept a negative value. - * @param attribute - * @param defaultValue - * @return + * + * @param attribute {@link DWARFAttribute} id + * @param defaultValue value to return if attribute is not present + * @return unsigned long value, or the defaultValue if attribute is not present */ - public long getUnsignedLong(int attribute, long defaultValue) { + public long getUnsignedLong(DWARFAttribute attribute, long defaultValue) { DWARFNumericAttribute attr = getAttribute(attribute, DWARFNumericAttribute.class); return (attr != null) ? attr.getUnsignedValue() : defaultValue; } - /** - * Returns the {@link DebugInfoEntry die} instance pointed to by the requested attribute, - * or null if the attribute does not exist. - *

- * @param attribute - * @return - */ - public DebugInfoEntry getRefDIE(int attribute) { - AttrInfo attrInfo = findAttribute(attribute); - if (attrInfo == null) { + private DebugInfoEntry getRefDIE(DWARFAttribute attribute) { + DWARFNumericAttribute val = getAttribute(attribute, DWARFNumericAttribute.class); + if (val == null) { return null; } - DWARFNumericAttribute val = attrInfo.getValue(DWARFNumericAttribute.class); - long offset = (val != null) ? val.getUnsignedValue() : -1; + long offset = val.getUnsignedValue(); DebugInfoEntry result = getProgram().getDIEByOffset(offset); if (result == null) { @@ -436,7 +431,14 @@ public class DIEAggregate { return result; } - public DIEAggregate getRef(int attribute) { + /** + * Returns the {@link DIEAggregate diea} instance pointed to by the requested attribute, + * or null if the attribute does not exist. + *

+ * @param attribute {@link DWARFAttribute} id + * @return {@link DIEAggregate}, or the null if attribute is not present + */ + public DIEAggregate getRef(DWARFAttribute attribute) { DebugInfoEntry die = getRefDIE(attribute); return getProgram().getAggregate(die); } @@ -447,11 +449,11 @@ public class DIEAggregate { * @return DIEA pointed to by the DW_AT_containing_type attribute, or null if not present. */ public DIEAggregate getContainingTypeRef() { - return getRef(DWARFAttribute.DW_AT_containing_type); + return getRef(DW_AT_containing_type); } public DIEAggregate getTypeRef() { - return getRef(DWARFAttribute.DW_AT_type); + return getRef(DW_AT_type); } /** @@ -460,7 +462,7 @@ public class DIEAggregate { * @return name of file this item was declared in, or null if info not available */ public String getSourceFile() { - AttrInfo attrInfo = findAttribute(DWARFAttribute.DW_AT_decl_file); + FoundAttribute attrInfo = findAttribute(DW_AT_decl_file); if (attrInfo == null) { return null; } @@ -469,10 +471,8 @@ public class DIEAggregate { return null; } int fileNum = (int) attr.getUnsignedValue(); - DWARFCompileUnit dcu = attrInfo.die.getCompilationUnit().getCompileUnit(); - return dcu.isValidFileIndex(fileNum) - ? dcu.getFileByIndex(fileNum) - : null; + DWARFCompilationUnit cu = attrInfo.die.getCompilationUnit(); + return cu.isValidFileIndex(fileNum) ? cu.getFileByIndex(fileNum) : null; } /** @@ -481,11 +481,17 @@ public class DIEAggregate { * @param childTag see {@link DWARFTag DWARFTag DW_TAG_* values} * @return List of children DIEs that match the specified tag */ - public List getChildren(int childTag) { + public List getChildren(DWARFTag childTag) { return getHeadFragment().getChildren(childTag); } - public boolean hasAttribute(int attribute) { + /** + * Returns true if the specified attribute is present. + * + * @param attribute attribute id + * @return boolean true if value is present + */ + public boolean hasAttribute(DWARFAttribute attribute) { return findAttribute(attribute) != null; } @@ -497,12 +503,11 @@ public class DIEAggregate { * abstract portion */ public DIEAggregate getAbstractInstance() { - AttrInfo aoAttr = findAttribute(DW_AT_abstract_origin); + FoundAttribute aoAttr = findAttribute(DW_AT_abstract_origin); if (aoAttr == null) { return null; } - int aoIndex = 0; - for (; aoIndex < fragments.length; aoIndex++) { + for (int aoIndex = 0; aoIndex < fragments.length; aoIndex++) { if (fragments[aoIndex] == aoAttr.die) { DebugInfoEntry[] partialFrags = new DebugInfoEntry[fragments.length - aoIndex - 1]; System.arraycopy(fragments, aoIndex + 1, partialFrags, 0, partialFrags.length); @@ -516,34 +521,32 @@ public class DIEAggregate { * Returns the signed integer value of the requested attribute after resolving * any DWARF expression opcodes. *

- * @param attribute - * @param defaultValue - * @return - * @throws IOException - * @throws DWARFExpressionException + * @param attribute {@link DWARFAttribute} id + * @param defaultValue value to return if attribute is not present + * @return int value, or the defaultValue if attribute is not present + * @throws IOException if error reading value or invalid value type + * @throws DWARFExpressionException if error evaluating a DWARF expression */ - public int parseInt(int attribute, int defaultValue) + public int parseInt(DWARFAttribute attribute, int defaultValue) throws IOException, DWARFExpressionException { - AttrInfo attrInfo = findAttribute(attribute); - if (attrInfo == null) { + DWARFAttributeValue attr = getAttribute(attribute); + if (attr == null) { return defaultValue; } - DWARFAttributeValue attr = attrInfo.attr; if (attr instanceof DWARFNumericAttribute dnum) { return assertValidInt(dnum.getValue()); } else if (attr instanceof DWARFBlobAttribute dblob) { byte[] exprBytes = dblob.getBytes(); - DWARFExpressionEvaluator evaluator = DWARFExpressionEvaluator.create(getHeadFragment()); + DWARFExpressionEvaluator evaluator = new DWARFExpressionEvaluator(getCompilationUnit()); DWARFExpression expr = evaluator.readExpr(exprBytes); evaluator.evaluate(expr, 0); return assertValidInt(evaluator.pop()); } else { - throw new IOException( - "DWARF attribute form not valid for integer value: " + attrInfo.form); + throw new IOException("Not integer attribute: %s".formatted(attr)); } } @@ -551,15 +554,15 @@ public class DIEAggregate { * Returns the unsigned integer value of the requested attribute after resolving * any DWARF expression opcodes. *

- * @param attribute - * @param defaultValue - * @return - * @throws IOException - * @throws DWARFExpressionException + * @param attribute {@link DWARFAttribute} id + * @param defaultValue value to return if attribute is not present + * @return unsigned long value, or the defaultValue if attribute is not present + * @throws IOException if error reading value or invalid value type + * @throws DWARFExpressionException if error evaluating a DWARF expression */ - public long parseUnsignedLong(int attribute, long defaultValue) + public long parseUnsignedLong(DWARFAttribute attribute, long defaultValue) throws IOException, DWARFExpressionException { - AttrInfo attrInfo = findAttribute(attribute); + FoundAttribute attrInfo = findAttribute(attribute); if (attrInfo == null) { return defaultValue; } @@ -570,15 +573,15 @@ public class DIEAggregate { } else if (attr instanceof DWARFBlobAttribute dblob) { byte[] exprBytes = dblob.getBytes(); - DWARFExpressionEvaluator evaluator = DWARFExpressionEvaluator.create(getHeadFragment()); + DWARFExpressionEvaluator evaluator = + new DWARFExpressionEvaluator(attrInfo.die().getCompilationUnit()); DWARFExpression expr = evaluator.readExpr(exprBytes); evaluator.evaluate(expr, 0); return evaluator.pop(); } else { - throw new IOException( - "DWARF attribute form not valid for integer value: " + attrInfo.form); + throw new IOException("Not integer attribute: %s".formatted(attr)); } } @@ -600,27 +603,26 @@ public class DIEAggregate { * Returns the unsigned integer value of the requested attribute after resolving * any DWARF expression opcodes. * - * @param attribute - * @param defaultValue - * @return - * @throws DWARFException - * @throws DWARFExpressionException + * @param attribute {@link DWARFAttribute} id + * @param defaultValue value to return if attribute is not present + * @return unsigned int value, or the defaultValue if attribute is not present + * @throws IOException if error reading value or invalid value type + * @throws DWARFExpressionException if error evaluating a DWARF expression */ - public int parseDataMemberOffset(int attribute, int defaultValue) - throws DWARFException, DWARFExpressionException { + public int parseDataMemberOffset(DWARFAttribute attribute, int defaultValue) + throws DWARFExpressionException, IOException { - AttrInfo attrInfo = findAttribute(attribute); - if (attrInfo == null) { + DWARFAttributeValue attr = getAttribute(attribute); + if (attr == null) { return defaultValue; } - DWARFAttributeValue attr = attrInfo.attr; if (attr instanceof DWARFNumericAttribute dnum) { - return assertValidUInt(dnum.getUnsignedValue()); + return dnum.getUnsignedIntExact(); } else if (attr instanceof DWARFBlobAttribute dblob) { byte[] exprBytes = dblob.getBytes(); - DWARFExpressionEvaluator evaluator = DWARFExpressionEvaluator.create(getHeadFragment()); + DWARFExpressionEvaluator evaluator = new DWARFExpressionEvaluator(getCompilationUnit()); DWARFExpression expr = evaluator.readExpr(exprBytes); // DW_AT_data_member_location expects the address of the containing object @@ -630,138 +632,43 @@ public class DIEAggregate { return assertValidUInt(evaluator.pop()); } else { - throw new DWARFException( - "DWARF attribute form not valid for data member offset: " + attrInfo.form); + throw new DWARFException("DWARF attribute form not valid for data member offset: %s" + .formatted(attr.getAttributeForm())); } } /** - * Returns the location list info specified in the attribute. - *

- * Numeric attributes are treated as offsets into the debug_loc section. - *

- * Blob attributes are treated as a single location record for the current CU, using the - * blob bytes as the DWARF expression of the location record. - *

- * @param attribute the attribute to evaluate - * @param range the address range the location covers (may be discarded if the attribute - * value is a location list with its own range values) - * @return list of locations, empty if missing, never null - * @throws IOException + * Parses a location attribute value, which can be a single expression that is valid for any + * PC, or a list of expressions that are tied to specific ranges. + * + * @param attribute typically {@link DWARFAttribute#DW_AT_location} + * @return a {@link DWARFLocationList}, never null, possibly empty + * @throws IOException if error reading data */ - public List getAsLocation(int attribute, DWARFRange range) throws IOException { - AttrInfo attrInfo = findAttribute(attribute); - if (attrInfo == null) { - return List.of(); - } - else if (attrInfo.attr instanceof DWARFNumericAttribute dnum) { - return readDebugLocList(dnum.getUnsignedValue()); - } - else if (attrInfo.attr instanceof DWARFBlobAttribute dblob) { - return _exprBytesAsLocation(dblob, range); - } - else { - throw new UnsupportedOperationException( - "This method is unsupported for the attribute type " + attrInfo.form + "."); - } + public DWARFLocationList getLocationList(DWARFAttribute attribute) throws IOException { + return getProgram().getLocationList(this, attribute); } /** - * Evaluate the DWARFExpression located in the DWARFLocation object in the context of - * this DIEA. - *

- * @param location - * @return - * @throws IOException - * @throws DWARFExpressionException + * Parses a location attribute value, and returns the {@link DWARFLocation} instance that + * covers the specified pc. + * + * @param attribute typically {@link DWARFAttribute#DW_AT_location} + * @param pc program counter + * @return a {@link DWARFLocationList}, never null, possibly empty + * @throws IOException if error reading data */ - public long evaluateLocation(DWARFLocation location) - throws IOException, DWARFExpressionException { - DWARFExpressionEvaluator evaluator = DWARFExpressionEvaluator.create(getHeadFragment()); - DWARFExpression expr = evaluator.readExpr(location.getLocation()); - - evaluator.evaluate(expr); - return evaluator.pop(); - } - - /** - * Return a list of DWARF locations read from the debug_loc section. - *

- * The deserialization done here is very similar to {@link #parseDebugRange(int)}, but in this - * case also contains a blob payload per location. - * - * @param offset offset into the debug_loc section - * @return list of DWARF locations (address range and location expression) - * @throws IOException if an I/O error occurs - */ - private List readDebugLocList(long offset) throws IOException { - BinaryReader debug_loc = getProgram().getDebugLocation(); - - List results = new ArrayList<>(); - if (debug_loc == null) { - return results; - } - - debug_loc.setPointerIndex(offset); - byte pointerSize = getCompilationUnit().getPointerSize(); - - Number baseAddress = getCompilationUnit().getCompileUnit().getLowPC(); - long baseAddressOffset = (baseAddress != null) ? baseAddress.longValue() : 0; - - Number cuLowPC = getCompilationUnit().getCompileUnit().getLowPC(); - long cuBase = (cuLowPC != null) ? cuLowPC.longValue() : Long.MAX_VALUE; - - // Loop through the debug_loc entry - while (debug_loc.getPointerIndex() < debug_loc.length()) { - long beginning = DWARFUtil.readAddressAsLong(debug_loc, pointerSize); - long ending = DWARFUtil.readAddressAsLong(debug_loc, pointerSize); - - // List end - if (beginning == 0 && ending == 0) { - break; - } - - // Check to see if this is a base address entry - if (beginning == -1 || - (pointerSize == 4 && beginning == NumericUtilities.MAX_UNSIGNED_INT32_AS_LONG)) { - baseAddressOffset = ending; - continue; - } - - // Size is 2 bytes - int size = debug_loc.readNextUnsignedShort(); - - // Read the location description - byte[] location = debug_loc.readNextByteArray(size); - - // Test to see if the 'offset' read from the debug_loc data is already - // greater-than the compunit's lowpc. This indicates the 'offset' isn't - // an offset, but already an absolute value. This occurs in some - // gcc dwarf compilation flag combinations. - boolean isBadOffset = (beginning > cuBase); - - long absStart = beginning; - long absEnd = ending; - if (!isBadOffset) { - absStart += baseAddressOffset; - absEnd += baseAddressOffset; - } - - // TODO: verify end addr calc with DWARFstd.pdf, inclusive vs exclusive - results.add(new DWARFLocation(new DWARFRange(absStart, absEnd + 1), location)); - } - return results; - } - - private List _exprBytesAsLocation(DWARFBlobAttribute attr, DWARFRange range) { - return List.of(new DWARFLocation(range, attr.getBytes())); + public DWARFLocation getLocation(DWARFAttribute attribute, long pc) throws IOException { + DWARFLocationList locList = getLocationList(attribute); + return locList.getLocationContaining(pc); } /** * Returns true if this DIE has a DW_AT_declaration attribute and * does NOT have a matching inbound DW_AT_specification reference. *

- * @return + * @return boolean true if this DIE has a DW_AT_declaration attribute and + * does NOT have a matching inbound DW_AT_specification reference */ public boolean isDanglingDeclaration() { return isPartialDeclaration() && fragments.length == 1; @@ -769,80 +676,10 @@ public class DIEAggregate { /** * Returns true if this DIE has a DW_AT_declaration attribute. - * @return + * @return true if this DIE has a DW_AT_declaration attribute */ public boolean isPartialDeclaration() { - return hasAttribute(DWARFAttribute.DW_AT_declaration); - } - - public boolean isNamedType() { - switch (getTag()) { - case DWARFTag.DW_TAG_base_type: - case DWARFTag.DW_TAG_typedef: - case DWARFTag.DW_TAG_namespace: - case DWARFTag.DW_TAG_subprogram: - case DWARFTag.DW_TAG_class_type: - case DWARFTag.DW_TAG_interface_type: - case DWARFTag.DW_TAG_structure_type: - case DWARFTag.DW_TAG_union_type: - case DWARFTag.DW_TAG_enumeration_type: - case DWARFTag.DW_TAG_subroutine_type: - case DWARFTag.DW_TAG_unspecified_type: - return true; - - case DWARFTag.DW_TAG_pointer_type: - case DWARFTag.DW_TAG_reference_type: - case DWARFTag.DW_TAG_const_type: - case DWARFTag.DW_TAG_lexical_block: - default: - return false; - } - } - - /** - * Returns true if the children of this DIE are within a new namespace. - *

- * Ie. Namespaces, subprogram, class, interface, struct, union, enum - * @return - */ - public boolean isNameSpaceContainer() { - switch (getTag()) { - case DWARFTag.DW_TAG_namespace: - case DWARFTag.DW_TAG_subprogram: - case DWARFTag.DW_TAG_lexical_block: - case DWARFTag.DW_TAG_class_type: - case DWARFTag.DW_TAG_interface_type: - case DWARFTag.DW_TAG_structure_type: - case DWARFTag.DW_TAG_union_type: - case DWARFTag.DW_TAG_enumeration_type: - return true; - } - return false; - } - - /** - * Returns true if this DIE defines a structure-like element (class, struct, interface, union). - * - * @return - */ - public boolean isStructureType() { - switch (getTag()) { - case DWARFTag.DW_TAG_class_type: - case DWARFTag.DW_TAG_interface_type: - case DWARFTag.DW_TAG_structure_type: - case DWARFTag.DW_TAG_union_type: - return true; - } - return false; - } - - public boolean isFuncDefType() { - switch (getTag()) { - case DWARFTag.DW_TAG_subprogram: - case DWARFTag.DW_TAG_subroutine_type: - return true; - } - return false; + return hasAttribute(DW_AT_declaration); } @Override @@ -850,7 +687,7 @@ public class DIEAggregate { StringBuilder sb = new StringBuilder(); sb.append("DIEAgregrate of: "); for (DebugInfoEntry die : fragments) { - sb.append("DIE [").append(Long.toHexString(die.getOffset())).append("], "); + sb.append("DIE [0x%x], ".formatted(die.getOffset())); } sb.append("\n"); for (DebugInfoEntry die : fragments) { @@ -860,137 +697,52 @@ public class DIEAggregate { } /** - * Parses a range list from the debug_ranges section. - * See DWARF4 Section 2.17.3 (Non-Contiguous Address Ranges). - *

- * The returned list of ranges is sorted. + * Parses a range list. * - * @param attribute attribute ie. {@link DWARFAttribute#DW_AT_ranges} - * @return list of ranges, in order + * @param attribute attribute eg {@link DWARFAttribute#DW_AT_ranges} + * @return list of ranges, or null if attribute is not present * @throws IOException if an I/O error occurs */ - public List parseDebugRange(int attribute) throws IOException { - List result = readRange(attribute); - Collections.sort(result); - return result; + public DWARFRangeList getRangeList(DWARFAttribute attribute) throws IOException { + return getProgram().getRangeList(this, attribute); } /** - * Parses a range list from the debug_ranges section. - * See DWARF4 Section 2.17.3 (Non-Contiguous Address Ranges). - *

- * The returned list is not sorted. + * Return the range specified by the low_pc...high_pc attribute values. * - * @param attribute attribute ie. {@link DWARFAttribute#DW_AT_ranges} - * @return list of ranges - * @throws IOException if an I/O error occurs + * @return {@link DWARFRange} containing low_pc - high_pc, or null if the low_pc is not present */ - public List readRange(int attribute) throws IOException { - byte pointerSize = getCompilationUnit().getPointerSize(); - BinaryReader reader = getProgram().getDebugRanges(); + public DWARFRange getPCRange() { + DWARFNumericAttribute lowPc = getAttribute(DW_AT_low_pc, DWARFNumericAttribute.class); + if (lowPc != null) { + try { + // TODO: previous code excluded lowPc values that were == 0 as invalid. + long rawLowPc = lowPc.getUnsignedValue(); + long lowPcOffset = getProgram().getAddress(lowPc.getAttributeForm(), rawLowPc, + getCompilationUnit()); + long highPcOffset = lowPcOffset + 1; - long offset = getUnsignedLong(attribute, -1); - if (offset == -1) { - throw new IOException("Bad / missing attribute " + attribute); - } - reader.setPointerIndex(offset); - List ranges = new ArrayList<>(); - - DWARFCompileUnit dcu = getCompilationUnit().getCompileUnit(); - long baseAddress = dcu != null && dcu.getLowPC() != null - ? dcu.getLowPC().longValue() - : 0L; - - while (reader.hasNext()) { - // Read the beginning and ending addresses - long beginning = DWARFUtil.readAddressAsLong(reader, pointerSize); - long ending = DWARFUtil.readAddressAsLong(reader, pointerSize); // dwarf end addrs are exclusive - - // End of the list - if (beginning == 0 && ending == 0) { - break; + DWARFNumericAttribute highPc = + getAttribute(DW_AT_high_pc, DWARFNumericAttribute.class); + if (highPc != null) { + if (highPc.getAttributeForm() == DWARFForm.DW_FORM_addr) { + long baseAddrFixup = getProgram().getProgramBaseAddressFixup(); + highPcOffset = highPc.getUnsignedValue() + baseAddrFixup; + } + else { + highPcOffset = highPc.getUnsignedValue(); + if (highPcOffset != 0) { + highPcOffset = lowPcOffset + highPcOffset; + } + } + } + return new DWARFRange(lowPcOffset, highPcOffset); } - - // Check to see if this is a base address entry - if (beginning == -1 || - (pointerSize == 4 && beginning == NumericUtilities.MAX_UNSIGNED_INT32_AS_LONG)) { - baseAddress = ending; - continue; - } - - // Add the range to the list - ranges.add(new DWARFRange(baseAddress + beginning, baseAddress + ending)); - } - return ranges; - } - - /** - * Returns the value of the DW_AT_low_pc attribute, if it exists. - * - * @param defaultValue - * @return - */ - public long getLowPC(long defaultValue) { - DWARFNumericAttribute attr = - getAttribute(DWARFAttribute.DW_AT_low_pc, DWARFNumericAttribute.class); - return (attr != null) ? attr.getUnsignedValue() + getProgram().getProgramBaseAddressFixup() - : defaultValue; - } - - /** - * Returns the value of the DW_AT_high_pc attribute, adjusted - * if necessary by the value of DW_AT_low_pc. - *

- * @return - * @throws IOException if the DW_AT_high_pc attribute isn't a numeric - * attribute, or if the DW_AT_low_pc value is needed and is not present. - */ - public long getHighPC() throws IOException { - AttrInfo high = findAttribute(DWARFAttribute.DW_AT_high_pc); - if (high != null && high.attr instanceof DWARFNumericAttribute highVal) { - // if the DWARF attr was a DW_FORM_addr, it doesn't need fixing up - if (high.form == DWARFForm.DW_FORM_addr) { - return highVal.getUnsignedValue() + getProgram().getProgramBaseAddressFixup() - 1; - } - - // else it was a DW_FORM_data value and is relative to the lowPC value - DWARFNumericAttribute low = - getAttribute(DWARFAttribute.DW_AT_low_pc, DWARFNumericAttribute.class); - - long lhighVal = highVal.getUnsignedValue(); - if (lhighVal == 0) { - lhighVal = 1; - } - if (low != null && lhighVal > 0) { - return low.getUnsignedValue() + getProgram().getProgramBaseAddressFixup() + - lhighVal - 1; + catch (IOException e) { + // fall thru, return null } } - throw new IOException("Bad/unsupported DW_AT_high_pc attribute value or type"); - } - - /** - * Returns true if the raw lowPc and highPc values are the same. - *

- * This indicates an empty range, in which case the caller may want to take - * special steps to avoid issues with Ghidra ranges. - *

- * Only seen in extremely old gcc versions. Typically the low and high - * pc values are omitted if the CU is empty. - * - * @return boolean true if the LowPC and HighPC values are present and equal - */ - public boolean isLowPCEqualHighPC() { - AttrInfo low = findAttribute(DWARFAttribute.DW_AT_low_pc); - AttrInfo high = findAttribute(DWARFAttribute.DW_AT_high_pc); - if (low != null && high != null && low.form == high.form && - low.attr instanceof DWARFNumericAttribute lowVal && - high.attr instanceof DWARFNumericAttribute highVal) { - - return lowVal.getValue() == highVal.getValue(); - - } - return false; + return null; } /** @@ -1026,7 +778,7 @@ public class DIEAggregate { newParams.add(getProgram().getAggregate(paramDIE)); } } - if ( !params.isEmpty() ) { + if (!params.isEmpty()) { //Msg.warn(this, "Extra params in concrete DIE instance: " + params); //Msg.warn(this, this.toString()); newParams.addAll(params); @@ -1049,18 +801,11 @@ public class DIEAggregate { /** * A simple class used by findAttribute() to return the found attribute, along with * the DIE it was found in, and the DWARFForm type of the raw attribute. + * + * @param attr attribute value + * @param die DIE the value was found in */ - static class AttrInfo { - DWARFAttributeValue attr; - DebugInfoEntry die; - DWARFForm form; - - AttrInfo(DWARFAttributeValue attr, DebugInfoEntry die, DWARFForm form) { - this.attr = attr; - this.die = die; - this.form = form; - } - + record FoundAttribute(DWARFAttributeValue attr, DebugInfoEntry die) { T getValue(Class clazz) { if (attr != null && clazz.isAssignableFrom(attr.getClass())) { return clazz.cast(attr); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFAbbreviation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFAbbreviation.java new file mode 100644 index 0000000000..6be1b256c8 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFAbbreviation.java @@ -0,0 +1,206 @@ +/* ### + * 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.util.bin.format.dwarf; + +import static ghidra.app.util.bin.format.dwarf.DWARFTag.*; + +import java.io.IOException; +import java.util.*; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.dwarf.attribs.*; +import ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.AttrDef; +import ghidra.program.model.data.LEB128; +import ghidra.util.Msg; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * This class represents the 'schema' for a DWARF DIE record. + *

+ * A raw DWARF DIE record specifies its abbreviation code (pointing to an instance of + * this class) and the corresponding DWARFAbbreviation instance has the information + * about how the raw DIE is laid out. + */ +public class DWARFAbbreviation { + private static final int EOL = 0; + private final int abbreviationCode; + private final DWARFTag tag; + private final int tagId; + private final boolean hasChildren; + private final AttrDef[] attributes; + + /** + * Reads a {@link DWARFAbbreviation} from the stream. + * + * @param reader {@link BinaryReader} stream + * @param prog {@link DWARFProgram} + * @param monitor {@link TaskMonitor} + * @return {@link DWARFAbbreviation}, or null if the stream was at a end-of-list marker + * @throws IOException if error reading + * @throws CancelledException if canceled + */ + public static DWARFAbbreviation read(BinaryReader reader, DWARFProgram prog, + TaskMonitor monitor) throws IOException, CancelledException { + + int ac = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + if (ac == EOL) { + return null; + } + int tag = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + int hasChildren = reader.readNextByte(); + + // Read each attribute specification until EOL marker value + List tmpAttrSpecs = new ArrayList<>(); + AttrDef attrSpec; + while ((attrSpec = AttrDef.read(reader)) != null) { + monitor.checkCancelled(); + attrSpec = prog.internAttributeSpec(attrSpec); + tmpAttrSpecs.add(attrSpec); + warnIfMismatchedForms(attrSpec); + } + AttrDef[] attrSpecArray = tmpAttrSpecs.toArray(new AttrDef[tmpAttrSpecs.size()]); + + DWARFAbbreviation result = new DWARFAbbreviation(ac, tag, + hasChildren == DWARFChildren.DW_CHILDREN_yes, attrSpecArray); + + return result; + } + + private static void warnIfMismatchedForms(DWARFAttribute.AttrDef attrSpec) { + DWARFForm form = attrSpec.getAttributeForm(); + DWARFAttribute attribute = attrSpec.getAttributeId(); + if (attribute != null && !form.getFormClasses().isEmpty() && + !attribute.getAttributeClass().isEmpty()) { + EnumSet tmp = + EnumSet.copyOf(attrSpec.getAttributeForm().getFormClasses()); + tmp.retainAll(attrSpec.getAttributeId().getAttributeClass()); + if (tmp.isEmpty()) { + Msg.warn(DWARFAbbreviation.class, + "Mismatched DWARF Attribute and Form: %s".formatted(attrSpec)); + } + } + + } + + /** + * Reads a list of {@link DWARFAbbreviation}, stopping when the end-of-list marker is + * encountered. + * + * @param reader {@link BinaryReader} .debug_abbr stream + * @param prog {@link DWARFProgram} + * @param monitor {@link TaskMonitor} + * @return map of abbrCode -> abbr instance + * @throws IOException if error reading + * @throws CancelledException if cancelled + */ + public static Map readAbbreviations(BinaryReader reader, + DWARFProgram prog, TaskMonitor monitor) throws IOException, CancelledException { + Map result = new HashMap<>(); + + // Read a list of abbreviations, terminated by a marker value that returns null from read() + DWARFAbbreviation abbrev = null; + while ((abbrev = DWARFAbbreviation.read(reader, prog, monitor)) != null) { + monitor.checkCancelled(); + result.put(abbrev.getAbbreviationCode(), abbrev); + } + + return result; + } + + public DWARFAbbreviation(int abbreviationCode, int tagId, boolean hasChildren, + AttrDef[] attributes) { + this.abbreviationCode = abbreviationCode; + this.tagId = tagId; + this.tag = DWARFTag.of(tagId); + this.hasChildren = hasChildren; + this.attributes = attributes; + } + + @Override + public String toString() { + return "%x:%s".formatted(abbreviationCode, getTagName()); + } + + /** + * Get the abbreviation code. + * @return the abbreviation code + */ + public int getAbbreviationCode() { + return this.abbreviationCode; + } + + /** + * Get the tag value. + * @return the tag value + */ + public DWARFTag getTag() { + return this.tag; + } + + public String getTagName() { + return tag != DW_TAG_UNKNOWN ? tag.name() : "DW_TAG_??? %d".formatted(tagId); + } + + /** + * Checks to see if this abbreviation has any DIE children. + * @return true if this abbreviation has DIE children + */ + public boolean hasChildren() { + return this.hasChildren; + } + + /** + * Return a live list of the attributes. + * @return list of attributes + */ + public AttrDef[] getAttributes() { + return attributes; + } + + /** + * Return number of attribute values. + * + * @return number of attribute values + */ + public int getAttributeCount() { + return attributes.length; + } + + /** + * Get the attribute at the given index. + * @param index index of the attribute + * @return attribute specification + */ + public AttrDef getAttributeAt(int index) { + return this.attributes[index]; + } + + /** + * Get the attribute with the given attribute key. + * @param attributeId attribute key + * @return attribute specification + */ + public AttrDef findAttribute(DWARFAttribute attributeId) { + for (AttrDef spec : this.attributes) { + if (spec.getAttributeId() == attributeId) { + return spec; + } + } + return null; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFAccessibility.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFAccessibility.java similarity index 97% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFAccessibility.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFAccessibility.java index 8a0519d42a..3c4963da73 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFAccessibility.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFAccessibility.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.encoding; +package ghidra.app.util.bin.format.dwarf; import java.util.HashMap; import java.util.Map; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFAddressListHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFAddressListHeader.java new file mode 100644 index 0000000000..b717f49a39 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFAddressListHeader.java @@ -0,0 +1,99 @@ +/* ### + * 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.util.bin.format.dwarf; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; + +/** + * Header at the beginning of a address list table + */ +public class DWARFAddressListHeader extends DWARFIndirectTableHeader { + + /** + * Reads a {@link DWARFAddressListHeader} from the stream. + * + * @param reader {@link BinaryReader} stream + * @param defaultIntSize native int size for the binary + * @return {@link DWARFAddressListHeader}, or null if end-of-list marker + * @throws IOException if error reading + */ + public static DWARFAddressListHeader read(BinaryReader reader, int defaultIntSize) + throws IOException { + // length : dwarf_length + // version : 2 bytes + // addr_size : 1 byte + // seg_sel_size : 1 byte + + long startOffset = reader.getPointerIndex(); + DWARFLengthValue lengthInfo = DWARFLengthValue.read(reader, defaultIntSize); + if (lengthInfo == null) { + return null; + } + + long endOffset = reader.getPointerIndex() + lengthInfo.length(); + short version = reader.readNextShort(); + if (version != 5) { + throw new DWARFException("Unsupported DWARF version [%d]".formatted(version)); + } + int addressSize = reader.readNextUnsignedByte(); + int segmentSelectorSize = reader.readNextUnsignedByte(); + long firstAddr = reader.getPointerIndex(); + reader.setPointerIndex(endOffset); + + int count = (int) ((endOffset - firstAddr) / (addressSize + segmentSelectorSize)); + return new DWARFAddressListHeader(startOffset, endOffset, firstAddr, addressSize, + segmentSelectorSize, count); + } + + private final int addressSize; + private final int segmentSelectorSize; + private final int addrCount; + + public DWARFAddressListHeader(long startOffset, long endOffset, long firstElementOffset, + int addressSize, int segmentSelectorSize, int addrCount) { + super(startOffset, endOffset, firstElementOffset); + + this.addressSize = addressSize; + this.segmentSelectorSize = segmentSelectorSize; + this.addrCount = addrCount; + } + + @Override + public long getOffset(int index, BinaryReader reader) throws IOException { + if (index < 0 || addrCount <= index) { + throw new IOException("Invalid address index: %d".formatted(index)); + } + long offset = firstElementOffset + (addressSize + segmentSelectorSize) * index; + + @SuppressWarnings("unused") + long seg = + segmentSelectorSize > 0 ? reader.readUnsignedValue(offset, segmentSelectorSize) : 0; + + long addr = reader.readUnsignedValue(offset + segmentSelectorSize, addressSize); + return addr; + } + + public int getAddressSize() { + return addressSize; + } + + public int getSegmentSelectorSize() { + return segmentSelectorSize; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFChildren.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFChildren.java new file mode 100644 index 0000000000..c49be56f72 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFChildren.java @@ -0,0 +1,28 @@ +/* ### + * 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.util.bin.format.dwarf; + +/** + * DWARF child determination consts from www.dwarfstd.org/doc/DWARF4.pdf. + *

+ * Yes, its a direct equiv to a boolean, but its in the spec. + */ +public class DWARFChildren +{ + public static final int DW_CHILDREN_no = 0; + public static final int DW_CHILDREN_yes = 1; + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFCompilationUnit.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFCompilationUnit.java new file mode 100644 index 0000000000..e2a7fcaee2 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFCompilationUnit.java @@ -0,0 +1,342 @@ +/* ### + * 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.util.bin.format.dwarf; + +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * A DWARF CompilationUnit is a contiguous block of {@link DebugInfoEntry DIE} records found + * in a .debug_info section of an program. The compilation unit block starts with a + * header that has a few important values and flags, and is followed by the DIE records. + *

+ * The first DIE record must be a DW_TAG_compile_unit. + *

+ * DIE records are identified by their byte offset in the .debug_info section. + *

+ */ +public class DWARFCompilationUnit extends DWARFUnitHeader { + /** + * Creates a new {@link DWARFCompilationUnit} by reading a compilationUnit's header data + * from the debug_info section and the debug_abbr section and its compileUnit DIE (ie. + * the first DIE right after the header). + *

+ * Returns {@code NULL} if there was an ignorable error while reading the compilation unit (and + * leaves the input stream at the next compilation unit to read), otherwise throws + * an IOException if there was an unrecoverable error. + *

+ * Also returns {@code NULL} (and leaves the stream at EOF) if the remainder of the stream + * is filled with null bytes. + * + * @param partial already read partial unit header + * @param reader .debug_info BinaryReader + * @param abbrReader .debug_abbr BinaryReader + * @param monitor the current task monitor + * @return the read compilation unit, or null if the compilation unit was bad/empty and should + * be ignored + * @throws DWARFException if an invalid or unsupported DWARF version is read. + * @throws IOException if the length of the compilation unit is invalid. + * @throws CancelledException if the task has been canceled. + */ + public static DWARFCompilationUnit readV4(DWARFUnitHeader partial, BinaryReader reader, + BinaryReader abbrReader, TaskMonitor monitor) + throws DWARFException, IOException, CancelledException { + + long abbreviationOffset = reader.readNextUnsignedValue(partial.getIntSize()); + byte pointerSize = reader.readNextByte(); + long firstDIEOffset = reader.getPointerIndex(); + + if (firstDIEOffset > partial.endOffset) { + throw new IOException("Invalid length %d for DWARF Compilation Unit at 0x%x" + .formatted(partial.endOffset - partial.startOffset, partial.startOffset)); + } + else if (firstDIEOffset == partial.endOffset) { + // silently skip this empty compunit + return null; + } + + abbrReader.setPointerIndex(abbreviationOffset); + Map abbrMap = + DWARFAbbreviation.readAbbreviations(abbrReader, partial.dprog, monitor); + + DWARFCompilationUnit cu = + new DWARFCompilationUnit(partial, pointerSize, firstDIEOffset, abbrMap); + return cu; + } + + /** + * Creates a new {@link DWARFCompilationUnit} by reading a compilationUnit's header data + * from the debug_info section and the debug_abbr section and its compileUnit DIE (ie. + * the first DIE right after the header). + *

+ * Returns {@code NULL} if there was an ignorable error while reading the compilation unit (and + * leaves the input stream at the next compilation unit to read), otherwise throws + * an IOException if there was an unrecoverable error. + *

+ * Also returns {@code NULL} (and leaves the stream at EOF) if the remainder of the stream + * is filled with null bytes. + * + * @param partial already read partial unit header + * @param reader .debug_info BinaryReader + * @param abbrReader .debug_abbr BinaryReader + * @param monitor the current task monitor + * @return the read compilation unit, or null if the compilation unit was bad/empty and should + * be ignored + * @throws DWARFException if an invalid or unsupported DWARF version is read. + * @throws IOException if the length of the compilation unit is invalid. + * @throws CancelledException if the task has been canceled. + */ + public static DWARFCompilationUnit readV5(DWARFUnitHeader partial, BinaryReader reader, + BinaryReader abbrReader, TaskMonitor monitor) + throws DWARFException, IOException, CancelledException { + + byte pointerSize = reader.readNextByte(); + long abbreviationOffset = reader.readNextUnsignedValue(partial.getIntSize()); + + long firstDIEOffset = reader.getPointerIndex(); + + if (firstDIEOffset > partial.endOffset) { + throw new IOException("Invalid length %d for DWARF Compilation Unit at 0x%x" + .formatted(partial.endOffset - partial.startOffset, partial.startOffset)); + } + else if (firstDIEOffset == partial.endOffset) { + // silently skip this empty compunit + return null; + } + + abbrReader.setPointerIndex(abbreviationOffset); + Map abbrMap = + DWARFAbbreviation.readAbbreviations(abbrReader, partial.dprog, monitor); + + DWARFCompilationUnit cu = + new DWARFCompilationUnit(partial, pointerSize, firstDIEOffset, abbrMap); + return cu; + } + + /** + * Size of pointers that are held in DIEs in this compUnit. (from header) + */ + private final byte pointerSize; + + /** + * Offset in the debug_info section of the first DIE of this compUnit. + */ + private final long firstDIEOffset; + + /** + * Map of abbrevCode to {@link DWARFAbbreviation} instances. + */ + private final Map codeToAbbreviationMap; + + /** + * The contents of the first DIE (that must be a compile unit) in this compUnit. + */ + protected DIEAggregate diea; + + private DWARFLine line; + + private DWARFCompilationUnit(DWARFUnitHeader partial, byte pointerSize, long firstDIEOffset, + Map abbrMap) { + super(partial); + + this.pointerSize = pointerSize; + this.firstDIEOffset = firstDIEOffset; + this.codeToAbbreviationMap = (abbrMap != null) ? abbrMap : new HashMap<>(); + } + + /** + * This ctor is public only for junit tests. Do not use directly. + * + * @param dwarfProgram {@link DWARFProgram} + * @param startOffset offset in provider where it starts + * @param endOffset offset in provider where it ends + * @param length how many bytes following the header the DIEs of this unit take + * @param intSize 4 (DWARF_32) or 8 (DWARF_64) + * @param dwarfVersion 2-5 + * @param pointerSize default size of pointers + * @param unitNumber this compunits ordinal in the file + * @param firstDIEOffset start of DIEs in the provider + * @param codeToAbbreviationMap map of abbreviation numbers to {@link DWARFAbbreviation} instances + */ + public DWARFCompilationUnit(DWARFProgram dwarfProgram, long startOffset, long endOffset, + long length, int intSize, short dwarfVersion, byte pointerSize, int unitNumber, + long firstDIEOffset, Map codeToAbbreviationMap) { + super(dwarfProgram, startOffset, endOffset, length, intSize, dwarfVersion, unitNumber); + this.pointerSize = pointerSize; + this.firstDIEOffset = firstDIEOffset; + this.codeToAbbreviationMap = + (codeToAbbreviationMap != null) ? codeToAbbreviationMap : new HashMap<>(); + } + + /** + * Initializes this compunit with the root DIE (first DIE) of the compunit. This comp unit + * isn't usable until this has happened. + * + * @param rootDIE {@link DebugInfoEntry} + * @throws IOException if error reading data from the DIE + */ + public void init(DebugInfoEntry rootDIE) throws IOException { + diea = DIEAggregate.createSingle(rootDIE); + line = getProgram().getLine(diea, DW_AT_stmt_list); + } + + /** + * Returns this comp unit's root DIE as a DIE Aggregate. + * + * @return the aggregate containing the root element of this comp unit + */ + public DIEAggregate getCompUnitDIEA() { + return diea; + } + + /** + * Returns the size of pointers in this compUnit. + * + * @return the size in bytes of pointers + */ + public byte getPointerSize() { + return this.pointerSize; + } + + public Map getCodeToAbbreviationMap() { + return codeToAbbreviationMap; + } + + public DWARFAbbreviation getAbbreviation(int ac) { + return codeToAbbreviationMap.get(ac); + } + + public long getFirstDIEOffset() { + return firstDIEOffset; + } + + /** + * Get the filename that produced the compile unit + * + * @return the filename that produced the compile unit + */ + public String getName() { + return diea.getString(DW_AT_name, null); + } + + /** + * Get a file name with the full path included based on a file index. + * @param index index of the file + * @return file name with full path or null if line information does not exist + * @throws IllegalArgumentException if a negative or invalid file index is given + */ + public String getFullFileByIndex(int index) { + if (index < 0) { + throw new IllegalArgumentException("Negative file index was given."); + } + if (this.line == null) { + return null; + } + + return this.line.getFullFile(index, null); + } + + /** + * Get a file name based on a file index. + * @param index index of the file + * @return file name or null if line information does not exist + * @throws IllegalArgumentException if a negative or invalid file index is given + */ + public String getFileByIndex(int index) { + if (index < 0) { + throw new IllegalArgumentException("Negative file index was given."); + } + if (this.line == null) { + return null; + } + + return this.line.getFile(index, null); + } + + /** + * Checks validity of a file index number. + * + * @param index file number, 1..N + * @return boolean true if index is a valid file number, false otherwise + */ + public boolean isValidFileIndex(int index) { + return line.isValidFileIndex(index); + } + + /** + * Get the producer of the compile unit + * @return the producer of the compile unit + */ + public String getProducer() { + return diea.getString(DW_AT_producer, null); + } + + /** + * Get the compile directory of the compile unit + * @return the compile directory of the compile unit + */ + public String getCompileDirectory() { + return diea.getString(DW_AT_comp_dir, null); + } + + /** + * Get the source language of the compile unit. + *

+ * See {@link DWARFSourceLanguage} for values. + * + * @return the source language of the compile unit, or -1 if not set + */ + public int getLanguage() { + return (int) diea.getUnsignedLong(DW_AT_language, -1); + } + + public boolean hasDWO() { + return diea.hasAttribute(DW_AT_GNU_dwo_id) && diea.hasAttribute(DW_AT_GNU_dwo_name); + } + + public long getAddrTableBase() { + return diea.getUnsignedLong(DW_AT_addr_base, 0); + } + + public long getRangeListsBase() { + return diea.getUnsignedLong(DW_AT_rnglists_base, 0); + } + + public long getLocListsBase() { + return diea.getUnsignedLong(DW_AT_loclists_base, 0); + } + + public long getStrOffsetsBase() { + return diea.getUnsignedLong(DW_AT_str_offsets_base, 0); + } + + public DWARFRange getPCRange() { + return diea.getPCRange(); + } + + @Override + public String toString() { + return "DWARFCompilationUnit @%x, ver %d, pointersize: %d\n".formatted(startOffset, + dwarfVersion, pointerSize) + diea.toString().indent(4); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataInstanceHelper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataInstanceHelper.java similarity index 99% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataInstanceHelper.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataInstanceHelper.java index 2b03830c7f..bcfb880ad5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataInstanceHelper.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataInstanceHelper.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; import ghidra.program.model.address.Address; import ghidra.program.model.data.*; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeConflictHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeConflictHandler.java similarity index 99% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeConflictHandler.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeConflictHandler.java index 2429b8be40..1b2b171960 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeConflictHandler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeConflictHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; import static ghidra.program.model.data.DataTypeConflictHandler.ConflictResult.*; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeImporter.java similarity index 97% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeImporter.java index a3a5a096fc..ab62a9af24 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeImporter.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.*; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.*; +import static ghidra.app.util.bin.format.dwarf.DWARFTag.*; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; import java.io.IOException; import java.util.*; @@ -25,11 +25,8 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import ghidra.app.util.DataTypeNamingUtil; -import ghidra.app.util.bin.format.dwarf4.*; -import ghidra.app.util.bin.format.dwarf4.attribs.DWARFNumericAttribute; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFEndianity; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFSourceLanguage; -import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException; +import ghidra.app.util.bin.format.dwarf.attribs.DWARFNumericAttribute; +import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException; import ghidra.app.util.bin.format.golang.rtti.types.GoKind; import ghidra.program.database.DatabaseObject; import ghidra.program.database.data.DataTypeUtilities; @@ -94,7 +91,7 @@ public class DWARFDataTypeImporter { this.dwarfDTM = dwarfDTM; this.importOptions = prog.getImportOptions(); this.voidDDT = new DWARFDataType(dwarfDTM.getVoidType(), - DWARFNameInfo.fromDataType(dwarfDTM.getVoidType()), -1); + DWARFName.fromDataType(dwarfDTM.getVoidType()), -1); } public DWARFDataType getDDTByInstance(DataType dtInstance) { @@ -269,14 +266,14 @@ public class DWARFDataTypeImporter { private DWARFDataType makeDataTypeForFunctionDefinition(DIEAggregate diea) throws IOException, DWARFExpressionException { - DWARFNameInfo dni = prog.getName(diea); + DWARFName dni = prog.getName(diea); // push an empty funcdef data type into the lookup cache to prevent recursive loops FunctionDefinitionDataType funcDef = new FunctionDefinitionDataType(dni.getParentCP(), dni.getName(), dataTypeManager); DataType dtToAdd = funcDef; - int cuLang = diea.getCompilationUnit().getCompileUnit().getLanguage(); + int cuLang = diea.getCompilationUnit().getLanguage(); if (cuLang == DWARFSourceLanguage.DW_LANG_Go) { if (diea.hasAttribute(DW_AT_byte_size)) { // if the funcdef has a bytesize attribute, we should convert this data type to a ptr @@ -358,7 +355,7 @@ public class DWARFDataTypeImporter { return makeNamedBaseType(prog.getName(diea), diea); } - private DWARFDataType makeNamedBaseType(DWARFNameInfo dni, DIEAggregate diea) + private DWARFDataType makeNamedBaseType(DWARFName dni, DIEAggregate diea) throws IOException, DWARFExpressionException { int dwarfSize = diea.parseInt(DW_AT_byte_size, 0); int dwarfEncoding = (int) diea.getUnsignedLong(DW_AT_encoding, -1); @@ -434,7 +431,7 @@ public class DWARFDataTypeImporter { */ private DWARFDataType makeDataTypeForEnum(DIEAggregate diea) { - DWARFNameInfo dni = prog.getName(diea); + DWARFName dni = prog.getName(diea); int enumSize = (int) diea.getUnsignedLong(DW_AT_byte_size, -1); // in addition to byte_size, enums can have encoding (signed/unsigned) and a basetype, which // itself might have a signed/unsigned encoding. @@ -568,7 +565,7 @@ public class DWARFDataTypeImporter { */ private DWARFDataType makeDataTypeForStruct(DIEAggregate diea) { - DWARFNameInfo dni = prog.getName(diea); + DWARFName dni = prog.getName(diea); long structSize = diea.getUnsignedLong(DW_AT_byte_size, 0); long origStructSize = structSize; @@ -841,7 +838,7 @@ public class DWARFDataTypeImporter { } private void populateStubStruct_worker(DWARFDataType ddt, StructureDataType structure, - DIEAggregate diea, int childTagType) throws IOException, DWARFExpressionException { + DIEAggregate diea, DWARFTag childTagType) throws IOException, DWARFExpressionException { for (DebugInfoEntry childEntry : diea.getChildren(childTagType)) { @@ -1206,7 +1203,7 @@ public class DWARFDataTypeImporter { private DWARFDataType makeDataTypeForPtrToMemberType(DIEAggregate diea) throws IOException, DWARFExpressionException { - DWARFNameInfo dni = prog.getName(diea); + DWARFName dni = prog.getName(diea); DIEAggregate type = diea.getTypeRef(); DIEAggregate containingType = diea.getContainingTypeRef(); @@ -1219,7 +1216,11 @@ public class DWARFDataTypeImporter { DataType offsetType = dwarfDTM.getOffsetType(byteSize); // create a typedef to the offsetType and put containing type and var type info in the typedef name. - String x = "offset_in_" + containingType.getName() + "_to_" + type.getName(); + String targetName = type.getName(); + if (targetName == null) { + targetName = type.getTag().getContainerTypeName(); + } + String x = "offset_in_" + containingType.getName() + "_to_" + targetName; DataType dt = new TypedefDataType(dni.getParentCP(), x, offsetType, dataTypeManager); if (!dni.isAnon()) { @@ -1246,7 +1247,7 @@ public class DWARFDataTypeImporter { private DWARFDataType makeDataTypeForTypedef(DIEAggregate diea) throws IOException, DWARFExpressionException { - DWARFNameInfo typedefDNI = prog.getName(diea); + DWARFName typedefDNI = prog.getName(diea); DIEAggregate refdDIEA = diea.getTypeRef(); if (refdDIEA != null && refdDIEA.getTag() == DW_TAG_base_type) { @@ -1305,7 +1306,7 @@ public class DWARFDataTypeImporter { * */ private DWARFDataType makeDataTypeForUnspecifiedType(DIEAggregate diea) { - DWARFNameInfo dni = prog.getName(diea); + DWARFName dni = prog.getName(diea); DataType dt = dwarfDTM.getBaseType(dni.getOriginalName()); if (dt == null) { return voidDDT; @@ -1315,17 +1316,17 @@ public class DWARFDataTypeImporter { static class DWARFDataType { DataType dataType; - DWARFNameInfo dni; + DWARFName dni; DWARFSourceInfo dsi; Set offsets = new HashSet<>(); - DWARFDataType(DataType dataType, DWARFNameInfo dni, long offset) { + DWARFDataType(DataType dataType, DWARFName dni, long offset) { this.dataType = dataType; this.dni = dni; this.offsets.add(offset); } - DWARFDataType(DataType dataType, DWARFNameInfo dni, Set offsets) { + DWARFDataType(DataType dataType, DWARFName dni, Set offsets) { this.dataType = dataType; this.dni = dni; this.offsets.addAll(offsets); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeManager.java similarity index 92% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeManager.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeManager.java index 65fc1c63af..97e9dd9a0f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeManager.java @@ -13,19 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.*; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; import java.io.IOException; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import ghidra.app.util.bin.format.dwarf4.*; -import ghidra.app.util.bin.format.dwarf4.encoding.*; -import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException; -import ghidra.app.util.bin.format.dwarf4.next.DWARFDataTypeImporter.DWARFDataType; +import ghidra.app.util.bin.format.dwarf.DWARFDataTypeImporter.DWARFDataType; +import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException; import ghidra.program.model.data.*; import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.listing.Program; @@ -41,12 +39,6 @@ import utility.function.Dummy; */ public class DWARFDataTypeManager { - private static final Set TYPE_TAGS = Set.of(DW_TAG_base_type, DW_TAG_array_type, - DW_TAG_typedef, DW_TAG_class_type, DW_TAG_interface_type, DW_TAG_structure_type, - DW_TAG_union_type, DW_TAG_enumeration_type, DW_TAG_pointer_type, DW_TAG_reference_type, - DW_TAG_rvalue_reference_type, DW_TAG_const_type, DW_TAG_volatile_type, - DW_TAG_ptr_to_member_type, DW_TAG_unspecified_type, DW_TAG_subroutine_type); - private final DataTypeManager dataTypeManager; private final DataTypeManager builtInDTM; private final DWARFProgram prog; @@ -185,8 +177,8 @@ public class DWARFDataTypeManager { } private boolean isGoodDWARFSourceInfo(DWARFSourceInfo dsi) { - return dsi.getFilename() != null && !dsi.getFilename().isEmpty() && - !dsi.getFilename().contains("built-in"); + return dsi.filename() != null && !dsi.filename().isEmpty() && + !dsi.filename().contains("built-in"); } private void cacheOffsetToDataTypeMapping(long dieOffset, DataType dt) { @@ -558,7 +550,7 @@ public class DWARFDataTypeManager { monitor.increment(); try { - if (TYPE_TAGS.contains(diea.getTag())) { + if (diea.getTag().isType()) { doGetDataType(diea); } } @@ -586,24 +578,22 @@ public class DWARFDataTypeManager { } private DIEAggregate getFuncDIEA(DIEAggregate diea) { - switch (diea.getTag()) { - case DWARFTag.DW_TAG_gnu_call_site: - case DWARFTag.DW_TAG_call_site: - case DWARFTag.DW_TAG_inlined_subroutine: - // these DIEs head elements have a different tag than the rest of the elements - // in this aggregate, which causes a problem handling this DIEA. Create - // a new instance skipping the head element. No information is typically - // lost. - diea = DIEAggregate.createSkipHead(diea); - // fall-thru: - case DWARFTag.DW_TAG_subprogram: - //case DWARFTag.DW_TAG_subroutine_type: - // Both of these tag types can be converted to Ghidra func definition data types, - // but dw_tag_subroutine_type was already handled in importAllDataTypes(), - // so it is being skipped here. - return diea; - } - return null; + return switch (diea.getTag()) { + // these DIEs head elements have a different tag than the rest of the elements + // in this aggregate, which causes a problem handling this DIEA. Create + // a new instance skipping the head element. No information is typically + // lost. + case DW_TAG_gnu_call_site, DW_TAG_call_site, DW_TAG_inlined_subroutine -> DIEAggregate + .createSkipHead(diea); + + case DW_TAG_subprogram -> diea; + + // dw_tag_subroutine_type was already handled in importAllDataTypes(), + // so it is being skipped here. + //case DW_TAG_subroutine_type -> null + + default -> null; + }; } /** @@ -617,7 +607,7 @@ public class DWARFDataTypeManager { public FunctionDefinition getFunctionSignature(DIEAggregate diea) { diea = getFuncDIEA(diea); if (diea != null) { - DWARFNameInfo dni = prog.getName(diea); + DWARFName dni = prog.getName(diea); return createFunctionDefinitionDataType(diea, dni); } return null; @@ -635,7 +625,7 @@ public class DWARFDataTypeManager { * @return new {@link FunctionDefinitionDataType}. */ private FunctionDefinitionDataType createFunctionDefinitionDataType(DIEAggregate diea, - DWARFNameInfo dni) { + DWARFName dni) { DataType returnDataType = getDataTypeForVariable(diea.getTypeRef()); boolean foundThisParam = false; List params = new ArrayList<>(); @@ -661,7 +651,7 @@ public class DWARFDataTypeManager { FunctionDefinitionDataType funcDef = new FunctionDefinitionDataType(dni.getParentCP(), dni.getName(), dataTypeManager); funcDef.setReturnType(returnDataType); - funcDef.setNoReturn(diea.getBool(DWARFAttribute.DW_AT_noreturn, false)); + funcDef.setNoReturn(diea.getBool(DW_AT_noreturn, false)); funcDef.setArguments(params.toArray(new ParameterDefinition[params.size()])); if (!diea.getHeadFragment().getChildren(DWARFTag.DW_TAG_unspecified_parameters).isEmpty()) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFEncoding.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFEncoding.java similarity index 94% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFEncoding.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFEncoding.java index da63554024..7be50ae0ab 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFEncoding.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFEncoding.java @@ -13,9 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.encoding; - -import ghidra.app.util.bin.format.dwarf4.DWARFUtil; +package ghidra.app.util.bin.format.dwarf; /** * DWARF attribute encoding consts from www.dwarfstd.org/doc/DWARF4.pdf diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFEndianity.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFEndianity.java similarity index 93% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFEndianity.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFEndianity.java index 0a00fcc6b7..8f1f3500a9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFEndianity.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFEndianity.java @@ -13,9 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.encoding; - -import ghidra.app.util.bin.format.dwarf4.DWARFUtil; +package ghidra.app.util.bin.format.dwarf; /** * DWARF Endianity consts from www.dwarfstd.org/doc/DWARF4.pdf diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFException.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFException.java similarity index 95% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFException.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFException.java index c31d399ff4..ec395b02ef 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFException.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4; +package ghidra.app.util.bin.format.dwarf; import java.io.IOException; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFFunction.java similarity index 82% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFFunction.java index c4769be4ca..646fd0ce89 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFFunction.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.*; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.*; +import static ghidra.app.util.bin.format.dwarf.DWARFTag.*; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; import java.io.IOException; import java.util.*; @@ -24,13 +24,10 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; import ghidra.app.cmd.label.SetLabelPrimaryCmd; -import ghidra.app.util.bin.format.dwarf4.*; -import ghidra.app.util.bin.format.dwarf4.attribs.DWARFNumericAttribute; -import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException; -import ghidra.app.util.bin.format.dwarf4.funcfixup.DWARFFunctionFixup; +import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException; +import ghidra.app.util.bin.format.dwarf.funcfixup.DWARFFunctionFixup; import ghidra.program.database.function.OverlappingFunctionException; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSet; +import ghidra.program.model.address.*; import ghidra.program.model.data.*; import ghidra.program.model.listing.*; import ghidra.program.model.listing.Function.FunctionUpdateType; @@ -45,10 +42,10 @@ public class DWARFFunction { public enum CommitMode { SKIP, FORMAL, STORAGE, } public DIEAggregate diea; - public DWARFNameInfo name; + public DWARFName name; public Namespace namespace; + private DWARFRangeList dwarfBody; public Address address; - public Address highAddress; public long frameBase; // TODO: change this to preserve the func's frameBase expr instead of value public Function function; // ghidra function @@ -80,34 +77,30 @@ public class DWARFFunction { if (diea.isDanglingDeclaration()) { return null; } - Address funcAddr = getFuncEntry(diea); - if (funcAddr == null) { + DWARFRangeList bodyRanges = getFuncBodyRanges(diea); + if (bodyRanges == null || bodyRanges.isEmpty()) { return null; } DWARFProgram prog = diea.getProgram(); DWARFDataTypeManager dwarfDTM = prog.getDwarfDTM(); - DWARFFunction dfunc = new DWARFFunction(diea, prog.getName(diea), funcAddr); + DWARFFunction dfunc = new DWARFFunction(diea, prog.getName(diea), bodyRanges); dfunc.namespace = dfunc.name.getParentNamespace(prog.getGhidraProgram()); dfunc.sourceInfo = DWARFSourceInfo.create(diea); - dfunc.highAddress = - diea.hasAttribute(DW_AT_high_pc) ? prog.getCodeAddress(diea.getHighPC()) : null; - // Check if the function is an external function dfunc.isExternal = diea.getBool(DW_AT_external, false); dfunc.noReturn = diea.getBool(DW_AT_noreturn, false); // Retrieve the frame base if it exists - DWARFLocation frameLoc = null; - if (diea.hasAttribute(DW_AT_frame_base)) { - List frameBase = diea.getAsLocation(DW_AT_frame_base, dfunc.getRange()); + DWARFLocationList frameBaseLocs = diea.getLocationList(DW_AT_frame_base); + if (!frameBaseLocs.isEmpty()) { + DWARFLocation frameLoc = frameBaseLocs.getLocationContaining(dfunc.getEntryPc()); // get the framebase register, find where the frame is finally setup. - frameLoc = DWARFLocation.getTopLocation(frameBase, dfunc.address.getOffset()); if (frameLoc != null) { - dfunc.frameBase = (int) diea.evaluateLocation(frameLoc); + dfunc.frameBase = frameLoc.evaluate(diea.getCompilationUnit()).pop(); } } @@ -124,25 +117,47 @@ public class DWARFFunction { return dfunc; } - private DWARFFunction(DIEAggregate diea, DWARFNameInfo dni, Address address) { + private DWARFFunction(DIEAggregate diea, DWARFName dni, DWARFRangeList dwarfBody) { this.diea = diea; this.name = dni; - this.address = address; + this.dwarfBody = dwarfBody; + this.address = diea.getProgram().getCodeAddress(dwarfBody.getFirstAddress()); } public DWARFProgram getProgram() { return diea.getProgram(); } - public DWARFRange getRange() { - return new DWARFRange(address.getOffset(), - highAddress != null ? highAddress.getOffset() : address.getOffset() + 1); + public String getDescriptiveName() { + return "%s@%s".formatted(name.getName(), address); + } + + public DWARFRangeList getRangeList() { + return dwarfBody; } public String getCallingConventionName() { return callingConventionName; } + public AddressSetView getBody() { + DWARFProgram dprog = getProgram(); + AddressSet result = new AddressSet(); + for (DWARFRange drange : dwarfBody.ranges()) { + if (drange.isEmpty()) { + continue; + } + Address start = dprog.getCodeAddress(drange.getFrom()); + Address end = dprog.getCodeAddress(drange.getTo() - 1); + result.add(new AddressRangeImpl(start, end)); + } + return result; + } + + public long getEntryPc() { + return dwarfBody.getFirstAddress(); + } + /** * Returns the DWARFVariable that starts at the specified stack offset. * @@ -264,18 +279,16 @@ public class DWARFFunction { try { varStorage = dvar.getVariableStorage(); if (hasConflictWithParamStorage(dvar)) { - appendComment(function.getEntryPoint(), CodeUnit.PLATE_COMMENT, + getProgram().logWarningAt(function.getEntryPoint(), function.getName(), "Local variable %s[%s] conflicts with parameter, skipped." - .formatted(dvar.getDeclInfoString(), varStorage), - "\n"); + .formatted(dvar.getDeclInfoString(), varStorage)); return; } if (hasConflictWithExistingLocalVariableStorage(dvar)) { - appendComment(function.getEntryPoint().add(dvar.lexicalOffset), - CodeUnit.EOL_COMMENT, "Local omitted variable %s[%s] scope starts here" - .formatted(dvar.getDeclInfoString(), varStorage), - "; "); + getProgram().logWarningAt(function.getEntryPoint().add(dvar.lexicalOffset), + function.getName(), "Local omitted variable %s[%s] scope starts here" + .formatted(dvar.getDeclInfoString(), varStorage)); return; } @@ -301,55 +314,41 @@ public class DWARFFunction { function.addLocalVariable(var, SourceType.IMPORTED); } catch (InvalidInputException | DuplicateNameException e) { - appendComment(function.getEntryPoint().add(dvar.lexicalOffset), CodeUnit.EOL_COMMENT, + getProgram() + .logWarningAt(function.getEntryPoint().add(dvar.lexicalOffset), + function.getName(), "Local omitted variable %s[%s] scope starts here".formatted( dvar.getDeclInfoString(), - varStorage != null ? varStorage.toString() : "UNKNOWN"), - "; "); - + varStorage != null ? varStorage.toString() : "UNKNOWN")); } } - private static boolean isBadSubprogramDef(DIEAggregate diea) { - if (diea.isDanglingDeclaration() || !diea.hasAttribute(DW_AT_low_pc)) { - return true; - } - - // fetch the low_pc attribute directly instead of calling diea.getLowPc() to avoid - // any fixups applied by lower level code - DWARFNumericAttribute attr = diea.getAttribute(DW_AT_low_pc, DWARFNumericAttribute.class); - if (attr != null && attr.getUnsignedValue() == 0) { - return true; - } - - return false; - } - - private void appendComment(Address address, int commentType, String comment, String sep) { - DWARFUtil.appendComment(getProgram().getGhidraProgram(), address, commentType, "", comment, - sep); - } - //--------------------------------------------------------------------------------------------- - private static Address getFuncEntry(DIEAggregate diea) throws IOException { - // TODO: dw_at_entry_pc is also sometimes available, typically in things like inlined_subroutines + public static AddressRange getFuncBody(DIEAggregate diea, boolean flattenDisjoint) + throws IOException { + // TODO: dw_at_entry_pc is also sometimes available, typically in things like inlined_subroutines DWARFProgram dprog = diea.getProgram(); - DWARFNumericAttribute lowpcAttr = - diea.getAttribute(DW_AT_low_pc, DWARFNumericAttribute.class); - if (lowpcAttr != null && lowpcAttr.getUnsignedValue() != 0) { - return dprog.getCodeAddress( - lowpcAttr.getUnsignedValue() + dprog.getProgramBaseAddressFixup()); - } - if (diea.hasAttribute(DW_AT_ranges)) { - List bodyRanges = diea.readRange(DW_AT_ranges); - if (!bodyRanges.isEmpty()) { - return dprog.getCodeAddress(bodyRanges.get(0).getFrom()); - } + DWARFRangeList bodyRangeList = getFuncBodyRanges(diea); + if (bodyRangeList != null && !bodyRangeList.isEmpty()) { + DWARFRange bodyRange = + flattenDisjoint ? bodyRangeList.getFlattenedRange() : bodyRangeList.getFirst(); + return dprog.getAddressRange(bodyRange, true); } return null; } + public static DWARFRangeList getFuncBodyRanges(DIEAggregate diea) throws IOException { + DWARFRange body = diea.getPCRange(); + if (body != null && !body.isEmpty()) { + return new DWARFRangeList(body); + } + if (diea.hasAttribute(DW_AT_ranges)) { + return diea.getRangeList(DW_AT_ranges); + } + return DWARFRangeList.EMTPY; + } + public boolean syncWithExistingGhidraFunction(boolean createIfMissing) { try { Program currentProgram = getProgram().getGhidraProgram(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFFunctionImporter.java similarity index 77% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFFunctionImporter.java index a4fbf5f113..88dcc044d7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFFunctionImporter.java @@ -13,21 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; - -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.*; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.*; +package ghidra.app.util.bin.format.dwarf; import java.io.IOException; import java.util.*; -import ghidra.app.cmd.label.SetLabelPrimaryCmd; -import ghidra.app.util.bin.format.dwarf4.*; -import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException; -import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction.CommitMode; -import ghidra.program.database.function.OverlappingFunctionException; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSet; +import ghidra.app.util.bin.format.dwarf.DWARFFunction.CommitMode; +import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException; +import ghidra.program.model.address.*; import ghidra.program.model.data.*; import ghidra.program.model.data.DataUtilities.ClearDataMode; import ghidra.program.model.listing.*; @@ -126,6 +119,9 @@ public class DWARFFunctionImporter { case DW_TAG_label: processLabel(diea); break; + default: + // do nothing + break; } } catch (OutOfMemoryError oom) { @@ -206,10 +202,8 @@ public class DWARFFunctionImporter { dfunc.updateFunctionSignature(); } else { - Msg.warn(this, - "Failed to get DWARF function signature information, leaving undefined: %s@%s" - .formatted(dfunc.function.getName(), dfunc.function.getEntryPoint())); - //Msg.debug(this, "DIE info: " + diea.toString()); + prog.logWarningAt(dfunc.function.getEntryPoint(), dfunc.function.getName(), + "Failed to get DWARF function signature information, leaving undefined"); } for (DWARFVariable localVar : dfunc.localVars) { @@ -255,9 +249,8 @@ public class DWARFFunctionImporter { FunctionDefinition origFuncDef) { if (dfunc.sourceInfo != null) { // Move the function into the program tree of the file - moveIntoFragment(dfunc.function.getName(), dfunc.address, - dfunc.highAddress != null ? dfunc.highAddress : dfunc.address.add(1), - dfunc.sourceInfo.getFilename()); + moveIntoFragment(dfunc.function.getName(), dfunc.getBody(), + dfunc.sourceInfo.filename()); if (importOptions.isOutputSourceLocationInfo()) { appendPlateComment(dfunc.address, "", dfunc.sourceInfo.getDescriptionStr()); @@ -323,7 +316,9 @@ public class DWARFFunctionImporter { DIEAggregate partDIEA = DIEAggregate.createSkipHead(diea); processSubprogram(partDIEA); break; - + default: + // do nothing + break; } } } @@ -397,8 +392,9 @@ public class DWARFFunctionImporter { Data varData = DataUtilities.createData(currentProgram, address, dataType, -1, ClearDataMode.CLEAR_ALL_CONFLICT_DATA); if (varData != null && globalVar.sourceInfo != null) { - moveIntoFragment(name, varData.getMinAddress(), varData.getMaxAddress(), - globalVar.sourceInfo.getFilename()); + AddressSet dataRng = + new AddressSet(varData.getMinAddress(), varData.getMaxAddress()); + moveIntoFragment(name, dataRng, globalVar.sourceInfo.filename()); } variablesProcesesed.add(address); importSummary.globalVarsAdded++; @@ -426,33 +422,16 @@ public class DWARFFunctionImporter { return; } - Number lowPC = null; - boolean disjoint = false; + DWARFRangeList blockRanges = + Objects.requireNonNullElse(DWARFFunction.getFuncBodyRanges(diea), DWARFRangeList.EMTPY); + Address blockStart = + !blockRanges.isEmpty() ? prog.getCodeAddress(blockRanges.getFirst().getFrom()) : null; - // TODO: Do we need to setup the correct frame base based on the - // location of this lexical block? - - // Process low and high pc if it exists - if (diea.hasAttribute(DW_AT_low_pc) && diea.hasAttribute(DW_AT_high_pc)) { - lowPC = diea.getLowPC(0); - } - // Otherwise process a range list - else if (diea.hasAttribute(DW_AT_ranges)) { - List ranges = diea.parseDebugRange(DW_AT_ranges); - - // No range found - if (ranges.isEmpty()) { - return; - } - - lowPC = ranges.get(0).getFrom(); - disjoint = ranges.size() > 1; - } - Address blockStart = lowPC != null ? prog.getCodeAddress(lowPC) : null; if (blockStart != null && importOptions.isOutputLexicalBlockComments()) { - DWARFNameInfo dni = prog.getName(diea); + boolean disjoint = blockRanges.getListCount() > 1; + DWARFName dni = prog.getName(diea); appendComment(blockStart, CodeUnit.PRE_COMMENT, - "Begin: " + dni.getName() + (disjoint ? " - Disjoint" : ""), "\n"); + "Begin: %s%s".formatted(dni.getName(), disjoint ? " - Disjoint" : ""), "\n"); } processFuncChildren(diea, dfunc, @@ -465,54 +444,30 @@ public class DWARFFunctionImporter { return; } - Number lowPC = null; - Number highPC = null; - - // Process low and high pc if it exists - if (diea.hasAttribute(DW_AT_low_pc) && diea.hasAttribute(DW_AT_high_pc)) { - lowPC = diea.getLowPC(0); - highPC = diea.getHighPC(); - } - // Otherwise process a range list - else if (diea.hasAttribute(DW_AT_ranges)) { - List ranges = diea.parseDebugRange(DW_AT_ranges); - - // No range found - if (ranges.isEmpty()) { - return; + AddressRange body = DWARFFunction.getFuncBody(diea, true); + if (body != null) { + if (importOptions.isOutputInlineFuncComments()) { + addCommentsForInlineFunc(diea, body); } - - lowPC = ranges.get(0).getFrom(); - highPC = ranges.get(ranges.size() - 1).getTo(); + processFuncChildren(diea, dfunc, body.getMinAddress().subtract(dfunc.address)); } - else { - return; - } - - Address startAddr = prog.getCodeAddress(lowPC); - Address endAddr = prog.getCodeAddress(highPC); - if (importOptions.isOutputInlineFuncComments()) { - addCommentsForInlineFunc(diea, startAddr, endAddr); - } - - processFuncChildren(diea, dfunc, startAddr.subtract(dfunc.address)); } /* * Constructs a function def signature for the function and adds it as a comment, either * EOL or PRE depending on how small the inline func is. */ - private void addCommentsForInlineFunc(DIEAggregate diea, Address blockStart, Address blockEnd) { + private void addCommentsForInlineFunc(DIEAggregate diea, AddressRange range) { FunctionDefinition funcDef = dwarfDTM.getFunctionSignature(diea); if (funcDef != null) { - long inlineFuncLen = blockEnd.subtract(blockStart); + long inlineFuncLen = range.getLength(); boolean isShort = inlineFuncLen < INLINE_FUNC_SHORT_LEN; if (isShort) { - appendComment(blockStart, CodeUnit.EOL_COMMENT, + appendComment(range.getMinAddress(), CodeUnit.EOL_COMMENT, "inline " + funcDef.getPrototypeString(), "; "); } else { - appendComment(blockStart, CodeUnit.PRE_COMMENT, + appendComment(range.getMinAddress(), CodeUnit.PRE_COMMENT, "Begin: inline " + funcDef.getPrototypeString(), "\n"); } } @@ -550,7 +505,7 @@ public class DWARFFunctionImporter { * @param end end address of the fragment * @param fileID offset of the file name in the debug_line section */ - private void moveIntoFragment(String name, Address start, Address end, String fileName) { + private void moveIntoFragment(String name, AddressSetView range, String fileName) { if (fileName != null) { ProgramModule module = null; int index = rootModule.getIndex(fileName); @@ -559,8 +514,8 @@ public class DWARFFunctionImporter { module = rootModule.createModule(fileName); } catch (DuplicateNameException e) { - Msg.error(this, - "Error while moving fragment " + name + " from " + start + " to " + end, e); + Msg.error(this, "Error while moving fragment %s (%s)".formatted(name, range), + e); return; } } @@ -579,10 +534,11 @@ public class DWARFFunctionImporter { Group[] children = module.getChildren();//TODO add a getChildAt(index) method... frag = (ProgramFragment) children[index]; } - frag.move(start, end); + frag.move(range.getMinAddress(), range.getMaxAddress()); } catch (NotFoundException e) { - Msg.error(this, "Error moving fragment from " + start + " to " + end, e); + Msg.error(this, "Error while moving fragment %s (%s)".formatted(name, range), + e); return; } catch (DuplicateNameException e) { @@ -592,61 +548,15 @@ public class DWARFFunctionImporter { } } - private Function createFunction(DWARFFunction dfunc, DIEAggregate diea) { - try { - // create a new symbol if one does not exist (symbol table will figure this out) - SymbolTable symbolTable = currentProgram.getSymbolTable(); - symbolTable.createLabel(dfunc.address, dfunc.name.getName(), dfunc.namespace, - SourceType.IMPORTED); - - // force new label to become primary (if already a function it will become function name) - SetLabelPrimaryCmd cmd = - new SetLabelPrimaryCmd(dfunc.address, dfunc.name.getName(), dfunc.namespace); - cmd.applyTo(currentProgram); - - setExternalEntryPoint(dfunc.isExternal, dfunc.address); - - Function function = currentProgram.getListing().getFunctionAt(dfunc.address); - if (function == null) { - - // TODO: If not contained within program memory should they be considered external? - - if (!currentProgram.getMemory() - .getLoadedAndInitializedAddressSet() - .contains(dfunc.address)) { - Msg.warn(this, - String.format( - "DWARF: unable to create function not contained within loaded memory: %s@%s", - dfunc.name, dfunc.address)); - return null; - } - - // create 1-byte function if one does not exist - primary label will become function names - function = currentProgram.getFunctionManager() - .createFunction(null, dfunc.address, new AddressSet(dfunc.address), - SourceType.IMPORTED); - } - - return function; - } - catch (OverlappingFunctionException e) { - throw new AssertException(e); - } - catch (InvalidInputException e) { - Msg.error(this, "Failed to create function " + dfunc.namespace + "/" + - dfunc.name.getName() + ": " + e.getMessage()); - } - return null; - } - private void processLabel(DIEAggregate diea) { if (!shouldProcess(diea)) { return; } String name = prog.getEntryName(diea); - if (name != null && diea.hasAttribute(DW_AT_low_pc)) { - Address address = prog.getCodeAddress(diea.getLowPC(0)); + DWARFRange labelPc; + if (name != null && (labelPc = diea.getPCRange()) != null) { + Address address = prog.getCodeAddress(labelPc.getFrom()); if (address.getOffset() != 0) { try { SymbolTable symbolTable = currentProgram.getSymbolTable(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFIdentifierCase.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFIdentifierCase.java similarity index 97% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFIdentifierCase.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFIdentifierCase.java index 7263d91fd1..0b95bca553 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFIdentifierCase.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFIdentifierCase.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.encoding; +package ghidra.app.util.bin.format.dwarf; import java.util.HashMap; import java.util.Map; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFImportOptions.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportOptions.java similarity index 99% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFImportOptions.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportOptions.java index 81b6f75a2d..68918bcbd2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFImportOptions.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportOptions.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; import ghidra.app.plugin.core.analysis.AnalysisOptionsUpdater; import ghidra.app.plugin.core.analysis.DWARFAnalyzer; @@ -97,6 +97,7 @@ public class DWARFImportOptions { private boolean tryPackStructs = true; private boolean specialCaseSizedBaseTypes = true; private boolean importLocalVariables = true; + private boolean useBookmarks = true; /** * Create new instance @@ -355,6 +356,10 @@ public class DWARFImportOptions { this.importLocalVariables = importLocalVariables; } + public boolean isUseBookmarks() { + return useBookmarks; + } + /** * See {@link Analyzer#registerOptions(Options, ghidra.program.model.listing.Program)} * diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFImportSummary.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportSummary.java similarity index 65% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFImportSummary.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportSummary.java index b6fba8270e..f36816b2ae 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFImportSummary.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportSummary.java @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; -import java.util.HashSet; -import java.util.Set; +import java.util.*; import ghidra.util.Msg; @@ -41,13 +40,23 @@ public class DWARFImportSummary { int varDWARFExpressionValue; int exprReadError; Set typeRemappings = new HashSet<>(); + int paramZeroLenDataType; + Set dwarfVers = new HashSet<>(); + int compUnitCount; + int dieCount; + Set compDirs = new HashSet<>(); + List compNames = new ArrayList<>(); + Set producers = new HashSet<>(); + Set sourceLangs = new HashSet<>(); /** * Writes summary information to the {@link Msg} log. */ public void logSummaryResults() { if (totalElapsedMS > 0) { - Msg.info(this, String.format("DWARF import - total elapsed: %dms", totalElapsedMS)); + Msg.info(this, "DWARF ver%s, %d compUnits, %d DIES, import - total elapsed: %dms" + .formatted(getSortedSet(dwarfVers).toString(), compUnitCount, dieCount, + totalElapsedMS)); } if (dataTypeElapsedMS > 0) { Msg.info(this, @@ -67,6 +76,18 @@ public class DWARFImportSummary { Msg.info(this, String.format("DWARF function signatures added: %d", funcSignaturesAdded)); } + if (!compDirs.isEmpty()) { + Msg.info(this, "DWARF compile dirs: " + getSortedSet(compDirs).toString()); + } + if (!producers.isEmpty()) { + Msg.info(this, "DWARF producers: " + getSortedSet(producers)); + } + if (!sourceLangs.isEmpty()) { + Msg.info(this, "DWARF source languages: " + getSortedSet(sourceLangs)); + } + for (String s : compNames) { + Msg.info(this, "DWARF compUnit: " + s); + } if (!typeRemappings.isEmpty()) { Msg.error(this, @@ -108,8 +129,40 @@ public class DWARFImportSummary { "DWARF variable definitions that failed because they are computed pseudo variables: " + varDWARFExpressionValue); } + + if (paramZeroLenDataType > 0) { + Msg.error(this, "DWARF zero-length parameters: " + paramZeroLenDataType); + } if (exprReadError > 0) { Msg.error(this, "DWARF expression failed to read: " + exprReadError); } } + + private > List getSortedSet(Set set) { + List result = new ArrayList<>(set); + Collections.sort(result); + return result; + } + + void addCompunitInfo(List compUnits) { + for (DWARFCompilationUnit cu : compUnits) { + String compileDirectory = cu.getCompileDirectory(); + if (compileDirectory != null && !compileDirectory.isBlank()) { + compDirs.add(compileDirectory); + } + String name = cu.getName(); + if (name != null && !name.isBlank()) { + compNames.add(name); + } + String prod = cu.getProducer(); + if (prod != null && !prod.isBlank()) { + producers.add(prod); + } + int lang = cu.getLanguage(); + if (lang != -1) { + sourceLangs.add(DWARFUtil.toString(DWARFSourceLanguage.class, lang)); + } + } + + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFParser.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImporter.java similarity index 94% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFParser.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImporter.java index 14cf1d2972..236246f274 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFParser.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImporter.java @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; import java.io.IOException; import java.util.Collections; import java.util.List; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; -import ghidra.app.util.bin.format.dwarf4.DWARFException; import ghidra.program.model.data.*; import ghidra.util.Msg; import ghidra.util.Swing; @@ -33,12 +32,12 @@ import utility.function.Dummy; * Performs a DWARF datatype import and a DWARF function import, under the control of the * {@link DWARFImportOptions}. */ -public class DWARFParser { +public class DWARFImporter { private DWARFProgram prog; private DWARFDataTypeManager dwarfDTM; private TaskMonitor monitor; - public DWARFParser(DWARFProgram prog, TaskMonitor monitor) { + public DWARFImporter(DWARFProgram prog, TaskMonitor monitor) { this.prog = prog; this.monitor = monitor; this.dwarfDTM = prog.getDwarfDTM(); @@ -87,9 +86,9 @@ public class DWARFParser { prog.getGhidraProgram().getDataTypeManager().getDataType(dataTypePath); if (dataType != null && !(dataType instanceof Pointer || dataType instanceof Array)) { DWARFSourceInfo dsi = dwarfDTM.getSourceInfo(dataType); - if (dsi != null && dsi.getFilename() != null) { + if (dsi != null && dsi.filename() != null) { CategoryPath dataTypeOrigCP = dataType.getCategoryPath(); - CategoryPath newRoot = new CategoryPath(rootCP, dsi.getFilename()); + CategoryPath newRoot = new CategoryPath(rootCP, dsi.filename()); CategoryPath newCP = rehomeCategoryPathSubTree(unCatRootCp, newRoot, dataTypeOrigCP); if (newCP != null) { @@ -181,7 +180,7 @@ public class DWARFParser { * @throws DWARFException * @throws CancelledException */ - public DWARFImportSummary parse() throws IOException, DWARFException, CancelledException { + public DWARFImportSummary performImport() throws IOException, DWARFException, CancelledException { monitor.setIndeterminate(false); monitor.setShowProgressValue(true); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFIndirectTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFIndirectTable.java new file mode 100644 index 0000000000..c462bd7e0f --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFIndirectTable.java @@ -0,0 +1,106 @@ +/* ### + * 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.util.bin.format.dwarf; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * Handles a grouping of {@link DWARFIndirectTableHeader}s that specify how to look up a + * certain type of item (per CU). + */ +public class DWARFIndirectTable { + public interface CheckedIOFunction { + R apply(T value) throws IOException; + } + + private final BinaryReader reader; + private final Map lookupMap = new HashMap<>(); + private final Function baseOffsetFunc; + + /** + * Creates a {@link DWARFIndirectTable} + * + * @param reader {@link BinaryReader} containing the {@link DWARFIndirectTableHeader}s + * @param baseOffsetFunc a function that will return the baseoffset value for a + * {@link DWARFCompilationUnit}. + */ + public DWARFIndirectTable(BinaryReader reader, + Function baseOffsetFunc) { + this.reader = reader; + this.baseOffsetFunc = baseOffsetFunc; + } + + /** + * Populates this instance will all {@link DWARFIndirectTableHeader} instances that can be + * read from the stream. + * + * @param msg String message to use for the taskmonitor + * @param headerReader a function that reads the specific table header type from the stream + * @param monitor {@link TaskMonitor} + * @throws CancelledException if cancelled + * @throws IOException if error reading a header + */ + public void bootstrap(String msg, + CheckedIOFunction headerReader, + TaskMonitor monitor) throws CancelledException, IOException { + if (reader == null) { + return; + } + reader.setPointerIndex(0); + monitor.initialize(reader.length(), msg); + while (reader.hasNext()) { + monitor.checkCancelled(); + monitor.setProgress(reader.getPointerIndex()); + monitor.setMessage(msg + " #" + lookupMap.size()); + + DWARFIndirectTableHeader header = headerReader.apply(reader); + if (header != null) { + lookupMap.put(header.getFirstElementOffset(), header); + } + } + } + + /** + * Returns the offset of an item, based on its index in a particular header (which is found + * by the controlling CU) + * + * @param index index of the item + * @param cu {@link DWARFCompilationUnit} + * @return long offset of the item. Caller responsible for reading the item themselves + * @throws IOException if error reading table data + */ + public long getOffset(int index, DWARFCompilationUnit cu) throws IOException { + long base = baseOffsetFunc.apply(cu); + DWARFIndirectTableHeader header = lookupMap.get(base); + if (header == null) { + throw new IOException( + "Invalid base %d for compUnit %x".formatted(base, cu.getStartOffset())); + } + return header.getOffset(index, reader); + } + + public void clear() { + lookupMap.clear(); + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFIndirectTableHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFIndirectTableHeader.java new file mode 100644 index 0000000000..8ffaadd42c --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFIndirectTableHeader.java @@ -0,0 +1,52 @@ +/* ### + * 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.util.bin.format.dwarf; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; + +/** + * Common base functionality of indirect table headers (DWARFAddressListHeader, + * DWARFLocationListHeader, etc) + */ +public abstract class DWARFIndirectTableHeader { + + protected final long startOffset; + protected final long endOffset; + protected final long firstElementOffset; + + public DWARFIndirectTableHeader(long startOffset, long endOffset, long firstElementOffset) { + this.startOffset = startOffset; + this.endOffset = endOffset; + this.firstElementOffset = firstElementOffset; + } + + public long getStartOffset() { + return startOffset; + } + + public long getFirstElementOffset() { + return firstElementOffset; + } + + public long getEndOffset() { + return endOffset; + } + + public abstract long getOffset(int index, BinaryReader reader) throws IOException; + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFInline.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFInline.java similarity index 97% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFInline.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFInline.java index c6625d3176..a4c6f22ccf 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFInline.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFInline.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.encoding; +package ghidra.app.util.bin.format.dwarf; import java.util.HashMap; import java.util.Map; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLengthValue.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLengthValue.java new file mode 100644 index 0000000000..acb4f95548 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLengthValue.java @@ -0,0 +1,95 @@ +/* ### + * 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.util.bin.format.dwarf; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; + +/** + * A tuple of length (of a thing in a dwarf stream) and size of integers used in the dwarf section. + * + * @param length the length of the following item + * @param intSize the size of integers used in the following item + */ +public record DWARFLengthValue(long length, int intSize) { + /** + * Read a variable-length length value from the stream. + *

+ * The length value will either occupy 4 (int32) or 12 bytes (int32 flag + int64 length) and + * as a side-effect signals the size integer values occupy. + * + * @param reader {@link BinaryReader} stream to read from + * @param defaultPointerSize size in bytes of pointers in the program + * @return new {@link DWARFLengthValue}, or null if the stream was just zero-padded data + * @throws IOException if io error + */ + public static DWARFLengthValue read(BinaryReader reader, int defaultPointerSize) + throws IOException { + long startOffset = reader.getPointerIndex(); + long length = reader.readNextUnsignedInt(); + int intSize = 4; + + if (length == 0xffff_ffffL /* max uint32 */) { + // Length of 0xffffffff implies 64-bit DWARF format + // Mostly untested as there is no easy way to force the compiler + // to generate this + length = reader.readNextLong(); + intSize = 8; + } + else if (length >= 0xffff_fff0L) { + // Length of 0xfffffff0 or greater is reserved for DWARF + throw new IOException( + "Reserved DWARF length value: %x. Unknown extension.".formatted(length)); + } + else if (length == 0) { + if (isAllZerosUntilEOF(reader)) { + // hack to handle trailing padding at end of section. (similar to the check for + // unexpectedTerminator in readDIEs(), when padding occurs inside the bounds + // of the compile unit's range after the end of the root DIE's children) + reader.setPointerIndex(reader.length()); + return null; + } + + // Test for special case of weird BE MIPS 64bit length value. + // Instead of following DWARF std (a few lines above with length == MAX_INT), + // it writes a raw 64bit long (BE). The upper 32 bits (already read as length) will + // always be 0 since super-large binaries from that system weren't really possible. + // The next 32 bits will be the remainder of the value. + if (reader.isBigEndian() && defaultPointerSize == 8) { + length = reader.readNextUnsignedInt(); + intSize = 8; + } + + if (length == 0) { + throw new IOException("Invalid DWARF length 0 at 0x%x".formatted(startOffset)); + } + } + + return new DWARFLengthValue(length, intSize); + } + + private static boolean isAllZerosUntilEOF(BinaryReader reader) throws IOException { + reader = reader.clone(); + while (reader.hasNext()) { + if (reader.readNextByte() != 0) { + return false; + } + } + return true; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLine.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLine.java new file mode 100644 index 0000000000..7bcaf9c077 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLine.java @@ -0,0 +1,424 @@ +/* ### + * 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.util.bin.format.dwarf; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.io.FilenameUtils; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.dwarf.DWARFLineContentType.Def; +import ghidra.app.util.bin.format.dwarf.attribs.*; +import ghidra.program.model.data.LEB128; + +/** + * Represents source file line number mapping info. + */ +public class DWARFLine { + private long unit_length; + private int version; + private long header_length; + private int minimum_instruction_length; + private int maximum_operations_per_instruction; + private int default_is_stmt; + private int line_base; + private int line_range; + private int opcode_base; + private int[] standard_opcode_length; + private List include_directories = new ArrayList<>(); + private List file_names = new ArrayList<>(); + private int address_size; + private int segment_selector_size; + + public static DWARFLine empty() { + return new DWARFLine(); + } + + /** + * Read a v4 DWARFLine. + * + * @param dprog {@link DWARFProgram} + * @param reader {@link BinaryReader} stream + * @param lengthInfo {@link DWARFLengthValue} + * @param version DWARFLine version (from header) + * @return a new DWARFLine instance if DW_AT_stmt_list and stream are present, otherwise null + * @throws IOException if error reading data + * @throws DWARFException if bad DWARF values + */ + public static DWARFLine readV4(DWARFProgram dprog, BinaryReader reader, + DWARFLengthValue lengthInfo, int version) throws IOException, DWARFException { + + // length : dwarf_length + // version : 2 bytes + // header_len : dwarf_intsize + // min_instr_len : 1 byte + // .... + DWARFLine result = new DWARFLine(); + result.unit_length = lengthInfo.length(); + + result.version = version; + result.header_length = reader.readNextUnsignedValue(lengthInfo.intSize()); + result.minimum_instruction_length = reader.readNextUnsignedByte(); + + if (result.version >= 4) { + // Maximum operations per instruction only exists in DWARF version 4 or higher + result.maximum_operations_per_instruction = reader.readNextUnsignedByte(); + } + else { + result.maximum_operations_per_instruction = 1; + } + result.default_is_stmt = reader.readNextUnsignedByte(); + result.line_base = reader.readNextByte(); + result.line_range = reader.readNextUnsignedByte(); + result.opcode_base = reader.readNextUnsignedByte(); + result.standard_opcode_length = new int[result.opcode_base]; + result.standard_opcode_length[0] = 1; /* Should never be used */ + for (int i = 1; i < result.opcode_base; i++) { + result.standard_opcode_length[i] = reader.readNextUnsignedByte(); + } + + // Read all include directories + String include = reader.readNextAsciiString(); + while (include.length() != 0) { + result.include_directories.add(new DWARFFile(include)); + include = reader.readNextAsciiString(); + } + + // Read all files, ending when null (hit empty filename) + DWARFFile file; + while ((file = DWARFFile.readV4(reader)) != null) { + result.file_names.add(file); + } + + return result; + } + + /** + * Read a v5 DWARFLine. + * + * @param dprog {@link DWARFProgram} + * @param reader {@link BinaryReader} stream + * @param lengthInfo {@link DWARFLengthValue} + * @param version DWARFLine version (from header) + * @param cu {@link DWARFCompilationUnit} + * @return a new DWARFLine instance if DW_AT_stmt_list and stream are present, otherwise null + * @throws IOException if error reading data + * @throws DWARFException if bad DWARF values + */ + public static DWARFLine readV5(DWARFProgram dprog, BinaryReader reader, + DWARFLengthValue lengthInfo, int version, DWARFCompilationUnit cu) + throws IOException, DWARFException { + + // length : dwarf_length + // version : 2 bytes + // address_size : 1 byte + // segment_selector_size : 1 byte + // header_len : dwarf_intsize + // min_instr_len : 1 byte + // ... + DWARFLine result = new DWARFLine(); + result.unit_length = lengthInfo.length(); + result.version = version; + result.address_size = reader.readNextUnsignedByte(); + result.segment_selector_size = reader.readNextUnsignedByte(); + result.header_length = reader.readNextUnsignedValue(lengthInfo.intSize()); + result.minimum_instruction_length = reader.readNextUnsignedByte(); + result.maximum_operations_per_instruction = reader.readNextUnsignedByte(); + result.default_is_stmt = reader.readNextUnsignedByte(); + result.line_base = reader.readNextByte(); + result.line_range = reader.readNextUnsignedByte(); + result.opcode_base = reader.readNextUnsignedByte(); + result.standard_opcode_length = new int[result.opcode_base]; + result.standard_opcode_length[0] = 1; /* Should never be used */ + for (int i = 1; i < result.opcode_base; i++) { + result.standard_opcode_length[i] = reader.readNextUnsignedByte(); + } + int directory_entry_format_count = reader.readNextUnsignedByte(); + List dirFormatDefs = new ArrayList<>(); + for (int i = 0; i < directory_entry_format_count; i++) { + Def lcntDef = DWARFLineContentType.Def.read(reader); + dirFormatDefs.add(lcntDef); + } + + int directories_count = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + for (int i = 0; i < directories_count; i++) { + DWARFFile dir = DWARFFile.readV5(reader, dirFormatDefs, cu); + result.include_directories.add(dir); + } + + int filename_entry_format_count = reader.readNextUnsignedByte(); + List fileFormatDefs = new ArrayList<>(); + for (int i = 0; i < filename_entry_format_count; i++) { + Def lcntDef = DWARFLineContentType.Def.read(reader); + fileFormatDefs.add(lcntDef); + } + + int file_names_count = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + for (int i = 0; i < file_names_count; i++) { + DWARFFile dir = DWARFFile.readV5(reader, fileFormatDefs, cu); + result.file_names.add(dir); + } + + return result; + } + + record DirectoryEntryFormat(int contentTypeCode, int formCode) { + static DirectoryEntryFormat read(BinaryReader reader) throws IOException, IOException { + int contentTypeCode = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + int formCode = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + + return new DirectoryEntryFormat(contentTypeCode, formCode); + } + } + + private DWARFLine() { + // empty, use #read() + } + + /** + * Get a file name with the full path included. + * @param index index of the file + * @param compileDirectory current compile unit directory + * @return file name with full path + */ + public String getFullFile(int index, String compileDirectory) { + if (index == 0) { + //TODO: Handle index = 0 + throw new UnsupportedOperationException( + "Currently does not support retrieving the primary source file."); + } + else if (index > 0) { + // Retrieve the file by index (index starts at 1) + DWARFFile file = this.file_names.get(index - 1); + + File fileObj = new File(file.getName()); + + // Check to see if the file is an absolute path and return if so + if (fileObj.isAbsolute()) { + return file.getName(); + } + + // Otherwise we need to retrieve the directory + int diridx = (int) file.getDirectoryIndex(); + if (diridx == 0) { + // Use the compile directory if a directory index of 0 is given + if (compileDirectory != null) { + return compileDirectory + file.getName(); + } + throw new IllegalArgumentException( + "No compile directory was given when one was expected."); + } + else if (diridx > 0) { + // Retrieve and append the directory + DWARFFile directory = this.include_directories.get(diridx - 1); + return directory.getName() + file.getName(); + } + throw new IndexOutOfBoundsException( + "Negative directory index was found: " + Integer.toString(diridx)); + } + throw new IllegalArgumentException( + "Negative file index was given: " + Integer.toString(index)); + } + + /** + * Get a file name given a file index. + * @param index index of the file + * @param compileDirectory current compile unit directory + * @return file name + */ + public String getFile(int index, String compileDirectory) { + if (version < 5) { + if (index == 0) { + //TODO: Handle index = 0 + throw new UnsupportedOperationException( + "Currently does not support retrieving the primary source file."); + } + else if (index > 0) { + // Retrieve the file by index (index starts at 1) + DWARFFile file = this.file_names.get(index - 1); + return FilenameUtils.getName(file.getName()); + } + throw new IllegalArgumentException( + "Negative file index was given: " + Integer.toString(index)); + } + else if (version >= 5) { + if (index < 0 || file_names.size() <= index) { + throw new IllegalArgumentException("Bad file index: " + index); + } + DWARFFile file = this.file_names.get(index); + return FilenameUtils.getName(file.getName()); + } + return null; + } + + /** + * Returns true if file exists. + * + * @param index file number, excluding 0 + * @return boolean true if file exists + */ + public boolean isValidFileIndex(int index) { + index--; + return 0 <= index && index < file_names.size(); + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append("Line Entry"); + buffer.append(" Include Directories: ["); + for (DWARFFile dir : this.include_directories) { + buffer.append(dir); + buffer.append(", "); + } + buffer.append("] File Names: ["); + for (DWARFFile file : this.file_names) { + buffer.append(file.toString()); + buffer.append(", "); + } + buffer.append("]"); + return buffer.toString(); + } + + /** + * DWARFFile is used to store file information for each entry in the line section header. + */ + public static class DWARFFile { + /** + * Reads a DWARFFile entry. + * + * @param reader BinaryReader + * @return new DWARFFile, or null if end-of-list was found + * @throws IOException if error reading + */ + public static DWARFFile readV4(BinaryReader reader) throws IOException { + String name = reader.readNextAsciiString(); + if (name.length() == 0) { + // empty name == end-of-list of files + return null; + } + + long directory_index = reader.readNext(LEB128::unsigned); + long modification_time = reader.readNext(LEB128::unsigned); + long length = reader.readNext(LEB128::unsigned); + + return new DWARFFile(name, directory_index, modification_time, length, null); + } + + /** + * Reads a DWARFFile entry. + * + * @param reader BinaryReader + * @param defs similar to a DIE's attributespec, a list of DWARFForms that define how values + * will be deserialized from the stream + * @param cu {@link DWARFCompilationUnit} + * @return new DWARFFile + * @throws IOException if error reading + */ + public static DWARFFile readV5(BinaryReader reader, List defs, + DWARFCompilationUnit cu) throws IOException { + + String name = null; + long directoryIndex = -1; + long modTime = 0; + long length = 0; + byte[] md5 = null; + for (DWARFLineContentType.Def def : defs) { + DWARFFormContext context = new DWARFFormContext(reader, cu, def); + DWARFAttributeValue val = def.getAttributeForm().readValue(context); + + switch (def.getAttributeId()) { + case DW_LNCT_path: + name = val instanceof DWARFStringAttribute strval + ? strval.getValue(cu) + : null; + break; + case DW_LNCT_directory_index: + directoryIndex = + val instanceof DWARFNumericAttribute numval ? numval.getValue() : -1; + break; + case DW_LNCT_timestamp: + modTime = + val instanceof DWARFNumericAttribute numval ? numval.getValue() : 0; + break; + case DW_LNCT_size: + length = val instanceof DWARFNumericAttribute numval + ? numval.getUnsignedValue() + : 0; + break; + case DW_LNCT_MD5: + md5 = val instanceof DWARFBlobAttribute blobval ? blobval.getBytes() : null; + break; + default: + // skip any DW_LNCT_??? values that we don't care about + break; + } + } + if (name == null) { + throw new IOException("No name value for DWARFLine file"); + } + return new DWARFFile(name, directoryIndex, modTime, length, md5); + } + + private String name; + private long directory_index; + private long modification_time; + private long length; + private byte[] md5; + + public DWARFFile(String name) { + this(name, -1, 0, 0, null); + } + + /** + * Create a new DWARF file entry with the given parameters. + * @param name name of the file + * @param directory_index index of the directory for this file + * @param modification_time modification time of the file + * @param length length of the file + */ + public DWARFFile(String name, long directory_index, long modification_time, long length, + byte[] md5) { + this.name = name; + this.directory_index = directory_index; + this.modification_time = modification_time; + this.length = length; + this.md5 = md5; + } + + public String getName() { + return this.name; + } + + public long getDirectoryIndex() { + return this.directory_index; + } + + public long getModificationTime() { + return this.modification_time; + } + + @Override + public String toString() { + return "Filename: %s, Length: 0x%x, Time: 0x%x, DirIndex: %d".formatted(name, length, + modification_time, directory_index); + } + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLineContentType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLineContentType.java new file mode 100644 index 0000000000..eb933c61d3 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLineContentType.java @@ -0,0 +1,111 @@ +/* ### + * 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.util.bin.format.dwarf; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.dwarf.attribs.*; + +/** + * Represents an identifier of a value in a DWARFLine/DWARFFile object. + *

+ * Similar to the {@link DWARFAttribute} enum, both are identifiers of an attribute value + * that is serialized by a DWARFForm. + *

+ * Users of this enum should be tolerant of unknown values. + */ +public enum DWARFLineContentType { + DW_LNCT_path(0x1), + DW_LNCT_directory_index(0x2), + DW_LNCT_timestamp(0x3), + DW_LNCT_size(0x4), + DW_LNCT_MD5(0x5), + DW_LNCT_lo_user(0x2000), + DW_LNCT_hi_user(0x3fff), + + DW_LNCT_UNKNOWN(-1); // fake ghidra value + + DWARFLineContentType(int id) { + this.id = id; + } + + private final int id; + + public static DWARFLineContentType of(int id) { + return lookupMap.getOrDefault(id, DW_LNCT_UNKNOWN); + } + + private static Map lookupMap = buildLookup(); + + private static Map buildLookup() { + Map result = new HashMap<>(); + for (DWARFLineContentType e : values()) { + result.put(e.id, e); + } + return result; + } + + /** + * Defines a {@link DWARFLineContentType} attribute value. + */ + public static class Def extends DWARFAttributeDef { + + /** + * Reads a {@link DWARFLineContentType.Def} instance from the {@link BinaryReader reader}. + *

+ * Returns a null if its a end-of-list marker. + *

+ * @param reader {@link BinaryReader} stream + * @return {@link DWARFLineContentType.Def}, or null if stream was at a end-of-list marker + * (which isn't really a thing for line content defs, but is a thing for attribute defs) + * @throws IOException if error reading + */ + public static Def read(BinaryReader reader) throws IOException { + DWARFAttributeDef tmp = + DWARFAttributeDef.read(reader, DWARFLineContentType::of); + if (tmp == null) { + return null; + } + + return new Def(tmp.getAttributeId(), tmp.getRawAttributeId(), tmp.getAttributeForm(), + tmp.getImplicitValue()); + } + + public Def(DWARFLineContentType attributeId, int rawAttributeId, DWARFForm attributeForm, + long implicitValue) { + super(attributeId, rawAttributeId, attributeForm, implicitValue); + } + + public DWARFLineContentType getId() { + return super.getAttributeId(); + } + + @Override + protected String getRawAttributeIdDescription() { + return "DW_LNCT_???? %d (0x%x)".formatted(attributeId, attributeId); + } + + @Override + public Def withForm(DWARFForm newForm) { + return new Def(attributeId, rawAttributeId, newForm, implicitValue); + } + + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLocation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLocation.java new file mode 100644 index 0000000000..fddae01ef5 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLocation.java @@ -0,0 +1,77 @@ +/* ### + * 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.util.bin.format.dwarf; + +import java.util.Arrays; + +import ghidra.app.util.bin.format.dwarf.expression.*; + +/** + * Represents the location of an item that is only valid for a certain range of program-counter + * locations. + *

+ * An instance that does not have a DWARFRange is considered valid for any pc. + */ +public class DWARFLocation { + private DWARFRange addressRange; + private byte[] expr; + + /** + * Create a Location given an address range and location expression. + * + * @param start start address range + * @param end end of address range + * @param expr bytes of a DWARFExpression + */ + public DWARFLocation(long start, long end, byte[] expr) { + this(new DWARFRange(start, end), expr); + } + + public DWARFLocation(DWARFRange addressRange, byte[] expr) { + this.addressRange = addressRange; + this.expr = expr; + } + + public DWARFRange getRange() { + return this.addressRange; + } + + public byte[] getExpr() { + return this.expr; + } + + public boolean isWildcard() { + return addressRange == null; + } + + public long getOffset(long pc) { + return addressRange != null ? addressRange.getFrom() - pc : 0; + } + + public boolean contains(long addr) { + return isWildcard() || addressRange.contains(addr); + } + + public DWARFExpressionResult evaluate(DWARFCompilationUnit cu) throws DWARFExpressionException { + DWARFExpressionEvaluator evaluator = new DWARFExpressionEvaluator(cu); + return evaluator.evaluate(evaluator.readExpr(this.expr)); + } + + @Override + public String toString() { + return "DWARFLocation: range: %s, expr: %s".formatted(addressRange, Arrays.toString(expr)); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLocationList.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLocationList.java new file mode 100644 index 0000000000..c8055d9239 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLocationList.java @@ -0,0 +1,229 @@ +/* ### + * 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.util.bin.format.dwarf; + +import static ghidra.app.util.bin.format.dwarf.DWARFLocationListEntry.*; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFForm.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.dwarf.expression.DWARFExpression; +import ghidra.program.model.data.LEB128; +import ghidra.util.NumericUtilities; + +/** + * A collection of {@link DWARFLocation} elements, each which represents a location of an item + * that is only valid for a certain range of program-counter locations. + */ +public class DWARFLocationList { + public static final DWARFLocationList EMPTY = new DWARFLocationList(List.of()); + + /** + * Creates a simple location list containing a single wildcarded range and the specified + * expression bytes. + * + * @param expr {@link DWARFExpression} bytes + * @return new {@link DWARFLocationList} containing a single wildcarded range + */ + public static DWARFLocationList withWildcardRange(byte[] expr) { + return new DWARFLocationList(List.of(new DWARFLocation(null, expr))); + } + + /** + * Read a v4 {@link DWARFLocationList} from the debug_loc section. + *

+ * @param reader stream positioned at the start of a .debug_loc location list + * @param cu the compUnit that refers to the location list + * @return list of DWARF locations (address range and location expression) + * @throws IOException if an I/O error occurs + */ + public static DWARFLocationList readV4(BinaryReader reader, DWARFCompilationUnit cu) + throws IOException { + List results = new ArrayList<>(); + + byte pointerSize = cu.getPointerSize(); + + DWARFRange cuRange = cu.getPCRange(); + long baseAddrOffset = (cuRange != null) ? cuRange.getFrom() : 0; + long baseFixup = cu.getProgram().getProgramBaseAddressFixup(); + long eolVal = pointerSize == 4 ? NumericUtilities.MAX_UNSIGNED_INT32_AS_LONG : -1; + + // Loop through the debug_loc entry + while (reader.hasNext()) { + long beginning = reader.readNextUnsignedValue(pointerSize); + long ending = reader.readNextUnsignedValue(pointerSize); + + if (beginning == 0 && ending == 0) { + // List end + break; + } + else if (beginning == ending) { + // don't add empty range + continue; + } + + // Check to see if this is a base address entry + if (beginning == eolVal) { + baseAddrOffset = ending + baseFixup; + } + else { + beginning += baseAddrOffset; + ending += baseAddrOffset; + + // byte array size is 2 bytes + int size = reader.readNextUnsignedShort(); + + // Read the exprloc bytes + byte[] expr = reader.readNextByteArray(size); + + // TODO: verify end addr calc with DWARFstd.pdf, inclusive vs exclusive + results.add(new DWARFLocation(new DWARFRange(beginning, ending), expr)); + } + } + return new DWARFLocationList(results); + } + + /** + * Reads a v5 {@link DWARFLocationList} from the debug_loclists stream. + * + * @param reader stream positioned at the start of a .debug_loclists location list + * @param cu the compUnit that refers to the location list + * @return list of DWARF locations (address range and location expression) + * @throws IOException if an I/O error occurs + */ + public static DWARFLocationList readV5(BinaryReader reader, DWARFCompilationUnit cu) + throws IOException { + long baseAddrFixup = cu.getProgram().getProgramBaseAddressFixup(); + long baseAddr = baseAddrFixup; + DWARFProgram dprog = cu.getProgram(); + + List list = new ArrayList<>(); + while (reader.hasNext()) { + int lleId = reader.readNextUnsignedByte(); + if (lleId == DW_LLE_end_of_list) { + break; + } + switch (lleId) { + case DW_LLE_base_addressx: { + int addrIndex = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + baseAddr = dprog.getAddress(DW_FORM_addrx, addrIndex, cu); + break; + } + case DW_LLE_startx_endx: { + int startAddrIndex = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + int endAddrIndex = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + byte[] expr = reader.readNext(DWARFLocationList::uleb128SizedByteArray); + long start = dprog.getAddress(DW_FORM_addrx, startAddrIndex, cu); + long end = dprog.getAddress(DW_FORM_addrx, endAddrIndex, cu); + list.add(new DWARFLocation(start, end, expr)); + break; + } + case DW_LLE_startx_length: { + int startAddrIndex = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + int len = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + byte[] expr = reader.readNext(DWARFLocationList::uleb128SizedByteArray); + long start = dprog.getAddress(DW_FORM_addrx, startAddrIndex, cu); + list.add(new DWARFLocation(start, start + len, expr)); + break; + } + case DW_LLE_offset_pair: { + int startOfs = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + int endOfs = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + byte[] expr = reader.readNext(DWARFLocationList::uleb128SizedByteArray); + list.add(new DWARFLocation(baseAddr + startOfs, baseAddr + endOfs, expr)); + break; + } + case DW_LLE_base_address: { + baseAddr = reader.readNextUnsignedValue(cu.getPointerSize()) + baseAddrFixup; + break; + } + case DW_LLE_start_end: { + long startAddr = reader.readNextUnsignedValue(cu.getPointerSize()); + long endAddr = reader.readNextUnsignedValue(cu.getPointerSize()); + byte[] expr = reader.readNext(DWARFLocationList::uleb128SizedByteArray); + list.add(new DWARFLocation(startAddr + baseAddrFixup, endAddr + baseAddrFixup, + expr)); + break; + } + case DW_LLE_start_length: { + long startAddr = reader.readNextUnsignedValue(cu.getPointerSize()); + int len = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + byte[] expr = reader.readNext(DWARFLocationList::uleb128SizedByteArray); + list.add(new DWARFLocation(startAddr + baseAddrFixup, + startAddr + baseAddrFixup + len, expr)); + break; + } + default: + throw new IOException( + "Unsupported DWARF Location List Entry type: %d".formatted(lleId)); + } + } + return new DWARFLocationList(list); + } + + private List list; + + public DWARFLocationList(List list) { + this.list = list; + } + + public boolean isEmpty() { + return list.isEmpty(); + } + + /** + * Get the location that corresponds to the specified PC location. + * + * @param pc programcounter address + * @return the byte array corresponding to the location expression + */ + public DWARFLocation getLocationContaining(long pc) { + for (DWARFLocation loc : list) { + if (loc.contains(pc)) { + return loc; + } + } + return null; + } + + public DWARFLocation getFirstLocation() { + return !list.isEmpty() ? list.get(0) : null; + } + + @Override + public String toString() { + return "DWARFLocationList: " + list; + } + + /** + * Reader func that reads a uleb128-length prefixed byte array. + * + * @param reader {@link BinaryReader} stream + * @return byte array, length specified by the leading leb128 value + * @throws IOException if error reading + */ + private static byte[] uleb128SizedByteArray(BinaryReader reader) throws IOException { + int len = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + if (len > DWARFExpression.MAX_SANE_EXPR) { + throw new IOException("Invalid DWARF exprloc size: %d".formatted(len)); + } + return reader.readNextByteArray(len); + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLocationListEntry.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLocationListEntry.java new file mode 100644 index 0000000000..9ca3585bfa --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLocationListEntry.java @@ -0,0 +1,34 @@ +/* ### + * 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.util.bin.format.dwarf; + +public class DWARFLocationListEntry { + + public static final int DW_LLE_end_of_list = 0x00; + public static final int DW_LLE_base_addressx = 0x01; + public static final int DW_LLE_startx_endx = 0x02; + public static final int DW_LLE_startx_length = 0x03; + public static final int DW_LLE_offset_pair = 0x04; + public static final int DW_LLE_default_location = 0x05; + public static final int DW_LLE_base_address = 0x06; + public static final int DW_LLE_start_end = 0x07; + public static final int DW_LLE_start_length = 0x08; + + public static String toString(long value) { + return DWARFUtil.toString(DWARFLocationListEntry.class, value); + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLocationListHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLocationListHeader.java new file mode 100644 index 0000000000..b14969d9d5 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLocationListHeader.java @@ -0,0 +1,88 @@ +/* ### + * 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.util.bin.format.dwarf; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionNames; + +/** + * Header found at the start of a set of DWARFLocationList entries, which are stored sequentially + * in the {@link DWARFSectionNames#DEBUG_LOCLISTS .debug_loclists} section. + */ +public class DWARFLocationListHeader extends DWARFIndirectTableHeader { + + public static DWARFLocationListHeader read(BinaryReader reader, int defaultIntSize) + throws IOException { + // length : dwarf_length + // version : 2 bytes + // address_size : 1 byte + // segment_selector_size : 1 byte + // offset entry count: 4 bytes + // offsets : array of elements are are dwarf_format_int sized + + long startOffset = reader.getPointerIndex(); + DWARFLengthValue lengthInfo = DWARFLengthValue.read(reader, defaultIntSize); + if (lengthInfo == null) { + return null; + } + + long endOffset = reader.getPointerIndex() + lengthInfo.length(); + short version = reader.readNextShort(); + if (version < 5) { + throw new DWARFException( + "DWARFLocationListHeader (0x%x): unsupported DWARF version [%d]" + .formatted(startOffset, version)); + } + int addressSize = reader.readNextUnsignedByte(); + int segmentSelectorSize = reader.readNextUnsignedByte(); + int offsetEntryCount = reader.readNextUnsignedIntExact(); + long offsetListPosition = reader.getPointerIndex(); + + reader.setPointerIndex(endOffset); + if (segmentSelectorSize != 0) { + throw new IOException("Unsupported segmentSelectorSize: " + segmentSelectorSize); + } + + return new DWARFLocationListHeader(startOffset, endOffset, offsetListPosition, + lengthInfo.intSize(), offsetEntryCount, addressSize, segmentSelectorSize); + } + + private final int offsetEntryCount; + private final int offsetIntSize; + private final int addressSize; + private final int segmentSelectorSize; + + public DWARFLocationListHeader(long startOffset, long endOffset, long firstElementOffset, + int offsetIntSize, int offsetEntryCount, int addressSize, int segmentSelectorSize) { + super(startOffset, endOffset, firstElementOffset); + this.offsetIntSize = offsetIntSize; + this.offsetEntryCount = offsetEntryCount; + this.addressSize = addressSize; + this.segmentSelectorSize = segmentSelectorSize; + } + + @Override + public long getOffset(int index, BinaryReader reader) throws IOException { + if (index < 0 || index >= offsetEntryCount) { + throw new IOException("Invalid location list index: " + index); + } + return firstElementOffset + + reader.readUnsignedValue(firstElementOffset + (index * offsetIntSize), offsetIntSize); + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFNameInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFName.java similarity index 81% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFNameInfo.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFName.java index c79e199844..92cd579305 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFNameInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFName.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; import java.util.List; import java.util.Objects; @@ -28,9 +28,9 @@ import ghidra.program.model.symbol.SymbolType; * {@link Namespace namespaces} or {@link CategoryPath categorypaths}. *

*/ -public class DWARFNameInfo { +public class DWARFName { - private final DWARFNameInfo parent; + private final DWARFName parent; private final CategoryPath organizationalCategoryPath; private final NamespacePath namespacePath; private final String originalName; @@ -40,42 +40,42 @@ public class DWARFNameInfo { * * @param rootCategory {@link CategoryPath} in the data type manager that will contain * any sub-categories that represent namespaces - * @return a new {@link DWARFNameInfo} instance + * @return a new {@link DWARFName} instance */ - public static DWARFNameInfo createRoot(CategoryPath rootCategory) { - return new DWARFNameInfo(null, rootCategory, NamespacePath.ROOT, null); + public static DWARFName createRoot(CategoryPath rootCategory) { + return new DWARFName(null, rootCategory, NamespacePath.ROOT, null); } /** - * Create a {@link DWARFNameInfo} instance using the specified {@link DataType}'s name. + * Create a {@link DWARFName} instance using the specified {@link DataType}'s name. * * @param dataType {@link DataType} - * @return new {@link DWARFNameInfo} using the same name / CategoryPath as the data type + * @return new {@link DWARFName} using the same name / CategoryPath as the data type */ - public static DWARFNameInfo fromDataType(DataType dataType) { - return new DWARFNameInfo(null, dataType.getCategoryPath(), + public static DWARFName fromDataType(DataType dataType) { + return new DWARFName(null, dataType.getCategoryPath(), NamespacePath.create(null, dataType.getName(), null), dataType.getName()); } /** - * Create a child {@link DWARFNameInfo} instance of the specified parent. + * Create a child {@link DWARFName} instance of the specified parent. *

* Example:
*

fromList(parent, List.of("name1", "name2")) → parent_name/name1/name2
* - * @param parent {@link DWARFNameInfo} parent + * @param parent {@link DWARFName} parent * @param names list of names - * @return new {@link DWARFNameInfo} instance that is a child of the parent + * @return new {@link DWARFName} instance that is a child of the parent */ - public static DWARFNameInfo fromList(DWARFNameInfo parent, List names) { + public static DWARFName fromList(DWARFName parent, List names) { for (String s : names) { - DWARFNameInfo tmp = new DWARFNameInfo(parent, s, s, SymbolType.NAMESPACE); + DWARFName tmp = new DWARFName(parent, s, s, SymbolType.NAMESPACE); parent = tmp; } return parent; } - private DWARFNameInfo(DWARFNameInfo parent, CategoryPath organizationalCategoryPath, + private DWARFName(DWARFName parent, CategoryPath organizationalCategoryPath, NamespacePath namespacePath, String originalName) { this.parent = parent; this.organizationalCategoryPath = @@ -84,7 +84,7 @@ public class DWARFNameInfo { this.originalName = originalName; } - private DWARFNameInfo(DWARFNameInfo parent, String originalName, String name, SymbolType type) { + private DWARFName(DWARFName parent, String originalName, String name, SymbolType type) { this.parent = parent; this.organizationalCategoryPath = parent.getOrganizationalCategoryPath(); this.namespacePath = NamespacePath.create(parent.getNamespacePath(), name, type); @@ -96,7 +96,7 @@ public class DWARFNameInfo { * * @return parent */ - public DWARFNameInfo getParent() { + public DWARFName getParent() { return parent; } @@ -154,8 +154,8 @@ public class DWARFNameInfo { * @param newOriginalName originalName for the new instance * @return new instance with new name */ - public DWARFNameInfo replaceName(String newName, String newOriginalName) { - return new DWARFNameInfo(getParent(), newOriginalName, newName, getType()); + public DWARFName replaceName(String newName, String newOriginalName) { + return new DWARFName(getParent(), newOriginalName, newName, getType()); } /** @@ -165,8 +165,8 @@ public class DWARFNameInfo { * @param newType new SymbolType value * @return new instance with the specified SymbolType */ - public DWARFNameInfo replaceType(SymbolType newType) { - return new DWARFNameInfo(parent, originalName, getName(), newType); + public DWARFName replaceType(SymbolType newType) { + return new DWARFName(parent, originalName, getName(), newType); } /** @@ -255,7 +255,7 @@ public class DWARFNameInfo { } /** - * Creates a {@link DWARFNameInfo} instance, which has a name that is contained with + * Creates a {@link DWARFName} instance, which has a name that is contained with * this instance's namespace, using the specified name and symbol type. * * @param childOriginalName the unmodified name @@ -263,9 +263,9 @@ public class DWARFNameInfo { * @param childType the type of the object being named * @return new DWARFNameInfo instance */ - public DWARFNameInfo createChild(String childOriginalName, String childName, + public DWARFName createChild(String childOriginalName, String childName, SymbolType childType) { - return new DWARFNameInfo(this, childOriginalName, childName, childType); + return new DWARFName(this, childOriginalName, childName, childType); } @Override @@ -288,10 +288,10 @@ public class DWARFNameInfo { if (obj == null) { return false; } - if (!(obj instanceof DWARFNameInfo)) { + if (!(obj instanceof DWARFName)) { return false; } - DWARFNameInfo other = (DWARFNameInfo) obj; + DWARFName other = (DWARFName) obj; if (namespacePath == null) { if (other.namespacePath != null) { return false; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFProgram.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFProgram.java similarity index 63% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFProgram.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFProgram.java index b6e75efd58..1527933c22 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFProgram.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFProgram.java @@ -13,7 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; + +import static ghidra.app.util.bin.format.dwarf.DWARFTag.*; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; import java.io.*; import java.util.*; @@ -23,20 +26,17 @@ import org.apache.commons.collections4.ListValuedMap; import org.apache.commons.collections4.multimap.ArrayListValuedHashMap; import ghidra.app.util.bin.*; -import ghidra.app.util.bin.format.dwarf4.*; -import ghidra.app.util.bin.format.dwarf4.attribs.DWARFAttributeFactory; -import ghidra.app.util.bin.format.dwarf4.encoding.*; -import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException; -import ghidra.app.util.bin.format.dwarf4.external.ExternalDebugInfo; -import ghidra.app.util.bin.format.dwarf4.funcfixup.DWARFFunctionFixup; -import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.*; +import ghidra.app.util.bin.format.dwarf.attribs.*; +import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException; +import ghidra.app.util.bin.format.dwarf.external.ExternalDebugInfo; +import ghidra.app.util.bin.format.dwarf.funcfixup.DWARFFunctionFixup; +import ghidra.app.util.bin.format.dwarf.sectionprovider.*; import ghidra.app.util.bin.format.golang.rtti.GoSymbolName; import ghidra.app.util.opinion.*; import ghidra.formats.gfilesystem.FSUtilities; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.address.*; import ghidra.program.model.data.CategoryPath; -import ghidra.program.model.listing.Program; +import ghidra.program.model.listing.*; import ghidra.program.model.symbol.SymbolUtilities; import ghidra.util.Msg; import ghidra.util.datastruct.*; @@ -53,8 +53,11 @@ public class DWARFProgram implements Closeable { public static final CategoryPath DWARF_ROOT_CATPATH = CategoryPath.ROOT.extend(DWARF_ROOT_NAME); public static final CategoryPath UNCAT_CATPATH = DWARF_ROOT_CATPATH.extend("_UNCATEGORIZED_"); + private static final String DWARF_BOOKMARK_CAT = "DWARF"; private static final int NAME_HASH_REPLACEMENT_SIZE = 8 + 2 + 2; private static final String ELLIPSES_STR = "..."; + protected static final EnumSet REF_ATTRS = + EnumSet.of(DW_AT_abstract_origin, DW_AT_specification); /** * Returns true if the {@link Program program} probably has DWARF information, without doing @@ -118,38 +121,43 @@ public class DWARFProgram implements Closeable { private final Program program; private final DWARFDataTypeManager dwarfDTM; - private DWARFNameInfo rootDNI = DWARFNameInfo.createRoot(DWARF_ROOT_CATPATH); - private DWARFNameInfo unCatDataTypeRoot = DWARFNameInfo.createRoot(UNCAT_CATPATH); + private DWARFName rootDNI = DWARFName.createRoot(DWARF_ROOT_CATPATH); + private DWARFName unCatDataTypeRoot = DWARFName.createRoot(UNCAT_CATPATH); private DWARFImportOptions importOptions; - private DWARFImportSummary importSummary; + private DWARFImportSummary importSummary = new DWARFImportSummary(); private DWARFSectionProvider sectionProvider; private StringTable debugStrings; - private DWARFAttributeFactory attributeFactory; + private StringTable lineStrings; private int totalAggregateCount; private long programBaseAddressFixup; private int maxDNICacheSize = 50; - private FixedSizeHashMap dniCache = + private FixedSizeHashMap dniCache = new FixedSizeHashMap<>(100, maxDNICacheSize); - private Map attributeSpecIntern = + private Map attributeSpecIntern = new HashMap<>(); private DWARFRegisterMappings dwarfRegisterMappings; private final boolean stackGrowsNegative; private List functionFixups; + + // BinaryReaders for each of the various dwarf sections private BinaryReader debugLocation; + private BinaryReader debugLocLists; // v5+ private BinaryReader debugRanges; + private BinaryReader debugRngLists; // v5+ private BinaryReader debugInfoBR; private BinaryReader debugLineBR; private BinaryReader debugAbbrBR; - + private BinaryReader debugAddr; // v5+ + private BinaryReader debugStrOffsets; // v5+ // dieOffsets, siblingIndexes, parentIndexes contain for each DIE the information needed // to read each DIE and to navigate to parent / child / sibling elements. - // Each DIE record in the binary will consume 8+4+4=16 bytes in ram in these indexes. + // Each DIE record in the binary will consume 8+4+4=16 bytes of ram in these indexes. // DIE instances do not keep references to other DIEs. protected long[] dieOffsets = new long[0]; // offset in the debuginfo stream of this DIE protected int[] siblingIndexes = new int[0]; // index of each DIE's next sibling. @@ -161,6 +169,14 @@ public class DWARFProgram implements Closeable { protected TreeMap compUnitDieIndex = new TreeMap<>(); protected List compUnits = new ArrayList<>(); + // Indirect tables, added with dwarf v5, provide an index -> offset lookup feature for + // index values such as DW_FORM_addrx or DW_FORM_strx and other similar 'x' attribute values. + // Each DWARFIndirectTable is made of per-CU lookup arrays held in a DWARFIndirectTableHeader. + private DWARFIndirectTable addressListTable; // DWARFAddressListHeaders, DW_AT_addr_base + private DWARFIndirectTable locationListTable; // DWARFLocationListHeaders, DW_AT_rgnlists_base + private DWARFIndirectTable rangeListTable; // DWARFRangeListHeaders, DW_AT_rgnlists_base + private DWARFIndirectTable stringsOffsetTable; // DWARFStringOffsetTableHeader, DW_AT_str_offsets_base + // boolean flag, per die record, indicating that the DIE is the target of another DIE via // an aggregate reference, and therefore not the root DIE record of an aggregate. protected BitSet indexHasRef = new BitSet(); @@ -173,7 +189,7 @@ public class DWARFProgram implements Closeable { // Map of DIE offsets of {@link DIEAggregate}s that are being pointed to by // other {@link DIEAggregate}s with a DW_AT_type property. // In other words, a map of inbound links to a DIEA. - private ListValuedMap typeReferers = new ArrayListValuedHashMap<>(); + private ListValuedMap typeReferers = new ArrayListValuedHashMap<>(); /** * Main constructor for DWARFProgram. @@ -214,16 +230,35 @@ public class DWARFProgram implements Closeable { this.program = program; this.sectionProvider = sectionProvider; this.importOptions = importOptions; - this.importSummary = new DWARFImportSummary(); this.dwarfDTM = new DWARFDataTypeManager(this, program.getDataTypeManager()); this.stackGrowsNegative = program.getCompilerSpec().stackGrowsNegative(); - this.attributeFactory = new DWARFAttributeFactory(this); + + this.debugInfoBR = getBinaryReaderFor(DWARFSectionNames.DEBUG_INFO, monitor); + this.debugAbbrBR = getBinaryReaderFor(DWARFSectionNames.DEBUG_ABBREV, monitor); this.debugLocation = getBinaryReaderFor(DWARFSectionNames.DEBUG_LOC, monitor); - this.debugInfoBR = getBinaryReaderFor(DWARFSectionNames.DEBUG_INFO, monitor); - this.debugLineBR = getBinaryReaderFor(DWARFSectionNames.DEBUG_LINE, monitor); - this.debugAbbrBR = getBinaryReaderFor(DWARFSectionNames.DEBUG_ABBREV, monitor); + this.debugLocLists = getBinaryReaderFor(DWARFSectionNames.DEBUG_LOCLISTS, monitor); + this.debugRanges = getBinaryReaderFor(DWARFSectionNames.DEBUG_RANGES, monitor); + this.debugRngLists = getBinaryReaderFor(DWARFSectionNames.DEBUG_RNGLISTS, monitor); + + this.debugLineBR = getBinaryReaderFor(DWARFSectionNames.DEBUG_LINE, monitor); + this.debugAddr = getBinaryReaderFor(DWARFSectionNames.DEBUG_ADDR, monitor); + this.debugStrOffsets = getBinaryReaderFor(DWARFSectionNames.DEBUG_STROFFSETS, monitor); + + this.rangeListTable = + new DWARFIndirectTable(this.debugRngLists, DWARFCompilationUnit::getRangeListsBase); + this.addressListTable = + new DWARFIndirectTable(this.debugAddr, DWARFCompilationUnit::getAddrTableBase); + this.stringsOffsetTable = + new DWARFIndirectTable(this.debugStrOffsets, DWARFCompilationUnit::getStrOffsetsBase); + this.locationListTable = + new DWARFIndirectTable(this.debugLocLists, DWARFCompilationUnit::getLocListsBase); + + this.debugStrings = + StringTable.of(getBinaryReaderFor(DWARFSectionNames.DEBUG_STR, monitor)); + this.lineStrings = + StringTable.of(getBinaryReaderFor(DWARFSectionNames.DEBUG_LINE_STR, monitor)); // if there are relocations (already handled by the ghidra loader) anywhere in the // debuginfo or debugrange sections, then we don't need to manually fix up addresses @@ -251,12 +286,53 @@ public class DWARFProgram implements Closeable { * @throws CancelledException if cancelled */ public void init(TaskMonitor monitor) throws IOException, DWARFException, CancelledException { - monitor.setMessage("DWARF: Reading string table"); - this.debugStrings = StringTable.readStringTable( - sectionProvider.getSectionAsByteProvider(DWARFSectionNames.DEBUG_STR, monitor)); - bootstrapCompilationUnits(monitor); + int defaultIntSize = getDefaultIntSize(); + rangeListTable.bootstrap("DWARF: Bootstrapping Range Lists", + reader -> DWARFRangeListHeader.read(reader, defaultIntSize), monitor); + locationListTable.bootstrap("DWARF: Bootstrapping Location Lists", + reader -> DWARFLocationListHeader.read(reader, defaultIntSize), monitor); + addressListTable.bootstrap("DWARF: Bootstrapping Address Lists", + reader -> DWARFAddressListHeader.read(reader, defaultIntSize), monitor); + stringsOffsetTable.bootstrap("DWARF: Bootstrapping String Offset Lists", + reader -> DWARFStringOffsetTableHeader.readV5(reader, defaultIntSize), monitor); + + indexDIEs(monitor); + indexDIEATypeRefs(monitor); + + importSummary.addCompunitInfo(compUnits); + } + + private void bootstrapCompilationUnits(TaskMonitor monitor) + throws CancelledException, IOException, DWARFException { + + debugInfoBR.setPointerIndex(0); + monitor.initialize(debugInfoBR.length(), "DWARF: Bootstrapping Compilation Units"); + while (debugInfoBR.hasNext()) { + monitor.checkCancelled(); + monitor.setProgress(debugInfoBR.getPointerIndex()); + monitor.setMessage("DWARF: Bootstrapping Compilation Unit #" + compUnits.size()); + + DWARFUnitHeader unitHeader = + DWARFUnitHeader.read(this, debugInfoBR, debugAbbrBR, compUnits.size(), monitor); + if (unitHeader != null) { + debugInfoBR.setPointerIndex(unitHeader.getEndOffset()); + } + + if (unitHeader instanceof DWARFCompilationUnit cu) { + compUnits.add(cu); + importSummary.dwarfVers.add((int) cu.getDWARFVersion()); + } + else { + Msg.info(this, "Unsupported unit header: " + unitHeader + " at " + + unitHeader.getStartOffset()); + } + } + importSummary.compUnitCount = compUnits.size(); + } + + private void indexDIEs(TaskMonitor monitor) throws CancelledException, IOException { LongArrayList dieOffsetList = new LongArrayList(); IntArrayList siblingIndexList = new IntArrayList(); IntArrayList parentIndexList = new IntArrayList(); @@ -266,8 +342,9 @@ public class DWARFProgram implements Closeable { for (DWARFCompilationUnit cu : compUnits) { debugInfoBR.setPointerIndex(cu.getFirstDIEOffset()); monitor.setMessage("DWARF: Indexing records - Compilation Unit #%d/%d" - .formatted(cu.getCompUnitNumber() + 1, compUnits.size())); - indexDIEsForCU(cu, dieOffsetList, parentIndexList, siblingIndexList, aggrTargets, monitor); + .formatted(cu.getUnitNumber() + 1, compUnits.size())); + indexDIEsForCU(cu, dieOffsetList, parentIndexList, siblingIndexList, aggrTargets, + monitor); compUnitDieIndex.put(dieOffsetList.size() - 1, cu); } @@ -279,10 +356,7 @@ public class DWARFProgram implements Closeable { int nonHeadCount = indexHasRef.cardinality(); totalAggregateCount = dieOffsetList.size() - nonHeadCount; - indexDIEATypeRefs(monitor); - - Msg.info(this, - "DWARF: %d compile units, %d DIEs".formatted(compUnits.size(), dieOffsets.length)); + importSummary.dieCount = dieOffsets.length; } protected void indexDIEATypeRefs(TaskMonitor monitor) throws CancelledException { @@ -291,7 +365,7 @@ public class DWARFProgram implements Closeable { monitor.increment(); DIEAggregate typeRef = diea.getTypeRef(); if (typeRef != null) { - typeReferers.put(typeRef.getOffset(), diea); + typeReferers.put(typeRef.getOffset(), diea.getOffset()); } } @@ -310,29 +384,9 @@ public class DWARFProgram implements Closeable { } } - private void bootstrapCompilationUnits(TaskMonitor monitor) - throws CancelledException, IOException, DWARFException { - - debugInfoBR.setPointerIndex(0); - monitor.initialize(debugInfoBR.length(), "DWARF: Bootstrapping Compilation Units"); - while (debugInfoBR.hasNext()) { - monitor.checkCancelled(); - monitor.setProgress(debugInfoBR.getPointerIndex()); - monitor.setMessage("DWARF: Bootstrapping Compilation Unit #" + compUnits.size()); - - DWARFCompilationUnit cu = DWARFCompilationUnit.readCompilationUnit(this, debugInfoBR, - debugAbbrBR, compUnits.size(), monitor); - - if (cu != null) { - compUnits.add(cu); - debugInfoBR.setPointerIndex(cu.getEndOffset()); - } - } - } - private void indexDIEsForCU(DWARFCompilationUnit cu, LongArrayList dieOffsetList, - IntArrayList parentIndexList, IntArrayList siblingIndexList, - LongArrayList aggrTargets, TaskMonitor monitor) throws CancelledException { + IntArrayList parentIndexList, IntArrayList siblingIndexList, LongArrayList aggrTargets, + TaskMonitor monitor) throws CancelledException { long endOffset = cu.getEndOffset(); int perCuDieCount = 0; @@ -347,8 +401,7 @@ public class DWARFProgram implements Closeable { try { int dieIndex = dieOffsetList.size(); - DebugInfoEntry die = - DebugInfoEntry.read(debugInfoBR, cu, dieIndex, attributeFactory); + DebugInfoEntry die = DebugInfoEntry.read(debugInfoBR, cu, dieIndex); if (die.isTerminator()) { if (parentIndex == -1) { @@ -379,8 +432,12 @@ public class DWARFProgram implements Closeable { parentIndex = dieIndex; } + if (die.getOffset() == cu.getFirstDIEOffset()) { + cu.init(die); + } + DIEAggregate diea = DIEAggregate.createSingle(die); - for (int attr : DIEAggregate.REF_ATTRS) { + for (DWARFAttribute attr : REF_ATTRS) { long refdOffset = diea.getUnsignedLong(attr, -1); if (refdOffset != -1) { aggrTargets.add(refdOffset); @@ -392,7 +449,7 @@ public class DWARFProgram implements Closeable { catch (IOException e) { Msg.error(this, "Failed to read DIE at offset 0x%x in compunit %d (at 0x%x), skipping remainder of compilation unit." - .formatted(startOfDIE, cu.getCompUnitNumber(), cu.getStartOffset()), + .formatted(startOfDIE, cu.getUnitNumber(), cu.getStartOffset()), e); debugInfoBR.setPointerIndex(endOffset); } @@ -416,6 +473,11 @@ public class DWARFProgram implements Closeable { } if (debugStrings != null) { debugStrings.clear(); + debugStrings = null; + } + if (lineStrings != null) { + lineStrings.clear(); + lineStrings = null; } compUnits.clear(); dniCache.clear(); @@ -424,7 +486,10 @@ public class DWARFProgram implements Closeable { debugInfoBR = null; debugLineBR = null; debugLocation = null; + debugLocLists = null; debugRanges = null; + debugRngLists = null; + debugAddr = null; dieOffsets = new long[0]; parentIndexes = new int[0]; @@ -435,6 +500,11 @@ public class DWARFProgram implements Closeable { typeReferers.clear(); compUnitDieIndex.clear(); + locationListTable.clear(); + rangeListTable.clear(); + stringsOffsetTable.clear(); + addressListTable.clear(); + if (functionFixups != null) { for (DWARFFunctionFixup funcFixup : functionFixups) { if (funcFixup instanceof Closeable c) { @@ -494,12 +564,12 @@ public class DWARFProgram implements Closeable { } public String getEntryName(DIEAggregate diea) { - String name = diea.getString(DWARFAttribute.DW_AT_name, null); + String name = diea.getString(DW_AT_name, null); if (name == null) { - String linkageName = diea.getString(DWARFAttribute.DW_AT_linkage_name, null); + String linkageName = diea.getString(DW_AT_linkage_name, null); if (linkageName == null) { - linkageName = diea.getString(DWARFAttribute.DW_AT_MIPS_linkage_name, null); + linkageName = diea.getString(DW_AT_MIPS_linkage_name, null); } name = linkageName; } @@ -513,21 +583,22 @@ public class DWARFProgram implements Closeable { * Always returns a name for the passed-in entry, but you should probably only use this * for entries that are {@link DIEAggregate#isNamedType()} */ - private DWARFNameInfo getDWARFNameInfo(DIEAggregate diea, DWARFNameInfo localRootDNI) { + private DWARFName getDWARFName(DIEAggregate diea, DWARFName localRootDNI) { - DWARFNameInfo parentDNI = localRootDNI; + DWARFName parentDNI = localRootDNI; DIEAggregate declParent = diea.getDeclParent(); - if ((declParent != null) && declParent.getTag() != DWARFTag.DW_TAG_compile_unit) { + if ((declParent != null) && declParent.getTag() != DW_TAG_compile_unit) { parentDNI = lookupDNIByOffset(declParent.getOffset()); if (parentDNI == null) { - parentDNI = getDWARFNameInfo(declParent, localRootDNI); + parentDNI = getDWARFName(declParent, localRootDNI); if (parentDNI != null) { cacheDNIByOffset(declParent.getOffset(), parentDNI); } } } + DWARFTag tag = diea.getTag(); String name = getEntryName(diea); // Mangled names can occur in linkage attributes or in the regular name attribute. @@ -537,7 +608,7 @@ public class DWARFProgram implements Closeable { if (!nestings.isEmpty()) { name = nestings.remove(nestings.size() - 1); if (parentDNI == localRootDNI && !nestings.isEmpty()) { - parentDNI = DWARFNameInfo.fromList(localRootDNI, nestings); + parentDNI = DWARFName.fromList(localRootDNI, nestings); } } } @@ -548,26 +619,25 @@ public class DWARFProgram implements Closeable { List nestings = DWARFUtil.findLinkageNameInChildren(diea.getHeadFragment()); if (!nestings.isEmpty()) { nestings.remove(nestings.size() - 1); - parentDNI = DWARFNameInfo.fromList(localRootDNI, nestings); + parentDNI = DWARFName.fromList(localRootDNI, nestings); } } if (name == null) { - // check to see if there is a single inbound typedef that we can - // steal its name. - DIEAggregate referringTypedef = DWARFUtil.getReferringTypedef(diea); - if (referringTypedef != null) { - return getDWARFNameInfo(referringTypedef, localRootDNI); + // check to see if there is a single inbound typedef that we can steal its name. + List referers = getTypeReferers(diea, DW_TAG_typedef); + if (referers.size() == 1) { + return getDWARFName(referers.get(0), localRootDNI); } } - if (name == null && diea.isStructureType()) { + if (name == null && tag.isStructureType()) { String fingerprint = DWARFUtil.getStructLayoutFingerprint(diea); // check to see if there are struct member defs that ref this anon type // and build a name using the field names List referringMembers = - diea.getProgram().getTypeReferers(diea, DWARFTag.DW_TAG_member); + diea.getProgram().getTypeReferers(diea, DW_TAG_member); String referringMemberNames = getReferringMemberFieldNames(referringMembers); if (!referringMemberNames.isEmpty()) { @@ -576,40 +646,38 @@ public class DWARFProgram implements Closeable { parentDNI = getName(referringMembers.get(0).getParent()); referringMemberNames = "_for_" + referringMemberNames; } - name = - "anon_" + DWARFUtil.getContainerTypeName(diea) + "_" + fingerprint + - referringMemberNames; - return parentDNI.createChild(null, name, DWARFUtil.getSymbolTypeFromDIE(diea)); + name = "anon_" + tag.getContainerTypeName() + "_" + fingerprint + referringMemberNames; + return parentDNI.createChild(null, name, tag.getSymbolType()); } boolean isAnon = false; if (name == null) { switch (diea.getTag()) { - case DWARFTag.DW_TAG_base_type: + case DW_TAG_base_type: name = getAnonBaseTypeName(diea); isAnon = true; break; - case DWARFTag.DW_TAG_enumeration_type: + case DW_TAG_enumeration_type: name = getAnonEnumName(diea); isAnon = true; break; - case DWARFTag.DW_TAG_subroutine_type: + case DW_TAG_subroutine_type: // unnamed subroutines (C func ptrs) // See {@link #isAnonSubroutine(DataType)} name = "anon_subr"; isAnon = true; break; - case DWARFTag.DW_TAG_lexical_block: - name = DWARFUtil.getLexicalBlockName(diea); + case DW_TAG_lexical_block: // "lexical_block_1_2_3" + name = "lexical_block" + getLexicalBlockNameWorker(diea.getHeadFragment()); break; - case DWARFTag.DW_TAG_formal_parameter: + case DW_TAG_formal_parameter: name = "param_%d".formatted(diea.getHeadFragment().getPositionInParent()); isAnon = true; break; - case DWARFTag.DW_TAG_subprogram: - case DWARFTag.DW_TAG_inlined_subroutine: - if (declParent != null && declParent.isStructureType() && - diea.getBool(DWARFAttribute.DW_AT_artificial, false)) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + if (declParent != null && declParent.getTag().isStructureType() && + diea.getBool(DW_AT_artificial, false)) { name = parentDNI.getName(); } else { @@ -618,7 +686,7 @@ public class DWARFProgram implements Closeable { } break; default: - if (declParent != null && declParent.isNameSpaceContainer()) { + if (declParent != null && declParent.getTag().isNameSpaceContainer()) { name = DWARFUtil.getAnonNameForMeFromParentContext2(diea); } break; @@ -627,7 +695,7 @@ public class DWARFProgram implements Closeable { // Name was not found if (isAnonDWARFName(name)) { - name = createAnonName("anon_" + DWARFUtil.getContainerTypeName(diea), diea); + name = createAnonName("anon_" + tag.getContainerTypeName(), diea); isAnon = true; } @@ -635,23 +703,38 @@ public class DWARFProgram implements Closeable { String workingName = ensureSafeNameLength(name); workingName = GoSymbolName.fixGolangSpecialSymbolnameChars(workingName); - if (diea.getCompilationUnit() - .getCompileUnit() - .getLanguage() == DWARFSourceLanguage.DW_LANG_Rust && + if (diea.getCompilationUnit().getLanguage() == DWARFSourceLanguage.DW_LANG_Rust && workingName.startsWith("{impl#") && parentDNI != null) { // if matches a Rust {impl#NN} name, skip it and re-use the parent name return parentDNI; } - DWARFNameInfo result = - parentDNI.createChild(origName, workingName, DWARFUtil.getSymbolTypeFromDIE(diea)); + DWARFName result = parentDNI.createChild(origName, workingName, tag.getSymbolType()); return result; } + /** + * Returns the {@link DIEAggregate} of a typedef that points to the specified datatype. + *

+ * Returns null if there is no typedef pointing to the specified DIEA or if there are + * multiple. + * + * @param diea {@link DIEAggregate} of a data type that might be the target of typedefs. + * @return {@link DIEAggregate} of the singular typedef that points to the arg, otherwise + * null if none or multiple found. + */ + public static DIEAggregate getReferringTypedef(DIEAggregate diea) { + if (diea == null) { + return null; + } + List referers = diea.getProgram().getTypeReferers(diea, DW_TAG_typedef); + return (referers.size() == 1) ? referers.get(0) : null; + } + private String getAnonBaseTypeName(DIEAggregate diea) { try { - int dwarfSize = diea.parseInt(DWARFAttribute.DW_AT_byte_size, 0); - int dwarfEncoding = (int) diea.getUnsignedLong(DWARFAttribute.DW_AT_encoding, -1); + int dwarfSize = diea.parseInt(DW_AT_byte_size, 0); + int dwarfEncoding = (int) diea.getUnsignedLong(DW_AT_encoding, -1); return "anon_basetype_%s_%d".formatted(DWARFEncoding.getTypeName(dwarfEncoding), dwarfSize); } @@ -661,7 +744,7 @@ public class DWARFProgram implements Closeable { } private String getAnonEnumName(DIEAggregate diea) { - int enumSize = Math.max(1, (int) diea.getUnsignedLong(DWARFAttribute.DW_AT_byte_size, 1)); + int enumSize = Math.max(1, (int) diea.getUnsignedLong(DW_AT_byte_size, 1)); return "anon_enum_%d".formatted(enumSize * 8); } @@ -669,6 +752,14 @@ public class DWARFProgram implements Closeable { return "%s.dwarf_%x".formatted(baseName, diea.getOffset()); } + private static String getLexicalBlockNameWorker(DebugInfoEntry die) { + if (die.getTag() == DW_TAG_lexical_block || die.getTag() == DW_TAG_inlined_subroutine) { + return "%s_%d".formatted(getLexicalBlockNameWorker(die.getParent()), + die.getPositionInParent()); + } + return ""; + } + private String getReferringMemberFieldNames(List referringMembers) { if (referringMembers == null || referringMembers.isEmpty()) { return ""; @@ -687,7 +778,7 @@ public class DWARFProgram implements Closeable { if (positionInParent == -1) { continue; } - DWARFNameInfo parentDNI = getName(commonParent); + DWARFName parentDNI = getName(commonParent); memberName = "%s_%d".formatted(parentDNI.getName(), positionInParent); } if (result.length() > 0) { @@ -696,7 +787,7 @@ public class DWARFProgram implements Closeable { result.append(memberName); } return result.toString(); - } + } /** * Transform a string with a C++ template-like syntax into a hopefully shorter version that @@ -741,21 +832,27 @@ public class DWARFProgram implements Closeable { return strs; } - public DWARFNameInfo getName(DIEAggregate diea) { - DWARFNameInfo dni = lookupDNIByOffset(diea.getOffset()); + /** + * Returns a {@link DWARFName} for a {@link DIEAggregate}. + * + * @param diea {@link DIEAggregate} + * @return {@link DWARFName}, never null + */ + public DWARFName getName(DIEAggregate diea) { + DWARFName dni = lookupDNIByOffset(diea.getOffset()); if (dni == null) { - dni = getDWARFNameInfo(diea, unCatDataTypeRoot); + dni = getDWARFName(diea, unCatDataTypeRoot); cacheDNIByOffset(diea.getOffset(), dni); } return dni; } - private DWARFNameInfo lookupDNIByOffset(long offset) { - DWARFNameInfo tmp = dniCache.get(offset); + private DWARFName lookupDNIByOffset(long offset) { + DWARFName tmp = dniCache.get(offset); return tmp; } - private void cacheDNIByOffset(long offset, DWARFNameInfo dni) { + private void cacheDNIByOffset(long offset, DWARFName dni) { dniCache.put(offset, dni); } @@ -864,7 +961,7 @@ public class DWARFProgram implements Closeable { if (dieOffset < cu.getFirstDIEOffset() || cu.getEndOffset() < dieOffset) { throw new RuntimeException(); } - die = DebugInfoEntry.read(debugInfoBR, cu, dieIndex, attributeFactory); + die = DebugInfoEntry.read(debugInfoBR, cu, dieIndex); diesByOffset.put(dieOffset, die); } catch (IOException e) { @@ -884,8 +981,7 @@ public class DWARFProgram implements Closeable { } private DebugInfoEntry getDIEByIndex(int dieIndex) { - long dieOffset = - 0 <= dieIndex && dieIndex < dieOffsets.length ? dieOffsets[dieIndex] : -1; + long dieOffset = 0 <= dieIndex && dieIndex < dieOffsets.length ? dieOffsets[dieIndex] : -1; return getDIEByOffset(dieOffset, dieIndex); } @@ -896,7 +992,6 @@ public class DWARFProgram implements Closeable { } } - /** * Returns the {@link DIEAggregate} that contains the specified {@link DebugInfoEntry}. * @@ -935,6 +1030,223 @@ public class DWARFProgram implements Closeable { return getAggregate(die); } + /** + * Returns a DWARF attribute string value, as specified by a form, offset/index, and the cu. + * + * @param form {@link DWARFForm} + * @param offset offset or index of the value + * @param cu {@link DWARFCompilationUnit} + * @return String value, never null + * @throws IOException if invalid form or bad offset/index + */ + public String getString(DWARFForm form, long offset, DWARFCompilationUnit cu) + throws IOException { + switch (form) { + case DW_FORM_line_strp: + return lineStrings.getStringAtOffset(offset); + case DW_FORM_strp: + return debugStrings.getStringAtOffset(offset); + case DW_FORM_strx, DW_FORM_strx1, DW_FORM_strx2, DW_FORM_strx3, DW_FORM_strx4: + long strOffset = stringsOffsetTable.getOffset((int) offset, cu); + return debugStrings.getStringAtOffset(strOffset); + default: + throw new IOException("Unsupported string form: " + form); + } + } + + /** + * Returns the {@link DWARFRangeList} pointed at by the specified attribute. + * + * @param diea {@link DIEAggregate} + * @param attribute attribute id to find in the DIEA + * @return {@link DWARFRangeList}, or null if attribute is not present + * @throws IOException if error reading range list + */ + public DWARFRangeList getRangeList(DIEAggregate diea, DWARFAttribute attribute) + throws IOException { + + DWARFNumericAttribute rngListAttr = + diea.getAttribute(attribute, DWARFNumericAttribute.class); + if (rngListAttr == null) { + return null; + } + + DWARFCompilationUnit cu = diea.getCompilationUnit(); + + switch (rngListAttr.getAttributeForm()) { + case DW_FORM_rnglistx: { // assumes v5 + int index = rngListAttr.getUnsignedIntExact(); + long rnglistOffset = rangeListTable.getOffset(index, cu); + debugRngLists.setPointerIndex(rnglistOffset); + return DWARFRangeList.readV5(debugRngLists, cu); + } + case DW_FORM_sec_offset: { + long rnglistOffset = rngListAttr.getValue(); + short dwarfVersion = cu.getDWARFVersion(); + if (dwarfVersion < 5) { + debugRanges.setPointerIndex(rnglistOffset); + return DWARFRangeList.readV4(debugRanges, cu); + } + else if (dwarfVersion == 5) { + debugRngLists.setPointerIndex(rnglistOffset); + return DWARFRangeList.readV5(debugRngLists, cu); + } + break; + } + default: + break; // fall thru to throw + } + throw new IOException("Unsupported attribute form " + rngListAttr); + } + + /** + * Returns the raw offset of an indexed item. For DW_FORM_addrx values, the returned value + * is not fixed up with Ghidra load offset. + * + * @param form {@link DWARFForm} of the index + * @param index int index into a lookup table (see {@link #addressListTable}, + * {@link #locationListTable}, {@link #rangeListTable}, {@link #stringsOffsetTable}) + * @param cu {@link DWARFCompilationUnit} + * @return raw offset of indexed item + * @throws IOException if error reading index table + */ + public long getOffsetOfIndexedElement(DWARFForm form, int index, DWARFCompilationUnit cu) + throws IOException { + DWARFIndirectTable table = switch (form) { + case DW_FORM_addrx: + case DW_FORM_addrx1: + case DW_FORM_addrx2: + case DW_FORM_addrx3: + case DW_FORM_addrx4: + yield addressListTable; + case DW_FORM_rnglistx: + yield rangeListTable; + case DW_FORM_loclistx: + yield locationListTable; + case DW_FORM_strx: + case DW_FORM_strx1: + case DW_FORM_strx2: + case DW_FORM_strx3: + case DW_FORM_strx4: + yield stringsOffsetTable; + default: + yield null; + }; + return table != null ? table.getOffset(index, cu) : -1; + } + + /** + * Returns an address value, corrected for any Ghidra load offset shenanigans. + * + * @param form the format of the numeric value + * @param value raw offset or indirect address index (depending on the DWARFForm) + * @param cu {@link DWARFCompilationUnit} + * @return address + * @throws IOException if error reading indirect lookup tables + */ + public long getAddress(DWARFForm form, long value, DWARFCompilationUnit cu) throws IOException { + switch (form) { + case DW_FORM_addr: + case DW_FORM_udata: + return value + programBaseAddressFixup; + case DW_FORM_addrx: + case DW_FORM_addrx1: + case DW_FORM_addrx2: + case DW_FORM_addrx3: + case DW_FORM_addrx4: { + long addr = addressListTable.getOffset((int) value, cu); + return addr + programBaseAddressFixup; + } + default: + throw new IOException("Unsupported form %s".formatted(form)); + } + } + + /** + * Returns the {@link DWARFLocationList} pointed to by the specified attribute value. + * + * @param diea {@link DIEAggregate} + * @param attribute attribute id that points to the location list + * @return {@link DWARFLocationList}, never null + * @throws IOException if specified attribute is not the correct type, or if other error reading + * data + */ + public DWARFLocationList getLocationList(DIEAggregate diea, DWARFAttribute attribute) + throws IOException { + DWARFAttributeValue attrib = diea.getAttribute(attribute); + if (attrib == null) { + return DWARFLocationList.EMPTY; + } + if (attrib instanceof DWARFNumericAttribute dnum) { + return readLocationList(dnum, diea.getCompilationUnit()); + } + else if (attrib instanceof DWARFBlobAttribute dblob) { + return DWARFLocationList.withWildcardRange(dblob.getBytes()); + } + else { + throw new IOException("Unsupported form %s.".formatted(attrib)); + } + + } + + private DWARFLocationList readLocationList(DWARFNumericAttribute loclistAttr, + DWARFCompilationUnit cu) throws IOException { + switch (loclistAttr.getAttributeForm()) { + case DW_FORM_sec_offset: + int dwarfVer = cu.getDWARFVersion(); + if (dwarfVer < 5) { + debugLocation.setPointerIndex(loclistAttr.getUnsignedValue()); + return DWARFLocationList.readV4(debugLocation, cu); + } + else if (dwarfVer == 5) { + debugLocLists.setPointerIndex(loclistAttr.getUnsignedValue()); + return DWARFLocationList.readV5(debugLocLists, cu); + } + break; + case DW_FORM_loclistx: + int index = loclistAttr.getUnsignedIntExact(); + long locOffset = locationListTable.getOffset(index, cu); + debugLocLists.setPointerIndex(locOffset); + return DWARFLocationList.readV5(debugLocLists, cu); + default: + break; // fallthru to throw + } + throw new IOException( + "Unsupported loclist form %s".formatted(loclistAttr.getAttributeForm())); + } + + /** + * Returns the DWARFLine info pointed to by the specified attribute. + * + * @param diea {@link DIEAggregate} + * @param attribute attribute id that points to the line info + * @return {@link DWARFLine}, or null if attribute + * @throws IOException if error reading line data + */ + public DWARFLine getLine(DIEAggregate diea, DWARFAttribute attribute) throws IOException { + DWARFNumericAttribute attrib = diea.getAttribute(attribute, DWARFNumericAttribute.class); + if (attrib == null || debugLineBR == null) { + return null; + } + long stmtListOffset = attrib.getUnsignedValue(); + debugLineBR.setPointerIndex(stmtListOffset); + + // probe for the DWARFLine version number + // length : dwarf_length + // version : 2 bytes + DWARFLengthValue lengthInfo = DWARFLengthValue.read(debugLineBR, getDefaultIntSize()); + if (lengthInfo == null) { + throw new DWARFException("Invalid DWARFLine length at 0x%x".formatted(stmtListOffset)); + } + + int version = debugLineBR.readNextUnsignedShort(); + + return version < 5 + ? DWARFLine.readV4(this, debugLineBR, lengthInfo, version) + : DWARFLine.readV5(this, debugLineBR, lengthInfo, version, + diea.getCompilationUnit()); + } + /** * Returns iterable that traverses all {@link DIEAggregate}s in the program. * @@ -953,52 +1265,28 @@ public class DWARFProgram implements Closeable { return totalAggregateCount; } - public BinaryReader getDebugLocation() { - return debugLocation; - } - - public BinaryReader getDebugRanges() { - return debugRanges; - } - - public BinaryReader getDebugLine() { - return debugLineBR; + public BinaryReader getReaderForCompUnit(DWARFCompilationUnit cu) { + return debugInfoBR; } public DWARFRegisterMappings getRegisterMappings() { return dwarfRegisterMappings; } - public DWARFNameInfo getRootDNI() { + public DWARFName getRootDNI() { return rootDNI; } - public DWARFNameInfo getUncategorizedRootDNI() { + public DWARFName getUncategorizedRootDNI() { return unCatDataTypeRoot; } - public StringTable getDebugStrings() { - return debugStrings; - } - - public void setDebugStrings(StringTable debugStrings) { - this.debugStrings = debugStrings; - } - public AddressSpace getStackSpace() { return program.getAddressFactory().getStackSpace(); } - public DWARFAttributeFactory getAttributeFactory() { - return attributeFactory; - } - - public void setAttributeFactory(DWARFAttributeFactory attributeFactory) { - this.attributeFactory = attributeFactory; - } - - public DWARFAttributeSpecification internAttributeSpec(DWARFAttributeSpecification das) { - DWARFAttributeSpecification inDAS = attributeSpecIntern.get(das); + public DWARFAttribute.AttrDef internAttributeSpec(DWARFAttribute.AttrDef das) { + DWARFAttribute.AttrDef inDAS = attributeSpecIntern.get(das); if (inDAS == null) { inDAS = das; attributeSpecIntern.put(inDAS, inDAS); @@ -1007,8 +1295,11 @@ public class DWARFProgram implements Closeable { } private List getTypeReferers(DIEAggregate targetDIEA) { - List result = typeReferers.get(targetDIEA.getOffset()); - return (result != null) ? result : Collections.emptyList(); + List dieaOffsets = typeReferers.get(targetDIEA.getOffset()); + if (dieaOffsets == null) { + return List.of(); + } + return dieaOffsets.stream().map(dieaOffset -> getAggregate(dieaOffset)).toList(); } /** @@ -1020,7 +1311,7 @@ public class DWARFProgram implements Closeable { * to refer to the target DIEA. * @return list of DIEAs that point to the target, empty list if nothing found. */ - public List getTypeReferers(DIEAggregate targetDIEA, int tag) { + public List getTypeReferers(DIEAggregate targetDIEA, DWARFTag tag) { List result = new ArrayList<>(); for (DIEAggregate referer : getTypeReferers(targetDIEA)) { @@ -1043,6 +1334,13 @@ public class DWARFProgram implements Closeable { return programBaseAddressFixup; } + public AddressRange getAddressRange(DWARFRange range, boolean isCode) { + AddressSpace defAS = program.getAddressFactory().getDefaultAddressSpace(); + Address start = defAS.getAddress(range.getFrom(), true /* TODO check this */); + Address end = defAS.getAddress(range.getTo() - 1, true /* TODO check this */); + return new AddressRangeImpl(start, end); + } + public Address getCodeAddress(Number offset) { return program.getAddressFactory() .getDefaultAddressSpace() @@ -1066,6 +1364,30 @@ public class DWARFProgram implements Closeable { return functionFixups; } + public int getDefaultIntSize() { + return program.getDefaultPointerSize(); + } + + public void logWarningAt(Address addr, String addrName, String msg) { + if (importOptions.isUseBookmarks()) { + BookmarkManager bmm = program.getBookmarkManager(); + Bookmark existingBM = bmm.getBookmark(addr, BookmarkType.WARNING, DWARF_BOOKMARK_CAT); + String existingTxt = existingBM != null ? existingBM.getComment() : ""; + if (existingTxt.contains(msg)) { + return; + } + msg = !existingTxt.isEmpty() ? existingTxt + "; " + msg : msg; + bmm.setBookmark(addr, BookmarkType.WARNING, DWARF_BOOKMARK_CAT, msg); + } + else { + Msg.warn(this, "%s: %s at %s@%s".formatted(DWARF_BOOKMARK_CAT, msg, addrName, addr)); + } + } + + /* for testing */ public void setStringTable(StringTable st) { + this.debugStrings = st; + } + //--------------------------------------------------------------------------------------------- private class DIEAggregateIterator implements Iterator, Iterable { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFRange.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRange.java similarity index 56% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFRange.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRange.java index d764c6a8fd..d9a7754f2f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFRange.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRange.java @@ -13,13 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4; +package ghidra.app.util.bin.format.dwarf; + +import ghidra.program.model.address.AddressRange; /** - * Holds the start (inclusive) and end (exclusive) addresses of a range. + * Holds the start (inclusive) and end (exclusive, 1 past the last included address) addresses + * of a range. + *

+ * DWARF ranges are slightly different than Ghidra {@link AddressRange ranges} because the + * end address of a Ghidra AddressRange is inclusive, and the DWARF range is exclusive. + *

+ * DWARF ranges can represent an empty range, Ghidra AddressRanges can not.
+ * Ghidra AddressRanges can include the maximum 64bit address (0xffffffffffffffff), but DWARF ranges + * can not include that. */ public class DWARFRange implements Comparable { - public static final DWARFRange EMPTY = new DWARFRange(0, 1); + public static final DWARFRange EMPTY = new DWARFRange(0, 0); private final long start; private final long end; @@ -31,9 +41,9 @@ public class DWARFRange implements Comparable { * @param end long ending address, exclusive */ public DWARFRange(long start, long end) { - if (end < start) { + if (Long.compareUnsigned(end, start) < 0) { throw new IllegalArgumentException( - "Range max (" + end + ") cannot be less than min (" + start + ")."); + "Range max (%d) cannot be less than min (%d).".formatted(end, start)); } this.start = start; this.end = end; @@ -41,18 +51,26 @@ public class DWARFRange implements Comparable { @Override public String toString() { - return "(" + this.start + "," + this.end + ")"; + return "[%x,%x)".formatted(start, end); } @Override public int compareTo(DWARFRange other) { - int tmp = Long.compare(start, other.start); + int tmp = Long.compareUnsigned(start, other.start); if (tmp == 0) { - tmp = Long.compare(end, other.end); + tmp = Long.compareUnsigned(end, other.end); } return tmp; } + public boolean isEmpty() { + return start == end; + } + + public boolean contains(long addr) { + return Long.compareUnsigned(start, addr) <= 0 && Long.compareUnsigned(addr, end) < 0; + } + /** * Returns starting address. * diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRangeList.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRangeList.java new file mode 100644 index 0000000000..850d3f00f8 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRangeList.java @@ -0,0 +1,206 @@ +/* ### + * 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.util.bin.format.dwarf; + +import static ghidra.app.util.bin.format.dwarf.DWARFRangeListEntry.*; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFForm.*; + +import java.io.IOException; +import java.util.*; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.program.model.data.LEB128; +import ghidra.util.NumericUtilities; + +/** + * Represents a list of {@link DWARFRange}s. + */ +public class DWARFRangeList { + + public static final DWARFRangeList EMTPY = new DWARFRangeList(List.of()); + + /** + * Reads a v4 {@link DWARFRangeList} from the .debug_ranges stream. + * + * @param reader stream positioned to the start of a .debug_ranges range list + * @param cu the compUnit referring to this range + * @return new {@link DWARFRangeList}, never null + * @throws IOException if error reading + */ + public static DWARFRangeList readV4(BinaryReader reader, DWARFCompilationUnit cu) + throws IOException { + byte pointerSize = cu.getPointerSize(); + List ranges = new ArrayList<>(); + + DWARFRange cuRange = cu.getPCRange(); + long baseAddress = cuRange != null ? cuRange.getFrom() : 0; + + while (reader.hasNext()) { + // Read the beginning and ending addresses + long beginning = reader.readNextUnsignedValue(pointerSize); + long ending = reader.readNextUnsignedValue(pointerSize); // dwarf end addrs are exclusive + + // End of the list + if (beginning == 0 && ending == 0) { + break; + } + + // Check to see if this is a base address entry + if (beginning == -1 || + (pointerSize == 4 && beginning == NumericUtilities.MAX_UNSIGNED_INT32_AS_LONG)) { + baseAddress = ending; + continue; + } + + // Add the range to the list + ranges.add(new DWARFRange(baseAddress + beginning, baseAddress + ending)); + } + return new DWARFRangeList(ranges); + } + + /** + * Reads a v5 {@link DWARFRangeList} from the .debug_rnglists stream. + * + * @param reader stream positioned to the start of a .debug_rnglists range list + * @param cu the compUnit referring to this range + * @return new {@link DWARFRangeList}, never null + * @throws IOException if error reading + */ + public static DWARFRangeList readV5(BinaryReader reader, DWARFCompilationUnit cu) + throws IOException { + + List list = new ArrayList<>(); + + DWARFProgram dprog = cu.getProgram(); + long baseAddrFixup = dprog.getProgramBaseAddressFixup(); + long baseAddr = baseAddrFixup; + + while (reader.hasNext()) { + int rleId = reader.readNextUnsignedByte(); + if (rleId == DW_RLE_end_of_list) { + break; + } + switch (rleId) { + case DW_RLE_base_addressx: { + int addrIndex = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + baseAddr = dprog.getAddress(DW_FORM_addrx, addrIndex, cu); + break; + } + case DW_RLE_startx_endx: { + int startAddrIndex = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + int endAddrIndex = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + long start = dprog.getAddress(DW_FORM_addrx, startAddrIndex, cu); + long end = dprog.getAddress(DW_FORM_addrx, endAddrIndex, cu); + list.add(new DWARFRange(start, end)); + break; + } + case DW_RLE_startx_length: { + int startAddrIndex = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + int len = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + long start = dprog.getAddress(DW_FORM_addrx, startAddrIndex, cu); + list.add(new DWARFRange(start, start + len)); + break; + } + case DW_RLE_offset_pair: { + int startOfs = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + int endOfs = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + list.add(new DWARFRange(baseAddr+startOfs, baseAddr+endOfs)); + break; + } + case DW_RLE_base_address: { + baseAddr = reader.readNextUnsignedValue(cu.getPointerSize()) + baseAddrFixup; + break; + } + case DW_RLE_start_end: { + long startAddr = reader.readNextUnsignedValue(cu.getPointerSize()); + long endAddr = reader.readNextUnsignedValue(cu.getPointerSize()); + list.add(new DWARFRange(startAddr + baseAddrFixup, endAddr + baseAddrFixup)); + break; + } + case DW_RLE_start_length: { + long startAddr = reader.readNextUnsignedValue(cu.getPointerSize()); + int len = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + list.add( + new DWARFRange(startAddr + baseAddrFixup, startAddr + baseAddrFixup + len)); + break; + } + default: + throw new IOException( + "Unsupported DWARF Range List Entry type: %d".formatted(rleId)); + } + } + return new DWARFRangeList(list); + } + + private List ranges; + + public DWARFRangeList(DWARFRange singleRange) { + ranges = List.of(singleRange); + } + + public DWARFRangeList(List ranges) { + this.ranges = ranges; + } + + public boolean isEmpty() { + return ranges.isEmpty(); + } + + public long getFirstAddress() { + return getFirst().getFrom(); + } + + public DWARFRange getFirst() { + return ranges.get(0); + } + + public DWARFRange get(int index) { + return ranges.get(index); + } + + public List ranges() { + return ranges; + } + + public int getListCount() { + return ranges.size(); + } + + public DWARFRange getLast() { + return ranges.get(ranges.size() - 1); + } + + public DWARFRange getFlattenedRange() { + if (isEmpty()) { + return null; + } + if (ranges.size() == 1) { + return getFirst(); + } + + List copy = new ArrayList<>(ranges); + Collections.sort(copy); + DWARFRange first = copy.get(0); + DWARFRange last = copy.get(copy.size() - 1); + return new DWARFRange(first.getFrom(), last.getTo()); + } + + @Override + public String toString() { + return "DWARFRangeList: " + ranges; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRangeListEntry.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRangeListEntry.java new file mode 100644 index 0000000000..2526f27805 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRangeListEntry.java @@ -0,0 +1,36 @@ +/* ### + * 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.util.bin.format.dwarf; + +/** + * DWARF Range List Entry id + */ +public class DWARFRangeListEntry { + + public static final int DW_RLE_end_of_list = 0x00; + public static final int DW_RLE_base_addressx = 0x01; + public static final int DW_RLE_startx_endx = 0x02; + public static final int DW_RLE_startx_length = 0x03; + public static final int DW_RLE_offset_pair = 0x04; + public static final int DW_RLE_base_address = 0x05; + public static final int DW_RLE_start_end = 0x06; + public static final int DW_RLE_start_length = 0x07; + + public static String toString(long value) { + return DWARFUtil.toString(DWARFRangeListEntry.class, value); + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRangeListHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRangeListHeader.java new file mode 100644 index 0000000000..e7a6b7c971 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRangeListHeader.java @@ -0,0 +1,86 @@ +/* ### + * 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.util.bin.format.dwarf; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionNames; + +/** + * Header found at the start of a set of DWARFRangeList entries, which are stored sequentially + * in the {@link DWARFSectionNames#DEBUG_RNGLISTS .debug_rnglists} section. + */ +public class DWARFRangeListHeader extends DWARFIndirectTableHeader { + + public static DWARFRangeListHeader read(BinaryReader reader, int defaultIntSize) + throws IOException { + // length : dwarf_length + // version : 2 bytes + // address_size : 1 byte + // segment_selector_size : 1 byte + // offset entry count: 4 bytes + // offsets : array of elements are are dwarf_format_int sized + + long startOffset = reader.getPointerIndex(); + DWARFLengthValue lengthInfo = DWARFLengthValue.read(reader, defaultIntSize); + if (lengthInfo == null) { + return null; + } + + long endOffset = reader.getPointerIndex() + lengthInfo.length(); + short version = reader.readNextShort(); + if (version != 5) { + throw new DWARFException("DWARFRangeList (%x): unsupported DWARF version [%d]" + .formatted(startOffset, version)); + } + int addressSize = reader.readNextUnsignedByte(); + int segmentSelectorSize = reader.readNextUnsignedByte(); + int offsetEntryCount = reader.readNextUnsignedIntExact(); + long offsetListPosition = reader.getPointerIndex(); + + reader.setPointerIndex(endOffset); + if (segmentSelectorSize != 0) { + throw new IOException("Unsupported segmentSelectorSize: " + segmentSelectorSize); + } + + return new DWARFRangeListHeader(startOffset, endOffset, offsetListPosition, + lengthInfo.intSize(), offsetEntryCount, addressSize, segmentSelectorSize); + } + + private final int offsetEntryCount; + private final int offsetIntSize; + private final int addressSize; + private final int segmentSelectorSize; + + public DWARFRangeListHeader(long startOffset, long endOffset, long firstElementOffset, + int offsetIntSize, int offsetEntryCount, int addressSize, int segmentSelectorSize) { + super(startOffset, endOffset, firstElementOffset); + this.offsetIntSize = offsetIntSize; + this.offsetEntryCount = offsetEntryCount; + this.addressSize = addressSize; + this.segmentSelectorSize = segmentSelectorSize; + } + + @Override + public long getOffset(int index, BinaryReader reader) throws IOException { + if (index < 0 || offsetEntryCount <= index) { + throw new IOException("Invalid range list index: " + index); + } + return firstElementOffset + + reader.readUnsignedValue(firstElementOffset + (index * offsetIntSize), offsetIntSize); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFRegisterMappings.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRegisterMappings.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFRegisterMappings.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRegisterMappings.java index 880dccc36f..8c7cf96c41 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFRegisterMappings.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRegisterMappings.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; import ghidra.program.model.lang.Register; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFRegisterMappingsManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRegisterMappingsManager.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFRegisterMappingsManager.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRegisterMappingsManager.java index 385e2add5e..ae7ba3e677 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFRegisterMappingsManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRegisterMappingsManager.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; import java.util.*; import java.util.regex.Matcher; @@ -26,7 +26,6 @@ import org.jdom.*; import org.jdom.input.SAXBuilder; import generic.jar.ResourceFile; -import ghidra.app.util.bin.format.dwarf4.DWARFUtil; import ghidra.program.model.lang.*; import ghidra.util.Msg; import ghidra.util.xml.XmlUtilities; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFSourceInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFSourceInfo.java similarity index 62% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFSourceInfo.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFSourceInfo.java index 593fea9a90..d13619950b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFSourceInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFSourceInfo.java @@ -13,22 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.DW_AT_decl_line; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.DW_TAG_formal_parameter; +import static ghidra.app.util.bin.format.dwarf.DWARFTag.*; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; -import ghidra.app.util.bin.format.dwarf4.DIEAggregate; -import ghidra.app.util.bin.format.dwarf4.DebugInfoEntry; -import ghidra.app.util.bin.format.dwarf4.attribs.DWARFNumericAttribute; +import ghidra.app.util.bin.format.dwarf.attribs.DWARFNumericAttribute; /** - * Small class to hold the filename and line number info values from - * DWARF {@link DebugInfoEntry DIEs}. - * + * Represents the filename and line number info values from DWARF {@link DebugInfoEntry DIEs}. + * + * @param filename String filename + * @param lineNum int line number */ -public class DWARFSourceInfo { - +public record DWARFSourceInfo(String filename, int lineNum) { /** * Creates a new {@link DWARFSourceInfo} instance from the supplied {@link DIEAggregate} * if the info is present, otherwise returns null; @@ -88,23 +86,6 @@ public class DWARFSourceInfo { return sourceInfo != null ? sourceInfo.getDescriptionStr() : null; } - final private String filename; - final private int lineNum; - - private DWARFSourceInfo(String filename, int lineNum) { - this.filename = filename; - this.lineNum = lineNum; - } - - /** - * Returns the filename - * - * @return string filename. - */ - public String getFilename() { - return filename; - } - /** * Returns the source location info as a string formatted as "filename:linenum" * @@ -114,50 +95,6 @@ public class DWARFSourceInfo { return filename + ":" + lineNum; } - /** - * Returns the source location info as a string formatted as "File: filename Line: linenum" - * - * @return "File: filename Line: linenum" - */ - public String getDescriptionStr2() { - return String.format("File: %s Line: %d", filename, lineNum); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((filename == null) ? 0 : filename.hashCode()); - result = prime * result + lineNum; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof DWARFSourceInfo)) { - return false; - } - DWARFSourceInfo other = (DWARFSourceInfo) obj; - if (filename == null) { - if (other.filename != null) { - return false; - } - } - else if (!filename.equals(other.filename)) { - return false; - } - if (lineNum != other.lineNum) { - return false; - } - return true; - } - @Override public String toString() { return "DWARFSourceInfo [filename=" + filename + ", lineNum=" + lineNum + "]"; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFSourceLanguage.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFSourceLanguage.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFSourceLanguage.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFSourceLanguage.java index 61b4237299..5426b6d067 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFSourceLanguage.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFSourceLanguage.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.encoding; +package ghidra.app.util.bin.format.dwarf; /** * DWARF source lang consts from www.dwarfstd.org/doc/DWARF4.pdf. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFStringOffsetTableHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFStringOffsetTableHeader.java new file mode 100644 index 0000000000..ed7cf235ce --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFStringOffsetTableHeader.java @@ -0,0 +1,92 @@ +/* ### + * 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.util.bin.format.dwarf; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute; +import ghidra.app.util.bin.format.dwarf.attribs.DWARFForm; +import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionNames; + +/** + * Table of offsets that point into the string table. These tables are stored sequentially in the + * {@link DWARFSectionNames#DEBUG_STROFFSETS .debug_str_offsets} section. + *

+ * Elements in the table are referred to by index via {@link DWARFForm#DW_FORM_strx} and friends. + *

+ * The table's {@link #getFirstElementOffset()} is referred to by a compUnit's + * {@link DWARFAttribute#DW_AT_str_offsets_base} value. + */ +public class DWARFStringOffsetTableHeader extends DWARFIndirectTableHeader { + + /** + * Reads a string offset table header (found in the .debug_str_offsets section) + * + * @param dprog {@link DWARFProgram} + * @param reader {@link BinaryReader} + * @return new {@link DWARFStringOffsetTableHeader} instance + * @throws IOException if error reading + */ + public static DWARFStringOffsetTableHeader readV5(BinaryReader reader, int defaultIntSize) + throws IOException { + // length : dwarf_length + // version : 2 bytes + // padding : 2 bytes + // offsets : array of elements are are dwarf_format_int sized + + long startOffset = reader.getPointerIndex(); + DWARFLengthValue lengthInfo = DWARFLengthValue.read(reader, defaultIntSize); + if (lengthInfo == null) { + return null; + } + + long endOffset = reader.getPointerIndex() + lengthInfo.length(); + + short version = reader.readNextShort(); + if (version != 5) { + throw new DWARFException("Unsupported DWARF version [%d]".formatted(version)); + } + + /* int padding = */ reader.readNextShort(); + + long offsetArrayStart = reader.getPointerIndex(); + reader.setPointerIndex(endOffset); + + int count = (int) ((endOffset - offsetArrayStart) / lengthInfo.intSize()); + return new DWARFStringOffsetTableHeader(startOffset, endOffset, offsetArrayStart, + lengthInfo.intSize(), count); + } + + private final int count; + private final int intSize; + + public DWARFStringOffsetTableHeader(long startOffset, long endOffset, long firstElementOffset, + int intSize, int count) { + super(startOffset, endOffset, firstElementOffset); + this.intSize = intSize; + this.count = count; + } + + @Override + public long getOffset(int index, BinaryReader reader) throws IOException { + if (index < 0 || count <= index) { + throw new IOException( + "Invalid indirect string index: %d [0x%x]".formatted(index, index)); + } + return reader.readUnsignedValue(firstElementOffset + (index * intSize), intSize); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFTag.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFTag.java new file mode 100644 index 0000000000..1b221c20d7 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFTag.java @@ -0,0 +1,289 @@ +/* ### + * 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.util.bin.format.dwarf; + +import java.util.*; + +import ghidra.program.model.symbol.SymbolType; + +/** + * Identifier/purpose of a DWARF DIE record. + *

+ * Users of this enum should be tolerant of unknown tag id values. See + * {@link DWARFAbbreviation}'s tagId. + */ +public enum DWARFTag { + DW_TAG_array_type(0x1), + DW_TAG_class_type(0x2), + DW_TAG_entry_point(0x3), + DW_TAG_enumeration_type(0x4), + DW_TAG_formal_parameter(0x5), + DW_TAG_imported_declaration(0x8), + DW_TAG_label(0xa), + DW_TAG_lexical_block(0xb), + DW_TAG_member(0xd), + DW_TAG_pointer_type(0xf), + DW_TAG_reference_type(0x10), + DW_TAG_compile_unit(0x11), + DW_TAG_string_type(0x12), + DW_TAG_structure_type(0x13), + DW_TAG_subroutine_type(0x15), + DW_TAG_typedef(0x16), + DW_TAG_union_type(0x17), + DW_TAG_unspecified_parameters(0x18), + DW_TAG_variant(0x19), + DW_TAG_common_block(0x1a), + DW_TAG_common_inclusion(0x1b), + DW_TAG_inheritance(0x1c), + DW_TAG_inlined_subroutine(0x1d), + DW_TAG_module(0x1e), + DW_TAG_ptr_to_member_type(0x1f), + DW_TAG_set_type(0x20), + DW_TAG_subrange_type(0x21), + DW_TAG_with_stmt(0x22), + DW_TAG_access_declaration(0x23), + DW_TAG_base_type(0x24), + DW_TAG_catch_block(0x25), + DW_TAG_const_type(0x26), + DW_TAG_constant(0x27), + DW_TAG_enumerator(0x28), + DW_TAG_file_type(0x29), + DW_TAG_friend(0x2a), + DW_TAG_namelist(0x2b), + DW_TAG_namelist_item(0x2c), + DW_TAG_packed_type(0x2d), + DW_TAG_subprogram(0x2e), + DW_TAG_template_type_param(0x2f), + DW_TAG_template_value_param(0x30), + DW_TAG_thrown_type(0x31), + DW_TAG_try_block(0x32), + DW_TAG_variant_part(0x33), + DW_TAG_variable(0x34), + DW_TAG_volatile_type(0x35), + DW_TAG_dwarf_procedure(0x36), + DW_TAG_restrict_type(0x37), + DW_TAG_interface_type(0x38), + DW_TAG_namespace(0x39), + DW_TAG_imported_module(0x3a), + DW_TAG_unspecified_type(0x3b), + DW_TAG_partial_unit(0x3c), + DW_TAG_imported_unit(0x3d), + DW_TAG_mutable_type(0x3e), + DW_TAG_condition(0x3f), + DW_TAG_shared_type(0x40), + DW_TAG_type_unit(0x41), + DW_TAG_rvalue_reference_type(0x42), + DW_TAG_template_alias(0x43), + DW_TAG_call_site(0x48), + DW_TAG_call_site_parameter(0x49), + + DW_TAG_lo_user(0x4080), + DW_TAG_gnu_call_site(0x4109), + DW_TAG_gnu_call_site_parameter(0x410a), + DW_TAG_APPLE_ptrauth_type(0x4300), // Apple proprietary + DW_TAG_hi_user(0xffff), + + DW_TAG_UNKNOWN(-1); // fake ghidra tag + + private int id; + + DWARFTag(int id) { + this.id = id; + } + + /** + * Returns the name of this enum, falling back to the rawTagId value if this enum is the + * DW_TAG_UNKNOWN value. + * + * @param rawTagId tag id that corresponds to actual tag id found in the DWARF data + * @return string name of this enum + */ + public String name(int rawTagId) { + return this != DW_TAG_UNKNOWN + ? name() + : "DW_TAG_??? %d (0x%x)".formatted(rawTagId, rawTagId); + } + + public int getId() { + return id; + } + + public boolean isType() { + return TYPE_TAGS.contains(this); + } + + public boolean isNamedType() { + switch (this) { + case DW_TAG_base_type: + case DW_TAG_typedef: + case DW_TAG_namespace: + case DW_TAG_subprogram: + case DW_TAG_class_type: + case DW_TAG_interface_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_enumeration_type: + case DW_TAG_subroutine_type: + case DW_TAG_unspecified_type: + return true; + default: + return false; + } + } + + /** + * Returns true if the children of this DIE are within a new namespace. + *

+ * Ie. Namespaces, subprogram, class, interface, struct, union, enum + * + * @return true if the children of this DIE are within a new namespace + */ + public boolean isNameSpaceContainer() { + switch (this) { + case DW_TAG_namespace: + case DW_TAG_subprogram: + case DW_TAG_lexical_block: + case DW_TAG_class_type: + case DW_TAG_interface_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_enumeration_type: + return true; + default: + return false; + } + } + + /** + * Returns true if this DIE defines a structure-like element (class, struct, interface, union). + * + * @return true if this DIE defines a structure-like element (class, struct, interface, union) + */ + public boolean isStructureType() { + switch (this) { + case DW_TAG_class_type: + case DW_TAG_interface_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + return true; + default: + return false; + } + } + + public boolean isFuncDefType() { + switch (this) { + case DW_TAG_subprogram: + case DW_TAG_subroutine_type: + return true; + default: + return false; + } + } + + /** + * Returns a string that describes what kind of object is specified by the {@link DIEAggregate}. + *

+ * Used to create a name for anonymous types. + * + * @return String describing the type of the DIEA. + */ + public String getContainerTypeName() { + switch (this) { + case DW_TAG_structure_type: + return "struct"; + case DW_TAG_class_type: + return "class"; + case DW_TAG_enumeration_type: + return "enum"; + case DW_TAG_union_type: + return "union"; + case DW_TAG_lexical_block: + return "lexical_block"; + case DW_TAG_subprogram: + return "subprogram"; + case DW_TAG_subroutine_type: + return "subr"; + case DW_TAG_variable: + return "var"; + default: + return "unknown"; + } + } + + /** + * Returns the {@link SymbolType} that corresponds to a DWARF tag + *

+ * The mapping between tag type and SymbolType is not exact. There is no matching + * SymbolType for a DWARF static variable, so "LOCAL_VAR" is used currently. + *

+ * This mainly is used in constructing a NamespacePath, and the only critical usage + * there is Namespace vs. Class vs. everything else. + * + * @return {@link SymbolType} + */ + public SymbolType getSymbolType() { + switch (this) { + + case DW_TAG_subprogram: + return SymbolType.FUNCTION; + + case DW_TAG_structure_type: + case DW_TAG_interface_type: + case DW_TAG_class_type: + case DW_TAG_union_type: + case DW_TAG_enumeration_type: + return SymbolType.CLASS; + + case DW_TAG_namespace: + return SymbolType.NAMESPACE; + case DW_TAG_formal_parameter: + return SymbolType.PARAMETER; + + case DW_TAG_variable: + return SymbolType.LOCAL_VAR; + + case DW_TAG_base_type: + case DW_TAG_typedef: + default: + return null; + + } + } + + //--------------------------------------------------------------------------------------------- + + public static DWARFTag of(int tagId) { + return lookupMap.getOrDefault(tagId, DW_TAG_UNKNOWN); + } + + private static Map lookupMap = buildLookup(); + + private static Map buildLookup() { + Map result = new HashMap<>(); + for (DWARFTag tag : values()) { + result.put(tag.id, tag); + } + return result; + } + + private static final Set TYPE_TAGS = EnumSet.of(DW_TAG_base_type, DW_TAG_array_type, + DW_TAG_typedef, DW_TAG_class_type, DW_TAG_interface_type, DW_TAG_structure_type, + DW_TAG_union_type, DW_TAG_enumeration_type, DW_TAG_pointer_type, DW_TAG_reference_type, + DW_TAG_rvalue_reference_type, DW_TAG_const_type, DW_TAG_volatile_type, + DW_TAG_ptr_to_member_type, DW_TAG_unspecified_type, DW_TAG_subroutine_type); + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFUnitHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFUnitHeader.java new file mode 100644 index 0000000000..f3e97ccc11 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFUnitHeader.java @@ -0,0 +1,191 @@ +/* ### + * 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.util.bin.format.dwarf; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * The base class for a set of headers that share a common field layout. + */ +public class DWARFUnitHeader { + /** + * Reads the initial fields found in a unit header. + * + * @param dprog {@link DWARFProgram} + * @param reader {@link BinaryReader} .debug_info stream + * @param abbrReader {@link BinaryReader} .debug_abbr stream + * @param unitNumber ordinal of this item + * @param monitor {@link TaskMonitor} + * @return a unit header (only comp units for now), or null if at end-of-list + * @throws DWARFException if invalid dwarf data + * @throws IOException if error reading data + * @throws CancelledException if cancelled + */ + public static DWARFUnitHeader read(DWARFProgram dprog, BinaryReader reader, + BinaryReader abbrReader, int unitNumber, TaskMonitor monitor) + throws DWARFException, IOException, CancelledException { + // unit_length : dwarf_length + // version : 2 bytes + // unit type : 1 byte [ version >= 5 ] + + long startOffset = reader.getPointerIndex(); + DWARFLengthValue lengthInfo = DWARFLengthValue.read(reader, dprog.getDefaultIntSize()); + if (lengthInfo == null) { + return null; + } + + long endOffset = reader.getPointerIndex() + lengthInfo.length(); + short version = reader.readNextShort(); + if (version < 2) { + throw new DWARFException("Unsupported DWARF version [%d]".formatted(version)); + } + + DWARFUnitHeader partial = new DWARFUnitHeader(dprog, startOffset, endOffset, + lengthInfo.length(), lengthInfo.intSize(), version, unitNumber); + + if (2 <= version && version <= 4) { + return DWARFCompilationUnit.readV4(partial, reader, abbrReader, monitor); + } + int unitType = reader.readNextUnsignedByte(); + switch (unitType) { + case DWARFUnitType.DW_UT_compile: + return DWARFCompilationUnit.readV5(partial, reader, abbrReader, monitor); + case DWARFUnitType.DW_UT_type: + case DWARFUnitType.DW_UT_partial: + case DWARFUnitType.DW_UT_skeleton: + case DWARFUnitType.DW_UT_split_compile: + case DWARFUnitType.DW_UT_split_type: + default: + throw new DWARFException("Unsupported unitType %d, %s".formatted(unitType, + DWARFUtil.toString(DWARFUnitType.class, unitType))); + } + } + + /** + * Reference to the owning {@link DWARFProgram}. + */ + protected final DWARFProgram dprog; + + /** + * Offset in the debug_info section of this compUnit's header + */ + protected final long startOffset; + + /** + * Offset in the debug_info section of the end of this compUnit. (right after + * the last DIE record) + */ + protected final long endOffset; + + /** + * Length in bytes of this compUnit header and DIE records. + */ + protected final long length; + + /** + * size of integers, 4=int32 or 8=int64 + */ + protected final int intSize; + + /** + * DWARF ver number, as read from the compunit structure, currently not used but being kept. + */ + protected final short dwarfVersion; + + /** + * Sequential number of this unit + */ + protected final int unitNumber; + + protected DWARFUnitHeader(DWARFUnitHeader other) { + this.dprog = other.dprog; + this.startOffset = other.startOffset; + this.endOffset = other.endOffset; + this.length = other.length; + this.intSize = other.intSize; + this.dwarfVersion = other.dwarfVersion; + this.unitNumber = other.unitNumber; + } + + protected DWARFUnitHeader(DWARFProgram dprog, long startOffset, long endOffset, long length, + int intSize, short version, int unitNumber) { + this.dprog = dprog; + this.startOffset = startOffset; + this.endOffset = endOffset; + this.length = length; + this.intSize = intSize; + this.dwarfVersion = version; + this.unitNumber = unitNumber; + } + + public DWARFProgram getProgram() { + return dprog; + } + + public short getDWARFVersion() { + return dwarfVersion; + } + + /** + * An unsigned long (4 bytes in 32-bit or 8 bytes in 64-bit format) representing + * the length of the .debug_info contribution for this unit, not including the length + * field itself. + * + * @return the length in bytes of this unit + */ + public long getLength() { + return this.length; + } + + /** + * Returns the byte offset to the start of this unit. + * @return the byte offset to the start of this unit + */ + public long getStartOffset() { + return this.startOffset; + } + + /** + * Returns the byte offset to the end of this unit. + * @return the byte offset to the end of this unit + */ + public long getEndOffset() { + return this.endOffset; + } + + /** + * Returns either 4 (for DWARF_32) or 8 (for DWARF_64) depending on the current unit format + * + * @return size of ints in this unit (4 or 8) + */ + public int getIntSize() { + return this.intSize; + } + + /** + * Return the ordinal number of this unit + * + * @return ordinal of this unit + */ + public int getUnitNumber() { + return unitNumber; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFUnitType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFUnitType.java new file mode 100644 index 0000000000..291684f3a3 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFUnitType.java @@ -0,0 +1,29 @@ +/* ### + * 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.util.bin.format.dwarf; + +public class DWARFUnitType { + public static final int DW_UT_compile = 0x01; + public static final int DW_UT_type = 0x02; + public static final int DW_UT_partial = 0x03; + public static final int DW_UT_skeleton = 0x04; + public static final int DW_UT_split_compile = 0x05; + public static final int DW_UT_split_type = 0x06; + + public static final int DW_UT_lo_user = 0x80; + public static final int DW_UT_hi_user = 0xff; + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFUtil.java similarity index 62% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFUtil.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFUtil.java index 87caeee053..310a74e11d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFUtil.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFUtil.java @@ -13,7 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4; +package ghidra.app.util.bin.format.dwarf; + +import static ghidra.app.util.bin.format.dwarf.DWARFTag.*; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; import java.io.IOException; import java.lang.reflect.Field; @@ -24,13 +27,9 @@ import java.util.regex.Pattern; import generic.jar.ResourceFile; import ghidra.app.cmd.comments.AppendCommentCmd; -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.dwarf4.attribs.DWARFAttributeValue; -import ghidra.app.util.bin.format.dwarf4.attribs.DWARFNumericAttribute; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag; -import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException; -import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram; +import ghidra.app.util.bin.format.dwarf.attribs.DWARFAttributeValue; +import ghidra.app.util.bin.format.dwarf.attribs.DWARFNumericAttribute; +import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException; import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSpace; @@ -38,8 +37,6 @@ import ghidra.program.model.data.*; import ghidra.program.model.lang.*; import ghidra.program.model.listing.*; import ghidra.program.model.pcode.Varnode; -import ghidra.program.model.symbol.SymbolType; -import ghidra.util.Conv; public class DWARFUtil { /** @@ -53,7 +50,7 @@ public class DWARFUtil { * @return the String name of the matching field. */ public static String toString(Class clazz, int value) { - return toString(clazz, Conv.intToLong(value)); + return toString(clazz, Integer.toUnsignedLong(value)); } /** @@ -101,77 +98,9 @@ public class DWARFUtil { //-------------------------------------- - /** - * Returns a string that describes what kind of object is specified by the {@link DIEAggregate}. - *

- * Used to create a name for anonymous types. - * - * @param diea {@link DIEAggregate} - * @return String describing the type of the DIEA. - */ - public static String getContainerTypeName(DIEAggregate diea) { - switch (diea.getTag()) { - case DWARFTag.DW_TAG_structure_type: - return "struct"; - case DWARFTag.DW_TAG_class_type: - return "class"; - case DWARFTag.DW_TAG_enumeration_type: - return "enum"; - case DWARFTag.DW_TAG_union_type: - return "union"; - case DWARFTag.DW_TAG_lexical_block: - return "lexical_block"; - case DWARFTag.DW_TAG_subprogram: - return "subprogram"; - case DWARFTag.DW_TAG_subroutine_type: - return "subr"; - case DWARFTag.DW_TAG_variable: - return "var"; - } - return "unknown"; - } - //------------------------------------------- - /** - * Returns the {@link SymbolType} that corresponds to the specified {@link DIEAggregate}. - *

- * The mapping between DIE type and SymbolType is not exact. There is no matching - * SymbolType for a DWARF static variable, so "LOCAL_VAR" is used currently. - *

- * This mainly is used in constructing a NamespacePath, and the only critical usage - * there is Namespace vs. Class vs. everything else. - * - * @param diea {@link DIEAggregate} to query - * @return {@link SymbolType} - */ - public static SymbolType getSymbolTypeFromDIE(DIEAggregate diea) { - switch (diea.getTag()) { - case DWARFTag.DW_TAG_subprogram: - return SymbolType.FUNCTION; - - case DWARFTag.DW_TAG_structure_type: - case DWARFTag.DW_TAG_interface_type: - case DWARFTag.DW_TAG_class_type: - case DWARFTag.DW_TAG_union_type: - case DWARFTag.DW_TAG_enumeration_type: - return SymbolType.CLASS; - - case DWARFTag.DW_TAG_namespace: - return SymbolType.NAMESPACE; - default: - case DWARFTag.DW_TAG_base_type: - case DWARFTag.DW_TAG_typedef: - return null; - - case DWARFTag.DW_TAG_formal_parameter: - return SymbolType.PARAMETER; - - case DWARFTag.DW_TAG_variable: - return SymbolType.LOCAL_VAR; - } - } private static Pattern MANGLED_NESTING_REGEX = Pattern.compile("(.*_Z)?N([0-9]+.*)"); @@ -223,9 +152,9 @@ public class DWARFUtil { DWARFProgram prog = die.getProgram(); for (DebugInfoEntry childDIE : die.getChildren(DWARFTag.DW_TAG_subprogram)) { DIEAggregate childDIEA = prog.getAggregate(childDIE); - String linkage = childDIEA.getString(DWARFAttribute.DW_AT_linkage_name, null); + String linkage = childDIEA.getString(DW_AT_linkage_name, null); if (linkage == null) { - linkage = childDIEA.getString(DWARFAttribute.DW_AT_MIPS_linkage_name, null); + linkage = childDIEA.getString(DW_AT_MIPS_linkage_name, null); } if (linkage != null) { @@ -236,7 +165,7 @@ public class DWARFUtil { } } } - return Collections.EMPTY_LIST; + return List.of(); } /** @@ -279,9 +208,10 @@ public class DWARFUtil { for (DebugInfoEntry childDIE : parent.getChildren()) { DIEAggregate childDIEA = prog.getAggregate(childDIE); if (diea == childDIEA || diea.getOffset() == childDIEA.getOffset()) { - return "anon_" + getContainerTypeName(childDIEA) + "_" + typeDefCount; + return "anon_%s_%d".formatted(childDIEA.getTag().getContainerTypeName(), + typeDefCount); } - if (childDIEA.isNamedType()) { + if (childDIEA.getTag().isNamedType()) { typeDefCount++; } } @@ -328,7 +258,7 @@ public class DWARFUtil { sb.append(childName); } - return "anon_" + getContainerTypeName(diea) + "_for_" + sb.toString(); + return "anon_" + diea.getTag().getContainerTypeName() + "_for_" + sb.toString(); } /** @@ -339,7 +269,7 @@ public class DWARFUtil { * @return formatted string, example "80_5_73dc6de9" (80 bytes, 5 fields, hex hash of field names) */ public static String getStructLayoutFingerprint(DIEAggregate diea) { - long structSize = diea.getUnsignedLong(DWARFAttribute.DW_AT_byte_size, 0); + long structSize = diea.getUnsignedLong(DW_AT_byte_size, 0); int memberCount = 0; List memberNames = new ArrayList<>(); for (DebugInfoEntry childEntry : diea.getHeadFragment().getChildren()) { @@ -348,7 +278,7 @@ public class DWARFUtil { continue; } DIEAggregate childDIEA = diea.getProgram().getAggregate(childEntry); - if (childDIEA.hasAttribute(DWARFAttribute.DW_AT_external)) { + if (childDIEA.hasAttribute(DW_AT_external)) { continue; } memberCount++; @@ -357,7 +287,7 @@ public class DWARFUtil { int memberOffset = 0; try { memberOffset = - childDIEA.parseDataMemberOffset(DWARFAttribute.DW_AT_data_member_location, 0); + childDIEA.parseDataMemberOffset(DW_AT_data_member_location, 0); } catch (DWARFExpressionException | IOException e) { // ignore, leave as default value 0 @@ -372,26 +302,6 @@ public class DWARFUtil { return String.format("%d_%d_%08x", structSize, memberCount, memberNames.hashCode()); } - /** - * Create a name for a lexical block, with "_" separated numbers indicating nesting - * information of the lexical block. - * - * @param diea {@link DIEAggregate} pointing to a lexical block entry. - * @return string, ie. "lexical_block_1_2_3" - */ - public static String getLexicalBlockName(DIEAggregate diea) { - return "lexical_block" + getLexicalBlockNameWorker(diea.getHeadFragment()); - } - - private static String getLexicalBlockNameWorker(DebugInfoEntry die) { - if (die.getTag() == DWARFTag.DW_TAG_lexical_block || - die.getTag() == DWARFTag.DW_TAG_inlined_subroutine) { - return "%s_%d".formatted(getLexicalBlockNameWorker(die.getParent()), - die.getPositionInParent()); - } - return ""; - } - /** * Append a string to a {@link DataType}'s description. * @@ -468,100 +378,6 @@ public class DWARFUtil { return cu; } - /** - * Read an offset value who's size depends on the DWARF format: 32 vs 64. - *

- * @param reader BinaryReader pointing to the value to read - * @param dwarfFormat - See {@link DWARFCompilationUnit#DWARF_32} and {@link DWARFCompilationUnit#DWARF_64}. - * @return the offset value - * @throws IOException if an I/O error occurs or bad dwarfFormat value - */ - public static long readOffsetByDWARFformat(BinaryReader reader, int dwarfFormat) - throws IOException { - switch (dwarfFormat) { - case DWARFCompilationUnit.DWARF_32: - return reader.readNextUnsignedInt(); - case DWARFCompilationUnit.DWARF_64: - return reader.readNextLong(); - } - throw new IOException("Unknown DWARF Format Value: " + dwarfFormat); - } - - /** - * Read a variable-sized unsigned integer and return it as a java signed long. - *

- * @param reader {@link BinaryReader} to read the data from - * @param pointerSize number of bytes the value is stored in, must be 1, 2, 4, or 8. - * @return unsigned long integer value. - * @throws IOException if error - */ - public static long readVarSizedULong(BinaryReader reader, int pointerSize) throws IOException { - switch (pointerSize) { - case 1: - return reader.readNextUnsignedByte(); - case 2: - return reader.readNextUnsignedShort(); - case 4: - return reader.readNextUnsignedInt(); - case 8: - return reader.readNextLong() /* no unsigned long mask possible */; - } - throw new IOException("Unsupported variable-sized int: " + pointerSize); - } - - /** - * Read a variable-sized unsigned integer and return it as a java signed int. - *

- * Unsigned 32 bit int values larger than java's signed Integer.MAX_VALUE are not - * supported and will throw an IOException. - * - * @param reader {@link BinaryReader} to read the data from - * @param size number of bytes the integer value is stored in, must be 1, 2 or 4. - * @return unsigned integer value. - * @throws IOException if error - */ - public static int readVarSizedUInt(BinaryReader reader, int size) throws IOException { - switch (size) { - case 1: - return reader.readNextUnsignedByte(); - case 2: - return reader.readNextUnsignedShort(); - case 4: - long l = reader.readNextUnsignedInt(); - if (l < 0 || l > Integer.MAX_VALUE) { - throw new IOException("Unsigned int value too large: " + l); - } - return (int) l; - } - throw new IOException("Unsupported variable-sized int: " + size); - } - - /** - * Reads a variable-sized unsigned 'address' value from a {@link BinaryReader} and - * returns it as a 64 bit java long. - *

- * The valid pointerSizes are 1, 2, 4, and 8. - *

- * @param reader {@link BinaryReader} to read the data from - * @param pointerSize number of bytes the value is stored in, must be 1, 2, 4, or 8. - * @return unsigned long value. - * @throws IOException if error - */ - public static long readAddressAsLong(BinaryReader reader, byte pointerSize) throws IOException { - switch (pointerSize) { - case 1: - return reader.readNextUnsignedByte(); - case 2: - return reader.readNextUnsignedShort(); - case 4: - return reader.readNextUnsignedInt(); - case 8: - return reader.readNextLong(); - } - throw new IllegalArgumentException( - "Unknown pointer size: 0x" + Integer.toHexString(pointerSize)); - } - public static boolean isThisParam(DIEAggregate paramDIEA) { // DWARF has multiple ways of indicating a DW_TAG_formal_parameter is // the "this" parameter, and different versions of different toolchains @@ -576,14 +392,14 @@ public class DWARFUtil { // referencing the param that points to the object instance (ie. "this"). // String paramName = paramDIEA.getName(); - if (paramDIEA.getBool(DWARFAttribute.DW_AT_artificial, false) || + if (paramDIEA.getBool(DW_AT_artificial, false) || Function.THIS_PARAM_NAME.equals(paramName)) { return true; } DIEAggregate funcDIEA = paramDIEA.getParent(); DWARFAttributeValue dwATObjectPointer = - funcDIEA.getAttribute(DWARFAttribute.DW_AT_object_pointer); + funcDIEA.getAttribute(DW_AT_object_pointer); if (dwATObjectPointer != null && dwATObjectPointer instanceof DWARFNumericAttribute dnum && paramDIEA.hasOffset(dnum.getUnsignedValue())) { return true; @@ -592,7 +408,7 @@ public class DWARFUtil { // If the variable is not named, check to see if the parent of the function // is a struct/class, and the parameter points to it DIEAggregate classDIEA = funcDIEA.getParent(); - if (paramName == null && classDIEA != null && classDIEA.isStructureType()) { + if (paramName == null && classDIEA != null && classDIEA.getTag().isStructureType()) { // Check to see if the parent data type equals the parameters' data type return isPointerTo(classDIEA, paramDIEA.getTypeRef()); } @@ -601,94 +417,15 @@ public class DWARFUtil { } public static boolean isPointerTo(DIEAggregate targetDIEA, DIEAggregate testDIEA) { - return testDIEA != null && testDIEA.getTag() == DWARFTag.DW_TAG_pointer_type && + return testDIEA != null && testDIEA.getTag() == DW_TAG_pointer_type && testDIEA.getTypeRef() == targetDIEA; } public static boolean isPointerDataType(DIEAggregate diea) { - while (diea.getTag() == DWARFTag.DW_TAG_typedef) { + while (diea.getTag() == DW_TAG_typedef) { diea = diea.getTypeRef(); } - return diea.getTag() == DWARFTag.DW_TAG_pointer_type; - } - - /** - * Returns the {@link DIEAggregate} of a typedef that points to the specified datatype. - *

- * Returns null if there is no typedef pointing to the specified DIEA or if there are - * multiple. - * - * @param diea {@link DIEAggregate} of a data type that might be the target of typedefs. - * @return {@link DIEAggregate} of the singular typedef that points to the arg, otherwise - * null if none or multiple found. - */ - public static DIEAggregate getReferringTypedef(DIEAggregate diea) { - if (diea == null) { - return null; - } - List referers = - diea.getProgram().getTypeReferers(diea, DWARFTag.DW_TAG_typedef); - return (referers.size() == 1) ? referers.get(0) : null; - } - - public static class LengthResult { - public final long length; - public final int format; // either DWARF_32 or DWARF_64 - - private LengthResult(long length, int format) { - this.length = length; - this.format = format; - } - } - - /** - * Read a variable-length length value from the stream. - *

- * - * @param reader {@link BinaryReader} stream to read from - * @param program Ghidra {@link Program} - * @return new {@link LengthResult}, never null; length == 0 should be checked for and treated - * specially - * @throws IOException if io error - * @throws DWARFException if invalid values - */ - public static LengthResult readLength(BinaryReader reader, Program program) - throws IOException, DWARFException { - long length = reader.readNextUnsignedInt(); - int format; - - if (length == 0xffffffffL) { - // Length of 0xffffffff implies 64-bit DWARF format - // Mostly untested as there is no easy way to force the compiler - // to generate this - length = reader.readNextLong(); - format = DWARFCompilationUnit.DWARF_64; - } - else if (length >= 0xfffffff0L) { - // Length of 0xfffffff0 or greater is reserved for DWARF - throw new DWARFException("Reserved DWARF length value: " + Long.toHexString(length) + - ". Unknown extension."); - } - else if (length == 0) { - // Test for special case of weird BE MIPS 64bit length value. - // Instead of following DWARF std (a few lines above with length == MAX_INT), - // it writes a raw 64bit long (BE). The upper 32 bits (already read as length) will - // always be 0 since super-large binaries from that system weren't really possible. - // The next 32 bits will be the remainder of the value. - if (reader.isBigEndian() && program.getDefaultPointerSize() == 8) { - length = reader.readNextUnsignedInt(); - format = DWARFCompilationUnit.DWARF_64; - } - else { - // length 0 signals an error to caller - format = DWARFCompilationUnit.DWARF_32; // doesn't matter - } - } - else { - format = DWARFCompilationUnit.DWARF_32; - } - - return new LengthResult(length, format); + return diea.getTag() == DW_TAG_pointer_type; } /** @@ -700,7 +437,7 @@ public class DWARFUtil { * @param lang {@link Language} to query * @param name name of the option in the ldefs file * @return file pointed to by the specified external_name tool entry - * @throws IOException + * @throws IOException if not a sleigh lang */ public static ResourceFile getLanguageExternalFile(Language lang, String name) throws IOException { @@ -715,14 +452,13 @@ public class DWARFUtil { * * @param lang {@link Language} to get base definition directory * @return base directory for language definition files - * @throws IOException + * @throws IOException if not a sleigh lang */ public static ResourceFile getLanguageDefinitionDirectory(Language lang) throws IOException { LanguageDescription langDesc = lang.getLanguageDescription(); - if (!(langDesc instanceof SleighLanguageDescription)) { + if (!(langDesc instanceof SleighLanguageDescription sld)) { throw new IOException("Not a Sleigh Language: " + lang.getLanguageID()); } - SleighLanguageDescription sld = (SleighLanguageDescription) langDesc; ResourceFile defsFile = sld.getDefsFile(); ResourceFile parentFile = defsFile.getParentFile(); return parentFile; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFVariable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFVariable.java similarity index 89% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFVariable.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFVariable.java index e704feceda..b1c98bb8be 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFVariable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFVariable.java @@ -13,16 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.*; +import static ghidra.app.util.bin.format.dwarf.DWARFTag.*; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; import java.io.IOException; import java.util.*; -import ghidra.app.util.bin.format.dwarf4.*; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag; -import ghidra.app.util.bin.format.dwarf4.expression.*; +import ghidra.app.util.bin.format.dwarf.expression.*; import ghidra.program.model.address.Address; import ghidra.program.model.data.*; import ghidra.program.model.lang.Register; @@ -34,7 +33,7 @@ import ghidra.util.Msg; import ghidra.util.exception.InvalidInputException; /** - * Represents a function parameter, local variable, or global variable. + * Represents a function's parameter or local variable; or a global variable. */ public class DWARFVariable { /** @@ -102,7 +101,7 @@ public class DWARFVariable { private final DWARFProgram program; private final DWARFFunction dfunc; - public DWARFNameInfo name; + public DWARFName name; public DataType type; public long lexicalOffset; // offset inside function where variable storage becomes valid public boolean isOutputParameter; // changes to parameter value escape back to the calling location @@ -245,16 +244,17 @@ public class DWARFVariable { private boolean readParamStorage(DIEAggregate diea) { try { if (DataTypeComponent.usesZeroLengthComponent(type)) { - Msg.warn(this, "DWARF: zero-length function parameter %s:%s in %s@%s".formatted( - name.getName(), type.getName(), dfunc.name.getName(), dfunc.address)); + program.getImportSummary().paramZeroLenDataType++; + program.logWarningAt(dfunc.address, dfunc.name.getName(), + "Zero-length function parameter: %s : %s".formatted(name.getName(), + type.getName())); return false; } - DWARFLocation topLocation = DWARFLocation.getTopLocation( - diea.getAsLocation(DW_AT_location, dfunc.getRange()), dfunc.address.getOffset()); - if (topLocation == null) { + DWARFLocation paramLoc = diea.getLocation(DW_AT_location, dfunc.getEntryPc()); + if (paramLoc == null) { return false; } - return readStorage(diea, topLocation); + return readStorage(diea, paramLoc); } catch (IOException e) { diea.getProgram().getImportSummary().exprReadError++; @@ -264,8 +264,7 @@ public class DWARFVariable { private boolean readLocalVariableStorage(DIEAggregate diea) { try { - DWARFLocation location = DWARFLocation - .getFirstLocation(diea.getAsLocation(DW_AT_location, dfunc.getRange())); + DWARFLocation location = diea.getLocation(DW_AT_location, dfunc.getEntryPc()); if (location == null) { return false; } @@ -274,7 +273,7 @@ public class DWARFVariable { // with the address from the dwarf range. This gives slightly better results with // test binaries in the decompiler, but might be wrong for other toolchains. // If it causes problems, always use the address from the location's range. - lexicalOffset = location.getRange().getFrom() - dfunc.address.getOffset(); + lexicalOffset = location.getOffset(dfunc.getEntryPc()); } return readStorage(diea, location); @@ -289,16 +288,16 @@ public class DWARFVariable { DWARFProgram prog = diea.getProgram(); try { - DWARFLocation location = DWARFLocation - .getFirstLocation(diea.getAsLocation(DW_AT_location, DWARFRange.EMPTY)); + DWARFLocationList locList = diea.getLocationList(DW_AT_location); + DWARFLocation location = locList.getFirstLocation(); if (location == null) { return false; } DWARFExpressionEvaluator exprEvaluator = - DWARFExpressionEvaluator.create(diea.getHeadFragment()); + new DWARFExpressionEvaluator(diea.getCompilationUnit()); - DWARFExpression expr = exprEvaluator.readExpr(location.getLocation()); + DWARFExpression expr = exprEvaluator.readExpr(location.getExpr()); exprEvaluator.evaluate(expr); if (exprEvaluator.getRawLastRegister() != -1) { @@ -333,17 +332,17 @@ public class DWARFVariable { return false; } - lexicalOffset = location.getRange().getFrom() - dfunc.address.getOffset(); + lexicalOffset = location.getOffset(dfunc.address.getOffset()); DWARFProgram prog = diea.getProgram(); DWARFImportSummary importSummary = prog.getImportSummary(); try { DWARFExpressionEvaluator exprEvaluator = - DWARFExpressionEvaluator.create(diea.getHeadFragment()); + new DWARFExpressionEvaluator(diea.getCompilationUnit()); exprEvaluator.setFrameBase(dfunc.frameBase); - DWARFExpression expr = exprEvaluator.readExpr(location.getLocation()); + DWARFExpression expr = exprEvaluator.readExpr(location.getExpr()); exprEvaluator.evaluate(expr); long res = exprEvaluator.pop(); @@ -391,12 +390,10 @@ public class DWARFVariable { } if ((type.getLength() > reg.getMinimumByteSize())) { importSummary.varFitError++; - - Msg.warn(this, - "%s %s [%s, size=%d] for function %s@%s can not fit into specified register %s, size=%d, skipping. DWARF DIE: %s" + program.logWarningAt(dfunc.address, dfunc.name.getName(), + "%s %s [%s, size=%d] can not fit into specified register %s, size=%d" .formatted(getVarTypeName(diea), name.getName(), type.getName(), - type.getLength(), dfunc.name.getName(), dfunc.address, - reg.getName(), reg.getMinimumByteSize(), diea.getHexOffset())); + type.getLength(), reg.getName(), reg.getMinimumByteSize())); return false; } setRegisterStorage(List.of(reg)); @@ -410,7 +407,7 @@ public class DWARFVariable { "%s location error for function %s@%s, %s: %s, DWARF DIE: %s, unsupported location information." .formatted(getVarTypeName(diea), dfunc.name.getName(), dfunc.address, name.getName(), - DWARFExpression.exprToString(location.getLocation(), diea), + DWARFExpression.exprToString(location.getExpr(), diea), diea.getHexOffset())); return false; } @@ -425,7 +422,7 @@ public class DWARFVariable { } private String getVarTypeName(DIEAggregate diea) { - return diea.getTag() == DWARFTag.DW_TAG_formal_parameter ? "Parameter" : "Variable"; + return diea.getTag() == DW_TAG_formal_parameter ? "Parameter" : "Variable"; } public int getStorageSize() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DataTypeGraphComparator.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DataTypeGraphComparator.java similarity index 99% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DataTypeGraphComparator.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DataTypeGraphComparator.java index 6ce5580fc2..2da22b1022 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DataTypeGraphComparator.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DataTypeGraphComparator.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; import java.util.*; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DebugInfoEntry.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DebugInfoEntry.java similarity index 54% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DebugInfoEntry.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DebugInfoEntry.java index 196697924d..3fcb4359d1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DebugInfoEntry.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DebugInfoEntry.java @@ -13,16 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4; +package ghidra.app.util.bin.format.dwarf; import java.io.IOException; import java.util.*; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.dwarf4.attribs.*; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag; -import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram; +import ghidra.app.util.bin.format.dwarf.attribs.*; +import ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.AttrDef; import ghidra.program.model.data.LEB128; import ghidra.util.datastruct.IntArrayList; @@ -36,17 +34,10 @@ import ghidra.util.datastruct.IntArrayList; */ public class DebugInfoEntry { - /** - * List of common DWARF attributes that are not used currently in Ghidra. These attributes values will be - * thrown away during reading to save some memory. There are lots of attributes that Ghidra doesn't - * currently use, but they do not appear frequently enough to consume a significant amount of memory. - */ - private static final Set ATTRIBUTES_TO_SKIP = - Set.of(DWARFAttribute.DW_AT_sibling, DWARFAttribute.DW_AT_accessibility); - private final DWARFCompilationUnit compilationUnit; private final DWARFAbbreviation abbreviation; private final DWARFAttributeValue[] attributes; + private final int[] attrOffsets; private final long offset; private final int dieIndex; @@ -54,71 +45,75 @@ public class DebugInfoEntry { * Read a DIE record. * * @param reader {@link BinaryReader} positioned at the start of a DIE record - * @param unit the compunit that contains the DIE + * @param cu the compunit that contains the DIE * @param dieIndex the index of the DIE - * @param attributeFactory the {@link DWARFAttributeFactory} to use to deserialize attribute - * values * @return new DIE instance * @throws IOException if error reading data, or bad DWARF */ - public static DebugInfoEntry read(BinaryReader reader, DWARFCompilationUnit unit, - int dieIndex, DWARFAttributeFactory attributeFactory) throws IOException { + public static DebugInfoEntry read(BinaryReader reader, DWARFCompilationUnit cu, int dieIndex) + throws IOException { long offset = reader.getPointerIndex(); - int abbreviationCode = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + int ac = reader.readNextUnsignedVarIntExact(LEB128::unsigned); // Check for terminator DIE - if (abbreviationCode == 0) { - return new DebugInfoEntry(unit, offset, -1, null); + if (ac == 0) { + return new DebugInfoEntry(cu, offset); } - DWARFAbbreviation abbreviation = unit.getCodeToAbbreviationMap().get(abbreviationCode); + DWARFAbbreviation abbreviation = cu.getAbbreviation(ac); if (abbreviation == null) { - throw new IOException("Abbreviation code " + abbreviationCode + - " not found in the abbreviation map for compunit " + unit); + throw new IOException("Abbreviation code %d not found in compunit %d at 0x%x" + .formatted(ac, cu.getUnitNumber(), cu.getStartOffset())); } + int[] attrOffsets = new int[abbreviation.getAttributeCount()]; - DebugInfoEntry result = new DebugInfoEntry(unit, offset, dieIndex, abbreviation); // Read in all of the attribute values based on the attribute specification - DWARFAttributeSpecification[] attributeSpecs = result.abbreviation.getAttributes(); + AttrDef[] attributeSpecs = abbreviation.getAttributes(); + int currentAttrOffset = (int) (reader.getPointerIndex() - offset); for (int i = 0; i < attributeSpecs.length; i++) { - DWARFAttributeSpecification attributeSpec = attributeSpecs[i]; - result.attributes[i] = - attributeFactory.read(reader, unit, attributeSpec.getAttributeForm()); + attrOffsets[i] = currentAttrOffset; - if (ATTRIBUTES_TO_SKIP.contains(attributeSpec.getAttribute())) { - // throw away the object holding the value and replace it with - // the static boolean true value object to hold its place in - // the list. This saves a little memory - result.attributes[i] = DWARFBooleanAttribute.TRUE; + AttrDef attributeSpec = attributeSpecs[i]; + DWARFFormContext context = new DWARFFormContext(reader, cu, attributeSpec); + long attrSize = attributeSpec.getAttributeForm().getSize(context); + if (attrSize < 0 || attrSize > Integer.MAX_VALUE) { + throw new IOException("Invalid attribute value size"); } + + currentAttrOffset += attrSize; + + // manually set stream position because some attributes don't read from the stream to determine size + reader.setPointerIndex(offset + currentAttrOffset); } - return result; + return new DebugInfoEntry(cu, offset, dieIndex, abbreviation, attrOffsets); + } + + private DebugInfoEntry(DWARFCompilationUnit unit, long offset) { + this(unit, offset, -1, null, null); } /** - * Creates a DIE. Used by - * {@link #read(BinaryReader, DWARFCompilationUnit, DWARFAttributeFactory) static read()} and - * junit tests. + * Creates a DIE. * - * @param unit compunit containing the DIE + * @param cu compunit containing the DIE * @param offset offset of the DIE * @param dieIndex index of the DIE * @param abbreviation that defines the schema of this DIE record + * @param attrOffsets offset (from the die offset) of each attribute value */ - public DebugInfoEntry(DWARFCompilationUnit unit, long offset, int dieIndex, - DWARFAbbreviation abbreviation) { - this.compilationUnit = unit; + public DebugInfoEntry(DWARFCompilationUnit cu, long offset, int dieIndex, + DWARFAbbreviation abbreviation, int[] attrOffsets) { + this.compilationUnit = cu; this.offset = offset; this.dieIndex = dieIndex; this.abbreviation = abbreviation; - this.attributes = abbreviation != null - ? new DWARFAttributeValue[abbreviation.getAttributeCount()] - : null; + this.attrOffsets = attrOffsets; + this.attributes = attrOffsets != null ? new DWARFAttributeValue[attrOffsets.length] : null; } /** - * Returns the index of this DIE (in the entire dwarf program) + * Returns the index of this DIE in the entire dwarf program. * * @return index of this DIE */ @@ -141,9 +136,10 @@ public class DebugInfoEntry { * @param childTag DIE tag used to filter the child DIEs * @return list of matching child DIE records */ - public List getChildren(int childTag) { - List result = new ArrayList<>(); - for (DebugInfoEntry child : getChildren()) { + public List getChildren(DWARFTag childTag) { + List children = getChildren(); + List result = new ArrayList<>(children.size()); + for (DebugInfoEntry child : children) { if (child.getTag() == childTag) { result.add(child); } @@ -154,7 +150,7 @@ public class DebugInfoEntry { /** * Get the parent DIE of this DIE. * - * @return the parent DIE, or null if this DIE is the root of the compunit + * @return the parent DIE, or null if this DIE is the root of the compilation unit */ public DebugInfoEntry getParent() { return getProgram().getParentOf(dieIndex); @@ -172,30 +168,65 @@ public class DebugInfoEntry { * Get the DWARFTag value of this DIE. * @return the DWARFTag value of this DIE */ - public int getTag() { - return (abbreviation != null) ? abbreviation.getTag() : 0; - } - - public DWARFAttributeValue[] getAttributes() { - return attributes; + public DWARFTag getTag() { + return (abbreviation != null) ? abbreviation.getTag() : null; } /** - * Check to see if this DIE has the given attribute key. - * @param attribute the attribute key - * @return true if the DIE contains the attribute and false otherwise + * Returns the number of attributes in this DIE. + * + * @return number of attribute values in this DIE */ - public boolean hasAttribute(int attribute) { - if (abbreviation == null) { - return false; - } + public int getAttributeCount() { + return attrOffsets.length; + } - for (DWARFAttributeSpecification as : abbreviation.getAttributes()) { - if (as.getAttribute() == attribute) { - return true; + /** + * Returns the indexed attribute value. + * + * @param attribIndex index (0..count) + * @return {@link DWARFAttributeValue} + * @throws IOException if error reading the value + */ + public DWARFAttributeValue getAttributeValue(int attribIndex) throws IOException { + if (attributes[attribIndex] == null) { + BinaryReader reader = getProgram().getReaderForCompUnit(compilationUnit) + .clone(offset + attrOffsets[attribIndex]); + DWARFFormContext context = new DWARFFormContext(reader, compilationUnit, + abbreviation.getAttributeAt(attribIndex)); + attributes[attribIndex] = context.def().getAttributeForm().readValue(context); + } + return attributes[attribIndex]; + } + + /* for testing */ public void setAttributeValue(int index, DWARFAttributeValue attrVal) { + attributes[index] = attrVal; + } + + private DWARFAttributeValue getAttributeValueUnchecked(int attribIndex) { + try { + return getAttributeValue(attribIndex); + } + catch (IOException e) { + return null; + } + } + + /** + * Searches the list of attributes for a specific attribute, by id. + * + * @param attributeId {@link DWARFAttribute} + * @return {@link DWARFAttributeValue}, or null if not found + */ + public DWARFAttributeValue findAttribute(DWARFAttribute attributeId) { + AttrDef[] attrDefs = abbreviation.getAttributes(); + for (int i = 0; i < attrDefs.length; i++) { + AttrDef attrDef = attrDefs[i]; + if (attrDef.getAttributeId() == attributeId) { + return getAttributeValueUnchecked(i); } } - return false; + return null; } /** @@ -268,24 +299,23 @@ public class DebugInfoEntry { @Override public String toString() { StringBuilder buffer = new StringBuilder(); - int tag = getTag(); + DWARFTag tag = getTag(); + int tagNum = tag != null ? tag.getId() : 0; int abbrNum = abbreviation != null ? abbreviation.getAbbreviationCode() : 0; int childCount = getProgram().getDIEChildIndexes(dieIndex).size(); buffer.append("<%d><%x>: %s [abbrev %d, tag %d, index %d, children %d]\n".formatted( - getDepth(), offset, DWARFUtil.toString(DWARFTag.class, tag), abbrNum, tag, dieIndex, - childCount)); + getDepth(), offset, tag, abbrNum, tagNum, dieIndex, childCount)); if (isTerminator()) { return buffer.toString(); } - DWARFAttributeSpecification[] attributeSpecs = abbreviation.getAttributes(); - for (int i = 0; i < attributeSpecs.length; i++) { - DWARFAttributeSpecification attributeSpec = attributeSpecs[i]; - buffer.append("\t\tAttribute: %s %s %s\n".formatted( - DWARFUtil.toString(DWARFAttribute.class, attributeSpec.getAttribute()), - attributes[i], attributeSpec.getAttributeForm().toString())); + for (int i = 0; i < attributes.length; i++) { + buffer.append("\t\t"); + DWARFAttributeValue attribVal = getAttributeValueUnchecked(i); + buffer.append(attribVal != null ? attribVal.toString(compilationUnit) : "-missing-"); + buffer.append("\n"); } return buffer.toString(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DwarfSectionNames.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DwarfSectionNames.java index 0100bcae31..c06e300916 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DwarfSectionNames.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DwarfSectionNames.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +15,15 @@ */ package ghidra.app.util.bin.format.dwarf; -import ghidra.app.util.opinion.*; +import ghidra.app.plugin.core.analysis.DwarfLineNumberAnalyzer; +import ghidra.app.util.opinion.ElfLoader; +import ghidra.app.util.opinion.MachoLoader; import ghidra.program.model.listing.Program; +/** + * Section name logic for the obsolete {@link DwarfLineNumberAnalyzer} + */ +@Deprecated(forRemoval = true) public final class DwarfSectionNames { private final static String MACHO_PREFIX = "__"; private final static String ELF_PREFIX = "."; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/NameDeduper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/NameDeduper.java similarity index 90% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/NameDeduper.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/NameDeduper.java index 11e8e022db..164adfa50d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/NameDeduper.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/NameDeduper.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; import java.util.*; @@ -42,7 +42,7 @@ public class NameDeduper { /** * Add names to the the de-duper that have already been used. * - * @param alreadyUsedNames + * @param alreadyUsedNames names already used */ public void addUsedNames(Collection alreadyUsedNames) { usedNames.addAll(alreadyUsedNames); @@ -53,7 +53,7 @@ public class NameDeduper { * calls to confirm that a name is unique, but instead prevent the name from being used * when an auto-generated name is created. * - * @param additionalReservedNames + * @param additionalReservedNames names to reserve */ public void addReservedNames(Collection additionalReservedNames) { reservedNames.addAll(additionalReservedNames); @@ -62,8 +62,8 @@ public class NameDeduper { /** * Returns true if the specified name hasn't been allocated yet. * - * @param name - * @return + * @param name string name to check + * @return boolean true if the specified name hasn't been allocated yet */ public boolean isUniqueName(String name) { return name == null || !usedNames.contains(name); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/NamespacePath.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/NamespacePath.java similarity index 99% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/NamespacePath.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/NamespacePath.java index 1b4a6e9679..9bd927f533 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/NamespacePath.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/NamespacePath.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; import java.util.*; import java.util.function.Consumer; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/StringTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/StringTable.java new file mode 100644 index 0000000000..1991ce42a1 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/StringTable.java @@ -0,0 +1,92 @@ +/* ### + * 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.util.bin.format.dwarf; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.util.datastruct.WeakValueHashMap; + +/** + * Represents a DWARF string table, backed by a memory section like .debug_str. + *

+ * Strings are read from the section the first time requested, and then cached in a weak lookup + * table. + */ +public class StringTable { + /** + * Creates a StringTable instance, if the supplied BinaryReader is non-null. + * + * @param reader BinaryReader + * @return new instance, or null if reader is null + */ + public static StringTable of(BinaryReader reader) { + if (reader == null) { + return null; + } + return new StringTable(reader); + } + + protected BinaryReader reader; + protected WeakValueHashMap cache = new WeakValueHashMap<>(); + + /** + * Creates a StringTable + * + * @param reader {@link BinaryReader} .debug_str or .debug_line_str + */ + public StringTable(BinaryReader reader) { + this.reader = reader; + } + + /** + * Returns true if the specified offset is a valid offset for this string table. + *

+ * @param offset location of possible string + * @return boolean true if location is valid + */ + public boolean isValid(long offset) { + return reader.isValidIndex(offset); + } + + public void clear() { + reader = null; + cache.clear(); + } + + /** + * Returns the string found at offset, or throws an {@link IOException} + * if the offset is out of bounds. + * + * @param offset location of string + * @return a string, never null + * @throws IOException if not valid location + */ + public String getStringAtOffset(long offset) throws IOException { + if (!isValid(offset)) { + throw new IOException("Invalid offset requested " + offset); + } + + String s = cache.get(offset); + if (s == null) { + s = reader.readUtf8String(offset); + cache.put(offset, s); + } + + return s; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttribute.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttribute.java new file mode 100644 index 0000000000..c1c581e51c --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttribute.java @@ -0,0 +1,265 @@ +/* ### + * 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.util.bin.format.dwarf.attribs; + +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttributeClass.*; + +import java.io.IOException; +import java.util.*; + +import ghidra.app.util.bin.BinaryReader; + +/** + * Defines the names and numeric ids of known DWARF attributes. Well-known attributes are also + * constrained to certain value types (see {@link DWARFAttributeClass}). + *

+ * Users of this enum should be tolerant of unknown attribute id values. See + * {@link AttrDef#getRawAttributeId()}. + */ +public enum DWARFAttribute { + DW_AT_sibling(0x1, reference), + DW_AT_location(0x2, exprloc, loclist), + DW_AT_name(0x3, string), + DW_AT_ordering(0x9, constant), + //DW_AT_subscr_data(0xa), + DW_AT_byte_size(0xb, constant, exprloc, reference), + DW_AT_bit_offset(0xc), // dwarf-3 + DW_AT_bit_size(0xd, constant, exprloc, reference), + //DW_AT_element_list(0xf), + DW_AT_stmt_list(0x10, lineptr), + DW_AT_low_pc(0x11, address), + DW_AT_high_pc(0x12, address, constant), + DW_AT_language(0x13, constant), + //DW_AT_member(0x14), + DW_AT_discr(0x15, reference), + DW_AT_discr_value(0x16, constant), + DW_AT_visibility(0x17, constant), + DW_AT_import(0x18, reference), + DW_AT_string_length(0x19, exprloc, loclist, reference), + DW_AT_common_reference(0x1a, reference), + DW_AT_comp_dir(0x1b, string), + DW_AT_const_value(0x1c, block, constant, string), + DW_AT_containing_type(0x1d, reference), + DW_AT_default_value(0x1e, constant, reference, flag), + DW_AT_inline(0x20, constant), + DW_AT_is_optional(0x21, flag), + DW_AT_lower_bound(0x22, constant, exprloc, reference), + DW_AT_producer(0x25, string), + DW_AT_prototyped(0x27, flag), + DW_AT_return_addr(0x2a, exprloc, loclist), + DW_AT_start_scope(0x2c, constant, rnglist), + DW_AT_bit_stride(0x2e, constant, exprloc, reference), + DW_AT_upper_bound(0x2f, constant, exprloc, reference), + DW_AT_abstract_origin(0x31, reference), + DW_AT_accessibility(0x32, constant), + DW_AT_address_class(0x33, constant), + DW_AT_artificial(0x34, flag), + DW_AT_base_types(0x35, reference), + DW_AT_calling_convention(0x36, constant), + DW_AT_count(0x37, constant, exprloc, reference), + DW_AT_data_member_location(0x38, constant, exprloc, loclist), + DW_AT_decl_column(0x39, constant), + DW_AT_decl_file(0x3a, constant), + DW_AT_decl_line(0x3b, constant), + DW_AT_declaration(0x3c, flag), + DW_AT_discr_list(0x3d, block), + DW_AT_encoding(0x3e, constant), + DW_AT_external(0x3f, flag), + DW_AT_frame_base(0x40, exprloc, loclist), + DW_AT_friend(0x41, reference), + DW_AT_identifier_case(0x42, constant), + DW_AT_macro_info(0x43, macptr), + DW_AT_namelist_item(0x44, reference), + DW_AT_priority(0x45, reference), + DW_AT_segment(0x46, exprloc, loclist), + DW_AT_specification(0x47, reference), + DW_AT_static_link(0x48, exprloc, loclist), + DW_AT_type(0x49, reference), + DW_AT_use_location(0x4a, exprloc, loclist), + DW_AT_variable_parameter(0x4b, flag), + DW_AT_virtuality(0x4c, constant), + DW_AT_vtable_elem_location(0x4d, exprloc, loclist), + DW_AT_allocated(0x4e, constant, exprloc, reference), + DW_AT_associated(0x4f, constant, exprloc, reference), + DW_AT_data_location(0x50, exprloc), + DW_AT_byte_stride(0x51, constant, exprloc, reference), + DW_AT_entry_pc(0x52, address, constant), + DW_AT_use_UTF8(0x53, flag), + DW_AT_extension(0x54, reference), + DW_AT_ranges(0x55, rnglist), + DW_AT_trampoline(0x56, address, flag, reference, string), + DW_AT_call_column(0x57, constant), + DW_AT_call_file(0x58, constant), + DW_AT_call_line(0x59, constant), + DW_AT_description(0x5a, string), + DW_AT_binary_scale(0x5b, constant), + DW_AT_decimal_scale(0x5c, constant), + DW_AT_small(0x5d, reference), + DW_AT_decimal_sign(0x5e, constant), + DW_AT_digit_count(0x5f, constant), + DW_AT_picture_string(0x60, string), + DW_AT_mutable(0x61, flag), + DW_AT_threads_scaled(0x62, flag), + DW_AT_explicit(0x63, flag), + DW_AT_object_pointer(0x64, reference), + DW_AT_endianity(0x65, constant), + DW_AT_elemental(0x66, flag), + DW_AT_pure(0x67, flag), + DW_AT_recursive(0x68, flag), + DW_AT_signature(0x69, reference), + DW_AT_main_subprogram(0x6a, flag), + DW_AT_data_bit_offset(0x6b, constant), + DW_AT_const_expr(0x6c, flag), + DW_AT_enum_class(0x6d, flag), + DW_AT_linkage_name(0x6e, string), + DW_AT_string_length_bit_size(0x6f, constant), + DW_AT_string_length_byte_size(0x70, constant), + DW_AT_rank(0x71, constant, exprloc), + DW_AT_str_offsets_base(0x72, stroffsetsptr), + DW_AT_addr_base(0x73, addrptr), + DW_AT_rnglists_base(0x74, rnglistsptr), + // 0x75 reserved, unused + DW_AT_dwo_name(0x76, string), + DW_AT_reference(0x77, flag), + DW_AT_rvalue_reference(0x78, flag), + DW_AT_macros(0x79, macptr), + DW_AT_call_all_calls(0x7a, flag), + DW_AT_call_all_source_calls(0x7b, flag), + DW_AT_call_all_tail_calls(0x7c, flag), + DW_AT_call_return_pc(0x7d, address), + DW_AT_call_value(0x7e, exprloc), + DW_AT_call_origin(0x7f, exprloc), + DW_AT_call_parameter(0x80, reference), + DW_AT_call_pc(0x81, address), + DW_AT_call_tail_call(0x82, flag), + DW_AT_call_target(0x83, exprloc), + DW_AT_call_target_clobbered(0x84, exprloc), + DW_AT_call_data_location(0x85, exprloc), + DW_AT_call_data_value(0x86, exprloc), + DW_AT_noreturn(0x87, flag), + DW_AT_alignment(0x88, constant), + DW_AT_export_symbols(0x89, flag), + DW_AT_deleted(0x8a, flag), + DW_AT_defaulted(0x8b, constant), + DW_AT_loclists_base(0x8c, loclistsptr), + + DW_AT_lo_user(0x2000), + DW_AT_hi_user(0x3fff), + DW_AT_MIPS_linkage_name(0x2007), + + // GNU DebugFission stuff + DW_AT_GNU_dwo_name(0x2130), + DW_AT_GNU_dwo_id(0x2131), + DW_AT_GNU_ranges_base(0x2132), + DW_AT_GNU_addr_base(0x2133), + DW_AT_GNU_pubnames(0x2134), + DW_AT_GNU_pubtypes(0x2135), + // end GNU DebugFission + + // Golang + DW_AT_go_kind(0x2900), + DW_AT_go_key(0x2901), + DW_AT_go_elem(0x2902), + DW_AT_go_embedded_field(0x2903), + DW_AT_go_runtime_type(0x2904), + DW_AT_go_package_name(0x2905), + DW_AT_go_dict_index(0x2906), + // end Golang + + // Apple proprietary tags + DW_AT_APPLE_ptrauth_key(0x3e04), + DW_AT_APPLE_ptrauth_address_discriminated(0x3e05), + DW_AT_APPLE_ptrauth_extra_discriminator(0x3e06), + DW_AT_APPLE_omit_frame_ptr(0x3fe7), + DW_AT_APPLE_optimized(0x3fe1); + // end Apple proprietary tags + + private int id; + private Set attributeClass; + + DWARFAttribute(int id, DWARFAttributeClass... attributeClass) { + this.id = id; + this.attributeClass = EnumSet.noneOf(DWARFAttributeClass.class); + this.attributeClass.addAll(List.of(attributeClass)); + } + + public int getId() { + return id; + } + + public Set getAttributeClass() { + return attributeClass; + } + + public static final int EOL = 0; // value used as end of attributespec list + + public static DWARFAttribute of(int attributeInt) { + return lookupMap.get(attributeInt); + } + + private static Map lookupMap = buildLookup(); + + private static Map buildLookup() { + Map result = new HashMap<>(); + for (DWARFAttribute attr : values()) { + result.put(attr.id, attr); + } + return result; + } + + /** + * Represents how a specific DWARF attribute is stored in a DIE record. + */ + public static class AttrDef extends DWARFAttributeDef { + + /** + * Reads a {@link DWARFAttribute.AttrDef} instance from the {@link BinaryReader reader}. + *

+ * Returns a null if its a end-of-list marker. + *

+ * @param reader {@link BinaryReader} abbr stream + * @return new {@link AttrDef}, or null if end-of-list + * @throws IOException if error reading + */ + public static AttrDef read(BinaryReader reader) throws IOException { + DWARFAttributeDef tmp = + DWARFAttributeDef.read(reader, DWARFAttribute::of); + if (tmp == null) { + return null; + } + + return new AttrDef(tmp.getAttributeId(), tmp.getRawAttributeId(), + tmp.getAttributeForm(), tmp.getImplicitValue()); + } + + public AttrDef(DWARFAttribute attributeId, int rawAttributeId, + DWARFForm attributeForm, long implicitValue) { + super(attributeId, rawAttributeId, attributeForm, implicitValue); + } + + @Override + protected String getRawAttributeIdDescription() { + return "DW_AT_???? %d (0x%x)".formatted(attributeId, attributeId); + } + + @Override + public AttrDef withForm(DWARFForm newForm) { + return new AttrDef(attributeId, rawAttributeId, newForm, implicitValue); + } + + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFBlobAttribute.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeClass.java similarity index 53% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFBlobAttribute.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeClass.java index 8b35a33a6a..9d37b81674 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFBlobAttribute.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeClass.java @@ -13,30 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.attribs; - -import java.util.Arrays; +package ghidra.app.util.bin.format.dwarf.attribs; /** - * DWARF attribute with binary bytes. + * Categories that a DWARF attribute value may belong to. */ -public class DWARFBlobAttribute implements DWARFAttributeValue { - private final byte[] bytes; +public enum DWARFAttributeClass { - public DWARFBlobAttribute(byte[] bytes) { - this.bytes = bytes; - } + address, + addrptr, + block, + constant, + exprloc, + flag, + lineptr, + loclist, + loclistsptr, + macptr, + reference, + rnglist, + rnglistsptr, + string, + stroffsetsptr - public byte[] getBytes() { - return bytes; - } - - public int getLength() { - return bytes.length; - } - - @Override - public String toString() { - return "DWARFBlobAttribute: len=" + bytes.length + ", contents=" + Arrays.toString(bytes); - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeDef.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeDef.java new file mode 100644 index 0000000000..56fb596bd9 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeDef.java @@ -0,0 +1,159 @@ +/* ### + * 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.util.bin.format.dwarf.attribs; + +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFForm.*; + +import java.io.IOException; +import java.util.Objects; +import java.util.function.Function; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.dwarf.DWARFAbbreviation; +import ghidra.program.model.data.LEB128; + +/** + * Information about a single DWARF attribute, as specified in a + * {@link DWARFAbbreviation abbreviation}. + *

+ * This class handles the case where a specified attribute id is unknown to us (therefore not + * listed in the attribute enum class), as well as the case where the form is customized with + * an implicitValue. + *

+ * Unknown forms are not supported and cause an exception. + * + * @param attribute id enum type + */ +public class DWARFAttributeDef> { + + /** + * Reads a {@link DWARFAttributeDef} instance from the {@link BinaryReader reader}. + *

+ * Returns a null if its a end-of-list marker (which is only used by an attributespec list). + *

+ * @param attribute id enum type + * @param reader {@link BinaryReader} + * @param mapper func that converts an attribute id int into its enum + * @return DWARFAttributeDef instance, or null if EOL marker was read from the stream + * @throws IOException if error reading + */ + public static > DWARFAttributeDef read(BinaryReader reader, + Function mapper) throws IOException { + + int attributeId = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + int formId = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + + if (attributeId == DWARFAttribute.EOL && formId == DWARFForm.EOL) { + // end of attributespec list + return null; + } + + DWARFForm form = DWARFForm.of(formId); + if ( form == null ) { + throw new IOException("Unknown DWARFForm %d (0x%x)".formatted(formId, formId)); + } + + E e = mapper.apply(attributeId); + + // NOTE: implicit value is a space saving hack built into DWARF. It adds an extra + // field in the attributespec that needs to be read. + long implicitValue = form == DWARFForm.DW_FORM_indirect // read leb128 if present + ? reader.readNext(LEB128::signed) + : 0; + + return new DWARFAttributeDef<>(e, attributeId, form, implicitValue); + } + + protected final E attributeId; + protected final DWARFForm attributeForm; + protected final int rawAttributeId; + protected final long implicitValue; + + public DWARFAttributeDef(E attributeId, int rawAttributeId, DWARFForm attributeForm, + long implicitValue) { + this.attributeId = attributeId; + this.rawAttributeId = rawAttributeId; + this.attributeForm = attributeForm; + this.implicitValue = implicitValue; + } + + /** + * Get the attribute id of the attribute specification. + * @return the attribute value + */ + public E getAttributeId() { + return attributeId; + } + + public int getRawAttributeId() { + return rawAttributeId; + } + + public String getAttributeName() { + return attributeId != null + ? attributeId.name() + : getRawAttributeIdDescription(); + } + + protected String getRawAttributeIdDescription() { + return "unknown attribute id %d (0x%x)".formatted(rawAttributeId, rawAttributeId); + } + + /** + * Get the form of the attribute specification. + * @return the form value + */ + public DWARFForm getAttributeForm() { + return this.attributeForm; + } + + public boolean isImplicit() { + return attributeForm == DW_FORM_implicit_const; + } + + public long getImplicitValue() { + return implicitValue; + } + + public DWARFAttributeDef withForm(DWARFForm newForm) { + return new DWARFAttributeDef<>(attributeId, rawAttributeId, newForm, implicitValue); + } + + @Override + public String toString() { + return getAttributeName() + "->" + getAttributeForm(); + } + + @Override + public int hashCode() { + return Objects.hash(attributeForm, attributeId, implicitValue, rawAttributeId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof DWARFAttributeDef)) { + return false; + } + DWARFAttributeDef other = (DWARFAttributeDef) obj; + return attributeForm == other.attributeForm && + Objects.equals(attributeId, other.attributeId) && + implicitValue == other.implicitValue && rawAttributeId == other.rawAttributeId; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeValue.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeValue.java new file mode 100644 index 0000000000..fc491f5848 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeValue.java @@ -0,0 +1,43 @@ +/* ### + * 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.util.bin.format.dwarf.attribs; + +import ghidra.app.util.bin.format.dwarf.DWARFCompilationUnit; + +/** + * Base class for all DWARF attribute value implementations. + */ +public abstract class DWARFAttributeValue { + + protected final DWARFAttributeDef def; + + public DWARFAttributeValue(DWARFAttributeDef def) { + this.def = def; + } + + public DWARFForm getAttributeForm() { + return def.getAttributeForm(); + } + + public String getAttributeName() { + return def.getAttributeName(); + } + + public String toString(DWARFCompilationUnit compilationUnit) { + return toString(); + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFBlobAttribute.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFBlobAttribute.java new file mode 100644 index 0000000000..2680fd43a7 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFBlobAttribute.java @@ -0,0 +1,56 @@ +/* ### + * 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.util.bin.format.dwarf.attribs; + +import ghidra.app.util.bin.format.dwarf.DWARFCompilationUnit; +import ghidra.app.util.bin.format.dwarf.expression.*; +import ghidra.util.NumericUtilities; + +/** + * DWARF attribute with binary bytes. + */ +public class DWARFBlobAttribute extends DWARFAttributeValue { + private final byte[] bytes; + + public DWARFBlobAttribute(byte[] bytes, DWARFAttributeDef def) { + super(def); + this.bytes = bytes; + } + + public byte[] getBytes() { + return bytes; + } + + public int getLength() { + return bytes.length; + } + + public DWARFExpressionEvaluator evaluateExpression(DWARFCompilationUnit cu) + throws DWARFExpressionException { + + DWARFExpressionEvaluator exprEvaluator = new DWARFExpressionEvaluator(cu); + DWARFExpression expr = exprEvaluator.readExpr(bytes); + exprEvaluator.evaluate(expr); + + return exprEvaluator; + } + + @Override + public String toString() { + return "%s : %s = [%d]%s".formatted(getAttributeName(), getAttributeForm(), bytes.length, + NumericUtilities.convertBytesToString(bytes, " ")); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFBooleanAttribute.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFBooleanAttribute.java similarity index 62% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFBooleanAttribute.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFBooleanAttribute.java index 9a22dddd1b..15544e6634 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFBooleanAttribute.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFBooleanAttribute.java @@ -13,22 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.attribs; +package ghidra.app.util.bin.format.dwarf.attribs; /** * DWARF boolean attribute. */ -public class DWARFBooleanAttribute implements DWARFAttributeValue { - public static final DWARFBooleanAttribute TRUE = new DWARFBooleanAttribute(true); - public static final DWARFBooleanAttribute FALSE = new DWARFBooleanAttribute(false); - - public static DWARFBooleanAttribute get(boolean b) { - return b ? TRUE : FALSE; - } - +public class DWARFBooleanAttribute extends DWARFAttributeValue { private final boolean value; - public DWARFBooleanAttribute(boolean value) { + public DWARFBooleanAttribute(boolean value, DWARFAttributeDef def) { + super(def); this.value = value; } @@ -38,6 +32,6 @@ public class DWARFBooleanAttribute implements DWARFAttributeValue { @Override public String toString() { - return "DWARFBooleanAttribute: " + value; + return "%s : %s = %s".formatted(getAttributeName(), getAttributeForm(), getValue()); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFDeferredStringAttribute.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFDeferredStringAttribute.java similarity index 57% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFDeferredStringAttribute.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFDeferredStringAttribute.java index fdfe9b744d..000e17e924 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFDeferredStringAttribute.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFDeferredStringAttribute.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.attribs; - -import ghidra.app.util.bin.format.dwarf4.next.StringTable; +package ghidra.app.util.bin.format.dwarf.attribs; import java.io.IOException; +import ghidra.app.util.bin.format.dwarf.DWARFCompilationUnit; +import ghidra.util.Msg; + /** * DWARF string attribute, where getting the value from the string table is deferred * until requested for the first time. @@ -26,27 +27,39 @@ import java.io.IOException; public class DWARFDeferredStringAttribute extends DWARFStringAttribute { private long offset; - public DWARFDeferredStringAttribute(long offset) { - super(null); + public DWARFDeferredStringAttribute(long offset, DWARFAttributeDef def) { + super(null, def); this.offset = offset; } @Override - public String getValue(StringTable stringTable) { + public String getValue(DWARFCompilationUnit cu) { if (value == null) { try { - value = stringTable.getStringAtOffset(offset); + value = cu.getProgram().getString(getAttributeForm(), offset, cu); } catch (IOException e) { + Msg.error(this, "error getting string value", e); return null; } } return value; } - @Override - public String toString() { - return "DWARFDeferredStringAttribute [ offset=" + offset + ", value=" + value + "]"; + public long getOffset() { + return offset; } + @Override + public String toString(DWARFCompilationUnit cu) { + String str = value == null && cu != null ? getValue(cu) : value; + str = str != null ? "\"%s\"".formatted(value) : "-missing-"; + return "%s : %s = %s (offset 0x%x)".formatted(getAttributeName(), getAttributeForm(), str, + offset); + } + + @Override + public String toString() { + return toString(null); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFForm.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFForm.java new file mode 100644 index 0000000000..5caee63b24 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFForm.java @@ -0,0 +1,425 @@ +/* ### + * 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.util.bin.format.dwarf.attribs; + +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttributeClass.*; + +import java.io.IOException; +import java.util.*; + +import ghidra.app.util.bin.LEB128Info; +import ghidra.program.model.data.LEB128; + +/** + * DWARF attribute encodings. + *

+ * Unknown encodings will prevent deserialization of DIE records. + */ +public enum DWARFForm { + + DW_FORM_addr(0x1, DWARFForm.DYNAMIC_SIZE, address) { + @Override + public long getSize(DWARFFormContext context) throws IOException { + return context.compUnit().getPointerSize(); + } + + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + return new DWARFNumericAttribute( + context.reader().readNextUnsignedValue(context.compUnit().getPointerSize()), + context.def()); + } + }, + DW_FORM_block2(0x3, DWARFForm.DYNAMIC_SIZE, block) { + @Override + public long getSize(DWARFFormContext context) throws IOException { + int arraySize = context.reader().readNextUnsignedShort(); + return 2 /*sizeof short */ + arraySize; + } + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + int length = context.reader().readNextUnsignedShort(); + return new DWARFBlobAttribute(context.reader().readNextByteArray(length), + context.def()); + } + }, + DW_FORM_block4(0x4, DWARFForm.DYNAMIC_SIZE, block) { + @Override + public long getSize(DWARFFormContext context) throws IOException { + int arraySize = context.reader().readNextUnsignedIntExact(); + return 4 /*sizeof int */ + arraySize; + } + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + int length = context.reader().readNextUnsignedIntExact(); + if (length < 0 || length > MAX_BLOCK4_SIZE) { + throw new IOException("Invalid/bad dw_form_block4 size: " + length); + } + return new DWARFBlobAttribute(context.reader().readNextByteArray(length), + context.def()); + } + }, + DW_FORM_data2(0x5, 2, constant), + DW_FORM_data4(0x6, 4, constant), + DW_FORM_data8(0x7, 8, constant) { + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + return new DWARFNumericAttribute(64, context.reader().readNextLong(), true, true, + context.def()); + } + }, + DW_FORM_string(0x8, DWARFForm.DYNAMIC_SIZE, string) { + @Override + public long getSize(DWARFFormContext context) throws IOException { + long start = context.reader().getPointerIndex(); + context.reader().readNextUtf8String(); + return context.reader().getPointerIndex() - start; + } + + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + return new DWARFStringAttribute(context.reader().readNextUtf8String(), context.def()); + } + }, + DW_FORM_block(0x9, DWARFForm.DYNAMIC_SIZE, block) { + @Override + public long getSize(DWARFFormContext context) throws IOException { + LEB128Info uleb128 = context.reader().readNext(LEB128Info::unsigned); + return uleb128.getLength() + uleb128.asUInt32(); + } + + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + int length = context.reader().readNextUnsignedVarIntExact(LEB128::unsigned); + if (length < 0 || length > MAX_BLOCK4_SIZE) { + throw new IOException("Invalid/bad dw_form_block size: " + length); + } + return new DWARFBlobAttribute(context.reader().readNextByteArray(length), + context.def()); + } + }, + DW_FORM_block1(0xa, DWARFForm.DYNAMIC_SIZE, block) { + @Override + public long getSize(DWARFFormContext context) throws IOException { + int length = context.reader().readNextUnsignedByte(); + return 1 /* sizeof byte */ + length; + } + + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + int length = context.reader().readNextUnsignedByte(); + return new DWARFBlobAttribute(context.reader().readNextByteArray(length), + context.def()); + } + }, + DW_FORM_data1(0xb, 1, constant), + DW_FORM_flag(0xc, 1, flag) { + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + return new DWARFBooleanAttribute(context.reader().readNextByte() != 0, context.def()); + } + }, + DW_FORM_sdata(0xd, DWARFForm.LEB128_SIZE, constant) { + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + return new DWARFNumericAttribute(64, context.reader().readNext(LEB128::signed), true, + context.def()); + } + }, + DW_FORM_strp(0xe, DWARFForm.DWARF_INTSIZE, string) { + // offset in .debug_str + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + long stringOffset = context.reader().readNextUnsignedValue(context.dwarfIntSize()); + return new DWARFDeferredStringAttribute(stringOffset, context.def()); + } + }, + DW_FORM_udata(0xf, DWARFForm.LEB128_SIZE, constant) { + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + return new DWARFNumericAttribute(64, context.reader().readNext(LEB128::unsigned), false, + context.def()); + } + }, + DW_FORM_ref_addr(0x10, DWARFForm.DWARF_INTSIZE, reference) { + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + long addr = context.reader().readNextUnsignedValue(context.dwarfIntSize()); + return new DWARFNumericAttribute(addr, context.def()); + } + }, + DW_FORM_ref1(0x11, 1, reference), + DW_FORM_ref2(0x12, 2, reference), + DW_FORM_ref4(0x13, 4, reference), + DW_FORM_ref8(0x14, 8, reference), + DW_FORM_ref_udata(0x15, DWARFForm.LEB128_SIZE, constant) { + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + long uoffset = context.reader().readNext(LEB128::unsigned); + return new DWARFNumericAttribute(uoffset + context.compUnit().getStartOffset(), + context.def()); + } + }, + DW_FORM_indirect(0x16, DWARFForm.DYNAMIC_SIZE /* value class will depend on the indirect form*/ ) { + @Override + public long getSize(DWARFFormContext context) throws IOException { + long start = context.reader().getPointerIndex(); + int indirectFormInt = context.reader().readNextUnsignedVarIntExact(LEB128::unsigned); + long firstSize = context.reader().getPointerIndex() - start; + + DWARFForm indirectForm = DWARFForm.of(indirectFormInt); + DWARFAttributeDef indirectAS = context.def().withForm(indirectForm); + DWARFFormContext indirectContext = + new DWARFFormContext(context.reader(), context.compUnit(), indirectAS); + long indirectSize = indirectForm.getSize(indirectContext); + + return firstSize + indirectSize; + } + + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + int indirectFormInt = context.reader().readNextUnsignedVarIntExact(LEB128::unsigned); + DWARFForm indirectForm = DWARFForm.of(indirectFormInt); + DWARFAttributeDef indirectAS = context.def().withForm(indirectForm); + DWARFFormContext indirectContext = + new DWARFFormContext(context.reader(), context.compUnit(), indirectAS); + return indirectForm.readValue(indirectContext); + } + }, + DW_FORM_sec_offset(0x17, DWARFForm.DWARF_INTSIZE, addrptr, lineptr, loclist, loclistsptr, macptr, rnglist, rnglistsptr, stroffsetsptr) { + // offset in a section other than .debug_info or .debug_str + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + long addr = context.reader().readNextUnsignedValue(context.dwarfIntSize()); + return new DWARFNumericAttribute(addr, context.def()); + } + }, + DW_FORM_exprloc(0x18, DWARFForm.DYNAMIC_SIZE, exprloc) { + @Override + public long getSize(DWARFFormContext context) throws IOException { + LEB128Info uleb128 = context.reader().readNext(LEB128Info::unsigned); + return uleb128.getLength() + uleb128.asInt32(); + } + + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + int length = context.reader().readNextUnsignedVarIntExact(LEB128::unsigned); + if (length < 0 || length > MAX_BLOCK4_SIZE) { + throw new IOException("Invalid/bad dw_form_exprloc size: " + length); + } + return new DWARFBlobAttribute(context.reader().readNextByteArray(length), + context.def()); + + } + }, + DW_FORM_flag_present(0x19, 0, flag) { + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + return new DWARFBooleanAttribute(true, context.def()); + } + }, + DW_FORM_strx(0x1a, DWARFForm.LEB128_SIZE, string) { + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + int index = context.reader().readNextUnsignedVarIntExact(LEB128::unsigned); + return new DWARFDeferredStringAttribute(index, context.def()); + } + }, + DW_FORM_addrx(0x1b, DWARFForm.LEB128_SIZE, address) { + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + int index = context.reader().readNextUnsignedVarIntExact(LEB128::unsigned); + return new DWARFIndirectAttribute(index, context.def()); + } + }, + DW_FORM_ref_sup4(0x1c, 4, reference), // unimpl + DW_FORM_strp_sup(0x1d, DWARFForm.DWARF_INTSIZE, string), // unimpl + DW_FORM_data16(0x1e, 16, constant) { + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + return new DWARFBlobAttribute(context.reader().readNextByteArray(16), context.def()); + } + }, + DW_FORM_line_strp(0x1f, DWARFForm.DWARF_INTSIZE, string) { + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + return new DWARFDeferredStringAttribute( + context.reader().readNextUnsignedValue(context.dwarfIntSize()), context.def()); + } + }, + DW_FORM_ref_sig8(0x20, 8, reference), // unimpl + DW_FORM_implicit_const(0x21, 0) { + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + return new DWARFNumericAttribute(64, context.def().getImplicitValue(), true, + context.def()); + } + }, + DW_FORM_loclistx(0x22, DWARFForm.LEB128_SIZE, loclist) { + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + return new DWARFIndirectAttribute(context.reader().readNext(LEB128::unsigned), + context.def()); + } + }, + DW_FORM_rnglistx(0x23, DWARFForm.LEB128_SIZE, rnglist) { + @Override + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + return new DWARFIndirectAttribute(context.reader().readNext(LEB128::unsigned), + context.def()); + } + }, + DW_FORM_ref_sup8(0x24, 8, reference), // unimpl + DW_FORM_strx1(0x25, 1, string), + DW_FORM_strx2(0x26, 2, string), + DW_FORM_strx3(0x27, 3, string), + DW_FORM_strx4(0x28, 4, string), + DW_FORM_addrx1(0x29, 1, address), + DW_FORM_addrx2(0x2a, 2, address), + DW_FORM_addrx3(0x2b, 3, address), + DW_FORM_addrx4(0x2c, 4, address); + + private final int id; + /** + * The static size of values of this type, or one of the special values {@link #DYNAMIC_SIZE}, + * {@link #DWARF_INTSIZE}, {@link #LEB128_SIZE} + */ + private final int size; + private final Set attributeClasses; + + private static final Map lookupMap = buildLookupmap(); + + DWARFForm(int id, int size, DWARFAttributeClass... attributeClasses) { + this.id = id; + this.size = size; + this.attributeClasses = EnumSet.noneOf(DWARFAttributeClass.class); + this.attributeClasses.addAll(List.of(attributeClasses)); + } + + /** + * Returns the id of this DWARFForm. + * + * @return DWARFForm numeric id + */ + public int getId() { + return this.id; + } + + public Set getFormClasses() { + return attributeClasses; + } + + public boolean isClass(DWARFAttributeClass attrClass) { + return attributeClasses.size() == 1 && attributeClasses.contains(attrClass); + } + + /** + * Returns the size the attribute value occupies in the stream. + *

+ * This default implementation handles static sizes, as well as LEB128 and DWARF_INT sizes. + * DWARFForms that are more complex and marked as {@link #DYNAMIC_SIZE} will need to override + * this method and provide custom logic to determine the size of a value. + * + * @param context {@link DWARFFormContext} + * @return size of the attribute value + * @throws IOException if error reading + */ + public long getSize(DWARFFormContext context) throws IOException { + switch (size) { + case DWARF_INTSIZE: + return context.compUnit().getIntSize(); + case LEB128_SIZE: + return context.reader().readNext(LEB128::getLength); + case DYNAMIC_SIZE: + throw new IOException("Unimplemented size for " + this); + default: + return size; + } + } + + /** + * Reads a DIE attribute value from a stream. + * + * @param context {@link DWARFFormContext} + * @return {@link DWARFAttributeValue} + * @throws IOException if error reading + */ + public DWARFAttributeValue readValue(DWARFFormContext context) throws IOException { + switch (this) { + case DW_FORM_addrx1: + case DW_FORM_addrx2: + case DW_FORM_addrx3: + case DW_FORM_addrx4: { + long index = context.reader().readNextUnsignedValue(size); + return new DWARFIndirectAttribute(index, context.def()); + } + + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: { + long val = context.reader().readNextValue(size); + return new DWARFNumericAttribute(size * 8, val, true, true, context.def()); + } + + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: { + long uoffset = context.reader().readNextUnsignedValue(size); + return new DWARFNumericAttribute(uoffset + context.compUnit().getStartOffset(), + context.def()); + } + + case DW_FORM_strx1: + case DW_FORM_strx2: + case DW_FORM_strx3: + case DW_FORM_strx4: { + long index = context.reader().readNextUnsignedValue(size); + return new DWARFDeferredStringAttribute(index, context.def()); + } + + default: + throw new IllegalArgumentException("Unsupported DWARF Form: " + this); + } + } + + public static final int EOL = 0; // value used as end of attributespec list + + /** + * Find the form value given raw int. + * + * @param key value to check + * @return DWARFForm enum, or null if it is an unknown form + */ + public static DWARFForm of(int key) { + return lookupMap.get(key); + } + + private static Map buildLookupmap() { + Map result = new HashMap<>(); + for (DWARFForm form : DWARFForm.values()) { + result.put(form.getId(), form); + } + return result; + + } + + public static final int MAX_BLOCK4_SIZE = 1024 * 1024; + private static final int LEB128_SIZE = -3; + private static final int DWARF_INTSIZE = -2; + private static final int DYNAMIC_SIZE = -1; +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFFormContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFFormContext.java new file mode 100644 index 0000000000..bab61a90cb --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFFormContext.java @@ -0,0 +1,39 @@ +/* ### + * 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.util.bin.format.dwarf.attribs; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.dwarf.*; + +/** + * Context given to the {@link DWARFForm#readValue(DWARFFormContext)} method to enable it to + * create {@link DWARFAttributeValue}s. + * + * @param reader {@link BinaryReader} + * @param compUnit {@link DWARFCompilationUnit} + * @param def {@link DWARFAttributeDef} + */ +public record DWARFFormContext(BinaryReader reader, DWARFCompilationUnit compUnit, + DWARFAttributeDef def) { + + DWARFProgram dprog() { + return compUnit.getProgram(); + } + + int dwarfIntSize() { + return compUnit.getIntSize(); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFIndirectAttribute.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFIndirectAttribute.java new file mode 100644 index 0000000000..f08634c758 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFIndirectAttribute.java @@ -0,0 +1,72 @@ +/* ### + * 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.util.bin.format.dwarf.attribs; + +import java.io.IOException; + +import ghidra.app.util.bin.format.dwarf.DWARFCompilationUnit; +import ghidra.app.util.bin.format.dwarf.DWARFProgram; +import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionNames; + +/** + * DWARF numeric attribute value that is an index into a lookup table + */ +public class DWARFIndirectAttribute extends DWARFNumericAttribute { + + public DWARFIndirectAttribute(long index, DWARFAttributeDef def) { + super(index, def); + } + + public int getIndex() throws IOException { + return getUnsignedIntExact(); + } + + @Override + public String toString(DWARFCompilationUnit cu) { + try { + DWARFProgram prog = cu.getProgram(); + int index = getIndex(); + long offset = prog.getOffsetOfIndexedElement(getAttributeForm(), index, cu); + if (getAttributeForm().isClass(DWARFAttributeClass.address)) { + return "%s : %s, addr v%d 0x%x (idx %d)".formatted(getAttributeName(), + getAttributeForm(), cu.getDWARFVersion(), offset, index); + } + else if (getAttributeForm().isClass(DWARFAttributeClass.rnglist)) { + return toElementLocationString("rnglist", DWARFSectionNames.DEBUG_RNGLISTS, index, + offset, cu.getDWARFVersion()); + } + else if (getAttributeForm().isClass(DWARFAttributeClass.loclist)) { + return toElementLocationString("loclist", DWARFSectionNames.DEBUG_LOCLISTS, index, + offset, cu.getDWARFVersion()); + } + else if (getAttributeForm().isClass(DWARFAttributeClass.string)) { + return toElementLocationString("string", DWARFSectionNames.DEBUG_LOCLISTS, index, + offset, cu.getDWARFVersion()); + } + } + catch (IOException e) { + // fall thru to default + } + return super.toString(cu); + } + + @Override + public String toString() { + long index = getUnsignedValue(); + return "%s : %s, index/offset %d [0x%x]".formatted(getAttributeName(), getAttributeForm(), + index, index); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFNumericAttribute.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFNumericAttribute.java new file mode 100644 index 0000000000..9f86eb8872 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFNumericAttribute.java @@ -0,0 +1,148 @@ +/* ### + * 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.util.bin.format.dwarf.attribs; + +import java.io.IOException; + +import ghidra.app.util.bin.InvalidDataException; +import ghidra.app.util.bin.format.dwarf.DWARFCompilationUnit; +import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionNames; +import ghidra.program.model.scalar.Scalar; + +/** + * DWARF numeric attribute. + */ +public class DWARFNumericAttribute extends DWARFAttributeValue { + + private final Scalar value; + private final boolean ambiguous; + + /** + * Creates a new numeric value, using 64 bits and marked as signed + * + * @param value long 64 bit value + * @param def attribute id and form of this value + */ + public DWARFNumericAttribute(long value, DWARFAttributeDef def) { + this(64, value, true, false, def); + } + + /** + * Creates a new numeric value, using the specific bitLength and value. + * + * @param bitLength number of bits, valid values are 1..64, or 0 if value is also 0 + * @param value value of the scalar, any bits that are set above bitLength will be ignored + * @param signed true for a signed value, false for an unsigned value. + * @param def attribute id and form of this value + */ + public DWARFNumericAttribute(int bitLength, long value, boolean signed, + DWARFAttributeDef def) { + this(bitLength, value, signed, false, def); + } + + /** + * Creates a new numeric value, using the specific bitLength and value. + * + * @param bitLength number of bits, valid values are 1..64, or 0 if value is also 0 + * @param value value of the scalar, any bits that are set above bitLength will be ignored + * @param signed true for a signed value, false for an unsigned value. + * @param ambiguous true for value with ambiguous signedness ({@code signed} parameter should + * not be trusted), false for value where the {@code signed} parameter is known to be correct + * @param def attribute id and form of this value + */ + public DWARFNumericAttribute(int bitLength, long value, boolean signed, boolean ambiguous, + DWARFAttributeDef def) { + super(def); + this.value = new Scalar(bitLength, value, signed); + this.ambiguous = ambiguous; + } + + /** + * {@return boolean flag, if true this value's signedness is up to the user of the value, + * if false the signedness was determined when the value was constructed} + */ + public boolean isAmbiguousSignedness() { + return ambiguous; + } + + /** + * {@return the value, forcing the signedness of ambiguous values using the specified hint} + * @param signednessHint true to default to a signed value, false to default to an + * unsigned value + */ + public long getValueWithSignednessHint(boolean signednessHint) { + return value.getValue(ambiguous ? signednessHint : value.isSigned()); + } + + public boolean isHighbitSet() { + return value.bitLength() > 0 ? value.testBit(value.bitLength() - 1) : false; + } + + public long getValue() { + return value.getValue(); + } + + public long getUnsignedValue() { + return value.getUnsignedValue(); + } + + public int getUnsignedIntExact() throws IOException { + long x = value.getUnsignedValue(); + if (x < 0 || Integer.MAX_VALUE < x) { + throw new InvalidDataException( + "Value out of range for positive java 32 bit unsigned int: %d [0x%d]".formatted(x, + x)); + } + return (int) x; + } + + public String toElementLocationString(String elementType, String sectionName, int index, + long offset, int ver) { + String indexStr = index >= 0 ? " (idx %d)".formatted(index) : ""; + return "%s : %s, %s v%d %s:%x%s".formatted(getAttributeName(), getAttributeForm(), + elementType, ver, sectionName, offset, indexStr); + } + + @Override + public String toString(DWARFCompilationUnit cu) { + short ver = cu.getDWARFVersion(); + if (getAttributeForm().isClass(DWARFAttributeClass.address)) { + return "%s : %s, addr v%d 0x%x".formatted(getAttributeName(), getAttributeForm(), ver, + getUnsignedValue()); + } + else if (getAttributeForm().isClass(DWARFAttributeClass.rnglist)) { + String sectionName = + ver < 5 ? DWARFSectionNames.DEBUG_RANGES : DWARFSectionNames.DEBUG_RNGLISTS; + return toElementLocationString("rnglist", sectionName, -1, getUnsignedValue(), + cu.getDWARFVersion()) + " offset: " + getUnsignedValue(); + } + else if (getAttributeForm().isClass(DWARFAttributeClass.loclist)) { + String sectionName = + ver < 5 ? DWARFSectionNames.DEBUG_LOC : DWARFSectionNames.DEBUG_LOCLISTS; + return toElementLocationString("loclist", sectionName, -1, getUnsignedValue(), + cu.getDWARFVersion()); + } + return toString(); + } + + @Override + public String toString() { + String orStr = + ambiguous && isHighbitSet() ? " or " + value.getValue(!value.isSigned()) : ""; + return "%s : %s = %d%s [%s]".formatted(getAttributeName(), getAttributeForm(), getValue(), + orStr, value.toString(16, true, false, "", "")); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFStringAttribute.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFStringAttribute.java similarity index 64% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFStringAttribute.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFStringAttribute.java index 27d112cad7..14a742e592 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFStringAttribute.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFStringAttribute.java @@ -13,26 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.attribs; +package ghidra.app.util.bin.format.dwarf.attribs; -import ghidra.app.util.bin.format.dwarf4.next.StringTable; +import ghidra.app.util.bin.format.dwarf.DWARFCompilationUnit; /** * DWARF string attribute. */ -public class DWARFStringAttribute implements DWARFAttributeValue { +public class DWARFStringAttribute extends DWARFAttributeValue { protected String value; - public DWARFStringAttribute(String value) { + public DWARFStringAttribute(String value, DWARFAttributeDef def) { + super(def); this.value = value; } - public String getValue(StringTable stringTable) { + public String getValue(DWARFCompilationUnit cu) { return value; } @Override public String toString() { - return "DWARFStringAttribute: [" + value + "]"; + return "%s : %s = \"%s\"".formatted(getAttributeName(), getAttributeForm(), value); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpression.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpression.java similarity index 92% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpression.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpression.java index 9a6ece3d8a..c48eb7e948 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpression.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpression.java @@ -13,15 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.expression; +package ghidra.app.util.bin.format.dwarf.expression; +import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.io.IOException; - import ghidra.app.util.bin.*; -import ghidra.app.util.bin.format.dwarf4.*; +import ghidra.app.util.bin.format.dwarf.DIEAggregate; import ghidra.program.model.data.LEB128; import ghidra.util.NumericUtilities; @@ -33,6 +32,7 @@ import ghidra.util.NumericUtilities; */ public class DWARFExpression { static long EMPTY_OPERANDS_VALUE[] = {}; + public static final int MAX_SANE_EXPR = 256; private final List operations; @@ -41,7 +41,7 @@ public class DWARFExpression { public static String exprToString(byte[] exprBytes, DIEAggregate diea) { try { DWARFExpression expr = - DWARFExpressionEvaluator.create(diea.getHeadFragment()).readExpr(exprBytes); + new DWARFExpressionEvaluator(diea.getCompilationUnit()).readExpr(exprBytes); return expr.toString(); } catch (DWARFExpressionException e) { @@ -51,14 +51,14 @@ public class DWARFExpression { } public static DWARFExpression read(byte[] exprBytes, byte addrSize, boolean isLittleEndian, - int dwarf_format) throws DWARFExpressionException { + int intSize) throws DWARFExpressionException { ByteProvider provider = new ByteArrayProvider(exprBytes); BinaryReader reader = new BinaryReader(provider, isLittleEndian); - return read(reader, addrSize, dwarf_format); + return read(reader, addrSize, intSize); } - public static DWARFExpression read(BinaryReader reader, byte addrSize, int dwarf_format) + public static DWARFExpression read(BinaryReader reader, byte addrSize, int intSize) throws DWARFExpressionException { List operations = new ArrayList<>(); @@ -91,8 +91,7 @@ public class DWARFExpression { blob = readSizedBlobOperand(reader, operandValues[i - 1]); } else { - operandValues[i] = - readOperandValue(optype, reader, addrSize, dwarf_format); + operandValues[i] = readOperandValue(optype, reader, addrSize, intSize); } } @@ -117,11 +116,11 @@ public class DWARFExpression { } private static long readOperandValue(DWARFExpressionOperandType operandType, - BinaryReader reader, byte addrSize, int dwarf_format) throws IOException { + BinaryReader reader, byte addrSize, int intSize) throws IOException { try { switch (operandType) { case ADDR: - return DWARFUtil.readAddressAsLong(reader, addrSize); + return reader.readNextUnsignedValue(addrSize); case S_BYTE: return reader.readNextByte(); case S_SHORT: @@ -145,9 +144,7 @@ public class DWARFExpression { case SIZED_BLOB: throw new IOException("Can't read SIZED_BLOB as a Long value"); case DWARF_INT: - return (dwarf_format == DWARFCompilationUnit.DWARF_32) - ? reader.readNextUnsignedInt() - : reader.readNextLong(); + return reader.readNextUnsignedValue(intSize); } } catch (ArrayIndexOutOfBoundsException aioob) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionEvaluator.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluator.java similarity index 94% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionEvaluator.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluator.java index 61c8feeb22..046e406582 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionEvaluator.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluator.java @@ -13,17 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.expression; +package ghidra.app.util.bin.format.dwarf.expression; -import static ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionOpCodes.*; +import static ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOpCodes.*; import java.util.ArrayDeque; import java.util.Objects; -import ghidra.app.util.bin.format.dwarf4.DWARFCompilationUnit; -import ghidra.app.util.bin.format.dwarf4.DebugInfoEntry; -import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram; -import ghidra.app.util.bin.format.dwarf4.next.DWARFRegisterMappings; +import ghidra.app.util.bin.format.dwarf.*; import ghidra.program.model.lang.Register; /** @@ -44,7 +41,7 @@ public class DWARFExpressionEvaluator { */ private static final int DEFAULT_MAX_STEP_COUNT = 1000; - private final int dwarfFormat; + private final int intSize; private int maxStepCount = DEFAULT_MAX_STEP_COUNT; @@ -89,20 +86,16 @@ public class DWARFExpressionEvaluator { private DWARFExpressionOperation currentOp; private int currentOpIndex = -1; - public static DWARFExpressionEvaluator create(DebugInfoEntry die) { - DWARFCompilationUnit compUnit = die.getCompilationUnit(); - DWARFProgram prog = die.getProgram(); - DWARFExpressionEvaluator evaluator = new DWARFExpressionEvaluator(compUnit.getPointerSize(), - !prog.isBigEndian(), compUnit.getFormat(), prog.getRegisterMappings()); - - return evaluator; + public DWARFExpressionEvaluator(DWARFCompilationUnit cu) { + this(cu.getPointerSize(), cu.getProgram().isLittleEndian(), cu.getIntSize(), + cu.getProgram().getRegisterMappings()); } - public DWARFExpressionEvaluator(byte pointerSize, boolean isLittleEndian, int dwarfFormat, + public DWARFExpressionEvaluator(byte pointerSize, boolean isLittleEndian, int intSize, DWARFRegisterMappings registerMappings) { this.pointerSize = pointerSize; this.isLittleEndian = isLittleEndian; - this.dwarfFormat = dwarfFormat; + this.intSize = intSize; this.registerMappings = Objects.requireNonNullElse(registerMappings, DWARFRegisterMappings.DUMMY); } @@ -151,7 +144,7 @@ public class DWARFExpressionEvaluator { public DWARFExpression readExpr(byte[] exprBytes) throws DWARFExpressionException { DWARFExpression tmp = - DWARFExpression.read(exprBytes, pointerSize, isLittleEndian, dwarfFormat); + DWARFExpression.read(exprBytes, pointerSize, isLittleEndian, intSize); return tmp; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFAttributeValue.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluatorContext.java similarity index 75% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFAttributeValue.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluatorContext.java index 2b522dcd5f..2d2738bce3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFAttributeValue.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluatorContext.java @@ -13,11 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.attribs; +package ghidra.app.util.bin.format.dwarf.expression; -/** - * Base interface for all DWARF attribute value implementations. Not much here. - */ -public interface DWARFAttributeValue { +import ghidra.app.util.bin.format.dwarf.DWARFCompilationUnit; -} +public record DWARFExpressionEvaluatorContext(DWARFCompilationUnit cu) {} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionException.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionException.java similarity index 97% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionException.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionException.java index 63d7227e76..ada4392161 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionException.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.expression; +package ghidra.app.util.bin.format.dwarf.expression; /** * A exception that is thrown when dealing with {@link DWARFExpression DWARF expressions} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionOpCodes.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOpCodes.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionOpCodes.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOpCodes.java index f3f5780659..6afbd81374 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionOpCodes.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOpCodes.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.expression; +package ghidra.app.util.bin.format.dwarf.expression; -import static ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionOperandType.*; +import static ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOperandType.*; import java.lang.reflect.Field; import java.util.*; -import ghidra.app.util.bin.format.dwarf4.DWARFUtil; +import ghidra.app.util.bin.format.dwarf.DWARFUtil; /** * DWARF expression opcode consts from www.dwarfstd.org/doc/DWARF4.pdf diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionOperandType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOperandType.java similarity index 96% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionOperandType.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOperandType.java index 7e6dcb7b36..9712e4789a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionOperandType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOperandType.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.expression; +package ghidra.app.util.bin.format.dwarf.expression; /** * Enumeration that represents the different type of operands that a diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionOperation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOperation.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionOperation.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOperation.java index bdcbdd5066..6eee3a9f75 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionOperation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOperation.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.expression; +package ghidra.app.util.bin.format.dwarf.expression; import java.util.Arrays; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionResult.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionResult.java similarity index 95% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionResult.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionResult.java index 3cf3235bed..2e40c7156b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionResult.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionResult.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.expression; +package ghidra.app.util.bin.format.dwarf.expression; import java.util.ArrayDeque; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/BuildIdSearchLocation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/BuildIdSearchLocation.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/BuildIdSearchLocation.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/BuildIdSearchLocation.java index 6fbc28fd32..c3e4adc0a6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/BuildIdSearchLocation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/BuildIdSearchLocation.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.external; +package ghidra.app.util.bin.format.dwarf.external; import java.io.File; import java.io.IOException; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/DWARFExternalDebugFilesPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/DWARFExternalDebugFilesPlugin.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/DWARFExternalDebugFilesPlugin.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/DWARFExternalDebugFilesPlugin.java index f5e4e86620..d2f3289a62 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/DWARFExternalDebugFilesPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/DWARFExternalDebugFilesPlugin.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.external; +package ghidra.app.util.bin.format.dwarf.external; import java.io.File; import java.util.ArrayList; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/ExternalDebugFilesService.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/ExternalDebugFilesService.java similarity index 97% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/ExternalDebugFilesService.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/ExternalDebugFilesService.java index 642ed54f0a..656ca428c5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/ExternalDebugFilesService.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/ExternalDebugFilesService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.external; +package ghidra.app.util.bin.format.dwarf.external; import java.io.IOException; import java.util.List; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/ExternalDebugInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/ExternalDebugInfo.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/ExternalDebugInfo.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/ExternalDebugInfo.java index 0e6df08413..5afa7e1ca2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/ExternalDebugInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/ExternalDebugInfo.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.external; +package ghidra.app.util.bin.format.dwarf.external; import ghidra.app.util.bin.format.elf.info.GnuDebugLink; import ghidra.app.util.bin.format.elf.info.NoteGnuBuildId; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/LocalDirectorySearchLocation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/LocalDirectorySearchLocation.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/LocalDirectorySearchLocation.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/LocalDirectorySearchLocation.java index f87c5f56e0..cbae723c95 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/LocalDirectorySearchLocation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/LocalDirectorySearchLocation.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.external; +package ghidra.app.util.bin.format.dwarf.external; import java.io.*; import java.util.zip.CRC32; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/SameDirSearchLocation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/SameDirSearchLocation.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/SameDirSearchLocation.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/SameDirSearchLocation.java index 1e7be9de7c..3ab0172962 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/SameDirSearchLocation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/SameDirSearchLocation.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.external; +package ghidra.app.util.bin.format.dwarf.external; import java.io.File; import java.io.IOException; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/SearchLocation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/SearchLocation.java similarity index 97% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/SearchLocation.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/SearchLocation.java index 9742073f99..f4b6cf3da6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/SearchLocation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/SearchLocation.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.external; +package ghidra.app.util.bin.format.dwarf.external; import java.io.IOException; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/SearchLocationCreatorContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/SearchLocationCreatorContext.java similarity index 96% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/SearchLocationCreatorContext.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/SearchLocationCreatorContext.java index 7b25cbb456..3ad2c3588d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/SearchLocationCreatorContext.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/SearchLocationCreatorContext.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.external; +package ghidra.app.util.bin.format.dwarf.external; import ghidra.program.model.listing.Program; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/SearchLocationRegistry.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/SearchLocationRegistry.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/SearchLocationRegistry.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/SearchLocationRegistry.java index 352371f23c..1bd4695c4d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/external/SearchLocationRegistry.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/SearchLocationRegistry.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.external; +package ghidra.app.util.bin.format.dwarf.external; import java.util.ArrayList; import java.util.List; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/DWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/DWARFFunctionFixup.java similarity index 93% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/DWARFFunctionFixup.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/DWARFFunctionFixup.java index 6977fb40a4..87f7ce688b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/DWARFFunctionFixup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/DWARFFunctionFixup.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.funcfixup; +package ghidra.app.util.bin.format.dwarf.funcfixup; import java.io.Closeable; import java.util.List; -import ghidra.app.util.bin.format.dwarf4.DWARFException; -import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction; +import ghidra.app.util.bin.format.dwarf.DWARFException; +import ghidra.app.util.bin.format.dwarf.DWARFFunction; import ghidra.util.classfinder.ClassSearcher; import ghidra.util.classfinder.ExtensionPoint; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/OutputParamCheckDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/OutputParamCheckDWARFFunctionFixup.java similarity index 78% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/OutputParamCheckDWARFFunctionFixup.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/OutputParamCheckDWARFFunctionFixup.java index 9123ce17e0..70f9d6e680 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/OutputParamCheckDWARFFunctionFixup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/OutputParamCheckDWARFFunctionFixup.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.funcfixup; +package ghidra.app.util.bin.format.dwarf.funcfixup; -import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction; -import ghidra.app.util.bin.format.dwarf4.next.DWARFVariable; -import ghidra.util.Msg; +import ghidra.app.util.bin.format.dwarf.DWARFFunction; +import ghidra.app.util.bin.format.dwarf.DWARFVariable; import ghidra.util.classfinder.ExtensionPointProperties; /** @@ -33,8 +32,10 @@ public class OutputParamCheckDWARFFunctionFixup implements DWARFFunctionFixup { // some other fixup, as we don't know what to do with them. for (DWARFVariable dvar : dfunc.params) { if (dvar.isOutputParameter && dvar.isMissingStorage()) { - Msg.warn(this, "Unsupported output parameter for %s@%s" - .formatted(dfunc.name.getName(), dfunc.address)); + String paramName = dvar.name.getName(); + dfunc.getProgram() + .logWarningAt(dfunc.address, dfunc.name.getName(), + "Unsupported output parameter %s".formatted(paramName)); } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ParamNameDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/ParamNameDWARFFunctionFixup.java similarity index 94% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ParamNameDWARFFunctionFixup.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/ParamNameDWARFFunctionFixup.java index 69607d4cd9..7c1f0598d8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ParamNameDWARFFunctionFixup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/ParamNameDWARFFunctionFixup.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.funcfixup; +package ghidra.app.util.bin.format.dwarf.funcfixup; -import ghidra.app.util.bin.format.dwarf4.next.*; +import ghidra.app.util.bin.format.dwarf.*; import ghidra.util.classfinder.ExtensionPointProperties; /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ParamSpillDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/ParamSpillDWARFFunctionFixup.java similarity index 90% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ParamSpillDWARFFunctionFixup.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/ParamSpillDWARFFunctionFixup.java index 3d5309bce3..ad9b746c27 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ParamSpillDWARFFunctionFixup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/ParamSpillDWARFFunctionFixup.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.funcfixup; +package ghidra.app.util.bin.format.dwarf.funcfixup; -import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction; -import ghidra.app.util.bin.format.dwarf4.next.DWARFVariable; +import ghidra.app.util.bin.format.dwarf.DWARFFunction; +import ghidra.app.util.bin.format.dwarf.DWARFVariable; import ghidra.util.classfinder.ExtensionPointProperties; /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/RustDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/RustDWARFFunctionFixup.java similarity index 82% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/RustDWARFFunctionFixup.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/RustDWARFFunctionFixup.java index 98e0f3e8db..b6ed905bb0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/RustDWARFFunctionFixup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/RustDWARFFunctionFixup.java @@ -13,15 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.funcfixup; +package ghidra.app.util.bin.format.dwarf.funcfixup; import java.io.IOException; -import ghidra.app.util.bin.format.dwarf4.DIEAggregate; -import ghidra.app.util.bin.format.dwarf4.DWARFException; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFSourceLanguage; -import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction; -import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction.CommitMode; +import ghidra.app.util.bin.format.dwarf.*; +import ghidra.app.util.bin.format.dwarf.DWARFFunction.CommitMode; import ghidra.program.database.data.ProgramBasedDataTypeManagerDB; import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.listing.Program; @@ -39,7 +36,7 @@ public class RustDWARFFunctionFixup implements DWARFFunctionFixup { @Override public void fixupDWARFFunction(DWARFFunction dfunc) throws DWARFException { DIEAggregate diea = dfunc.diea; - int cuLang = diea.getCompilationUnit().getCompileUnit().getLanguage(); + int cuLang = diea.getCompilationUnit().getLanguage(); if (cuLang == DWARFSourceLanguage.DW_LANG_Rust) { dfunc.callingConventionName = getRustCC(dfunc.getProgram().getGhidraProgram()); dfunc.signatureCommitMode = CommitMode.FORMAL; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/SanityCheckDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/SanityCheckDWARFFunctionFixup.java similarity index 74% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/SanityCheckDWARFFunctionFixup.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/SanityCheckDWARFFunctionFixup.java index 41a2fbc5f9..7087308a74 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/SanityCheckDWARFFunctionFixup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/SanityCheckDWARFFunctionFixup.java @@ -13,19 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.funcfixup; +package ghidra.app.util.bin.format.dwarf.funcfixup; -import ghidra.app.util.bin.format.dwarf4.DWARFException; -import ghidra.app.util.bin.format.dwarf4.attribs.DWARFAttributeValue; -import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction; -import ghidra.util.Msg; +import ghidra.app.util.bin.format.dwarf.DWARFException; +import ghidra.app.util.bin.format.dwarf.DWARFFunction; import ghidra.util.classfinder.ExtensionPointProperties; /** * Check for errors and prevent probable bad function info from being locked in */ @ExtensionPointProperties(priority = DWARFFunctionFixup.PRIORITY_NORMAL_LATE) -public class SanityCheckDWARFFunctionFixup implements DWARFFunctionFixup, DWARFAttributeValue { +public class SanityCheckDWARFFunctionFixup implements DWARFFunctionFixup { @Override public void fixupDWARFFunction(DWARFFunction dfunc) throws DWARFException { @@ -33,8 +31,9 @@ public class SanityCheckDWARFFunctionFixup implements DWARFFunctionFixup, DWARFA // don't force the method to have an empty param signature because there are other // issues afoot. if (dfunc.params.isEmpty() && dfunc.localVarErrors) { - Msg.error(this, "Inconsistent function signature information, leaving undefined: %s@%s" - .formatted(dfunc.name.getName(), dfunc.address)); + dfunc.getProgram() + .logWarningAt(dfunc.address, dfunc.name.getName(), + "Inconsistent function signature information, leaving undefined"); throw new DWARFException("Failed sanity check"); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/StorageVerificationDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/StorageVerificationDWARFFunctionFixup.java similarity index 86% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/StorageVerificationDWARFFunctionFixup.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/StorageVerificationDWARFFunctionFixup.java index 0d167b5571..c364be9782 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/StorageVerificationDWARFFunctionFixup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/StorageVerificationDWARFFunctionFixup.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.funcfixup; +package ghidra.app.util.bin.format.dwarf.funcfixup; -import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction; -import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction.CommitMode; -import ghidra.app.util.bin.format.dwarf4.next.DWARFVariable; +import ghidra.app.util.bin.format.dwarf.DWARFFunction; +import ghidra.app.util.bin.format.dwarf.DWARFVariable; +import ghidra.app.util.bin.format.dwarf.DWARFFunction.CommitMode; import ghidra.util.classfinder.ExtensionPointProperties; /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ThisCallingConventionDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/ThisCallingConventionDWARFFunctionFixup.java similarity index 81% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ThisCallingConventionDWARFFunctionFixup.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/ThisCallingConventionDWARFFunctionFixup.java index 62e17682e3..682274c796 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ThisCallingConventionDWARFFunctionFixup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/ThisCallingConventionDWARFFunctionFixup.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.funcfixup; +package ghidra.app.util.bin.format.dwarf.funcfixup; -import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction; -import ghidra.app.util.bin.format.dwarf4.next.DWARFVariable; +import ghidra.app.util.bin.format.dwarf.DWARFFunction; +import ghidra.app.util.bin.format.dwarf.DWARFVariable; import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.listing.Function; -import ghidra.util.Msg; import ghidra.util.classfinder.ExtensionPointProperties; /** @@ -39,9 +38,10 @@ public class ThisCallingConventionDWARFFunctionFixup implements DWARFFunctionFix if (firstParam.isThis) { if (!firstParam.name.isAnon() && !Function.THIS_PARAM_NAME.equals(firstParam.name.getOriginalName())) { - Msg.warn(this, "Renaming %s to %s in function %s@%s".formatted( - firstParam.name.getName(), Function.THIS_PARAM_NAME, dfunc.name.getName(), - dfunc.address)); + dfunc.getProgram() + .logWarningAt(dfunc.address, dfunc.name.getName(), + "Renamed parameter \"%s\" to %s".formatted(firstParam.name.getName(), + Function.THIS_PARAM_NAME)); } firstParam.name = firstParam.name.replaceName(Function.THIS_PARAM_NAME, Function.THIS_PARAM_NAME); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/BaseSectionProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/BaseSectionProvider.java similarity index 97% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/BaseSectionProvider.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/BaseSectionProvider.java index 6fafd698c5..d3802481a1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/BaseSectionProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/BaseSectionProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next.sectionprovider; +package ghidra.app.util.bin.format.dwarf.sectionprovider; import java.util.List; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/CompressedSectionProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/CompressedSectionProvider.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/CompressedSectionProvider.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/CompressedSectionProvider.java index 9049d4a18a..dd00181eb2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/CompressedSectionProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/CompressedSectionProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next.sectionprovider; +package ghidra.app.util.bin.format.dwarf.sectionprovider; import java.util.HashMap; import java.util.Map; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/DSymSectionProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/DSymSectionProvider.java similarity index 97% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/DSymSectionProvider.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/DSymSectionProvider.java index ff22331653..647b6e3776 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/DSymSectionProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/DSymSectionProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next.sectionprovider; +package ghidra.app.util.bin.format.dwarf.sectionprovider; import java.util.HashMap; import java.util.Map; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/DWARFSectionNames.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/DWARFSectionNames.java similarity index 78% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/DWARFSectionNames.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/DWARFSectionNames.java index f52f59fa72..2b3ff4afce 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/DWARFSectionNames.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/DWARFSectionNames.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next.sectionprovider; +package ghidra.app.util.bin.format.dwarf.sectionprovider; public final class DWARFSectionNames { public static final String DEBUG_INFO = "debug_info"; @@ -21,13 +21,18 @@ public final class DWARFSectionNames { public static final String DEBUG_ABBREV = "debug_abbrev"; public static final String DEBUG_ARRANGES = "debug_arranges"; public static final String DEBUG_LINE = "debug_line"; + public static final String DEBUG_LINE_STR = "debug_line_str"; // v5+ public static final String DEBUG_FRAME = "debug_frame"; public static final String DEBUG_LOC = "debug_loc"; + public static final String DEBUG_LOCLISTS = "debug_loclists"; // v5+ public static final String DEBUG_STR = "debug_str"; + public static final String DEBUG_STROFFSETS = "debug_str_offsets"; // v5+ public static final String DEBUG_RANGES = "debug_ranges"; + public static final String DEBUG_RNGLISTS = "debug_rnglists"; // v5+ public static final String DEBUG_PUBNAMES = "debug_pubnames"; public static final String DEBUG_PUBTYPES = "debug_pubtypes"; public static final String DEBUG_MACINFO = "debug_macinfo"; + public static final String DEBUG_ADDR = "debug_addr"; public static final String[] MINIMAL_DWARF_SECTIONS = { DEBUG_INFO, DEBUG_ABBREV }; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/DWARFSectionProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/DWARFSectionProvider.java similarity index 97% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/DWARFSectionProvider.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/DWARFSectionProvider.java index 734ec8e75d..b3dec2635e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/DWARFSectionProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/DWARFSectionProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next.sectionprovider; +package ghidra.app.util.bin.format.dwarf.sectionprovider; import java.io.Closeable; import java.io.IOException; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/DWARFSectionProviderFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/DWARFSectionProviderFactory.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/DWARFSectionProviderFactory.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/DWARFSectionProviderFactory.java index 4aeb051cdf..cdca67c653 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/DWARFSectionProviderFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/DWARFSectionProviderFactory.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next.sectionprovider; +package ghidra.app.util.bin.format.dwarf.sectionprovider; import java.io.Closeable; import java.util.List; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/ExternalDebugFileSectionProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/ExternalDebugFileSectionProvider.java similarity index 97% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/ExternalDebugFileSectionProvider.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/ExternalDebugFileSectionProvider.java index 914e95b09a..6412034576 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/ExternalDebugFileSectionProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/ExternalDebugFileSectionProvider.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next.sectionprovider; +package ghidra.app.util.bin.format.dwarf.sectionprovider; import java.io.IOException; import java.net.MalformedURLException; import java.util.List; import ghidra.app.util.bin.ByteProvider; -import ghidra.app.util.bin.format.dwarf4.external.*; +import ghidra.app.util.bin.format.dwarf.external.*; import ghidra.app.util.importer.MessageLog; import ghidra.app.util.opinion.ElfLoader; import ghidra.formats.gfilesystem.*; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/NullSectionProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/NullSectionProvider.java similarity index 94% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/NullSectionProvider.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/NullSectionProvider.java index fe9df0206c..7adaef0174 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/NullSectionProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/sectionprovider/NullSectionProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next.sectionprovider; +package ghidra.app.util.bin.format.dwarf.sectionprovider; import java.io.IOException; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFAbbreviation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFAbbreviation.java deleted file mode 100644 index d01f7c7fd4..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFAbbreviation.java +++ /dev/null @@ -1,159 +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.util.bin.format.dwarf4; - -import java.util.*; - -import java.io.IOException; - -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFChildren; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag; -import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram; -import ghidra.program.model.data.LEB128; -import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskMonitor; - -/** - * This class represents the 'schema' for a DWARF DIE record. - *

- * A raw DWARF DIE record specifies its abbreviation code (pointing to an instance of - * this class) and the corresponding DWARFAbbreviation instance has the information - * about how the raw DIE is laid out. - */ -public class DWARFAbbreviation -{ - private final int abbreviationCode; - private final int tag; - private final boolean hasChildren; - private final DWARFAttributeSpecification[] attributes; - - public static DWARFAbbreviation read(BinaryReader reader, DWARFProgram prog, - TaskMonitor monitor) - throws IOException, CancelledException { - - int ac = reader.readNextUnsignedVarIntExact(LEB128::unsigned); - if (ac == 0) { - return null; - } - int tag = reader.readNextUnsignedVarIntExact(LEB128::unsigned); - DWARFChildren hasChildren = DWARFChildren.find((int) reader.readNextByte()); - - // Read each attribute specification until attribute and its value is 0 - List tmpAttrSpecs = new ArrayList<>(); - DWARFAttributeSpecification attr; - while ((attr = DWARFAttributeSpecification.read(reader)) != null) { - monitor.checkCancelled(); - tmpAttrSpecs.add(prog.internAttributeSpec(attr)); - } - DWARFAttributeSpecification[] attrSpecArray = - tmpAttrSpecs.toArray(new DWARFAttributeSpecification[tmpAttrSpecs.size()]); - - DWARFAbbreviation result = new DWARFAbbreviation(ac, tag, - hasChildren == DWARFChildren.DW_CHILDREN_yes, attrSpecArray); - - return result; - } - - public static Map readAbbreviations(BinaryReader reader, - DWARFProgram prog, TaskMonitor monitor) throws IOException, CancelledException { - Map result = new HashMap<>(); - - // Read all abbreviations for this compilation unit and add to a map - DWARFAbbreviation abbrev = null; - while ((abbrev = DWARFAbbreviation.read(reader, prog, monitor)) != null) { - monitor.checkCancelled(); - result.put(abbrev.getAbbreviationCode(), abbrev); - } - - return result; - } - - public DWARFAbbreviation(int abbreviationCode, int tag, boolean hasChildren, - DWARFAttributeSpecification[] attributes) { - this.abbreviationCode = abbreviationCode; - this.tag = tag; - this.hasChildren = hasChildren; - this.attributes = attributes; - } - - @Override - public String toString() - { - return Integer.toHexString(getAbbreviationCode()) + ":" + - DWARFUtil.toString(DWARFTag.class, getTag()); - } - - /** - * Get the abbreviation code. - * @return the abbreviation code - */ - public int getAbbreviationCode() - { - return this.abbreviationCode; - } - - /** - * Get the tag value. - * @return the tag value - */ - public int getTag() { - return this.tag; - } - - /** - * Checks to see if this abbreviation has any DIE children. - * @return true if this abbreviation has DIE children - */ - public boolean hasChildren() { - return this.hasChildren; - } - - /** - * Return a live list of the attributes. - * @return list of attributes - */ - public DWARFAttributeSpecification[] getAttributes() { - return attributes; - } - - public int getAttributeCount() { - return attributes.length; - } - - /** - * Get the attribute at the given index. - * @param index index of the attribute - * @return attribute specification - */ - public DWARFAttributeSpecification getAttributeAt(int index) { - return this.attributes[index]; - } - - /** - * Get the attribute with the given attribute key. - * @param attribute attribute key - * @return attribute specification - */ - public DWARFAttributeSpecification findAttribute(int attribute) { - for(DWARFAttributeSpecification spec : this.attributes) { - if(spec.getAttribute() == attribute) { - return spec; - } - } - return null; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFAttributeSpecification.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFAttributeSpecification.java deleted file mode 100644 index d22339954a..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFAttributeSpecification.java +++ /dev/null @@ -1,100 +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.util.bin.format.dwarf4; - -import java.io.IOException; - -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFForm; -import ghidra.program.model.data.LEB128; - -/** - * Information about a single DWARF attribute. - */ -public class DWARFAttributeSpecification { - private final int attribute; - private final DWARFForm attributeForm; - - /** - * Reads a {@link DWARFAttributeSpecification} instance from the {@link BinaryReader reader}. - *

- * Returns a null if its a end-of-list marker. - *

- * @param reader - * @return - * @throws IOException - */ - public static DWARFAttributeSpecification read(BinaryReader reader) throws IOException { - int attribute = reader.readNextUnsignedVarIntExact(LEB128::unsigned); - DWARFForm attributeForm = - DWARFForm.find(reader.readNextUnsignedVarIntExact(LEB128::unsigned)); - - return attribute != 0 && attributeForm != DWARFForm.NULL - ? new DWARFAttributeSpecification(attribute, attributeForm) : null; - } - - public DWARFAttributeSpecification(int attribute, DWARFForm attributeForm) { - this.attribute = attribute; - this.attributeForm = attributeForm; - } - - /** - * Get the attribute of the attribute specification. - * @return the attribute value - */ - public int getAttribute() { - return this.attribute; - } - - /** - * Get the form of the attribute specification. - * @return the form value - */ - public DWARFForm getAttributeForm() { - return this.attributeForm; - } - - @Override - public String toString() { - return DWARFUtil.toString(DWARFAttribute.class, getAttribute()) + "->" + getAttributeForm(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + attribute; - result = prime * result + ((attributeForm == null) ? 0 : attributeForm.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof DWARFAttributeSpecification)) - return false; - DWARFAttributeSpecification other = (DWARFAttributeSpecification) obj; - if (attribute != other.attribute) - return false; - if (attributeForm != other.attributeForm) - return false; - return true; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFCompilationUnit.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFCompilationUnit.java deleted file mode 100644 index 457b3b3433..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFCompilationUnit.java +++ /dev/null @@ -1,326 +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.util.bin.format.dwarf4; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.dwarf4.DWARFUtil.LengthResult; -import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram; -import ghidra.util.Msg; -import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskMonitor; - -/** - * A DWARF "CompilationUnit" is a contiguous block of {@link DebugInfoEntry DIE} records found - * in a ".debug_info" section of an ELF program. The compilation unit block starts with a - * header that has a few important values and flags, and is followed by the DIE records. - *

- * The first DIE record must be a DW_TAG_compile_unit (see {@link DWARFCompileUnit}, - * and {@link #getCompileUnit()}). - *

- * DIE records are identified by their byte offset in the ".debug_info" section. - *

- */ -public class DWARFCompilationUnit { - - public static final int DWARF_32 = 32; - public static final int DWARF_64 = 64; - - /** - * Reference to the owning {@link DWARFProgram}. - */ - private final DWARFProgram dwarfProgram; - - /** - * Offset in the debug_info section of this compUnit's header - */ - private final long startOffset; - - /** - * Offset in the debug_info section of the end of this compUnit. (right after - * the last DIE record) - */ - private final long endOffset; - - /** - * Length in bytes of this compUnit header and DIE records. - */ - private final long length; - - /** - * {@link #DWARF_32} or {@link #DWARF_64} - */ - private final int format; - - /** - * DWARF ver number, as read from the compunit structure, currently not used but being kept. - */ - @SuppressWarnings("unused") - private final short version; - - /** - * Sequential number of this compUnit - */ - private final int compUnitNumber; - - /** - * Size of pointers that are held in DIEs in this compUnit. - */ - private final byte pointerSize; - - /** - * Offset in the abbr section of this compUnit's abbreviations. - */ - private final long abbreviationOffset; - - /** - * Offset in the debug_info section of the first DIE of this compUnit. - */ - private final long firstDIEOffset; - - /** - * Map of abbrevCode to {@link DWARFAbbreviation} instances. - */ - private final Map codeToAbbreviationMap; - - /** - * The contents of the first DIE (that must be a compile unit) in this compUnit. - */ - protected DWARFCompileUnit compUnit; - - /** - * Creates a new {@link DWARFCompilationUnit} by reading a compilationUnit's header data - * from the debug_info section and the debug_abbr section and its compileUnit DIE (ie. - * the first DIE right after the header). - *

- * Returns {@code NULL} if there was an ignorable error while reading the compilation unit (and - * leaves the input stream at the next compilation unit to read), otherwise throws - * an IOException if there was an unrecoverable error. - *

- * Also returns {@code NULL} (and leaves the stream at EOF) if the remainder of the stream - * is filled with null bytes. - * - * @param dwarfProgram the dwarf program. - * @param debugInfoBR the debug info binary reader. - * @param debugAbbrBR the debug abbreviation binary reader - * @param cuNumber the compilation unit number - * @param monitor the current task monitor - * @return the read compilation unit, or null if the compilation unit was bad/empty and should - * be ignored - * @throws DWARFException if an invalid or unsupported DWARF version is read. - * @throws IOException if the length of the compilation unit is invalid. - * @throws CancelledException if the task has been canceled. - */ - public static DWARFCompilationUnit readCompilationUnit(DWARFProgram dwarfProgram, - BinaryReader debugInfoBR, BinaryReader debugAbbrBR, int cuNumber, TaskMonitor monitor) - throws DWARFException, IOException, CancelledException { - - long startOffset = debugInfoBR.getPointerIndex(); - LengthResult lengthInfo = - DWARFUtil.readLength(debugInfoBR, dwarfProgram.getGhidraProgram()); - if (lengthInfo.length == 0) { - if (isAllZerosUntilEOF(debugInfoBR)) { - // hack to handle trailing padding at end of section. (similar to the check for - // unexpectedTerminator in readDIEs(), when padding occurs inside the bounds - // of the compile unit's range after the end of the root DIE's children) - debugInfoBR.setPointerIndex(debugInfoBR.length()); - return null; - } - throw new DWARFException( - "Invalid DWARF length 0 at 0x" + Long.toHexString(startOffset)); - } - - long endOffset = debugInfoBR.getPointerIndex() + lengthInfo.length; - short version = debugInfoBR.readNextShort(); - long abbreviationOffset = DWARFUtil.readOffsetByDWARFformat(debugInfoBR, lengthInfo.format); - byte pointerSize = debugInfoBR.readNextByte(); - long firstDIEOffset = debugInfoBR.getPointerIndex(); - - if (version < 2 || version > 4) { - throw new DWARFException( - "Only DWARF version 2, 3, or 4 information is currently supported (detected " + - version + ")."); - } - if (firstDIEOffset > endOffset) { - throw new IOException("Invalid length " + (endOffset - startOffset) + - " for DWARF Compilation Unit at 0x" + Long.toHexString(startOffset)); - } - else if (firstDIEOffset == endOffset) { - // silently skip this empty compunit - return null; - } - - debugAbbrBR.setPointerIndex(abbreviationOffset); - Map abbrMap = - DWARFAbbreviation.readAbbreviations(debugAbbrBR, dwarfProgram, monitor); - - DWARFCompilationUnit cu = new DWARFCompilationUnit(dwarfProgram, startOffset, endOffset, - lengthInfo.length, lengthInfo.format, version, abbreviationOffset, pointerSize, - cuNumber, firstDIEOffset, abbrMap); - - try { - DebugInfoEntry compileUnitDIE = - DebugInfoEntry.read(debugInfoBR, cu, -1, dwarfProgram.getAttributeFactory()); - - DWARFCompileUnit compUnit = - DWARFCompileUnit.read(DIEAggregate.createSingle(compileUnitDIE)); - cu.compUnit = compUnit; - return cu; - } - catch (IOException ioe) { - Msg.error(DWARFCompilationUnit.class, - "Failed to parse the DW_TAG_compile_unit DIE at the start of compilation unit %d at offset %d (0x%x), skipping entire compilation unit" - .formatted(cuNumber, startOffset, startOffset), - ioe); - debugInfoBR.setPointerIndex(cu.getEndOffset()); - return null; - } - } - - private static boolean isAllZerosUntilEOF(BinaryReader reader) throws IOException { - reader = reader.clone(); - while (reader.hasNext()) { - if (reader.readNextByte() != 0) { - return false; - } - } - return true; - } - - /** - * This ctor is public only for junit tests. Do not use directly. - * - * @param dwarfProgram {@link DWARFProgram} - * @param startOffset offset in provider where it starts - * @param endOffset offset in provider where it ends - * @param length how many bytes following the header the DIEs of this unit take - * @param format DWARF_32 or DWARF_64 - * @param version 2, 3, 4 - * @param abbreviationOffset offset into abbrev section - * @param pointerSize default size of pointers - * @param compUnitNumber this compunits ordinal in the file - * @param firstDIEOffset start of DIEs in the provider - * @param codeToAbbreviationMap map of abbreviation numbers to {@link DWARFAbbreviation} instances - */ - public DWARFCompilationUnit(DWARFProgram dwarfProgram, long startOffset, long endOffset, - long length, int format, short version, long abbreviationOffset, byte pointerSize, - int compUnitNumber, long firstDIEOffset, - Map codeToAbbreviationMap) { - this.dwarfProgram = dwarfProgram; - this.startOffset = startOffset; - this.endOffset = endOffset; - this.length = length; - this.format = format; - this.version = version; - this.abbreviationOffset = abbreviationOffset; - this.pointerSize = pointerSize; - this.compUnitNumber = compUnitNumber; - this.firstDIEOffset = firstDIEOffset; - this.codeToAbbreviationMap = - (codeToAbbreviationMap != null) ? codeToAbbreviationMap : new HashMap<>(); - } - - public DWARFCompileUnit getCompileUnit() { - return compUnit; - } - - public DWARFProgram getProgram() { - return dwarfProgram; - } - - /** - * An unsigned long (4 bytes in 32-bit or 8 bytes in 64-bit format) representing - * the length of the .debug_info contribution for that compilation unit, - * not including the length field itself. - * @return the length in bytes of the this compilation unit - */ - public long getLength() { - return this.length; - } - - /** - * A 1-byte unsigned integer representing the size - * in bytes of an address on the target - * architecture. If the system uses segmented addressing, this - * value represents the size of the offset portion of an address. - * @return the size in bytes of pointers - */ - public byte getPointerSize() { - return this.pointerSize; - } - - /** - * Returns the byte offset to the start of this compilation unit. - * @return the byte offset to the start of this compilation unit - */ - public long getStartOffset() { - return this.startOffset; - } - - /** - * Returns the byte offset to the end of this compilation unit. - * @return the byte offset to the end of this compilation unit - */ - public long getEndOffset() { - return this.endOffset; - } - - /** - * Returns either DWARF_32 or DWARF_64 depending on the current compilation unit format - * @return DWARF_32 or DWARF_64 constant depending on the current compilation unit format - */ - public int getFormat() { - return this.format; - } - - /** - * Returns true if the {@code offset} value is within - * this compUnit's start and end position in the debug_info section. - * @param offset DIE offset - * @return true if within range of this compunit - */ - public boolean containsOffset(long offset) { - return firstDIEOffset <= offset && offset < endOffset; - } - - @Override - public String toString() { - StringBuilder buffer = new StringBuilder(); - buffer.append("Compilation Unit"); - buffer.append(" [Start:0x" + Long.toHexString(this.startOffset) + "]"); - buffer.append(" [Length:0x" + Long.toHexString(this.length) + "]"); - buffer.append(" [AbbreviationOffset:0x" + Long.toHexString(this.abbreviationOffset) + "]"); - buffer.append( - " [CompileUnit: " + (compUnit != null ? compUnit.toString() : "not present") + "]"); - return buffer.toString(); - } - - public Map getCodeToAbbreviationMap() { - return codeToAbbreviationMap; - } - - public long getFirstDIEOffset() { - return firstDIEOffset; - } - - public int getCompUnitNumber() { - return compUnitNumber; - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFCompileUnit.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFCompileUnit.java deleted file mode 100644 index 8ecec8c08c..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFCompileUnit.java +++ /dev/null @@ -1,202 +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.util.bin.format.dwarf4; - -import java.io.File; -import java.io.IOException; - -import ghidra.app.util.bin.format.dwarf4.encoding.*; - -/** - * DWARFCompileUnit hold some values retrieved from a DWARF DW_TAG_compile_unit DIE. - *

- */ -public class DWARFCompileUnit { - private final String name; - private final String producer; - private final String comp_dir; - private final Number high_pc; - private final Number low_pc; - private final Number language; - private final boolean hasDWO; - - private DWARFLine line = null; - - public static DWARFCompileUnit read(DIEAggregate diea) - throws IOException, DWARFException { - if (diea.getTag() != DWARFTag.DW_TAG_compile_unit) { - throw new IOException("Expecting a DW_TAG_compile_unit DIE, found " + diea.getTag()); - } - - String name = diea.getString(DWARFAttribute.DW_AT_name, null); - String producer = diea.getString(DWARFAttribute.DW_AT_producer, null); - String comp_dir = diea.getString(DWARFAttribute.DW_AT_comp_dir, null); - - Number high_pc = null, low_pc = null, language = null; - - if (diea.hasAttribute(DWARFAttribute.DW_AT_low_pc)) { - low_pc = diea.getLowPC(0); - } - - // if lowPC and highPC values are the same, don't read the high value - // because Ghidra can't express an empty range. - if (diea.hasAttribute(DWARFAttribute.DW_AT_high_pc) && !diea.isLowPCEqualHighPC()) { - high_pc = diea.getHighPC(); - } - - if (diea.hasAttribute(DWARFAttribute.DW_AT_language)) { - language = diea.getUnsignedLong(DWARFAttribute.DW_AT_language, -1); - } - - boolean hasDWO = diea.hasAttribute(DWARFAttribute.DW_AT_GNU_dwo_id) && - diea.hasAttribute(DWARFAttribute.DW_AT_GNU_dwo_name); - - DWARFLine line = DWARFLine.read(diea); - - return new DWARFCompileUnit(name, producer, comp_dir, low_pc, high_pc, language, hasDWO, - line); - } - - /* - * Construct a DWARF compile unit with the given values. - */ - public DWARFCompileUnit(String name, String producer, String comp_dir, Number low_pc, - Number high_pc, Number language, boolean hasDWO, DWARFLine line) { - this.name = name; - this.producer = producer; - this.comp_dir = comp_dir; - this.low_pc = low_pc; - this.high_pc = high_pc; - this.language = language; - this.hasDWO = hasDWO; - this.line = line; - } - - /** - * Get the name of the compile unit - * @return the name of the compile unit - */ - public String getName() { - return this.name; - } - - /** - * Get the filename of the compile unit - * @return the filename of the compile unit - */ - public String getFileName() { - return getName() == null ? null : new File(getName()).getName(); - } - - /** - * Get a file name with the full path included based on a file index. - * @param index index of the file - * @return file name with full path or null if line information does not exist - * @throws IllegalArgumentException if a negative or invalid file index is given - */ - public String getFullFileByIndex(int index) { - if (index < 0) { - throw new IllegalArgumentException("Negative file index was given."); - } - if (this.line == null) { - return null; - } - - return this.line.getFullFile(index, this.comp_dir); - } - - /** - * Get a file name based on a file index. - * @param index index of the file - * @return file name or null if line information does not exist - * @throws IllegalArgumentException if a negative or invalid file index is given - */ - public String getFileByIndex(int index) { - if (index < 0) { - throw new IllegalArgumentException("Negative file index was given."); - } - if (this.line == null) { - return null; - } - - return this.line.getFile(index, this.comp_dir); - } - - /** - * Checks validity of a file index number. - * - * @param index file number, 1..N - * @return boolean true if index is a valid file number, false otherwise - */ - public boolean isValidFileIndex(int index) { - return line.isValidFileIndex(index); - } - - /** - * Get the producer of the compile unit - * @return the producer of the compile unit - */ - public String getProducer() { - return this.producer; - } - - /** - * Get the compile directory of the compile unit - * @return the compile directory of the compile unit - */ - public String getCompileDirectory() { - return this.comp_dir; - } - - /** - * Get the high PC value of the compile unit - * @return the high PC value of the compile unit - */ - public Number getHighPC() { - return this.high_pc; - } - - /** - * Get the low PC value of the compile unit - * @return the low PC value of the compile unit - */ - public Number getLowPC() { - return this.low_pc; - } - - /** - * Get the source language of the compile unit. - *

- * See {@link DWARFSourceLanguage} for values. - * - * @return the source language of the compile unit - */ - public int getLanguage() { - return this.language == null ? -1 : this.language.intValue(); - } - - public boolean hasDWO() { - return hasDWO; - } - - @Override - public String toString() { - return String.format( - "DWARFCompileUnit [name=%s, producer=%s, comp_dir=%s, high_pc=%s, low_pc=%s, language=%s, hasDWO=%s, line=%s]", - name, producer, comp_dir, high_pc, low_pc, language, hasDWO, line); - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFLine.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFLine.java deleted file mode 100644 index d59f400acf..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFLine.java +++ /dev/null @@ -1,284 +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.util.bin.format.dwarf4; - -import java.util.ArrayList; -import java.util.List; - -import java.io.File; -import java.io.IOException; - -import org.apache.commons.io.FilenameUtils; - -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.dwarf4.DWARFUtil.LengthResult; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute; -import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram; -import ghidra.program.model.data.LEB128; - -public class DWARFLine { - private long unit_length; - private int format; - private int version; - private long header_length; - private int minimum_instruction_length; - private int maximum_operations_per_instruction; - private int default_is_stmt; - private int line_base; - private int line_range; - private int opcode_base; - private int[] standard_opcode_length; - private List include_directories; - private List file_names; - - /** - * Read a DWARFLine from the compile unit's DW_AT_stmt_list location in the - * DebugLine stream (if present). - * - * @param diea {@link DIEAggregate} compile unit DIE(a) - * @return a new DWARFLine instance if DW_AT_stmt_list and stream are present, otherwise null - * @throws IOException if error reading data - * @throws DWARFException if bad DWARF values - */ - public static DWARFLine read(DIEAggregate diea) throws IOException, DWARFException { - DWARFProgram dProg = diea.getProgram(); - BinaryReader reader = dProg.getDebugLine(); - - // DW_AT_stmt_list can be const or ptr form types. - long stmtListOffset = diea.getUnsignedLong(DWARFAttribute.DW_AT_stmt_list, -1); - - if (reader == null || stmtListOffset < 0) { - return null; - } - - reader.setPointerIndex(stmtListOffset); - - DWARFLine result = new DWARFLine(); - - LengthResult lengthInfo = DWARFUtil.readLength(reader, dProg.getGhidraProgram()); - result.unit_length = lengthInfo.length; - result.format = lengthInfo.format; - if (result.unit_length == 0) { - throw new DWARFException( - "Invalid DWARFLine length 0 at 0x" + Long.toHexString(stmtListOffset)); - } - - // A version number for this line number information section - result.version = reader.readNextUnsignedShort(); - - // Get the header length based on the current format - result.header_length = DWARFUtil.readOffsetByDWARFformat(reader, result.format); - - result.minimum_instruction_length = reader.readNextUnsignedByte(); - - // Maximum operations per instruction only exists in DWARF version 4 or higher - if (result.version >= 4) { - result.maximum_operations_per_instruction = reader.readNextUnsignedByte(); - } - else { - result.maximum_operations_per_instruction = 1; - } - result.default_is_stmt = reader.readNextUnsignedByte(); - result.line_base = reader.readNextByte(); - result.line_range = reader.readNextUnsignedByte(); - result.opcode_base = reader.readNextUnsignedByte(); - result.standard_opcode_length = new int[result.opcode_base]; - result.standard_opcode_length[0] = 1; /* Should never be used */ - for (int i = 1; i < result.opcode_base; i++) { - result.standard_opcode_length[i] = reader.readNextUnsignedByte(); - } - - // Read all include directories - result.include_directories = new ArrayList<>(); - String include = reader.readNextAsciiString(); - while (include.length() != 0) { - result.include_directories.add(include); - include = reader.readNextAsciiString(); - } - - // Read all files - result.file_names = new ArrayList<>(); - DWARFFile file = new DWARFFile(reader); - while (file.getName().length() != 0) { - result.file_names.add(file); - file = new DWARFFile(reader); - } - - return result; - } - - private DWARFLine() { - // empty, use #read() - } - - /** - * Get a file name with the full path included. - * @param index index of the file - * @param compileDirectory current compile unit directory - * @return file name with full path - */ - public String getFullFile(int index, String compileDirectory) { - if (index == 0) { - //TODO: Handle index = 0 - throw new UnsupportedOperationException( - "Currently does not support retrieving the primary source file."); - } - else if (index > 0) { - // Retrieve the file by index (index starts at 1) - DWARFFile file = this.file_names.get(index - 1); - - File fileObj = new File(file.getName()); - - // Check to see if the file is an absolute path and return if so - if (fileObj.isAbsolute()) { - return file.getName(); - } - - // Otherwise we need to retrieve the directory - int diridx = (int) file.getDirectoryIndex(); - if (diridx == 0) { - // Use the compile directory if a directory index of 0 is given - if (compileDirectory != null) { - return compileDirectory + file.getName(); - } - throw new IllegalArgumentException( - "No compile directory was given when one was expected."); - } - else if (diridx > 0) { - // Retrieve and append the directory - String directory = this.include_directories.get(diridx - 1); - return directory + file.getName(); - } - throw new IndexOutOfBoundsException( - "Negative directory index was found: " + Integer.toString(diridx)); - } - throw new IllegalArgumentException( - "Negative file index was given: " + Integer.toString(index)); - } - - /** - * Get a file name given a file index. - * @param index index of the file - * @param compileDirectory current compile unit directory - * @return file name - */ - public String getFile(int index, String compileDirectory) { - if (index == 0) { - //TODO: Handle index = 0 - throw new UnsupportedOperationException( - "Currently does not support retrieving the primary source file."); - } - else if (index > 0) { - // Retrieve the file by index (index starts at 1) - DWARFFile file = this.file_names.get(index - 1); - return FilenameUtils.getName(file.getName()); - } - throw new IllegalArgumentException( - "Negative file index was given: " + Integer.toString(index)); - } - - /** - * Returns true if file exists. - * - * @param index file number, excluding 0 - * @return boolean true if file exists - */ - public boolean isValidFileIndex(int index) { - index--; - return 0 <= index && index < file_names.size(); - } - - @Override - public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer.append("Line Entry"); - buffer.append(" Include Directories: ["); - for (String dir : this.include_directories) { - buffer.append(dir); - buffer.append(", "); - } - buffer.append("] File Names: ["); - for (DWARFFile file : this.file_names) { - buffer.append(file.toString()); - buffer.append(", "); - } - buffer.append("]"); - return buffer.toString(); - } - - /** - * DWARFFile is used to store file information for each entry in the line section header. - */ - public static class DWARFFile { - private String name; - private long directory_index; - private long modification_time; - private long length; - - /** - * Read in a new file entry and store into this object. - * @param reader binary reader to read the file entry - * @throws IOException if an I/O error occurs - */ - public DWARFFile(BinaryReader reader) throws IOException { - this.name = reader.readNextAsciiString(); - - // This entry exists only if the length of the string is more than 0 - if (this.name.length() > 0) { - this.directory_index = reader.readNext(LEB128::unsigned); - this.modification_time = reader.readNext(LEB128::unsigned); - this.length = reader.readNext(LEB128::unsigned); - } - } - - /** - * Create a new DWARF file entry with the given parameters. - * @param name name of the file - * @param directory_index index of the directory for this file - * @param modification_time modification time of the file - * @param length length of the file - */ - public DWARFFile(String name, long directory_index, long modification_time, long length) { - this.name = name; - this.directory_index = directory_index; - this.modification_time = modification_time; - this.length = length; - } - - public String getName() { - return this.name; - } - - public long getDirectoryIndex() { - return this.directory_index; - } - - public long getModificationTime() { - return this.modification_time; - } - - @Override - public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer.append("Filename: "); - buffer.append(this.name); - buffer.append(" Length: "); - buffer.append(Long.toHexString(this.length)); - return buffer.toString(); - } - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFLocation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFLocation.java deleted file mode 100644 index f37b4e14ee..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFLocation.java +++ /dev/null @@ -1,148 +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.util.bin.format.dwarf4; - -import java.util.List; - -public class DWARFLocation { - private DWARFRange addressRange; - private byte[] location; - - /** - * Create a Location given an address range and location expression. - * @param addressRange memory range of this location - * @param location byte array holding location expression - */ - public DWARFLocation(DWARFRange addressRange, byte[] location) { - this.addressRange = addressRange; - this.location = location; - } - - public DWARFRange getRange() { - return this.addressRange; - } - - public byte[] getLocation() { - return this.location; - } - - /** - * Get the location that corresponds to the entry point of the function If - * there is only a single location, assume it applies to whole function - * - * @param locList - * @param funcAddr - * @return the byte array corresponding to the location expression - */ - public static DWARFLocation getTopLocation(List locList, long funcAddr) { - if (locList.size() == 1) { - return locList.get(0); - } - for (DWARFLocation loc : locList) { - if (loc.getRange().getFrom() == funcAddr) { - return loc; - } - } - return null; - } - - public static DWARFLocation getEntryLocation(List locList, long funcAddr) { - for (DWARFLocation loc : locList) { - if (loc.getRange().getFrom() == funcAddr) { - return loc; - } - } - return null; - } - - public static DWARFLocation getFirstLocation(List locList) { - return !locList.isEmpty() ? locList.get(0) : null; - } - - /* - * I know we frown on keeping large chunks of code around that have been commented - * out, but... - * 1) this is how a core DWARF data structure is read from disk, and contains some gotchas - * and hard-won knowledge - * 2) isn't being used right now due to changes in the analyzer and how it uses addr data. - * 3) might be needed in the future if the analyzer changes its ways - */ -// /** -// * Return a list of DWARF locations read from the debug_loc section. -// * @param offset offset into the debug_loc section -// * @param die the DIE that pointed to this debug_loc location list -// * @return list of DWARF locations (address range and location expression) -// * @throws IOException if an I/O error occurs -// */ -// public static List parseLocationList(long offset, DebugInfoEntry die) -// throws IOException { -// DWARFProgram prog = die.getCompilationUnit().getProgram(); -// BinaryReader debug_loc = prog.getDebugLocation(); -// -// List ranges = new ArrayList<>(); -// if (debug_loc == null) { -// return ranges; -// } -// -// debug_loc.setPointerIndex(offset); -// byte pointerSize = die.getCompilationUnit().getPointerSize(); -// -// Number baseAddress = die.getCompilationUnit().getCompileUnit().getLowPC(); -// long baseAddressOffset = (baseAddress != null) ? baseAddress.longValue() : 0; -// -// Number cuLowPC = die.getCompilationUnit().getCompileUnit().getLowPC(); -// long cuBase = (cuLowPC != null) ? cuLowPC.longValue() : Long.MAX_VALUE; -// -// // Loop through the debug_loc entry -// while (debug_loc.getPointerIndex() < debug_loc.length()) { -// Number beginning = DWARFUtil.readAddress(debug_loc, pointerSize); -// Number ending = DWARFUtil.readAddress(debug_loc, pointerSize); // dwarf end addrs are exclusive -// -// // List end -// if (beginning.longValue() == 0 && ending.longValue() == 0) { -// break; -// } -// -// // Check to see if this is a base address entry -// if (NumberUtil.equalsMaxUnsignedValue(beginning)) { -// baseAddressOffset = ending.longValue(); -// continue; -// } -// -// // Size is 2 bytes -// int size = debug_loc.readNextUnsignedShort(); -// -// // Read the location description -// byte[] location = debug_loc.readNextByteArray(size); -// -// // Test to see if the 'offset' read from the debug_loc data is already -// // greater-than the compunit's lowpc. This indicates the 'offset' isn't -// // an offset, but already an absolute value. This occurs in some -// // gcc dwarf compilation flag combinations. -// boolean isBadOffset = (beginning.longValue() > cuBase); -// -// long absStart = beginning.longValue(); -// long absEnd = ending.longValue(); -// if (!isBadOffset) { -// absStart += baseAddressOffset; -// absEnd += baseAddressOffset; -// } -// -// ranges.add(new DWARFLocation(new DWARFRange(absStart, absEnd), location)); -// } -// return ranges; -// } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFAttributeFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFAttributeFactory.java deleted file mode 100644 index c4ba40ce40..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFAttributeFactory.java +++ /dev/null @@ -1,168 +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.util.bin.format.dwarf4.attribs; - -import java.io.IOException; - -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.dwarf4.DWARFCompilationUnit; -import ghidra.app.util.bin.format.dwarf4.DWARFUtil; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFForm; -import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram; -import ghidra.app.util.bin.format.dwarf4.next.StringTable; -import ghidra.program.model.data.LEB128; - -/** - * A factory for deserializing {@link DWARFAttributeValue dwarf attribute} from - * a stream. - */ -public class DWARFAttributeFactory { - - /** - * Max number of bytes that dw_form_block4 is allowed to specify, 1Mb. - */ - public static final int MAX_BLOCK4_SIZE = 1024 * 1024; - - private DWARFProgram prog; - - public DWARFAttributeFactory(DWARFProgram prog) { - this.prog = prog; - } - - /** - * Read from the given BinaryReader based on the type of DWARFForm that is given. - * @param reader BinaryReader pointing to the value to read - * @param unit the current compilation unit - * @param form DWARFForm type defining the type of value to read - * @return Object representing the value that was read - * @throws IOException if an I/O error occurs - */ - public DWARFAttributeValue read(BinaryReader reader, DWARFCompilationUnit unit, DWARFForm form) - throws IOException { - StringTable debugStrings = prog.getDebugStrings(); - switch (form) { - case DW_FORM_addr: - return new DWARFNumericAttribute( - reader.readNextUnsignedValue(unit.getPointerSize())); - case DW_FORM_ref1: { - long uoffset = reader.readNextUnsignedValue(1); - return new DWARFNumericAttribute(uoffset + unit.getStartOffset()); - } - case DW_FORM_ref2: { - long uoffset = reader.readNextUnsignedValue(2); - return new DWARFNumericAttribute(uoffset + unit.getStartOffset()); - } - case DW_FORM_ref4: { - long uoffset = reader.readNextUnsignedValue(4); - return new DWARFNumericAttribute(uoffset + unit.getStartOffset()); - } - case DW_FORM_ref8: { - long uoffset = reader.readNextUnsignedValue(8); - return new DWARFNumericAttribute(uoffset + unit.getStartOffset()); - } - case DW_FORM_ref_udata: { - long uoffset = reader.readNext(LEB128::unsigned); - return new DWARFNumericAttribute(uoffset + unit.getStartOffset()); - } - - // DW_FORM_ref_addr and DW_FORM_sec_offset have identical raw forms, - // but point to different items (ref_addr points to elements in .debug_info, - // sec_offset points to elements in other sections) - case DW_FORM_ref_addr: - return new DWARFNumericAttribute( - DWARFUtil.readOffsetByDWARFformat(reader, unit.getFormat())); - case DW_FORM_sec_offset: - return new DWARFNumericAttribute( - DWARFUtil.readOffsetByDWARFformat(reader, unit.getFormat())); - - case DW_FORM_block1: { - int length = DWARFUtil.readVarSizedUInt(reader, 1); - return new DWARFBlobAttribute(reader.readNextByteArray(length)); - } - case DW_FORM_block2: { - int length = DWARFUtil.readVarSizedUInt(reader, 2); - return new DWARFBlobAttribute(reader.readNextByteArray(length)); - } - case DW_FORM_block4: { - int length = DWARFUtil.readVarSizedUInt(reader, 4); - if (length < 0 || length > MAX_BLOCK4_SIZE) { - throw new IOException("Invalid/bad dw_form_block4 size: " + length); - } - return new DWARFBlobAttribute(reader.readNextByteArray(length)); - } - case DW_FORM_block: { - int length = reader.readNextUnsignedVarIntExact(LEB128::unsigned); - if (length < 0 || length > MAX_BLOCK4_SIZE) { - throw new IOException("Invalid/bad dw_form_block size: " + length); - } - return new DWARFBlobAttribute(reader.readNextByteArray(length)); - } - case DW_FORM_data1: - return new DWARFNumericAttribute(8, reader.readNextByte(), true, true); - case DW_FORM_data2: - return new DWARFNumericAttribute(16, reader.readNextShort(), true, true); - case DW_FORM_data4: - return new DWARFNumericAttribute(32, reader.readNextInt(), true, true); - case DW_FORM_data8: - return new DWARFNumericAttribute(64, reader.readNextLong(), true, true); - case DW_FORM_sdata: - return new DWARFNumericAttribute(64, reader.readNext(LEB128::signed), true); - case DW_FORM_udata: - return new DWARFNumericAttribute(64, reader.readNext(LEB128::unsigned), false); - - case DW_FORM_exprloc: { - int length = reader.readNextUnsignedVarIntExact(LEB128::unsigned); - if (length < 0 || length > MAX_BLOCK4_SIZE) { - throw new IOException("Invalid/bad dw_form_exprloc size: " + length); - } - return new DWARFBlobAttribute(reader.readNextByteArray(length)); - } - - case DW_FORM_flag: - return DWARFBooleanAttribute.get(reader.readNextByte() != 0); - case DW_FORM_flag_present: - return DWARFBooleanAttribute.TRUE; - - case DW_FORM_string: - return new DWARFStringAttribute(reader.readNextUtf8String()); - case DW_FORM_strp: - // Note: we can either read the string from the string section (via. the - // string table) here and put it in a DWARFStringAttribute and hope - // it is used and not wasted memory, or use a DWARFDeferredStringAttribute - // to hold the offset until the string is actually requested in DIEAggregate.getString() - long stringOffset = DWARFUtil.readOffsetByDWARFformat(reader, unit.getFormat()); - if (!debugStrings.isValid(stringOffset)) - throw new IOException("Bad string offset " + Long.toHexString(stringOffset)); - return new DWARFDeferredStringAttribute(stringOffset); - //return new DWARFStringAttribute(debugStrings.getStringAtOffset(stringOffset)); - - case DW_FORM_ref_sig8: - throw new UnsupportedOperationException( - "DW_FORM_ref_sig8 is currently not implemented"); - - // Indirect Form - case DW_FORM_indirect: - DWARFForm formValue = - DWARFForm.find(reader.readNextUnsignedVarIntExact(LEB128::unsigned)); - DWARFAttributeValue value = read(reader, unit, formValue); - - return new DWARFIndirectAttribute(value, formValue); - default: - } - throw new IllegalArgumentException("Unknown DWARF Form: " + form.toString()); - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFIndirectAttribute.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFIndirectAttribute.java deleted file mode 100644 index ed0ae0765b..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFIndirectAttribute.java +++ /dev/null @@ -1,46 +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.util.bin.format.dwarf4.attribs; - -import ghidra.app.util.bin.format.dwarf4.DWARFAbbreviation; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFForm; - -/** - * DWARF indirect attribute. - *

- * Holds a reference to an actual {@link DWARFAttributeValue} instance and its {@link DWARFForm type}. - *

- * Used with DW_FORM_indirect attributes that encode the {@link DWARFForm form type} of the attribute - * value inline instead of in the DIE's {@link DWARFAbbreviation abbreviation}. - *

- */ -public class DWARFIndirectAttribute implements DWARFAttributeValue { - private DWARFAttributeValue value; - private DWARFForm form; - - public DWARFIndirectAttribute(DWARFAttributeValue value, DWARFForm form) { - this.value = value; - this.form = form; - } - - public DWARFAttributeValue getValue() { - return value; - } - - public DWARFForm getForm() { - return form; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFNumericAttribute.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFNumericAttribute.java deleted file mode 100644 index 31a5273901..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFNumericAttribute.java +++ /dev/null @@ -1,82 +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.util.bin.format.dwarf4.attribs; - -import ghidra.program.model.scalar.Scalar; - -/** - * DWARF numeric attribute. - */ -public class DWARFNumericAttribute extends Scalar implements DWARFAttributeValue { - - private final boolean ambiguous; - - /** - * Creates a new numeric value, using 64 bits and marked as signed - * - * @param value long 64 bit value - */ - public DWARFNumericAttribute(long value) { - this(64, value, true, false); - } - - /** - * Creates a new numeric value, using the specific bitLength and value. - * - * @param bitLength number of bits, valid values are 1..64, or 0 if value is also 0 - * @param value value of the scalar, any bits that are set above bitLength will be ignored - * @param signed true for a signed value, false for an unsigned value. - */ - public DWARFNumericAttribute(int bitLength, long value, boolean signed) { - this(bitLength, value, signed, false); - } - - /** - * Creates a new numeric value, using the specific bitLength and value. - * - * @param bitLength number of bits, valid values are 1..64, or 0 if value is also 0 - * @param value value of the scalar, any bits that are set above bitLength will be ignored - * @param signed true for a signed value, false for an unsigned value. - * @param ambiguous true for value with ambiguous signedness ({@code signed} parameter should - * not be trusted), false for value where the {@code signed} parameter is known to be correct - */ - public DWARFNumericAttribute(int bitLength, long value, boolean signed, boolean ambiguous) { - super(bitLength, value, signed); - this.ambiguous = ambiguous; - } - - /** - * {@return boolean flag, if true this value's signedness is up to the user of the value, - * if false the signedness was determined when the value was constructed} - */ - public boolean isAmbiguousSignedness() { - return ambiguous; - } - - /** - * {@return the value, forcing the signedness of ambiguous values using the specified hint} - * @param signednessHint true to default to a signed value, false to default to an - * unsigned value - */ - public long getValueWithSignednessHint(boolean signednessHint) { - return getValue(ambiguous ? signednessHint : isSigned()); - } - - @Override - public String toString() { - return String.format("DWARFNumericAttribute: %d [%08x]", getValue(), getValue()); - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFAttribute.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFAttribute.java deleted file mode 100644 index 9048590952..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFAttribute.java +++ /dev/null @@ -1,181 +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.util.bin.format.dwarf4.encoding; - -import ghidra.app.util.bin.format.dwarf4.DWARFUtil; - -public final class DWARFAttribute { - public static final int DW_AT_sibling = 0x1; - public static final int DW_AT_location = 0x2; - public static final int DW_AT_name = 0x3; - public static final int DW_AT_ordering = 0x9; - //public static final int DW_AT_subscr_data = 0xa; - public static final int DW_AT_byte_size = 0xb; - public static final int DW_AT_bit_offset = 0xc; - public static final int DW_AT_bit_size = 0xd; - //public static final int DW_AT_element_list = 0xf; - public static final int DW_AT_stmt_list = 0x10; - public static final int DW_AT_low_pc = 0x11; - public static final int DW_AT_high_pc = 0x12; - public static final int DW_AT_language = 0x13; - //public static final int DW_AT_member = 0x14; - public static final int DW_AT_discr = 0x15; - public static final int DW_AT_discr_value = 0x16; - public static final int DW_AT_visibility = 0x17; - public static final int DW_AT_import = 0x18; - public static final int DW_AT_string_length = 0x19; - public static final int DW_AT_common_reference = 0x1a; - public static final int DW_AT_comp_dir = 0x1b; - public static final int DW_AT_const_value = 0x1c; - public static final int DW_AT_containing_type = 0x1d; - public static final int DW_AT_default_value = 0x1e; - public static final int DW_AT_inline = 0x20; - public static final int DW_AT_is_optional = 0x21; - public static final int DW_AT_lower_bound = 0x22; - public static final int DW_AT_producer = 0x25; - public static final int DW_AT_prototyped = 0x27; - public static final int DW_AT_return_addr = 0x2a; - public static final int DW_AT_start_scope = 0x2c; - public static final int DW_AT_bit_stride = 0x2e; - public static final int DW_AT_upper_bound = 0x2f; - public static final int DW_AT_abstract_origin = 0x31; - public static final int DW_AT_accessibility = 0x32; - public static final int DW_AT_address_class = 0x33; - public static final int DW_AT_artificial = 0x34; - public static final int DW_AT_base_types = 0x35; - public static final int DW_AT_calling_convention = 0x36; - public static final int DW_AT_count = 0x37; - public static final int DW_AT_data_member_location = 0x38; - public static final int DW_AT_decl_column = 0x39; - public static final int DW_AT_decl_file = 0x3a; - public static final int DW_AT_decl_line = 0x3b; - public static final int DW_AT_declaration = 0x3c; - public static final int DW_AT_discr_list = 0x3d; - public static final int DW_AT_encoding = 0x3e; - public static final int DW_AT_external = 0x3f; - public static final int DW_AT_frame_base = 0x40; - public static final int DW_AT_friend = 0x41; - public static final int DW_AT_identifier_case = 0x42; - public static final int DW_AT_macro_info = 0x43; - public static final int DW_AT_namelist_item = 0x44; - public static final int DW_AT_priority = 0x45; - public static final int DW_AT_segment = 0x46; - public static final int DW_AT_specification = 0x47; - public static final int DW_AT_static_link = 0x48; - public static final int DW_AT_type = 0x49; - public static final int DW_AT_use_location = 0x4a; - public static final int DW_AT_variable_parameter = 0x4b; - public static final int DW_AT_virtuality = 0x4c; - public static final int DW_AT_vtable_elem_location = 0x4d; - public static final int DW_AT_allocated = 0x4e; - public static final int DW_AT_associated = 0x4f; - public static final int DW_AT_data_location = 0x50; - public static final int DW_AT_byte_stride = 0x51; - public static final int DW_AT_entry_pc = 0x52; - public static final int DW_AT_use_UTF8 = 0x53; - public static final int DW_AT_extension = 0x54; - public static final int DW_AT_ranges = 0x55; - public static final int DW_AT_trampoline = 0x56; - public static final int DW_AT_call_column = 0x57; - public static final int DW_AT_call_file = 0x58; - public static final int DW_AT_call_line = 0x59; - public static final int DW_AT_description = 0x5a; - public static final int DW_AT_binary_scale = 0x5b; - public static final int DW_AT_decimal_scale = 0x5c; - public static final int DW_AT_small = 0x5d; - public static final int DW_AT_decimal_sign = 0x5e; - public static final int DW_AT_digit_count = 0x5f; - public static final int DW_AT_picture_string = 0x60; - public static final int DW_AT_mutable = 0x61; - public static final int DW_AT_threads_scaled = 0x62; - public static final int DW_AT_explicit = 0x63; - public static final int DW_AT_object_pointer = 0x64; - public static final int DW_AT_endianity = 0x65; - public static final int DW_AT_elemental = 0x66; - public static final int DW_AT_pure = 0x67; - public static final int DW_AT_recursive = 0x68; - public static final int DW_AT_signature = 0x69; - public static final int DW_AT_main_subprogram = 0x6a; - public static final int DW_AT_data_bit_offset = 0x6b; - public static final int DW_AT_const_expr = 0x6c; - public static final int DW_AT_enum_class = 0x6d; - public static final int DW_AT_linkage_name = 0x6e; - public static final int DW_AT_string_length_bit_size = 0x6f; - public static final int DW_AT_string_length_byte_size = 0x70; - public static final int DW_AT_rank = 0x71; - public static final int DW_AT_str_offsets_base = 0x72; - public static final int DW_AT_addr_base = 0x73; - public static final int DW_AT_rnglists_base = 0x74; - // 0x75 reserved, unused - public static final int DW_AT_dwo_name = 0x76; - public static final int DW_AT_reference = 0x77; - public static final int DW_AT_rvalue_reference = 0x78; - public static final int DW_AT_macros = 0x79; - public static final int DW_AT_call_all_calls = 0x7a; - public static final int DW_AT_call_all_source_calls = 0x7b; - public static final int DW_AT_call_all_tail_calls = 0x7c; - public static final int DW_AT_call_return_pc = 0x7d; - public static final int DW_AT_call_value = 0x7e; - public static final int DW_AT_call_origin = 0x7f; - public static final int DW_AT_call_parameter = 0x80; - public static final int DW_AT_call_pc = 0x81; - public static final int DW_AT_call_tail_call = 0x82; - public static final int DW_AT_call_target = 0x83; - public static final int DW_AT_call_target_clobbered = 0x84; - public static final int DW_AT_call_data_location = 0x85; - public static final int DW_AT_call_data_value = 0x86; - public static final int DW_AT_noreturn = 0x87; - public static final int DW_AT_alignment = 0x88; - public static final int DW_AT_export_symbols = 0x89; - public static final int DW_AT_deleted = 0x8a; - public static final int DW_AT_defaulted = 0x8b; - public static final int DW_AT_loclists_base = 0x8c; - - public static final int DW_AT_lo_user = 0x2000; - public static final int DW_AT_hi_user = 0x3fff; - public static final int DW_AT_MIPS_linkage_name = 0x2007; - - // GNU DebugFission stuff - public static final int DW_AT_GNU_dwo_name = 0x2130; - public static final int DW_AT_GNU_dwo_id = 0x2131; - public static final int DW_AT_GNU_ranges_base = 0x2132; - public static final int DW_AT_GNU_addr_base = 0x2133; - public static final int DW_AT_GNU_pubnames = 0x2134; - public static final int DW_AT_GNU_pubtypes = 0x2135; - // end GNU DebugFission - - // Golang - public static final int DW_AT_go_kind = 0x2900; - public static final int DW_AT_go_key = 0x2901; - public static final int DW_AT_go_elem = 0x2902; - public static final int DW_AT_go_embedded_field = 0x2903; - public static final int DW_AT_go_runtime_type = 0x2904; - public static final int DW_AT_go_package_name = 0x2905; - public static final int DW_AT_go_dict_index = 0x2906; - // end Golang - - // Apple proprietary tags - public static final int DW_AT_APPLE_ptrauth_key = 0x3e04; - public static final int DW_AT_APPLE_ptrauth_address_discriminated = 0x3e05; - public static final int DW_AT_APPLE_ptrauth_extra_discriminator = 0x3e06; - public static final int DW_AT_APPLE_omit_frame_ptr = 0x3fe7; - public static final int DW_AT_APPLE_optimized = 0x3fe1; - // end Apple proprietary tags - - public static String toString(long value) { - return DWARFUtil.toString(DWARFAttribute.class, value); - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFChildren.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFChildren.java deleted file mode 100644 index b20c6e0aa0..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFChildren.java +++ /dev/null @@ -1,66 +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.util.bin.format.dwarf4.encoding; - -import java.util.HashMap; -import java.util.Map; - -/** - * DWARF child determination consts from www.dwarfstd.org/doc/DWARF4.pdf. - *

- * Yes, its a direct equiv to a boolean, but its in the spec. - */ -public enum DWARFChildren -{ - DW_CHILDREN_no(0), - DW_CHILDREN_yes(1); - - private final int value; - - private static final Map valueMap; - - static { - valueMap = new HashMap(); - for(DWARFChildren access : DWARFChildren.values()) { - valueMap.put(access.getValue(), access); - } - } - - private DWARFChildren(int value) { - this.value = value; - } - - /** - * Get the integer value of this enum. - * @return the integer value of the enum - */ - public int getValue() { - return this.value; - } - - /** - * Find the children value given a Number value. - * @param key Number value to check - * @return DWARFChildren enum if it exists - * @throws IllegalArgumentException if the key is not found - */ - public static DWARFChildren find(Number key) { - DWARFChildren access = valueMap.get(key.intValue()); - if(access != null) - return access; - throw new IllegalArgumentException("Invalid Integer value: " + key.toString()); - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFForm.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFForm.java deleted file mode 100644 index 8f718aa429..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFForm.java +++ /dev/null @@ -1,88 +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.util.bin.format.dwarf4.encoding; - -import java.util.HashMap; -import java.util.Map; - -/** - * DWARF attribute encoding consts from www.dwarfstd.org/doc/DWARF4.pdf - */ -public enum DWARFForm { - NULL(0x0), - DW_FORM_addr(0x1), - DW_FORM_block2(0x3), - DW_FORM_block4(0x4), - DW_FORM_data2(0x5), - DW_FORM_data4(0x6), - DW_FORM_data8(0x7), - DW_FORM_string(0x8), - DW_FORM_block(0x9), - DW_FORM_block1(0xa), - DW_FORM_data1(0xb), - DW_FORM_flag(0xc), - DW_FORM_sdata(0xd), - DW_FORM_strp(0xe), - DW_FORM_udata(0xf), - DW_FORM_ref_addr(0x10), - DW_FORM_ref1(0x11), - DW_FORM_ref2(0x12), - DW_FORM_ref4(0x13), - DW_FORM_ref8(0x14), - DW_FORM_ref_udata(0x15), - DW_FORM_indirect(0x16), - DW_FORM_sec_offset(0x17), - DW_FORM_exprloc(0x18), - DW_FORM_flag_present(0x19), - DW_FORM_ref_sig8(0x20); - - private final int value; - - private static final Map valueMap; - - static { - valueMap = new HashMap<>(); - for (DWARFForm access : DWARFForm.values()) { - valueMap.put(access.getValue(), access); - } - } - - private DWARFForm(int value) { - this.value = value; - } - - /** - * Get the integer value of this enum. - * @return the integer value of the enum - */ - public int getValue() { - return this.value; - } - - /** - * Find the form value given a Number value. - * @param key Number value to check - * @return DWARFForm enum if it exists - * @throws IllegalArgumentException if the key is not found - */ - public static DWARFForm find(int key) { - DWARFForm access = valueMap.get(key); - if (access != null) { - return access; - } - throw new IllegalArgumentException("Invalid Integer value: " + key); - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFTag.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFTag.java deleted file mode 100644 index b7016c7ad4..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFTag.java +++ /dev/null @@ -1,103 +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.util.bin.format.dwarf4.encoding; - -/** - * DWARF uses a series of debugging information entries to define a - * low-level representation of a source program. Each debugging - * information entry is described by an identifying tag and - * contains a series of attributes. The tag specifies the class - * to which an entry belongs, and the attributes define the - * specific characteristics of the entry. - *

- * The debugging information entries in DWARF Version 2, 3, and 4 are - * intended to exist in the .debug_info section of an object file. - *

- * The set of required tag names is listed below. - */ -public final class DWARFTag -{ - public static final int DW_TAG_array_type = 0x1; - public static final int DW_TAG_class_type = 0x2; - public static final int DW_TAG_entry_point = 0x3; - public static final int DW_TAG_enumeration_type = 0x4; - public static final int DW_TAG_formal_parameter = 0x5; - public static final int DW_TAG_imported_declaration = 0x8; - public static final int DW_TAG_label = 0xa; - public static final int DW_TAG_lexical_block = 0xb; - public static final int DW_TAG_member = 0xd; - public static final int DW_TAG_pointer_type = 0xf; - public static final int DW_TAG_reference_type = 0x10; - public static final int DW_TAG_compile_unit = 0x11; - public static final int DW_TAG_string_type = 0x12; - public static final int DW_TAG_structure_type = 0x13; - public static final int DW_TAG_subroutine_type = 0x15; - public static final int DW_TAG_typedef = 0x16; - public static final int DW_TAG_union_type = 0x17; - public static final int DW_TAG_unspecified_parameters = 0x18; - public static final int DW_TAG_variant = 0x19; - public static final int DW_TAG_common_block = 0x1a; - public static final int DW_TAG_common_inclusion = 0x1b; - public static final int DW_TAG_inheritance = 0x1c; - public static final int DW_TAG_inlined_subroutine = 0x1d; - public static final int DW_TAG_module = 0x1e; - public static final int DW_TAG_ptr_to_member_type = 0x1f; - public static final int DW_TAG_set_type = 0x20; - public static final int DW_TAG_subrange_type = 0x21; - public static final int DW_TAG_with_stmt = 0x22; - public static final int DW_TAG_access_declaration = 0x23; - public static final int DW_TAG_base_type = 0x24; - public static final int DW_TAG_catch_block = 0x25; - public static final int DW_TAG_const_type = 0x26; - public static final int DW_TAG_constant = 0x27; - public static final int DW_TAG_enumerator = 0x28; - public static final int DW_TAG_file_type = 0x29; - public static final int DW_TAG_friend = 0x2a; - public static final int DW_TAG_namelist = 0x2b; - public static final int DW_TAG_namelist_item = 0x2c; - public static final int DW_TAG_packed_type = 0x2d; - public static final int DW_TAG_subprogram = 0x2e; - public static final int DW_TAG_template_type_param = 0x2f; - public static final int DW_TAG_template_value_param = 0x30; - public static final int DW_TAG_thrown_type = 0x31; - public static final int DW_TAG_try_block = 0x32; - public static final int DW_TAG_variant_part = 0x33; - public static final int DW_TAG_variable = 0x34; - public static final int DW_TAG_volatile_type = 0x35; - public static final int DW_TAG_dwarf_procedure = 0x36; - public static final int DW_TAG_restrict_type = 0x37; - public static final int DW_TAG_interface_type = 0x38; - public static final int DW_TAG_namespace = 0x39; - public static final int DW_TAG_imported_module = 0x3a; - public static final int DW_TAG_unspecified_type = 0x3b; - public static final int DW_TAG_partial_unit = 0x3c; - public static final int DW_TAG_imported_unit = 0x3d; - public static final int DW_TAG_mutable_type = 0x3e; - public static final int DW_TAG_condition = 0x3f; - public static final int DW_TAG_shared_type = 0x40; - public static final int DW_TAG_type_unit = 0x41; - public static final int DW_TAG_rvalue_reference_type = 0x42; - public static final int DW_TAG_template_alias = 0x43; - public static final int DW_TAG_call_site = 0x48; - public static final int DW_TAG_call_site_parameter = 0x49; - public static final int DW_TAG_lo_user = 0x4080; - public static final int DW_TAG_gnu_call_site = 0x4109; - public static final int DW_TAG_gnu_call_site_parameter = 0x410a; - public static final int DW_TAG_hi_user = 0xffff; - - public static final int DW_TAG_APPLE_ptrauth_type = 0x4300; // Apple proprietary - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/StringTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/StringTable.java deleted file mode 100644 index 1a3cf7da1a..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/StringTable.java +++ /dev/null @@ -1,117 +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.util.bin.format.dwarf4.next; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -import ghidra.app.util.bin.ByteProvider; - -/** - * A offset-to-String string table backed by a simple byte array (encoded as UTF-8). - *

- * Requested strings are instantiated when requested. - */ -public class StringTable { - private byte[] bytes; - - /** - * Create a {@link StringTable} by reading the entire contents of a {@link ByteProvider} - * into memory. - *

- * If the specified {@link ByteProvider} is null, an empty string table will be constructed. - *

- * @param bp - * @return - * @throws IOException - */ - public static StringTable readStringTable(ByteProvider bp) throws IOException { - byte[] bytes = (bp != null) ? bp.readBytes(0, bp.length()) : new byte[0]; - return new StringTable(bytes); - } - - /** - * Creates a StringTable using the bytes contained in the supplied array. - */ - public StringTable(byte[] bytes) { - this.bytes = bytes; - } - - /** - * Returns true if the specified offset is a valid offset for this string table. - *

- * @param offset - * @return - */ - public boolean isValid(long offset) { - return (offset >= 0) && (offset < bytes.length); - } - - public void clear() { - bytes = null; - } - - /** - * Returns the string found at offset, or throws an {@link IOException} - * if the offset is out of bounds. - * - * @param offset - * @return a string, never null. - * @throws IOException if not found - */ - public String getStringAtOffset(long offset) throws IOException { - if (!isValid(offset)) { - throw new IOException("Invalid offset requested " + offset); - } - - String tmp = new String(bytes, (int) offset, getNullTermStringLen(bytes, (int) offset), - StandardCharsets.UTF_8); - - return tmp; - } - - private static final int getNullTermStringLen(byte[] bytes, int startOffset) { - int cp = startOffset; - while (cp < bytes.length && bytes[cp] != 0) { - cp++; - } - return cp - startOffset; - } - - public int getByteCount() { - return bytes.length; - } - - /** - * Modifies the string table to add a string at a specified offset, growing the - * internal byte[] storage as necessary to accommodate the string at the offset. - *

- * Used for unit tests to construct a custom string table for test cases. - *

- * @param offset where to place the string in the table - * @param s string to insert into table - */ - public void add(int offset, String s) { - byte[] sBytes = s.getBytes(); - int newLen = Math.max(bytes.length, offset + sBytes.length + 1); - byte[] newBytes = new byte[newLen]; - System.arraycopy(bytes, 0, newBytes, 0, bytes.length); - System.arraycopy(sBytes, 0, newBytes, offset, sBytes.length); - - this.bytes = newBytes; - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoFunctionFixup.java index 00a8f4a212..1d9cd12682 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoFunctionFixup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoFunctionFixup.java @@ -18,7 +18,7 @@ package ghidra.app.util.bin.format.golang; import java.util.ArrayList; import java.util.List; -import ghidra.app.util.bin.format.dwarf4.DWARFUtil; +import ghidra.app.util.bin.format.dwarf.DWARFUtil; import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper; import ghidra.program.model.address.Address; import ghidra.program.model.data.*; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoFunctionMultiReturn.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoFunctionMultiReturn.java index 62cbf71ddd..92c11096b1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoFunctionMultiReturn.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoFunctionMultiReturn.java @@ -19,7 +19,7 @@ import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import ghidra.app.util.bin.format.dwarf4.next.*; +import ghidra.app.util.bin.format.dwarf.*; import ghidra.program.model.data.*; import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult; import ghidra.program.model.lang.Register; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoParamStorageAllocator.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoParamStorageAllocator.java index 74c1169b27..8fdb451fc0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoParamStorageAllocator.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoParamStorageAllocator.java @@ -17,7 +17,7 @@ package ghidra.app.util.bin.format.golang; import java.util.*; -import ghidra.app.util.bin.format.dwarf4.DWARFUtil; +import ghidra.app.util.bin.format.dwarf.DWARFUtil; import ghidra.program.model.data.*; import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Register; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoRegisterInfoManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoRegisterInfoManager.java index ab2fd04905..c01d261806 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoRegisterInfoManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoRegisterInfoManager.java @@ -23,7 +23,7 @@ import org.jdom.*; import org.jdom.input.SAXBuilder; import generic.jar.ResourceFile; -import ghidra.app.util.bin.format.dwarf4.DWARFUtil; +import ghidra.app.util.bin.format.dwarf.DWARFUtil; import ghidra.program.model.lang.*; import ghidra.util.Msg; import ghidra.util.xml.XmlUtilities; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GolangDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GolangDWARFFunctionFixup.java index 1ffc556ea3..d50f254488 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GolangDWARFFunctionFixup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GolangDWARFFunctionFixup.java @@ -19,11 +19,9 @@ import java.util.*; import ghidra.app.plugin.core.analysis.TransientProgramProperties; import ghidra.app.plugin.core.analysis.TransientProgramProperties.SCOPE; -import ghidra.app.util.bin.format.dwarf4.*; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFSourceLanguage; -import ghidra.app.util.bin.format.dwarf4.funcfixup.DWARFFunctionFixup; -import ghidra.app.util.bin.format.dwarf4.next.*; -import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction.CommitMode; +import ghidra.app.util.bin.format.dwarf.*; +import ghidra.app.util.bin.format.dwarf.DWARFFunction.CommitMode; +import ghidra.app.util.bin.format.dwarf.funcfixup.DWARFFunctionFixup; import ghidra.app.util.bin.format.golang.rtti.GoFuncData; import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper; import ghidra.program.model.address.Address; @@ -32,7 +30,6 @@ import ghidra.program.model.lang.Register; import ghidra.program.model.listing.*; import ghidra.program.model.pcode.Varnode; import ghidra.program.model.symbol.SymbolType; -import ghidra.util.Msg; import ghidra.util.classfinder.ExtensionPointProperties; import ghidra.util.task.TaskMonitor; @@ -66,7 +63,7 @@ public class GolangDWARFFunctionFixup implements DWARFFunctionFixup { */ public static boolean isGolangFunction(DWARFFunction dfunc) { DIEAggregate diea = dfunc.diea; - int cuLang = diea.getCompilationUnit().getCompileUnit().getLanguage(); + int cuLang = diea.getCompilationUnit().getLanguage(); if (cuLang != DWARFSourceLanguage.DW_LANG_Go) { return false; } @@ -175,12 +172,11 @@ public class GolangDWARFFunctionFixup implements DWARFFunctionFixup { spillVars.add(dvar); if (dvar.type instanceof Structure && dvar.getStorageSize() != dvar.type.getLength()) { - Msg.warn(GoFunctionFixup.class, - "Known storage allocation problem: func %s@%s param %s register allocation for structs missing inter-field padding." - .formatted(dfunc.name.getName(), dfunc.address, - dvar.name.getName())); + dfunc.getProgram() + .logWarningAt(dfunc.address, dfunc.name.getName(), + "Golang known storage allocation problem: param \"%s\" register allocation for structs missing inter-field padding." + .formatted(dvar.name.getName())); } - } else { if (!dvar.isZeroByte()) { @@ -210,7 +206,7 @@ public class GolangDWARFFunctionFixup implements DWARFFunctionFixup { // because we will do it manually for (DataTypeComponent dtc : multiReturn.getNormalStorageComponents()) { allocateReturnStorage(dfunc, dfunc.retval, - dtc.getFieldName() + "-return-result-alias", dtc.getDataType(), + dtc.getFieldName() + "_return_result_alias", dtc.getDataType(), storageAllocator, false); } @@ -218,7 +214,7 @@ public class GolangDWARFFunctionFixup implements DWARFFunctionFixup { // the decompiler's expectations for storage layout) for (DataTypeComponent dtc : multiReturn.getStackStorageComponents()) { allocateReturnStorage(dfunc, dfunc.retval, - dtc.getFieldName() + "-return-result-alias", dtc.getDataType(), + dtc.getFieldName() + "_return_result_alias", dtc.getDataType(), storageAllocator, false); } @@ -231,7 +227,7 @@ public class GolangDWARFFunctionFixup implements DWARFFunctionFixup { } } else { - allocateReturnStorage(dfunc, dfunc.retval, "return-value-alias-variable", + allocateReturnStorage(dfunc, dfunc.retval, "return_value_alias_variable", dfunc.retval.type, storageAllocator, true); } } @@ -251,7 +247,7 @@ public class GolangDWARFFunctionFixup implements DWARFFunctionFixup { // by variables that we create artificially. for (DWARFVariable dvar : spillVars) { DWARFVariable spill = DWARFVariable.fromDataType(dfunc, dvar.type); - String paramName = dvar.name.getName() + "-spill"; + String paramName = dvar.name.getName() + "_spill"; spill.name = dvar.name.replaceName(paramName, paramName); spill.setStackStorage(storageAllocator.getStackAllocation(spill.type)); dfunc.localVars.add(spill); @@ -282,8 +278,7 @@ public class GolangDWARFFunctionFixup implements DWARFFunctionFixup { private DWARFVariable createReturnResultAliasVar(DWARFFunction dfunc, DataType dataType, String name, long stackOffset) { - DWARFVariable returnResultVar = - DWARFVariable.fromDataType(dfunc, dataType); + DWARFVariable returnResultVar = DWARFVariable.fromDataType(dfunc, dataType); returnResultVar.name = dfunc.name.createChild(name, name, SymbolType.LOCAL_VAR); returnResultVar.setStackStorage(stackOffset); return returnResultVar; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoRttiMapper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoRttiMapper.java index f5e194dc4b..4a8e8853e3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoRttiMapper.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoRttiMapper.java @@ -29,8 +29,8 @@ import ghidra.app.plugin.core.analysis.AutoAnalysisManager; import ghidra.app.plugin.core.analysis.TransientProgramProperties; import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.dwarf4.next.DWARFDataTypeConflictHandler; -import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram; +import ghidra.app.util.bin.format.dwarf.DWARFDataTypeConflictHandler; +import ghidra.app.util.bin.format.dwarf.DWARFProgram; import ghidra.app.util.bin.format.golang.*; import ghidra.app.util.bin.format.golang.rtti.types.*; import ghidra.app.util.bin.format.golang.structmapping.*; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoString.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoString.java index 162144b3d7..a142e77831 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoString.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoString.java @@ -19,7 +19,7 @@ import java.io.IOException; import java.util.function.Predicate; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.dwarf4.next.DWARFDataInstanceHelper; +import ghidra.app.util.bin.format.dwarf.DWARFDataInstanceHelper; import ghidra.app.util.bin.format.golang.structmapping.*; import ghidra.program.model.address.*; import ghidra.program.model.data.*; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoStructType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoStructType.java index 85f7de8b62..db5ba15483 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoStructType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoStructType.java @@ -18,7 +18,7 @@ package ghidra.app.util.bin.format.golang.rtti.types; import java.io.IOException; import java.util.*; -import ghidra.app.util.bin.format.dwarf4.DWARFUtil; +import ghidra.app.util.bin.format.dwarf.DWARFUtil; import ghidra.app.util.bin.format.golang.rtti.GoName; import ghidra.app.util.bin.format.golang.rtti.GoSlice; import ghidra.app.util.bin.format.golang.structmapping.*; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapper.java index 246e3da45d..690b4aa795 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapper.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapper.java @@ -21,7 +21,7 @@ import java.util.*; import generic.jar.ResourceFile; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.MemoryByteProvider; -import ghidra.app.util.bin.format.dwarf4.next.DWARFDataTypeConflictHandler; +import ghidra.app.util.bin.format.dwarf.DWARFDataTypeConflictHandler; import ghidra.program.model.address.Address; import ghidra.program.model.data.*; import ghidra.program.model.listing.Program; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/MarkupSession.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/MarkupSession.java index e716c8432b..93682197b2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/MarkupSession.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/MarkupSession.java @@ -19,8 +19,8 @@ import java.io.IOException; import java.lang.reflect.Array; import java.util.*; -import ghidra.app.util.bin.format.dwarf4.DWARFUtil; -import ghidra.app.util.bin.format.dwarf4.next.DWARFDataInstanceHelper; +import ghidra.app.util.bin.format.dwarf.DWARFDataInstanceHelper; +import ghidra.app.util.bin.format.dwarf.DWARFUtil; import ghidra.program.database.function.OverlappingFunctionException; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSet; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFConflictHandlerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFConflictHandlerTest.java similarity index 99% rename from Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFConflictHandlerTest.java rename to Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFConflictHandlerTest.java index 75298dc17e..e6502c3afb 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFConflictHandlerTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFConflictHandlerTest.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; import static org.junit.Assert.*; import org.junit.*; +import ghidra.app.util.bin.format.dwarf.DWARFDataTypeConflictHandler; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; import ghidra.program.model.data.*; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporterTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeImporterTest.java similarity index 98% rename from Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporterTest.java rename to Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeImporterTest.java index c33f431603..5c5c21c7b4 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporterTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeImporterTest.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.*; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; import static org.junit.Assert.*; import java.io.IOException; @@ -23,9 +23,6 @@ import java.util.*; import org.junit.Test; -import ghidra.app.util.bin.format.dwarf4.*; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFEncoding; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; import ghidra.program.model.symbol.SymbolUtilities; @@ -1318,12 +1315,12 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase { buildMockDIEIndexes(); - DWARFNameInfo sDNI = dwarfProg.getName(getAggregate(structDIE)); - DWARFNameInfo exactDNI = dwarfProg.getName(getAggregate(exactDIE)); - DWARFNameInfo exactTemplateDNI = dwarfProg.getName(getAggregate(exactTemplateDIE)); - DWARFNameInfo exactTemplateDNI2 = dwarfProg.getName(getAggregate(exactTemplateDIE2)); - DWARFNameInfo templateDNI = dwarfProg.getName(getAggregate(templateDIE)); - DWARFNameInfo s3DNI = dwarfProg.getName(getAggregate(struct3DIE)); + DWARFName sDNI = dwarfProg.getName(getAggregate(structDIE)); + DWARFName exactDNI = dwarfProg.getName(getAggregate(exactDIE)); + DWARFName exactTemplateDNI = dwarfProg.getName(getAggregate(exactTemplateDIE)); + DWARFName exactTemplateDNI2 = dwarfProg.getName(getAggregate(exactTemplateDIE2)); + DWARFName templateDNI = dwarfProg.getName(getAggregate(templateDIE)); + DWARFName s3DNI = dwarfProg.getName(getAggregate(struct3DIE)); assertEquals(structLongName, sDNI.getOriginalName()); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporterTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFFunctionImporterTest.java similarity index 97% rename from Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporterTest.java rename to Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFFunctionImporterTest.java index 3b936c8075..af00c4c1d1 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporterTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFFunctionImporterTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; import static org.junit.Assert.*; @@ -23,10 +23,8 @@ import java.util.List; import org.junit.Test; import ghidra.app.util.NamespaceUtils; -import ghidra.app.util.bin.format.dwarf4.*; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFSourceLanguage; -import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionOpCodes; +import ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute; +import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOpCodes; import ghidra.program.database.function.OverlappingFunctionException; import ghidra.program.model.address.AddressSet; import ghidra.program.model.data.*; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFNameInfoTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFNameInfoTest.java similarity index 88% rename from Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFNameInfoTest.java rename to Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFNameInfoTest.java index 901f93eab5..f4f04010fa 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFNameInfoTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFNameInfoTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; import static org.junit.Assert.*; @@ -21,7 +21,6 @@ import java.io.IOException; import org.junit.Test; -import ghidra.app.util.bin.format.dwarf4.*; import ghidra.program.model.data.*; import ghidra.program.model.listing.GhidraClass; import ghidra.program.model.symbol.Namespace; @@ -30,12 +29,12 @@ import ghidra.util.exception.CancelledException; public class DWARFNameInfoTest extends DWARFTestBase { - private DWARFNameInfo root = - DWARFNameInfo.createRoot(new CategoryPath(CategoryPath.ROOT, "TEST_DNI")); + private DWARFName root = + DWARFName.createRoot(new CategoryPath(CategoryPath.ROOT, "TEST_DNI")); @Test public void testSimple() { - DWARFNameInfo dni = root.createChild("simplename", "simplename", SymbolType.CLASS); + DWARFName dni = root.createChild("simplename", "simplename", SymbolType.CLASS); assertEquals(new CategoryPath("/TEST_DNI/simplename"), dni.asCategoryPath()); assertEquals( NamespacePath.create(NamespacePath.ROOT, "simplename", SymbolType.CLASS), @@ -44,7 +43,7 @@ public class DWARFNameInfoTest extends DWARFTestBase { @Test public void testSlash() { - DWARFNameInfo dni = root.createChild("blah/", "blah/", SymbolType.CLASS); + DWARFName dni = root.createChild("blah/", "blah/", SymbolType.CLASS); assertEquals(new CategoryPath(CategoryPath.ROOT, "TEST_DNI", "blah/"), dni.asCategoryPath()); @@ -57,8 +56,8 @@ public class DWARFNameInfoTest extends DWARFTestBase { @Test public void testColon() { - DWARFNameInfo dni = root.createChild("blah::blah", "blah::blah", SymbolType.CLASS); - DWARFNameInfo dni2 = dni.createChild("sub::sub", "sub::sub", SymbolType.CLASS); + DWARFName dni = root.createChild("blah::blah", "blah::blah", SymbolType.CLASS); + DWARFName dni2 = dni.createChild("sub::sub", "sub::sub", SymbolType.CLASS); assertEquals(new CategoryPath("/TEST_DNI/blah::blah/sub::sub"), dni2.asCategoryPath()); @@ -71,8 +70,8 @@ public class DWARFNameInfoTest extends DWARFTestBase { @Test public void testNamespaceTypes() { - DWARFNameInfo dni_ns1 = root.createChild("ns1", "ns1", SymbolType.NAMESPACE); - DWARFNameInfo dni_ns1class1 = dni_ns1.createChild("class1", "class1", SymbolType.CLASS); + DWARFName dni_ns1 = root.createChild("ns1", "ns1", SymbolType.NAMESPACE); + DWARFName dni_ns1class1 = dni_ns1.createChild("class1", "class1", SymbolType.CLASS); assertEquals(new CategoryPath("/TEST_DNI/ns1/class1"), dni_ns1class1.asCategoryPath()); Namespace class1_ns = dni_ns1class1.asNamespace(program); @@ -84,12 +83,12 @@ public class DWARFNameInfoTest extends DWARFTestBase { @Test public void testNamespaceConvertToClass() { // tests that a plain namespace can be 'upgraded' to a class namespace - DWARFNameInfo dni_ns1 = root.createChild("ns1", "ns1", SymbolType.NAMESPACE); + DWARFName dni_ns1 = root.createChild("ns1", "ns1", SymbolType.NAMESPACE); Namespace ns1 = dni_ns1.asNamespace(program); assertFalse(ns1 instanceof GhidraClass); - DWARFNameInfo dni_ns1a = root.createChild("ns1", "ns1", SymbolType.CLASS); + DWARFName dni_ns1a = root.createChild("ns1", "ns1", SymbolType.CLASS); Namespace ns1a = dni_ns1a.asNamespace(program); assertTrue(ns1a instanceof GhidraClass); } @@ -97,12 +96,12 @@ public class DWARFNameInfoTest extends DWARFTestBase { @Test public void testClassConvertToNamespace() { // tests that a class namespace isn't 'downgraded' to a plain namespace - DWARFNameInfo dni_class1 = root.createChild("class1", "class1", SymbolType.CLASS); + DWARFName dni_class1 = root.createChild("class1", "class1", SymbolType.CLASS); Namespace class1 = dni_class1.asNamespace(program); assertTrue(class1 instanceof GhidraClass); - DWARFNameInfo dni_class1a = root.createChild("class1", "class1", SymbolType.NAMESPACE); + DWARFName dni_class1a = root.createChild("class1", "class1", SymbolType.NAMESPACE); Namespace class1a = dni_class1a.asNamespace(program); // symbol should NOT have been converted to a plain namespace. assertTrue(class1a instanceof GhidraClass); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFStaticVarImporterTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFStaticVarImporterTest.java similarity index 94% rename from Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFStaticVarImporterTest.java rename to Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFStaticVarImporterTest.java index d900764cec..85864100ae 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFStaticVarImporterTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFStaticVarImporterTest.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.next; +package ghidra.app.util.bin.format.dwarf; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.*; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; import static org.junit.Assert.*; import java.io.IOException; @@ -24,9 +24,7 @@ import java.util.Set; import org.junit.Test; -import ghidra.app.util.bin.format.dwarf4.*; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag; -import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionOpCodes; +import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOpCodes; import ghidra.program.model.data.*; import ghidra.program.model.listing.CodeUnit; import ghidra.program.model.listing.Data; diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DIECreator.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DIECreator.java new file mode 100644 index 0000000000..51fe051395 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DIECreator.java @@ -0,0 +1,136 @@ +/* ### + * 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.util.bin.format.dwarf; + +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFForm.*; + +import java.util.*; + +import ghidra.app.util.bin.format.dwarf.attribs.*; +import ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.AttrDef; + +/** + * Helper class to create mock DebugInfoEntry instances for use during junit tests. + */ +public class DIECreator { + record AttrInfo(DWARFAttribute attribute, AttrDef spec, DWARFAttributeValue value) {} + + private MockDWARFProgram dwarfProg; + private DWARFTag tag; + private Map attributes = new HashMap<>(); + private DebugInfoEntry parent; + + public DIECreator(MockDWARFProgram dwarfProg, DWARFTag tag) { + this.dwarfProg = dwarfProg; + this.tag = tag; + } + + private void add(AttrDef attrSpec, DWARFAttributeValue attrVal) { + attributes.put(attrSpec.getAttributeId(), + new AttrInfo(attrSpec.getAttributeId(), attrSpec, attrVal)); + } + + public DIECreator addString(DWARFAttribute attribute, String value) { + AttrDef attrSpec = new AttrDef(attribute, attribute.getId(), DW_FORM_string, 0); + add(attrSpec, new DWARFStringAttribute(value, attrSpec)); + return this; + } + + public DIECreator addUInt(DWARFAttribute attribute, long value) { + AttrDef attrSpec = new AttrDef(attribute, attribute.getId(), DW_FORM_udata, 0); + add(attrSpec, new DWARFNumericAttribute(value, attrSpec)); + return this; + } + + public DIECreator addInt(DWARFAttribute attribute, long value) { + AttrDef attrSpec = new AttrDef(attribute, attribute.getId(), DW_FORM_sdata, 0); + add(attrSpec, new DWARFNumericAttribute(value, attrSpec)); + return this; + } + + public DIECreator addRef(DWARFAttribute attribute, DebugInfoEntry die) { + AttrDef attrSpec = new AttrDef(attribute, attribute.getId(), DW_FORM_ref8, 0); + add(attrSpec, new DWARFNumericAttribute(die.getOffset(), attrSpec)); + return this; + } + + public DIECreator addRef(DWARFAttribute attribute, long offset) { + AttrDef attrSpec = new AttrDef(attribute, attribute.getId(), DW_FORM_ref8, 0); + add(attrSpec, new DWARFNumericAttribute(offset, attrSpec)); + return this; + } + + public DIECreator addBoolean(DWARFAttribute attribute, boolean value) { + AttrDef attrSpec = new AttrDef(attribute, attribute.getId(), DW_FORM_flag, 0); + add(attrSpec, new DWARFBooleanAttribute(value, attrSpec)); + return this; + } + + public DIECreator addBlock(DWARFAttribute attribute, int... intBytes) { + byte[] bytes = new byte[intBytes.length]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = (byte) intBytes[i]; + } + AttrDef attrSpec = new AttrDef(attribute, attribute.getId(), DW_FORM_block1, 0); + add(attrSpec, new DWARFBlobAttribute(bytes, attrSpec)); + return this; + } + + AttrDef[] makeAttrSpecArray() { + AttrDef[] attrSpecs = new AttrDef[attributes.size()]; + List attrInfoList = new ArrayList<>(attributes.values()); + for (int i = 0; i < attrInfoList.size(); i++) { + AttrInfo attrInfo = attrInfoList.get(i); + attrSpecs[i] = attrInfo.spec; + } + return attrSpecs; + } + + public DIECreator setParent(DebugInfoEntry parent) { + this.parent = parent; + return this; + } + + public DebugInfoEntry createRootDIE() { + MockDWARFCompilationUnit cu = dwarfProg.getCurrentCompUnit(); + DWARFAbbreviation abbr = cu.createAbbreviation(makeAttrSpecArray(), tag); + DebugInfoEntry die = dwarfProg.addDIE(abbr, null); + + int attrNum = 0; + for (AttrInfo attrInfo : attributes.values()) { + die.setAttributeValue(attrNum++, attrInfo.value); + } + + return die; + } + + public DebugInfoEntry create() { + MockDWARFCompilationUnit cu = dwarfProg.getCurrentCompUnit(); + if (cu == null) { + cu = dwarfProg.addCompUnit(); + } + DWARFAbbreviation abbr = cu.createAbbreviation(makeAttrSpecArray(), tag); + DebugInfoEntry die = + dwarfProg.addDIE(abbr, parent != null ? parent : cu.getCompileUnitDIE()); + + int attrNum = 0; + for (AttrInfo attrInfo : attributes.values()) { + die.setAttributeValue(attrNum++, attrInfo.value); + } + + return die; + } +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DIECreatorTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DIECreatorTest.java similarity index 96% rename from Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DIECreatorTest.java rename to Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DIECreatorTest.java index 8a472bf6ef..f68db68b94 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DIECreatorTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DIECreatorTest.java @@ -13,16 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4; +package ghidra.app.util.bin.format.dwarf; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.*; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.*; +import static ghidra.app.util.bin.format.dwarf.DWARFTag.*; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; import static org.junit.Assert.*; import java.io.IOException; import org.junit.Test; +import ghidra.app.util.bin.format.dwarf.*; import ghidra.util.exception.CancelledException; /** diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DWARFTestBase.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DWARFTestBase.java similarity index 89% rename from Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DWARFTestBase.java rename to Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DWARFTestBase.java index f8c68df4f0..5ef82fce88 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DWARFTestBase.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DWARFTestBase.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4; +package ghidra.app.util.bin.format.dwarf; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.*; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFEncoding.*; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.*; +import static ghidra.app.util.bin.format.dwarf.DWARFEncoding.*; +import static ghidra.app.util.bin.format.dwarf.DWARFTag.*; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; import static org.junit.Assert.*; import java.io.IOException; @@ -27,9 +27,9 @@ import org.junit.Before; import ghidra.app.plugin.core.analysis.AutoAnalysisManager; import ghidra.app.services.DataTypeManagerService; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag; -import ghidra.app.util.bin.format.dwarf4.next.*; -import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.NullSectionProvider; +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.ByteArrayProvider; +import ghidra.app.util.bin.format.dwarf.sectionprovider.NullSectionProvider; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; import ghidra.program.database.data.DataTypeManagerDB; @@ -57,14 +57,16 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest { protected DWARFImportOptions importOptions; protected MockDWARFProgram dwarfProg; - protected MockDWARFCompilationUnit currentCU; + protected MockDWARFCompilationUnit cu; protected DWARFDataTypeManager dwarfDTM; + protected MockStringTable stringTable; protected CategoryPath uncatCP; protected CategoryPath dwarfRootCP; + @Before public void setUp() throws Exception { - program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._X64, this); + program = createProgram(); space = program.getAddressFactory().getDefaultAddressSpace(); dataMgr = program.getDataTypeManager(); @@ -81,11 +83,18 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest { importOptions = new DWARFImportOptions(); dwarfProg = new MockDWARFProgram(program, importOptions, TaskMonitor.DUMMY, new NullSectionProvider()); + stringTable = new MockStringTable(br()); + dwarfProg.setStringTable(stringTable); + dwarfDTM = dwarfProg.getDwarfDTM(); dwarfRootCP = dwarfProg.getRootDNI().asCategoryPath(); uncatCP = dwarfProg.getUncategorizedRootDNI().asCategoryPath(); } + public ProgramDB createProgram() throws Exception { + return createDefaultProgram(testName.getMethodName(), ProgramBuilder._X64, this); + } + @After public void tearDown() throws Exception { endTransaction(); @@ -101,8 +110,13 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest { program.endTransaction(transactionID, true); } + protected BinaryReader br(byte... bytes) { + return new BinaryReader(new ByteArrayProvider(bytes), dwarfProg.isLittleEndian()); + } + protected void buildMockDIEIndexes() throws CancelledException, DWARFException { dwarfProg.buildMockDIEIndexes(); + dwarfProg.dumpDIEs(System.out); } protected void importAllDataTypes() throws CancelledException, IOException, DWARFException { @@ -123,17 +137,27 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest { } protected void ensureCompUnit() { - if (dwarfProg.getCurrentCompUnit() == null) { - dwarfProg.addCompUnit(); + if (cu == null) { + addCompUnit(); } } protected MockDWARFCompilationUnit addCompUnit() { - return dwarfProg.addCompUnit(); + return addCompUnit(DWARFSourceLanguage.DW_LANG_C); + } + + protected MockDWARFCompilationUnit addCompUnit64() { + setCompUnit(dwarfProg.addCompUnit(DWARFSourceLanguage.DW_LANG_C, 8 /* dwarf64 */)); + return cu; } protected MockDWARFCompilationUnit addCompUnit(int cuLang) { - return dwarfProg.addCompUnit(cuLang); + setCompUnit(dwarfProg.addCompUnit(cuLang)); + return cu; + } + + protected void setCompUnit(MockDWARFCompilationUnit cu) { + this.cu = cu; } protected DebugInfoEntry addBaseType(String name, int size, int encoding) { diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/MockDWARFCompilationUnit.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/MockDWARFCompilationUnit.java similarity index 50% rename from Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/MockDWARFCompilationUnit.java rename to Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/MockDWARFCompilationUnit.java index 7e8559df59..18bf93b58f 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/MockDWARFCompilationUnit.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/MockDWARFCompilationUnit.java @@ -13,31 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4; +package ghidra.app.util.bin.format.dwarf; + +import java.util.Map; + +import ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.AttrDef; public class MockDWARFCompilationUnit extends DWARFCompilationUnit { - private DebugInfoEntry compUnitDIE; - private MockDWARFProgram dwarfProgram; private int dieCount; public MockDWARFCompilationUnit(MockDWARFProgram dwarfProgram, long startOffset, long endOffset, - long length, int format, short version, long abbreviationOffset, byte pointerSize, - int compUnitNumber, int language) { - super(dwarfProgram, startOffset, endOffset, length, format, version, abbreviationOffset, - pointerSize, compUnitNumber, startOffset, null); - this.dwarfProgram = dwarfProgram; - - this.compUnit = new DWARFCompileUnit("Mock Comp Unit", "Mock Comp Unit Producer", - "Mock Comp Unit Dir", 0, 0, language, false, null); - } - - public void setCompUnitDIE(DebugInfoEntry compUnitDIE) { - this.compUnitDIE = compUnitDIE; + long length, int intSize, short version, byte pointerSize, int compUnitNumber) { + super(dwarfProgram, startOffset, endOffset, length, intSize, version, pointerSize, + compUnitNumber, startOffset, null); } public DebugInfoEntry getCompileUnitDIE() { - return compUnitDIE; + return diea.getHeadFragment(); } public int incDIECount() { @@ -46,13 +39,14 @@ public class MockDWARFCompilationUnit extends DWARFCompilationUnit { @Override public MockDWARFProgram getProgram() { - return dwarfProgram; + return (MockDWARFProgram) dprog; } - public DWARFAbbreviation createAbbreviation(DWARFAttributeSpecification[] attrSpecs, int tag) { + public DWARFAbbreviation createAbbreviation(AttrDef[] attrSpecs, DWARFTag tag) { + Map map = getCodeToAbbreviationMap(); DWARFAbbreviation abbr = - new DWARFAbbreviation(getCodeToAbbreviationMap().size(), tag, true /*??*/, attrSpecs); - getCodeToAbbreviationMap().put(abbr.getAbbreviationCode(), abbr); + new DWARFAbbreviation(map.size(), tag.getId(), true /*??*/, attrSpecs); + map.put(abbr.getAbbreviationCode(), abbr); return abbr; } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/MockDWARFProgram.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/MockDWARFProgram.java similarity index 84% rename from Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/MockDWARFProgram.java rename to Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/MockDWARFProgram.java index de2ed3d2e0..112e8b187e 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/MockDWARFProgram.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/MockDWARFProgram.java @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4; +package ghidra.app.util.bin.format.dwarf; + +import static org.junit.Assert.*; import java.io.IOException; import java.util.ArrayList; @@ -21,11 +23,8 @@ import java.util.List; import org.junit.Assert; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFSourceLanguage; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag; -import ghidra.app.util.bin.format.dwarf4.next.DWARFImportOptions; -import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram; -import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.DWARFSectionProvider; +import ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute; +import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProvider; import ghidra.program.model.listing.Program; import ghidra.util.datastruct.IntArrayList; import ghidra.util.datastruct.LongArrayList; @@ -57,6 +56,10 @@ public class MockDWARFProgram extends DWARFProgram { } public MockDWARFCompilationUnit addCompUnit(int cuLang) { + return addCompUnit(cuLang, 4 /* dwarf32 */); + } + + public MockDWARFCompilationUnit addCompUnit(int cuLang, int dwarfIntSize) { if (currentCompUnit == null && !compUnitDieIndex.isEmpty()) { Assert.fail(); } @@ -66,14 +69,19 @@ public class MockDWARFProgram extends DWARFProgram { } long start = compUnits.size() * 0x1000; currentCompUnit = new MockDWARFCompilationUnit(this, start, start + 0x1000, 0, - DWARFCompilationUnit.DWARF_32, (short) 4, 0, (byte) 8, 0, cuLang); - + dwarfIntSize, (short) 4, (byte) 8, 0); compUnits.add(currentCompUnit); compUnitDieIndex.put(dieOffsets.length - 1, currentCompUnit); - DebugInfoEntry compUnitRootDIE = - new DIECreator(this, DWARFTag.DW_TAG_compile_unit).createRootDIE(); - currentCompUnit.setCompUnitDIE(compUnitRootDIE); + DebugInfoEntry compUnitRootDIE = new DIECreator(this, DWARFTag.DW_TAG_compile_unit) + .addInt(DWARFAttribute.DW_AT_language, cuLang) + .createRootDIE(); + try { + currentCompUnit.init(compUnitRootDIE); + } + catch (IOException e) { + fail(); + } return currentCompUnit; } @@ -92,7 +100,7 @@ public class MockDWARFProgram extends DWARFProgram { int cuDIECount = currentCompUnit.incDIECount(); DebugInfoEntry die = new DebugInfoEntry(currentCompUnit, currentCompUnit.getStartOffset() + cuDIECount, - dieIndex, abbr); + dieIndex, abbr, new int[abbr.getAttributeCount()]); diesByOffset.put(die.getOffset(), die); dieOffsetList.add(die.getOffset()); @@ -121,7 +129,7 @@ public class MockDWARFProgram extends DWARFProgram { LongArrayList aggrTargets = new LongArrayList(); for (DebugInfoEntry die : dies) { DIEAggregate diea = DIEAggregate.createSingle(die); - for (int attr : DIEAggregate.REF_ATTRS) { + for (DWARFAttribute attr : REF_ATTRS) { long refdOffset = diea.getUnsignedLong(attr, -1); if (refdOffset != -1) { aggrTargets.add(refdOffset); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/MockStringTable.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/MockStringTable.java new file mode 100644 index 0000000000..e1f14e141f --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/MockStringTable.java @@ -0,0 +1,44 @@ +/* ### + * 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.util.bin.format.dwarf; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import ghidra.app.util.bin.*; + +public class MockStringTable extends StringTable { + + public MockStringTable(BinaryReader reader) { + super(new BinaryReader(new ByteArrayProvider(new byte[4 * 1024]), true /* LE */)); + } + + public void add(int index, String s) throws IOException { + s = s + "\0"; + byte[] stringBytes = s.getBytes(StandardCharsets.UTF_8); + ByteProvider bp = reader.getByteProvider(); + byte[] allBytes = bp.readBytes(0, bp.length()); + if ((index + stringBytes.length) >= allBytes.length) { + byte[] newBytes = new byte[index + stringBytes.length]; + System.arraycopy(allBytes, 0, newBytes, 0, allBytes.length); + allBytes = newBytes; + } + System.arraycopy(stringBytes, 0, allBytes, index, stringBytes.length); + reader = new BinaryReader(new ByteArrayProvider(allBytes), true /* LE */); + cache.clear(); + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/NamespacePathTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/NamespacePathTest.java similarity index 94% rename from Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/NamespacePathTest.java rename to Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/NamespacePathTest.java index 1df32ffe4e..1235e2cac3 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/NamespacePathTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/NamespacePathTest.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4; +package ghidra.app.util.bin.format.dwarf; import static org.junit.Assert.*; import org.junit.Test; -import ghidra.app.util.bin.format.dwarf4.next.NamespacePath; +import ghidra.app.util.bin.format.dwarf.NamespacePath; import ghidra.program.model.symbol.SymbolType; public class NamespacePathTest { diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/StringTableTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/StringTableTest.java similarity index 79% rename from Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/StringTableTest.java rename to Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/StringTableTest.java index bcd1a6ee39..33002c10b7 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/StringTableTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/StringTableTest.java @@ -13,20 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4; +package ghidra.app.util.bin.format.dwarf; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.ByteArrayProvider; -import ghidra.app.util.bin.format.dwarf4.next.StringTable; +import static org.junit.Assert.*; import java.io.IOException; import org.junit.Test; import generic.test.AbstractGenericTest; +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.ByteArrayProvider; +import ghidra.app.util.bin.format.dwarf.StringTable; /** * Test reading DWARF string table @@ -47,7 +45,7 @@ public class StringTableTest extends AbstractGenericTest { /* str3 */ (byte) 'x', (byte) 'y', (byte) '\n', (byte) 0 ); // @formatter:on - StringTable st = StringTable.readStringTable(br.getByteProvider()); + StringTable st = new StringTable(br); assertEquals("ab", st.getStringAtOffset(0)); assertEquals("c", st.getStringAtOffset(3)); @@ -65,7 +63,7 @@ public class StringTableTest extends AbstractGenericTest { /* str3 */ (byte) 'x', (byte) 'y', (byte) '\n', (byte) 0 ); // @formatter:on - StringTable st = StringTable.readStringTable(br.getByteProvider()); + StringTable st = new StringTable(br); assertEquals("ab", st.getStringAtOffset(0)); assertEquals("b", st.getStringAtOffset(1)); @@ -74,7 +72,7 @@ public class StringTableTest extends AbstractGenericTest { } @Test - public void testTrailingOffcutStr() throws IOException { + public void testTrailingOffcutStr() { // @formatter:off BinaryReader br = br( /* str1 */ (byte) 'a', (byte) 'b', (byte) 0, @@ -82,7 +80,7 @@ public class StringTableTest extends AbstractGenericTest { /* str3 */ (byte) 'x', (byte) 'y', (byte) '\n', (byte) 0 ); // @formatter:on - StringTable st = StringTable.readStringTable(br.getByteProvider()); + StringTable st = new StringTable(br); try { st.getStringAtOffset(9); @@ -94,7 +92,7 @@ public class StringTableTest extends AbstractGenericTest { } @Test - public void testNegOffset() throws IOException { + public void testNegOffset() { // @formatter:off BinaryReader br = br( /* str1 */ (byte) 'a', (byte) 'b', (byte) 0, @@ -102,7 +100,7 @@ public class StringTableTest extends AbstractGenericTest { /* str3 */ (byte) 'x', (byte) 'y', (byte) '\n', (byte) 0 ); // @formatter:on - StringTable st = StringTable.readStringTable(br.getByteProvider()); + StringTable st = new StringTable(br); try { st.getStringAtOffset(-2); @@ -114,9 +112,9 @@ public class StringTableTest extends AbstractGenericTest { } @Test - public void testEmptyStrTable() throws IOException { + public void testEmptyStrTable() { BinaryReader br = br(); - StringTable st = StringTable.readStringTable(br.getByteProvider()); + StringTable st = new StringTable(br); try { st.getStringAtOffset(0); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFAttributeFactoryTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeFactoryTest.java similarity index 70% rename from Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFAttributeFactoryTest.java rename to Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeFactoryTest.java index 178dd23475..a8451ffe93 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFAttributeFactoryTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeFactoryTest.java @@ -13,60 +13,40 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.attribs; +package ghidra.app.util.bin.format.dwarf.attribs; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFForm.*; import static org.junit.Assert.*; import java.io.IOException; -import org.junit.Before; import org.junit.Test; -import generic.test.AbstractGenericTest; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.ByteArrayProvider; -import ghidra.app.util.bin.format.dwarf4.DWARFCompilationUnit; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFForm; -import ghidra.app.util.bin.format.dwarf4.next.*; -import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.NullSectionProvider; -import ghidra.program.model.listing.Program; -import ghidra.test.ToyProgramBuilder; -import ghidra.util.task.TaskMonitor; +import ghidra.app.util.bin.format.dwarf.DWARFSourceLanguage; +import ghidra.app.util.bin.format.dwarf.DWARFTestBase; +import ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.AttrDef; +import ghidra.program.database.ProgramBuilder; +import ghidra.program.database.ProgramDB; /** * Test reading binary representations of DWARF attributes from raw bytes. */ -public class DWARFAttributeFactoryTest extends AbstractGenericTest { +public class DWARFAttributeFactoryTest extends DWARFTestBase { - private DWARFProgram prog; - private StringTable stringTable; - private DWARFAttributeFactory attribFactory; - private DWARFCompilationUnit cu; - private DWARFCompilationUnit cu64; - - @Before - public void setUp() throws Exception { - - ToyProgramBuilder builder = new ToyProgramBuilder("Test", true); - Program ghidraProgram = builder.getProgram(); - - ghidraProgram.startTransaction("Test"); - prog = new DWARFProgram(ghidraProgram, new DWARFImportOptions(), TaskMonitor.DUMMY, - new NullSectionProvider()); - prog.setDebugStrings(new StringTable(new byte[1000])); - stringTable = prog.getDebugStrings(); - attribFactory = prog.getAttributeFactory(); - - cu = new DWARFCompilationUnit(prog, 0x1000, 0x2000, 0, DWARFCompilationUnit.DWARF_32, - (short) 4, 0, (byte) 8, 0, 0, null); - cu64 = new DWARFCompilationUnit(prog, 0x2000, 0x4000, 0, DWARFCompilationUnit.DWARF_64, - (short) 4, 0, (byte) 8, 0, 0, null); - - assertTrue("These tests were written for big endian", prog.isBigEndian()); + @Override + public ProgramDB createProgram() throws Exception { + return createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY64_BE, this); } - private BinaryReader br(byte... bytes) { - return new BinaryReader(new ByteArrayProvider(bytes), prog.isLittleEndian()); + private DWARFAttributeValue read(BinaryReader br, DWARFAttribute attr, DWARFForm form) + throws IOException { + ensureCompUnit(); + AttrDef spec = new AttrDef(attr, attr.getId(), form, 0); + DWARFFormContext context = new DWARFFormContext(br, cu, spec); + DWARFAttributeValue val = form.readValue(context); + return val; } @Test @@ -78,15 +58,15 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { /* str3 */ (byte) 'x', (byte) 'y', (byte) '\n', (byte) 0, /* guard byte for test */ (byte) 0xff); // @formatter:on - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_string); + DWARFAttributeValue result = read(br, DW_AT_name, DW_FORM_string); assertTrue("Should be string", result instanceof DWARFStringAttribute); assertEquals("ab", ((DWARFStringAttribute) result).getValue(null)); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_string); + result = read(br, DW_AT_name, DW_FORM_string); assertTrue("Should be string", result instanceof DWARFStringAttribute); assertEquals("c", ((DWARFStringAttribute) result).getValue(null)); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_string); + result = read(br, DW_AT_name, DW_FORM_string); assertTrue("Should be string", result instanceof DWARFStringAttribute); assertEquals("Test reading nullterm string with non-printable chars", "xy\n", ((DWARFStringAttribute) result).getValue(null)); @@ -96,8 +76,8 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { @Test public void testStrp_32() throws IOException { - prog.getDebugStrings().add(1, "string1 at 1"); - prog.getDebugStrings().add(100, "string2 at 100"); + stringTable.add(1, "string1 at 1"); + stringTable.add(100, "string2 at 100"); // @formatter:off BinaryReader br = br( @@ -107,26 +87,26 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { /* guard byte for test */ (byte) 0xff); // @formatter:on - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_strp); + DWARFAttributeValue result = read(br, DW_AT_name, DW_FORM_strp); assertTrue("Should be string", result instanceof DWARFStringAttribute); - assertEquals("string2 at 100", ((DWARFStringAttribute) result).getValue(stringTable)); + assertEquals("string2 at 100", ((DWARFStringAttribute) result).getValue(cu)); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_strp); + result = read(br, DW_AT_name, DW_FORM_strp); assertTrue("Should be string", result instanceof DWARFStringAttribute); - assertEquals("string1 at 1", ((DWARFStringAttribute) result).getValue(stringTable)); + assertEquals("string1 at 1", ((DWARFStringAttribute) result).getValue(cu)); // test string ref to substring of string2 - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_strp); + result = read(br, DW_AT_name, DW_FORM_strp); assertTrue("Should be string", result instanceof DWARFStringAttribute); - assertEquals("tring2 at 100", ((DWARFStringAttribute) result).getValue(stringTable)); + assertEquals("tring2 at 100", ((DWARFStringAttribute) result).getValue(cu)); assertEquals("guard byte", (byte) 0xff, br.readNextByte()); } @Test public void testStrp_64() throws IOException { - prog.getDebugStrings().add(1, "string1 at 1"); - prog.getDebugStrings().add(100, "string2 at 100"); + stringTable.add(1, "string1 at 1"); + stringTable.add(100, "string2 at 100"); // @formatter:off BinaryReader br = br( @@ -135,13 +115,15 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { /* guard byte for test */ (byte) 0xff); // @formatter:on - DWARFAttributeValue result1 = attribFactory.read(br, cu64, DWARFForm.DW_FORM_strp); - assertTrue("Should be string", result1 instanceof DWARFStringAttribute); - assertEquals("string2 at 100", ((DWARFStringAttribute) result1).getValue(stringTable)); + setCompUnit(dwarfProg.addCompUnit(DWARFSourceLanguage.DW_LANG_C, 8 /* dwarf64 */)); - DWARFAttributeValue result2 = attribFactory.read(br, cu64, DWARFForm.DW_FORM_strp); + DWARFAttributeValue result1 = read(br, DW_AT_name, DW_FORM_strp); + assertTrue("Should be string", result1 instanceof DWARFStringAttribute); + assertEquals("string2 at 100", ((DWARFStringAttribute) result1).getValue(cu)); + + DWARFAttributeValue result2 = read(br, DW_AT_name, DW_FORM_strp); assertTrue("Should be string", result2 instanceof DWARFStringAttribute); - assertEquals("string1 at 1", ((DWARFStringAttribute) result2).getValue(stringTable)); + assertEquals("string1 at 1", ((DWARFStringAttribute) result2).getValue(cu)); assertEquals("guard byte", (byte) 0xff, br.readNextByte()); } @@ -150,11 +132,11 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { public void testData1() throws IOException { BinaryReader br = br((byte) 55, (byte) 0xfe); - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_data1); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_data1); assertTrue("Should be const", result instanceof DWARFNumericAttribute); assertEquals("should be 55", 55, ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_data1); + result = read(br, DW_AT_byte_size, DW_FORM_data1); assertTrue("Should be const", result instanceof DWARFNumericAttribute); assertEquals("should be -2", -2, ((DWARFNumericAttribute) result).getValue()); assertEquals("should be fe", 0xfe, ((DWARFNumericAttribute) result).getUnsignedValue()); @@ -164,11 +146,11 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { public void testData2() throws IOException { BinaryReader br = br((byte) 0, (byte) 55, (byte) 0xff, (byte) 0xfe); - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_data2); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_data2); assertTrue("Should be const", result instanceof DWARFNumericAttribute); assertEquals("should be 55", 55, ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_data2); + result = read(br, DW_AT_byte_size, DW_FORM_data2); assertTrue("Should be const", result instanceof DWARFNumericAttribute); assertEquals("should be -2", -2, ((DWARFNumericAttribute) result).getValue()); assertEquals("should be fffe", 0xfffe, ((DWARFNumericAttribute) result).getUnsignedValue()); @@ -179,11 +161,11 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { BinaryReader br = br((byte) 0, (byte) 0, (byte) 0, (byte) 55, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xfe); - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_data4); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_data4); assertTrue("Should be const", result instanceof DWARFNumericAttribute); assertEquals("should be 55", 55, ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_data4); + result = read(br, DW_AT_byte_size, DW_FORM_data4); assertTrue("Should be const", result instanceof DWARFNumericAttribute); assertEquals("should be -2", -2, ((DWARFNumericAttribute) result).getValue()); assertEquals("should be 0xfffffffe", 0xffff_fffeL, @@ -198,11 +180,11 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xfe); // @formatter:on - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_data8); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_data8); assertTrue("Should be const", result instanceof DWARFNumericAttribute); assertEquals("should be 55", 55, ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_data8); + result = read(br, DW_AT_byte_size, DW_FORM_data8); assertTrue("Should be const", result instanceof DWARFNumericAttribute); // these next two asserts are probably the same test as there isn't a way to // simulate unsigned long values @@ -215,15 +197,15 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { public void testSData() throws IOException { BinaryReader br = br((byte) 0, (byte) 55, (byte) 0xff, (byte) 0x7e); - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_sdata); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_sdata); assertTrue("Should be const", result instanceof DWARFNumericAttribute); assertEquals("should be 0", 0, ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_sdata); + result = read(br, DW_AT_byte_size, DW_FORM_sdata); assertTrue("Should be const", result instanceof DWARFNumericAttribute); assertEquals("should be 55", 55, ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_sdata); + result = read(br, DW_AT_byte_size, DW_FORM_sdata); assertTrue("Should be const", result instanceof DWARFNumericAttribute); assertEquals("should be -129", -129, ((DWARFNumericAttribute) result).getValue()); } @@ -232,15 +214,15 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { public void testUData() throws IOException { BinaryReader br = br((byte) 0, (byte) 55, (byte) 0xff, (byte) 0x7e); - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_udata); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_udata); assertTrue("Should be const", result instanceof DWARFNumericAttribute); assertEquals("should be 0", 0, ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_udata); + result = read(br, DW_AT_byte_size, DW_FORM_udata); assertTrue("Should be const", result instanceof DWARFNumericAttribute); assertEquals("should be 55", 55, ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_udata); + result = read(br, DW_AT_byte_size, DW_FORM_udata); assertTrue("Should be const", result instanceof DWARFNumericAttribute); assertEquals("should be 16255", 16255, ((DWARFNumericAttribute) result).getValue()); } @@ -253,11 +235,11 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xfe); // @formatter:on - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_addr); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_addr); assertTrue("Should be addr", result instanceof DWARFNumericAttribute); assertEquals("should be 55", 55, ((DWARFNumericAttribute) result).getUnsignedValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_addr); + result = read(br, DW_AT_byte_size, DW_FORM_addr); assertTrue("Should be addr", result instanceof DWARFNumericAttribute); assertEquals("should be feffffffffffffff", 0xfffffffffffffffeL, ((DWARFNumericAttribute) result).getUnsignedValue()); @@ -267,18 +249,18 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { public void testBlock1() throws IOException { BinaryReader br = br((byte) 1, (byte) 0x55, (byte) 0); - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_block1); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_block1); assertTrue("Should be block", result instanceof DWARFBlobAttribute); assertEquals("should be 1", 1, ((DWARFBlobAttribute) result).getLength()); assertEquals("should be 0x55", 0x55, ((DWARFBlobAttribute) result).getBytes()[0]); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_block1); + result = read(br, DW_AT_byte_size, DW_FORM_block1); assertTrue("Should be block", result instanceof DWARFBlobAttribute); assertEquals("should be 0", 0, ((DWARFBlobAttribute) result).getLength()); byte[] bytes = new byte[1 + 255 /* max_ubyte */]; bytes[0] = (byte) 0xff; - result = attribFactory.read(br(bytes), cu, DWARFForm.DW_FORM_block1); + result = read(br(bytes), DW_AT_byte_size, DW_FORM_block1); assertTrue("Should be block", result instanceof DWARFBlobAttribute); assertEquals("should be 255", 255, ((DWARFBlobAttribute) result).getLength()); } @@ -287,19 +269,19 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { public void testBlock2() throws IOException { BinaryReader br = br((byte) 0, (byte) 1, (byte) 0x55, (byte) 0, (byte) 0); - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_block2); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_block2); assertTrue("Should be block", result instanceof DWARFBlobAttribute); assertEquals("should be 1", 1, ((DWARFBlobAttribute) result).getLength()); assertEquals("should be 0x55", 0x55, ((DWARFBlobAttribute) result).getBytes()[0]); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_block1); + result = read(br, DW_AT_byte_size, DW_FORM_block1); assertTrue("Should be block", result instanceof DWARFBlobAttribute); assertEquals("should be 0", 0, ((DWARFBlobAttribute) result).getLength()); byte[] bytes = new byte[2 + 0xffff /* max_ushort */]; bytes[0] = (byte) 0xff; bytes[1] = (byte) 0xff; - result = attribFactory.read(br(bytes), cu, DWARFForm.DW_FORM_block2); + result = read(br(bytes), DW_AT_byte_size, DW_FORM_block2); assertTrue("Should be block", result instanceof DWARFBlobAttribute); assertEquals("should be 64k", 0xffff, ((DWARFBlobAttribute) result).getLength()); } @@ -309,36 +291,36 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { BinaryReader br = br((byte) 0, (byte) 0, (byte) 0, (byte) 1, (byte) 0x55, (byte) 0, (byte) 0, (byte) 0, (byte) 0); - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_block4); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_block4); assertTrue("Should be block", result instanceof DWARFBlobAttribute); assertEquals("should be 1", 1, ((DWARFBlobAttribute) result).getLength()); assertEquals("should be 0x55", 0x55, ((DWARFBlobAttribute) result).getBytes()[0]); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_block4); + result = read(br, DW_AT_byte_size, DW_FORM_block4); assertTrue("Should be block", result instanceof DWARFBlobAttribute); assertEquals("should be 0", 0, ((DWARFBlobAttribute) result).getLength()); // Test max block4 sized chunk - byte[] bytes = new byte[4 + DWARFAttributeFactory.MAX_BLOCK4_SIZE]; + byte[] bytes = new byte[4 + DWARFForm.MAX_BLOCK4_SIZE]; //DWARFAttributeFactory.MAX_BLOCK4_SIZE == 0x00_10_00_00 bytes[0] = (byte) 0x00; bytes[1] = (byte) 0x10; bytes[2] = (byte) 0x00; bytes[3] = (byte) 0x00; - result = attribFactory.read(br(bytes), cu, DWARFForm.DW_FORM_block4); + result = read(br(bytes), DW_AT_byte_size, DW_FORM_block4); assertTrue("Should be block", result instanceof DWARFBlobAttribute); - assertEquals("should be MAX_BLOCK4_SIZE", DWARFAttributeFactory.MAX_BLOCK4_SIZE, + assertEquals("should be MAX_BLOCK4_SIZE", DWARFForm.MAX_BLOCK4_SIZE, ((DWARFBlobAttribute) result).getLength()); // Test block4 size that is larger than max - bytes = new byte[4 + DWARFAttributeFactory.MAX_BLOCK4_SIZE + 1]; + bytes = new byte[4 + DWARFForm.MAX_BLOCK4_SIZE + 1]; //DWARFAttributeFactory.MAX_BLOCK4_SIZE == 0x00_10_00_00 + 1 == 0x00_10_00_01 bytes[0] = (byte) 0x00; bytes[1] = (byte) 0x10; bytes[2] = (byte) 0x00; bytes[3] = (byte) 0x01; try { - result = attribFactory.read(br(bytes), cu, DWARFForm.DW_FORM_block4); + result = read(br(bytes), DW_AT_byte_size, DW_FORM_block4); fail( "Should not get here, dw_form_block4 size should have been larger than max sanity check"); } @@ -351,12 +333,12 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { public void testBlock() throws IOException { BinaryReader br = br((byte) 1, (byte) 0x55, (byte) 0); - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_block); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_block); assertTrue("Should be block", result instanceof DWARFBlobAttribute); assertEquals("should be 1", 1, ((DWARFBlobAttribute) result).getLength()); assertEquals("should be 0x55", 0x55, ((DWARFBlobAttribute) result).getBytes()[0]); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_block); + result = read(br, DW_AT_byte_size, DW_FORM_block); assertTrue("Should be block", result instanceof DWARFBlobAttribute); assertEquals("should be 0", 0, ((DWARFBlobAttribute) result).getLength()); } @@ -365,12 +347,12 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { public void testExprLoc() throws IOException { BinaryReader br = br((byte) 1, (byte) 0x55, (byte) 0); - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_exprloc); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_exprloc); assertTrue("Should be exprloc", result instanceof DWARFBlobAttribute); assertEquals("should be 1", 1, ((DWARFBlobAttribute) result).getLength()); assertEquals("should be 0x55", 0x55, ((DWARFBlobAttribute) result).getBytes()[0]); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_exprloc); + result = read(br, DW_AT_byte_size, DW_FORM_exprloc); assertTrue("Should be exprloc", result instanceof DWARFBlobAttribute); assertEquals("should be 0", 0, ((DWARFBlobAttribute) result).getLength()); } @@ -379,11 +361,11 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { public void testFlag() throws IOException { BinaryReader br = br((byte) 55, (byte) 0x00); - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_flag); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_flag); assertTrue("Should be flag", result instanceof DWARFBooleanAttribute); assertEquals("should be true", true, ((DWARFBooleanAttribute) result).getValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_flag); + result = read(br, DW_AT_byte_size, DW_FORM_flag); assertTrue("Should be flag", result instanceof DWARFBooleanAttribute); assertEquals("should be false", false, ((DWARFBooleanAttribute) result).getValue()); } @@ -392,7 +374,7 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { public void testFlagPresent() throws IOException { BinaryReader br = br(new byte[] {} /* no bytes needed for flag_present */); - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_flag_present); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_flag_present); assertTrue("Should be flag", result instanceof DWARFBooleanAttribute); assertEquals("should be true", true, ((DWARFBooleanAttribute) result).getValue()); } @@ -401,12 +383,12 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { public void testRef1() throws IOException { BinaryReader br = br((byte) 55, (byte) 0xfe); - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_ref1); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_ref1); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); assertEquals("should be 55 + cuOffset", 55 + cu.getStartOffset(), ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_ref1); + result = read(br, DW_AT_byte_size, DW_FORM_ref1); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); assertEquals("should be fe + cuOffset", 0xfe + cu.getStartOffset(), ((DWARFNumericAttribute) result).getValue()); @@ -416,12 +398,12 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { public void testRef2() throws IOException { BinaryReader br = br((byte) 0, (byte) 55, (byte) 0xff, (byte) 0xfe); - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_ref2); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_ref2); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); assertEquals("should be 55 + cuOffset", 55 + cu.getStartOffset(), ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_ref2); + result = read(br, DW_AT_byte_size, DW_FORM_ref2); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); assertEquals("should be fffe + cuOffset", 0xfffe + cu.getStartOffset(), ((DWARFNumericAttribute) result).getValue()); @@ -432,12 +414,12 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { BinaryReader br = br((byte) 0, (byte) 0, (byte) 0, (byte) 55, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xfe); - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_ref4); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_ref4); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); assertEquals("should be 55 + cuOffset", 55 + cu.getStartOffset(), ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_ref4); + result = read(br, DW_AT_byte_size, DW_FORM_ref4); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); assertEquals("should be fffffffe + cuOffset", 0xff_ff_ff_feL + cu.getStartOffset(), ((DWARFNumericAttribute) result).getValue()); @@ -452,11 +434,12 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { ); // @formatter:on - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_sec_offset); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_sec_offset); assertTrue("Should be ptr", result instanceof DWARFNumericAttribute); assertEquals("should be 55", 55, ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu64, DWARFForm.DW_FORM_sec_offset); + addCompUnit64(); + result = read(br, DW_AT_byte_size, DW_FORM_sec_offset); assertTrue("Should be ptr", result instanceof DWARFNumericAttribute); assertEquals("should be 56", 56, ((DWARFNumericAttribute) result).getValue()); } @@ -469,12 +452,12 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xfe); // @formatter:on - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_ref8); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_ref8); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); assertEquals("should be 55 + cuOffset", 55 + cu.getStartOffset(), ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_ref8); + result = read(br, DW_AT_byte_size, DW_FORM_ref8); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); assertEquals("should be fffffffffffffffe + cuOffset", 0xff_ff_ff_ff_ff_ff_ff_feL + cu.getStartOffset(), @@ -485,12 +468,12 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { public void testRefUData() throws IOException { BinaryReader br = br((byte) 55, (byte) 0xff, (byte) 0x7e); - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_ref_udata); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_ref_udata); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); assertEquals("should be 55 + cuOffset", 55 + cu.getStartOffset(), ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_ref_udata); + result = read(br, DW_AT_byte_size, DW_FORM_ref_udata); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); assertEquals("should be 0x3f7f + cuOffset", 0x3f7f + cu.getStartOffset(), ((DWARFNumericAttribute) result).getValue()); @@ -507,20 +490,23 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { ); // @formatter:on - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_ref_addr); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_ref_addr); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); assertEquals("should be 55", 55, ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_ref_addr); + result = read(br, DW_AT_byte_size, DW_FORM_ref_addr); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); assertEquals("should be ffffffff", 0xff_ff_ff_ffL, ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu64, DWARFForm.DW_FORM_ref_addr); + addCompUnit64(); + + result = read(br, DW_AT_byte_size, DW_FORM_ref_addr); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); assertEquals("should be 55", 55, ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu64, DWARFForm.DW_FORM_ref_addr); + addCompUnit64(); + result = read(br, DW_AT_byte_size, DW_FORM_ref_addr); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); assertEquals("should be fffffffffffffffe", 0xff_ff_ff_ff_ff_ff_ff_feL, ((DWARFNumericAttribute) result).getValue()); @@ -530,22 +516,19 @@ public class DWARFAttributeFactoryTest extends AbstractGenericTest { public void testIndirect() throws IOException { // @formatter:off BinaryReader br = br( - (byte)DWARFForm.DW_FORM_data1.getValue(), + (byte)DW_FORM_data1.getId(), (byte) 55, - (byte)DWARFForm.DW_FORM_ref4.getValue(), + (byte)DW_FORM_ref4.getId(), (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xaa ); // @formatter:on - DWARFAttributeValue result = attribFactory.read(br, cu, DWARFForm.DW_FORM_indirect); - DWARFIndirectAttribute dia = (DWARFIndirectAttribute) result; - DWARFNumericAttribute nestedAttr = (DWARFNumericAttribute) dia.getValue(); - assertEquals("should be 55", 55, nestedAttr.getValue()); + DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_indirect); + assertEquals("should be 55", 55, ((DWARFNumericAttribute) result).getValue()); - result = attribFactory.read(br, cu, DWARFForm.DW_FORM_indirect); - dia = (DWARFIndirectAttribute) result; - nestedAttr = (DWARFNumericAttribute) dia.getValue(); - assertEquals("should be aa + cuOffset", 0xaa + cu.getStartOffset(), nestedAttr.getValue()); + result = read(br, DW_AT_byte_size, DW_FORM_indirect); + assertEquals("should be aa + cuOffset", 0xaa + cu.getStartOffset(), + ((DWARFNumericAttribute) result).getValue()); } } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionEvaluatorTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluatorTest.java similarity index 97% rename from Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionEvaluatorTest.java rename to Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluatorTest.java index 98eee4f70a..a27d3f3fc6 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionEvaluatorTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluatorTest.java @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.dwarf4.expression; +package ghidra.app.util.bin.format.dwarf.expression; -import static ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionOpCodes.*; +import static ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOpCodes.*; import static org.junit.Assert.*; import java.io.IOException; import org.junit.*; -import ghidra.app.util.bin.format.dwarf4.DWARFCompilationUnit; -import ghidra.app.util.bin.format.dwarf4.next.DWARFRegisterMappings; +import ghidra.app.util.bin.format.dwarf.DWARFRegisterMappings; +import ghidra.app.util.bin.format.dwarf.expression.*; public class DWARFExpressionEvaluatorTest { @@ -31,7 +31,7 @@ public class DWARFExpressionEvaluatorTest { @Before public void setup() { - evaluator = new DWARFExpressionEvaluator((byte) 8, true, DWARFCompilationUnit.DWARF_32, + evaluator = new DWARFExpressionEvaluator((byte) 8, true, 4 /* dwarf32 */, DWARFRegisterMappings.DUMMY); } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DIECreator.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DIECreator.java deleted file mode 100644 index de57d1f997..0000000000 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DIECreator.java +++ /dev/null @@ -1,144 +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.util.bin.format.dwarf4; - -import java.util.*; - -import ghidra.app.util.bin.format.dwarf4.attribs.*; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFForm; - -/** - * Helper class to create mock DebugInfoEntry instances for use during junit tests. - */ -public class DIECreator { - private static class AttrInfo { - int attribute; - DWARFForm form; - DWARFAttributeValue value; - - public AttrInfo(int attribute, DWARFForm form, DWARFAttributeValue value) { - this.attribute = attribute; - this.form = form; - this.value = value; - } - } - - private MockDWARFProgram dwarfProg; - private int tag; - private Map attributes = new HashMap<>(); - private DebugInfoEntry parent; - - public DIECreator(MockDWARFProgram dwarfProg, int tag) { - this.dwarfProg = dwarfProg; - this.tag = tag; - } - - public DIECreator addString(int attribute, String value) { - attributes.put(attribute, - new AttrInfo(attribute, DWARFForm.DW_FORM_string, new DWARFStringAttribute(value))); - return this; - } - - public DIECreator addUInt(int attribute, long value) { - attributes.put(attribute, - new AttrInfo(attribute, DWARFForm.DW_FORM_udata, new DWARFNumericAttribute(value))); - return this; - } - - public DIECreator addInt(int attribute, long value) { - attributes.put(attribute, - new AttrInfo(attribute, DWARFForm.DW_FORM_sdata, new DWARFNumericAttribute(value))); - - return this; - } - - public DIECreator addRef(int attribute, DebugInfoEntry die) { - attributes.put(attribute, new AttrInfo(attribute, DWARFForm.DW_FORM_ref8, - new DWARFNumericAttribute(die.getOffset()))); - - return this; - } - - public DIECreator addRef(int attribute, long offset) { - attributes.put(attribute, - new AttrInfo(attribute, DWARFForm.DW_FORM_ref8, new DWARFNumericAttribute(offset))); - - return this; - } - - public DIECreator addBoolean(int attribute, boolean value) { - attributes.put(attribute, - new AttrInfo(attribute, DWARFForm.DW_FORM_flag, new DWARFBooleanAttribute(value))); - - return this; - } - - public DIECreator addBlock(int attribute, int... intBytes) { - byte[] bytes = new byte[intBytes.length]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = (byte) intBytes[i]; - } - attributes.put(attribute, - new AttrInfo(attribute, DWARFForm.DW_FORM_block1, new DWARFBlobAttribute(bytes))); - return this; - } - - DWARFAttributeSpecification[] makeAttrSpecArray() { - DWARFAttributeSpecification[] attrSpecs = - new DWARFAttributeSpecification[attributes.size()]; - List attrInfoList = new ArrayList<>(attributes.values()); - for (int i = 0; i < attrInfoList.size(); i++) { - AttrInfo attrInfo = attrInfoList.get(i); - attrSpecs[i] = new DWARFAttributeSpecification(attrInfo.attribute, attrInfo.form); - } - return attrSpecs; - } - - public DIECreator setParent(DebugInfoEntry parent) { - this.parent = parent; - return this; - } - - public DebugInfoEntry createRootDIE() { - MockDWARFCompilationUnit cu = dwarfProg.getCurrentCompUnit(); - DWARFAbbreviation abbr = cu.createAbbreviation(makeAttrSpecArray(), tag); - DebugInfoEntry die = dwarfProg.addDIE(abbr, null); - - int attrNum = 0; - for (AttrInfo attrInfo : attributes.values()) { - die.getAttributes()[attrNum++] = attrInfo.value; - } - - return die; - } - - public DebugInfoEntry create() { - MockDWARFCompilationUnit cu = dwarfProg.getCurrentCompUnit(); - if (cu == null) { - cu = dwarfProg.addCompUnit(); - } - DWARFAbbreviation abbr = cu.createAbbreviation(makeAttrSpecArray(), tag); - DebugInfoEntry die = - dwarfProg.addDIE(abbr, parent != null ? parent : cu.getCompileUnitDIE()); - - int attrNum = 0; - for (AttrInfo attrInfo : attributes.values()) { - die.getAttributes()[attrNum++] = attrInfo.value; - } - - return die; - } -} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFAttributeFactoryFlaky.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFAttributeFactoryFlaky.java deleted file mode 100644 index a0545b936e..0000000000 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/attribs/DWARFAttributeFactoryFlaky.java +++ /dev/null @@ -1,119 +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.util.bin.format.dwarf4.attribs; - -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.dwarf4.DWARFCompilationUnit; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFForm; -import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -/** - * Test helper that injects errors when reading DWARF attributes from a stream. - *

- * Used with real DWARF binaries to inject changes / errors at specific locations - * in the file that the tester determined was an important point. - */ -public class DWARFAttributeFactoryFlaky extends DWARFAttributeFactory { - - private int counter; - private Set offsets = new HashSet<>(); - - public DWARFAttributeFactoryFlaky(DWARFProgram prog) { - super(prog); - } - - public DWARFAttributeFactoryFlaky addOffset(long offset) { - offsets.add(offset); - return this; - } - - public DWARFAttributeValue read(BinaryReader reader, DWARFCompilationUnit unit, DWARFForm form) - throws IOException { - counter++; - - long offset = reader.getPointerIndex(); - DWARFAttributeValue result = super.read(reader, unit, form); - - if (shouldError(offset, result, form)) { - result = injectError(offset, result, form); - } - - return result; - } - - private boolean shouldError(long offset, DWARFAttributeValue attribute, DWARFForm form) { - return offsets.contains(offset); - } - - private DWARFAttributeValue injectError(long offset, DWARFAttributeValue attribute, - DWARFForm form) { - switch (form) { - case DW_FORM_addr: - return new DWARFNumericAttribute(0); - - // Block Form - case DW_FORM_block4: - case DW_FORM_block2: - case DW_FORM_block1: - case DW_FORM_block: - return new DWARFBlobAttribute(new byte[] {}); - - // Constant Form - case DW_FORM_data8: - case DW_FORM_data4: - case DW_FORM_data2: - case DW_FORM_data1: - case DW_FORM_udata: - case DW_FORM_sdata: - return new DWARFNumericAttribute(0); - - // Exprloc Form - case DW_FORM_exprloc: - return new DWARFBlobAttribute(new byte[] {}); - - case DW_FORM_flag_present: - case DW_FORM_flag: - return DWARFBooleanAttribute.TRUE; - - // Pointer Types Form (lineptr, loclistptr, macptr, rangelistptr) - case DW_FORM_sec_offset: - return new DWARFNumericAttribute(0); - - // Reference Form - case DW_FORM_ref8: - case DW_FORM_ref4: - case DW_FORM_ref2: - case DW_FORM_ref1: - case DW_FORM_ref_udata: - case DW_FORM_ref_addr: - return new DWARFNumericAttribute(0); - - // String Form - case DW_FORM_strp: - case DW_FORM_string: - return new DWARFStringAttribute(""); - - case DW_FORM_indirect: - case NULL: - default: - throw new IllegalArgumentException("Invalid DWARF Form: " + form); - } - } -} diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java b/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java index 488f4abecd..40166304b8 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java @@ -66,10 +66,10 @@ import ghidra.app.script.GhidraScript; import ghidra.app.services.Analyzer; import ghidra.app.services.GraphDisplayBroker; import ghidra.app.util.NamespaceUtils; -import ghidra.app.util.bin.format.dwarf4.next.DWARFFunctionImporter; -import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram; -import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.DWARFSectionProvider; -import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.DWARFSectionProviderFactory; +import ghidra.app.util.bin.format.dwarf.DWARFFunctionImporter; +import ghidra.app.util.bin.format.dwarf.DWARFProgram; +import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProvider; +import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProviderFactory; import ghidra.app.util.bin.format.pdb.PdbParserConstants; import ghidra.app.util.importer.MessageLog; import ghidra.app.util.opinion.PeLoader; diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFRegisterMappingsTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFRegisterMappingsTest.java index 0e7d411053..e30cafb70a 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFRegisterMappingsTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFRegisterMappingsTest.java @@ -29,6 +29,8 @@ import org.junit.experimental.categories.Category; import generic.test.category.NightlyCategory; import ghidra.GhidraTestApplicationLayout; +import ghidra.app.util.bin.format.dwarf.DWARFRegisterMappings; +import ghidra.app.util.bin.format.dwarf.DWARFRegisterMappingsManager; import ghidra.program.model.lang.*; import ghidra.program.util.DefaultLanguageService; import ghidra.test.AbstractGhidraHeadlessIntegrationTest;