GP-2798 initial DWARF5 support

Adds support for simple DWARF5 debug info.  Split .dwo not supported.

Uses bookmarks to tag problematic issues instead of log messages during import.
This commit is contained in:
dev747368 2024-03-11 10:59:20 -04:00
parent 204081db01
commit f17ebb78ab
142 changed files with 5845 additions and 4147 deletions

View file

@ -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 {

View file

@ -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);

View file

@ -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.

View file

@ -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.
* <p>
* @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 <T>
* @param <T> 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 extends DWARFAttributeValue> T findAttributeInChildren(int attribute, int childTag,
Class<T> clazz) {
public <T extends DWARFAttributeValue> T findAttributeInChildren(DWARFAttribute attribute,
DWARFTag childTag, Class<T> 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.
* <p>
* Returns null if the attribute does not exist or is wrong java class type.
* <p>
@ -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 extends DWARFAttributeValue> T getAttribute(int attribute, Class<T> clazz) {
AttrInfo attrInfo = findAttribute(attribute);
public <T extends DWARFAttributeValue> T getAttribute(DWARFAttribute attribute,
Class<T> 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.
* <p>
* Returns null if the attribute does not exist.
* <p>
* Attributes are searched for in each fragment in this aggregate, starting with the
* 'head' fragment, progressing toward the 'decl' fragment.
* <p>
*
* @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.
* <p>
* @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.
* <p>
* @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.
* <p>
* @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).
* <p>
* 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.
* <p>
* @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.
* <p>
* @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<DebugInfoEntry> getChildren(int childTag) {
public List<DebugInfoEntry> 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.
* <p>
* @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.
* <p>
* @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.
* <p>
* Numeric attributes are treated as offsets into the debug_loc section.
* <p>
* 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.
* <p>
* @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<DWARFLocation> 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.
* <p>
* @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.
* <p>
* 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<DWARFLocation> readDebugLocList(long offset) throws IOException {
BinaryReader debug_loc = getProgram().getDebugLocation();
List<DWARFLocation> 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<DWARFLocation> _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.
* <p>
* @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.
* <p>
* 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).
* <p>
* 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<DWARFRange> parseDebugRange(int attribute) throws IOException {
List<DWARFRange> 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).
* <p>
* 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<DWARFRange> 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<DWARFRange> 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.
* <p>
* @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.
* <p>
* This indicates an empty range, in which case the caller may want to take
* special steps to avoid issues with Ghidra ranges.
* <p>
* 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 extends DWARFAttributeValue> T getValue(Class<T> clazz) {
if (attr != null && clazz.isAssignableFrom(attr.getClass())) {
return clazz.cast(attr);

View file

@ -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.
* <p>
* 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<AttrDef> 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<DWARFAttributeClass> 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<Integer, DWARFAbbreviation> readAbbreviations(BinaryReader reader,
DWARFProgram prog, TaskMonitor monitor) throws IOException, CancelledException {
Map<Integer, DWARFAbbreviation> 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;
}
}

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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.
* <p>
* 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;
}

View file

@ -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.
* <p>
* The first DIE record must be a DW_TAG_compile_unit.
* <p>
* DIE records are identified by their byte offset in the .debug_info section.
* <p>
*/
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).
* <p>
* 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.
* <p>
* 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<Integer, DWARFAbbreviation> 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).
* <p>
* 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.
* <p>
* 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<Integer, DWARFAbbreviation> 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<Integer, DWARFAbbreviation> 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<Integer, DWARFAbbreviation> 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<Integer, DWARFAbbreviation> 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<Integer, DWARFAbbreviation> 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.
* <p>
* 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);
}
}

View file

@ -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.*;

View file

@ -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.*;

View file

@ -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<Long> 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<Long> offsets) {
DWARFDataType(DataType dataType, DWARFName dni, Set<Long> offsets) {
this.dataType = dataType;
this.dni = dni;
this.offsets.addAll(offsets);

View file

@ -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<Integer> 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<ParameterDefinition> 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()) {

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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<DWARFLocation> 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<DWARFRange> 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();

View file

@ -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<DWARFRange> 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<DWARFRange> 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();

View file

@ -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;

View file

@ -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)}
*

View file

@ -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<String> typeRemappings = new HashSet<>();
int paramZeroLenDataType;
Set<Integer> dwarfVers = new HashSet<>();
int compUnitCount;
int dieCount;
Set<String> compDirs = new HashSet<>();
List<String> compNames = new ArrayList<>();
Set<String> producers = new HashSet<>();
Set<String> 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 <T extends Comparable<T>> List<T> getSortedSet(Set<T> set) {
List<T> result = new ArrayList<>(set);
Collections.sort(result);
return result;
}
void addCompunitInfo(List<DWARFCompilationUnit> 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));
}
}
}
}

View file

@ -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);

View file

@ -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<T, R> {
R apply(T value) throws IOException;
}
private final BinaryReader reader;
private final Map<Long, DWARFIndirectTableHeader> lookupMap = new HashMap<>();
private final Function<DWARFCompilationUnit, Long> 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<DWARFCompilationUnit, Long> 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<BinaryReader, ? extends DWARFIndirectTableHeader> 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();
}
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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.
* <p>
* 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;
}
}

View file

@ -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<DWARFFile> include_directories = new ArrayList<>();
private List<DWARFFile> 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<DWARFLineContentType.Def> 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<DWARFLineContentType.Def> 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<DWARFLineContentType.Def> 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);
}
}
}

View file

@ -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.
* <p>
* Similar to the {@link DWARFAttribute} enum, both are identifiers of an attribute value
* that is serialized by a DWARFForm.
* <p>
* 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<Integer, DWARFLineContentType> lookupMap = buildLookup();
private static Map<Integer, DWARFLineContentType> buildLookup() {
Map<Integer, DWARFLineContentType> 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<DWARFLineContentType> {
/**
* Reads a {@link DWARFLineContentType.Def} instance from the {@link BinaryReader reader}.
* <p>
* Returns a null if its a end-of-list marker.
* <p>
* @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<DWARFLineContentType> 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);
}
}
}

View file

@ -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.
* <p>
* 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));
}
}

View file

@ -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.
* <p>
* @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<DWARFLocation> 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<DWARFLocation> 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<DWARFLocation> list;
public DWARFLocationList(List<DWARFLocation> 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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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}.
* <p>
*/
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.
* <p>
* Example:<br>
* <pre>fromList(parent, List.of("name1", "name2")) &rarr; parent_name/name1/name2</pre>
*
* @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<String> names) {
public static DWARFName fromList(DWARFName parent, List<String> 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;

View file

@ -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<DWARFAttribute> 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<Long, DWARFNameInfo> dniCache =
private FixedSizeHashMap<Long, DWARFName> dniCache =
new FixedSizeHashMap<>(100, maxDNICacheSize);
private Map<DWARFAttributeSpecification, DWARFAttributeSpecification> attributeSpecIntern =
private Map<DWARFAttribute.AttrDef, DWARFAttribute.AttrDef> attributeSpecIntern =
new HashMap<>();
private DWARFRegisterMappings dwarfRegisterMappings;
private final boolean stackGrowsNegative;
private List<DWARFFunctionFixup> 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<Integer, DWARFCompilationUnit> compUnitDieIndex = new TreeMap<>();
protected List<DWARFCompilationUnit> 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<Long, DIEAggregate> typeReferers = new ArrayListValuedHashMap<>();
private ListValuedMap<Long, Long> 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<String> 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<DIEAggregate> 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<DIEAggregate> 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.
* <p>
* 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<DIEAggregate> 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<DIEAggregate> 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<DIEAggregate> getTypeReferers(DIEAggregate targetDIEA) {
List<DIEAggregate> result = typeReferers.get(targetDIEA.getOffset());
return (result != null) ? result : Collections.emptyList();
List<Long> 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<DIEAggregate> getTypeReferers(DIEAggregate targetDIEA, int tag) {
public List<DIEAggregate> getTypeReferers(DIEAggregate targetDIEA, DWARFTag tag) {
List<DIEAggregate> 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<DIEAggregate>, Iterable<DIEAggregate> {

View file

@ -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.
* <p>
* 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.
* <p>
* DWARF ranges can represent an empty range, Ghidra AddressRanges can not.<br>
* Ghidra AddressRanges can include the maximum 64bit address (0xffffffffffffffff), but DWARF ranges
* can not include that.
*/
public class DWARFRange implements Comparable<DWARFRange> {
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<DWARFRange> {
* @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<DWARFRange> {
@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.
*

View file

@ -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<DWARFRange> 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<DWARFRange> 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<DWARFRange> ranges;
public DWARFRangeList(DWARFRange singleRange) {
ranges = List.of(singleRange);
}
public DWARFRangeList(List<DWARFRange> 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<DWARFRange> 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<DWARFRange> 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;
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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 + "]";

View file

@ -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.

View file

@ -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.
* <p>
* Elements in the table are referred to by index via {@link DWARFForm#DW_FORM_strx} and friends.
* <p>
* 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);
}
}

View file

@ -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.
* <p>
* 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.
* <p>
* 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}.
* <p>
* 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
* <p>
* 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.
* <p>
* 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<Integer, DWARFTag> lookupMap = buildLookup();
private static Map<Integer, DWARFTag> buildLookup() {
Map<Integer, DWARFTag> result = new HashMap<>();
for (DWARFTag tag : values()) {
result.put(tag.id, tag);
}
return result;
}
private static final Set<DWARFTag> 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);
}

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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}.
* <p>
* 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}.
* <p>
* 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.
* <p>
* 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<String> 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.
* <p>
* @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.
* <p>
* @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.
* <p>
* 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.
* <p>
* The valid pointerSizes are 1, 2, 4, and 8.
* <p>
* @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.
* <p>
* 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<DIEAggregate> 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.
* <p>
*
* @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;

View file

@ -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() {

View file

@ -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.*;

View file

@ -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<Integer> 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<DebugInfoEntry> getChildren(int childTag) {
List<DebugInfoEntry> result = new ArrayList<>();
for (DebugInfoEntry child : getChildren()) {
public List<DebugInfoEntry> getChildren(DWARFTag childTag) {
List<DebugInfoEntry> children = getChildren();
List<DebugInfoEntry> 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();

View file

@ -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 = ".";

View file

@ -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<String> 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<String> 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);

View file

@ -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;

View file

@ -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.
* <p>
* 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<Long, String> 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.
* <p>
* @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 <code>offset</code>, 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;
}
}

View file

@ -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}).
* <p>
* 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<DWARFAttributeClass> 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<DWARFAttributeClass> 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<Integer, DWARFAttribute> lookupMap = buildLookup();
private static Map<Integer, DWARFAttribute> buildLookup() {
Map<Integer, DWARFAttribute> 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<DWARFAttribute> {
/**
* Reads a {@link DWARFAttribute.AttrDef} instance from the {@link BinaryReader reader}.
* <p>
* Returns a null if its a end-of-list marker.
* <p>
* @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<DWARFAttribute> 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);
}
}
}

View file

@ -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);
}
}

View file

@ -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}.
* <p>
* 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.
* <p>
* Unknown forms are not supported and cause an exception.
*
* @param <E> attribute id enum type
*/
public class DWARFAttributeDef<E extends Enum<E>> {
/**
* Reads a {@link DWARFAttributeDef} instance from the {@link BinaryReader reader}.
* <p>
* Returns a null if its a end-of-list marker (which is only used by an attributespec list).
* <p>
* @param <E> 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 <E extends Enum<E>> DWARFAttributeDef<E> read(BinaryReader reader,
Function<Integer, E> 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<E> 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;
}
}

View file

@ -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();
}
}

View file

@ -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, " "));
}
}

View file

@ -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());
}
}

View file

@ -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);
}
}

View file

@ -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.
* <p>
* 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<DWARFAttributeClass> attributeClasses;
private static final Map<Integer, DWARFForm> 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<DWARFAttributeClass> 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.
* <p>
* 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<Integer, DWARFForm> buildLookupmap() {
Map<Integer, DWARFForm> 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;
}

View file

@ -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();
}
}

View file

@ -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);
}
}

View file

@ -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, "", ""));
}
}

View file

@ -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);
}
}

View file

@ -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<DWARFExpressionOperation> 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<DWARFExpressionOperation> 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) {

View file

@ -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;
}

View file

@ -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) {}

View file

@ -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}

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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));
}
}
}

View file

@ -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;
/**

View file

@ -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;
/**

View file

@ -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;

View file

@ -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");
}
}

View file

@ -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;
/**

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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 };

View file

@ -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;

View file

@ -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;

View file

@ -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.*;

View file

@ -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;

View file

@ -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.
* <p>
* 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<DWARFAttributeSpecification> 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<Integer, DWARFAbbreviation> readAbbreviations(BinaryReader reader,
DWARFProgram prog, TaskMonitor monitor) throws IOException, CancelledException {
Map<Integer, DWARFAbbreviation> 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;
}
}

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