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
@@ -26,7 +26,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import ghidra.app.script.GhidraScript; 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; import ghidra.util.Msg;
public class DWARFSetExternalDebugFilesLocationPrescript extends GhidraScript { public class DWARFSetExternalDebugFilesLocationPrescript extends GhidraScript {
@@ -18,9 +18,9 @@ package ghidra.app.plugin.core.analysis;
import java.io.IOException; import java.io.IOException;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.bin.format.dwarf4.next.*; import ghidra.app.util.bin.format.dwarf.*;
import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.DWARFSectionProvider; import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProvider;
import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.DWARFSectionProviderFactory; import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProviderFactory;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
@@ -94,15 +94,15 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
try { try {
try (DWARFProgram prog = new DWARFProgram(program, importOptions, monitor, dsp)) { try (DWARFProgram prog = new DWARFProgram(program, importOptions, monitor, dsp)) {
if (prog.getRegisterMappings() == null && importOptions.isImportFuncs()) { if (prog.getRegisterMappings() == null && importOptions.isImportFuncs()) {
log.appendMsg( log.appendMsg("No DWARF to Ghidra register mappings found for this program's " +
"No DWARF to Ghidra register mappings found for this program's language [%s], function information may be incorrect / incomplete." "language [%s], function information may be incorrect / incomplete."
.formatted(program.getLanguageID().getIdAsString())); .formatted(program.getLanguageID().getIdAsString()));
} }
prog.init(monitor); prog.init(monitor);
DWARFParser dp = new DWARFParser(prog, monitor); DWARFImporter importer = new DWARFImporter(prog, monitor);
DWARFImportSummary parseResults = dp.parse(); DWARFImportSummary importResults = importer.performImport();
parseResults.logSummaryResults(); importResults.logSummaryResults();
} }
Options propList = program.getOptions(Program.PROGRAM_INFO); Options propList = program.getOptions(Program.PROGRAM_INFO);
propList.setBoolean(DWARF_LOADED_OPTION_NAME, true); propList.setBoolean(DWARF_LOADED_OPTION_NAME, true);
@@ -17,9 +17,7 @@ package ghidra.app.util.bin;
import java.io.IOException; import java.io.IOException;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.LEB128; 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. * Class to hold result of reading a {@link LEB128} value, along with size and position metadata.
@@ -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;
}
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.HashMap;
import java.util.Map; import java.util.Map;
@@ -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;
}
}
@@ -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;
}
@@ -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);
}
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.address.Address;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.*; import static ghidra.program.model.data.DataTypeConflictHandler.ConflictResult.*;
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.dwarf4.encoding.DWARFTag.*; import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
@@ -25,11 +25,8 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import ghidra.app.util.DataTypeNamingUtil; import ghidra.app.util.DataTypeNamingUtil;
import ghidra.app.util.bin.format.dwarf4.*; import ghidra.app.util.bin.format.dwarf.attribs.DWARFNumericAttribute;
import ghidra.app.util.bin.format.dwarf4.attribs.DWARFNumericAttribute; import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException;
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.golang.rtti.types.GoKind; import ghidra.app.util.bin.format.golang.rtti.types.GoKind;
import ghidra.program.database.DatabaseObject; import ghidra.program.database.DatabaseObject;
import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.database.data.DataTypeUtilities;
@@ -94,7 +91,7 @@ public class DWARFDataTypeImporter {
this.dwarfDTM = dwarfDTM; this.dwarfDTM = dwarfDTM;
this.importOptions = prog.getImportOptions(); this.importOptions = prog.getImportOptions();
this.voidDDT = new DWARFDataType(dwarfDTM.getVoidType(), this.voidDDT = new DWARFDataType(dwarfDTM.getVoidType(),
DWARFNameInfo.fromDataType(dwarfDTM.getVoidType()), -1); DWARFName.fromDataType(dwarfDTM.getVoidType()), -1);
} }
public DWARFDataType getDDTByInstance(DataType dtInstance) { public DWARFDataType getDDTByInstance(DataType dtInstance) {
@@ -269,14 +266,14 @@ public class DWARFDataTypeImporter {
private DWARFDataType makeDataTypeForFunctionDefinition(DIEAggregate diea) private DWARFDataType makeDataTypeForFunctionDefinition(DIEAggregate diea)
throws IOException, DWARFExpressionException { 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 // push an empty funcdef data type into the lookup cache to prevent recursive loops
FunctionDefinitionDataType funcDef = FunctionDefinitionDataType funcDef =
new FunctionDefinitionDataType(dni.getParentCP(), dni.getName(), dataTypeManager); new FunctionDefinitionDataType(dni.getParentCP(), dni.getName(), dataTypeManager);
DataType dtToAdd = funcDef; DataType dtToAdd = funcDef;
int cuLang = diea.getCompilationUnit().getCompileUnit().getLanguage(); int cuLang = diea.getCompilationUnit().getLanguage();
if (cuLang == DWARFSourceLanguage.DW_LANG_Go) { if (cuLang == DWARFSourceLanguage.DW_LANG_Go) {
if (diea.hasAttribute(DW_AT_byte_size)) { if (diea.hasAttribute(DW_AT_byte_size)) {
// if the funcdef has a bytesize attribute, we should convert this data type to a ptr // 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); return makeNamedBaseType(prog.getName(diea), diea);
} }
private DWARFDataType makeNamedBaseType(DWARFNameInfo dni, DIEAggregate diea) private DWARFDataType makeNamedBaseType(DWARFName dni, DIEAggregate diea)
throws IOException, DWARFExpressionException { throws IOException, DWARFExpressionException {
int dwarfSize = diea.parseInt(DW_AT_byte_size, 0); int dwarfSize = diea.parseInt(DW_AT_byte_size, 0);
int dwarfEncoding = (int) diea.getUnsignedLong(DW_AT_encoding, -1); int dwarfEncoding = (int) diea.getUnsignedLong(DW_AT_encoding, -1);
@@ -434,7 +431,7 @@ public class DWARFDataTypeImporter {
*/ */
private DWARFDataType makeDataTypeForEnum(DIEAggregate diea) { 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); 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 // in addition to byte_size, enums can have encoding (signed/unsigned) and a basetype, which
// itself might have a signed/unsigned encoding. // itself might have a signed/unsigned encoding.
@@ -568,7 +565,7 @@ public class DWARFDataTypeImporter {
*/ */
private DWARFDataType makeDataTypeForStruct(DIEAggregate diea) { 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 structSize = diea.getUnsignedLong(DW_AT_byte_size, 0);
long origStructSize = structSize; long origStructSize = structSize;
@@ -841,7 +838,7 @@ public class DWARFDataTypeImporter {
} }
private void populateStubStruct_worker(DWARFDataType ddt, StructureDataType structure, 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)) { for (DebugInfoEntry childEntry : diea.getChildren(childTagType)) {
@@ -1206,7 +1203,7 @@ public class DWARFDataTypeImporter {
private DWARFDataType makeDataTypeForPtrToMemberType(DIEAggregate diea) private DWARFDataType makeDataTypeForPtrToMemberType(DIEAggregate diea)
throws IOException, DWARFExpressionException { throws IOException, DWARFExpressionException {
DWARFNameInfo dni = prog.getName(diea); DWARFName dni = prog.getName(diea);
DIEAggregate type = diea.getTypeRef(); DIEAggregate type = diea.getTypeRef();
DIEAggregate containingType = diea.getContainingTypeRef(); DIEAggregate containingType = diea.getContainingTypeRef();
@@ -1219,7 +1216,11 @@ public class DWARFDataTypeImporter {
DataType offsetType = dwarfDTM.getOffsetType(byteSize); DataType offsetType = dwarfDTM.getOffsetType(byteSize);
// create a typedef to the offsetType and put containing type and var type info in the typedef name. // 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); DataType dt = new TypedefDataType(dni.getParentCP(), x, offsetType, dataTypeManager);
if (!dni.isAnon()) { if (!dni.isAnon()) {
@@ -1246,7 +1247,7 @@ public class DWARFDataTypeImporter {
private DWARFDataType makeDataTypeForTypedef(DIEAggregate diea) private DWARFDataType makeDataTypeForTypedef(DIEAggregate diea)
throws IOException, DWARFExpressionException { throws IOException, DWARFExpressionException {
DWARFNameInfo typedefDNI = prog.getName(diea); DWARFName typedefDNI = prog.getName(diea);
DIEAggregate refdDIEA = diea.getTypeRef(); DIEAggregate refdDIEA = diea.getTypeRef();
if (refdDIEA != null && refdDIEA.getTag() == DW_TAG_base_type) { if (refdDIEA != null && refdDIEA.getTag() == DW_TAG_base_type) {
@@ -1305,7 +1306,7 @@ public class DWARFDataTypeImporter {
* *
*/ */
private DWARFDataType makeDataTypeForUnspecifiedType(DIEAggregate diea) { private DWARFDataType makeDataTypeForUnspecifiedType(DIEAggregate diea) {
DWARFNameInfo dni = prog.getName(diea); DWARFName dni = prog.getName(diea);
DataType dt = dwarfDTM.getBaseType(dni.getOriginalName()); DataType dt = dwarfDTM.getBaseType(dni.getOriginalName());
if (dt == null) { if (dt == null) {
return voidDDT; return voidDDT;
@@ -1315,17 +1316,17 @@ public class DWARFDataTypeImporter {
static class DWARFDataType { static class DWARFDataType {
DataType dataType; DataType dataType;
DWARFNameInfo dni; DWARFName dni;
DWARFSourceInfo dsi; DWARFSourceInfo dsi;
Set<Long> offsets = new HashSet<>(); Set<Long> offsets = new HashSet<>();
DWARFDataType(DataType dataType, DWARFNameInfo dni, long offset) { DWARFDataType(DataType dataType, DWARFName dni, long offset) {
this.dataType = dataType; this.dataType = dataType;
this.dni = dni; this.dni = dni;
this.offsets.add(offset); this.offsets.add(offset);
} }
DWARFDataType(DataType dataType, DWARFNameInfo dni, Set<Long> offsets) { DWARFDataType(DataType dataType, DWARFName dni, Set<Long> offsets) {
this.dataType = dataType; this.dataType = dataType;
this.dni = dni; this.dni = dni;
this.offsets.addAll(offsets); this.offsets.addAll(offsets);
@@ -13,19 +13,17 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.io.IOException;
import java.util.*; import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import ghidra.app.util.bin.format.dwarf4.*; import ghidra.app.util.bin.format.dwarf.DWARFDataTypeImporter.DWARFDataType;
import ghidra.app.util.bin.format.dwarf4.encoding.*; import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException;
import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException;
import ghidra.app.util.bin.format.dwarf4.next.DWARFDataTypeImporter.DWARFDataType;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
@@ -41,12 +39,6 @@ import utility.function.Dummy;
*/ */
public class DWARFDataTypeManager { 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 dataTypeManager;
private final DataTypeManager builtInDTM; private final DataTypeManager builtInDTM;
private final DWARFProgram prog; private final DWARFProgram prog;
@@ -185,8 +177,8 @@ public class DWARFDataTypeManager {
} }
private boolean isGoodDWARFSourceInfo(DWARFSourceInfo dsi) { private boolean isGoodDWARFSourceInfo(DWARFSourceInfo dsi) {
return dsi.getFilename() != null && !dsi.getFilename().isEmpty() && return dsi.filename() != null && !dsi.filename().isEmpty() &&
!dsi.getFilename().contains("built-in"); !dsi.filename().contains("built-in");
} }
private void cacheOffsetToDataTypeMapping(long dieOffset, DataType dt) { private void cacheOffsetToDataTypeMapping(long dieOffset, DataType dt) {
@@ -558,7 +550,7 @@ public class DWARFDataTypeManager {
monitor.increment(); monitor.increment();
try { try {
if (TYPE_TAGS.contains(diea.getTag())) { if (diea.getTag().isType()) {
doGetDataType(diea); doGetDataType(diea);
} }
} }
@@ -586,24 +578,22 @@ public class DWARFDataTypeManager {
} }
private DIEAggregate getFuncDIEA(DIEAggregate diea) { private DIEAggregate getFuncDIEA(DIEAggregate diea) {
switch (diea.getTag()) { return switch (diea.getTag()) {
case DWARFTag.DW_TAG_gnu_call_site: // these DIEs head elements have a different tag than the rest of the elements
case DWARFTag.DW_TAG_call_site: // in this aggregate, which causes a problem handling this DIEA. Create
case DWARFTag.DW_TAG_inlined_subroutine: // a new instance skipping the head element. No information is typically
// these DIEs head elements have a different tag than the rest of the elements // lost.
// in this aggregate, which causes a problem handling this DIEA. Create case DW_TAG_gnu_call_site, DW_TAG_call_site, DW_TAG_inlined_subroutine -> DIEAggregate
// a new instance skipping the head element. No information is typically .createSkipHead(diea);
// lost.
diea = DIEAggregate.createSkipHead(diea); case DW_TAG_subprogram -> diea;
// fall-thru:
case DWARFTag.DW_TAG_subprogram: // dw_tag_subroutine_type was already handled in importAllDataTypes(),
//case DWARFTag.DW_TAG_subroutine_type: // so it is being skipped here.
// Both of these tag types can be converted to Ghidra func definition data types, //case DW_TAG_subroutine_type -> null
// but dw_tag_subroutine_type was already handled in importAllDataTypes(),
// so it is being skipped here. default -> null;
return diea; };
}
return null;
} }
/** /**
@@ -617,7 +607,7 @@ public class DWARFDataTypeManager {
public FunctionDefinition getFunctionSignature(DIEAggregate diea) { public FunctionDefinition getFunctionSignature(DIEAggregate diea) {
diea = getFuncDIEA(diea); diea = getFuncDIEA(diea);
if (diea != null) { if (diea != null) {
DWARFNameInfo dni = prog.getName(diea); DWARFName dni = prog.getName(diea);
return createFunctionDefinitionDataType(diea, dni); return createFunctionDefinitionDataType(diea, dni);
} }
return null; return null;
@@ -635,7 +625,7 @@ public class DWARFDataTypeManager {
* @return new {@link FunctionDefinitionDataType}. * @return new {@link FunctionDefinitionDataType}.
*/ */
private FunctionDefinitionDataType createFunctionDefinitionDataType(DIEAggregate diea, private FunctionDefinitionDataType createFunctionDefinitionDataType(DIEAggregate diea,
DWARFNameInfo dni) { DWARFName dni) {
DataType returnDataType = getDataTypeForVariable(diea.getTypeRef()); DataType returnDataType = getDataTypeForVariable(diea.getTypeRef());
boolean foundThisParam = false; boolean foundThisParam = false;
List<ParameterDefinition> params = new ArrayList<>(); List<ParameterDefinition> params = new ArrayList<>();
@@ -661,7 +651,7 @@ public class DWARFDataTypeManager {
FunctionDefinitionDataType funcDef = FunctionDefinitionDataType funcDef =
new FunctionDefinitionDataType(dni.getParentCP(), dni.getName(), dataTypeManager); new FunctionDefinitionDataType(dni.getParentCP(), dni.getName(), dataTypeManager);
funcDef.setReturnType(returnDataType); 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()])); funcDef.setArguments(params.toArray(new ParameterDefinition[params.size()]));
if (!diea.getHeadFragment().getChildren(DWARFTag.DW_TAG_unspecified_parameters).isEmpty()) { if (!diea.getHeadFragment().getChildren(DWARFTag.DW_TAG_unspecified_parameters).isEmpty()) {
@@ -13,9 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.app.util.bin.format.dwarf4.encoding; package ghidra.app.util.bin.format.dwarf;
import ghidra.app.util.bin.format.dwarf4.DWARFUtil;
/** /**
* DWARF attribute encoding consts from www.dwarfstd.org/doc/DWARF4.pdf * DWARF attribute encoding consts from www.dwarfstd.org/doc/DWARF4.pdf
@@ -13,9 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.app.util.bin.format.dwarf4.encoding; package ghidra.app.util.bin.format.dwarf;
import ghidra.app.util.bin.format.dwarf4.DWARFUtil;
/** /**
* DWARF Endianity consts from www.dwarfstd.org/doc/DWARF4.pdf * DWARF Endianity consts from www.dwarfstd.org/doc/DWARF4.pdf
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.app.util.bin.format.dwarf4; package ghidra.app.util.bin.format.dwarf;
import java.io.IOException; import java.io.IOException;
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.dwarf4.encoding.DWARFTag.*; import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
@@ -24,13 +24,10 @@ import java.util.stream.Collectors;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
import ghidra.app.cmd.label.SetLabelPrimaryCmd; import ghidra.app.cmd.label.SetLabelPrimaryCmd;
import ghidra.app.util.bin.format.dwarf4.*; import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException;
import ghidra.app.util.bin.format.dwarf4.attribs.DWARFNumericAttribute; import ghidra.app.util.bin.format.dwarf.funcfixup.DWARFFunctionFixup;
import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException;
import ghidra.app.util.bin.format.dwarf4.funcfixup.DWARFFunctionFixup;
import ghidra.program.database.function.OverlappingFunctionException; import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.model.address.Address; import ghidra.program.model.address.*;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.listing.Function.FunctionUpdateType; import ghidra.program.model.listing.Function.FunctionUpdateType;
@@ -45,10 +42,10 @@ public class DWARFFunction {
public enum CommitMode { SKIP, FORMAL, STORAGE, } public enum CommitMode { SKIP, FORMAL, STORAGE, }
public DIEAggregate diea; public DIEAggregate diea;
public DWARFNameInfo name; public DWARFName name;
public Namespace namespace; public Namespace namespace;
private DWARFRangeList dwarfBody;
public Address address; public Address address;
public Address highAddress;
public long frameBase; // TODO: change this to preserve the func's frameBase expr instead of value public long frameBase; // TODO: change this to preserve the func's frameBase expr instead of value
public Function function; // ghidra function public Function function; // ghidra function
@@ -80,34 +77,30 @@ public class DWARFFunction {
if (diea.isDanglingDeclaration()) { if (diea.isDanglingDeclaration()) {
return null; return null;
} }
Address funcAddr = getFuncEntry(diea); DWARFRangeList bodyRanges = getFuncBodyRanges(diea);
if (funcAddr == null) { if (bodyRanges == null || bodyRanges.isEmpty()) {
return null; return null;
} }
DWARFProgram prog = diea.getProgram(); DWARFProgram prog = diea.getProgram();
DWARFDataTypeManager dwarfDTM = prog.getDwarfDTM(); 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.namespace = dfunc.name.getParentNamespace(prog.getGhidraProgram());
dfunc.sourceInfo = DWARFSourceInfo.create(diea); 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 // Check if the function is an external function
dfunc.isExternal = diea.getBool(DW_AT_external, false); dfunc.isExternal = diea.getBool(DW_AT_external, false);
dfunc.noReturn = diea.getBool(DW_AT_noreturn, false); dfunc.noReturn = diea.getBool(DW_AT_noreturn, false);
// Retrieve the frame base if it exists // Retrieve the frame base if it exists
DWARFLocation frameLoc = null; DWARFLocationList frameBaseLocs = diea.getLocationList(DW_AT_frame_base);
if (diea.hasAttribute(DW_AT_frame_base)) { if (!frameBaseLocs.isEmpty()) {
List<DWARFLocation> frameBase = diea.getAsLocation(DW_AT_frame_base, dfunc.getRange()); DWARFLocation frameLoc = frameBaseLocs.getLocationContaining(dfunc.getEntryPc());
// get the framebase register, find where the frame is finally setup. // get the framebase register, find where the frame is finally setup.
frameLoc = DWARFLocation.getTopLocation(frameBase, dfunc.address.getOffset());
if (frameLoc != null) { 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; return dfunc;
} }
private DWARFFunction(DIEAggregate diea, DWARFNameInfo dni, Address address) { private DWARFFunction(DIEAggregate diea, DWARFName dni, DWARFRangeList dwarfBody) {
this.diea = diea; this.diea = diea;
this.name = dni; this.name = dni;
this.address = address; this.dwarfBody = dwarfBody;
this.address = diea.getProgram().getCodeAddress(dwarfBody.getFirstAddress());
} }
public DWARFProgram getProgram() { public DWARFProgram getProgram() {
return diea.getProgram(); return diea.getProgram();
} }
public DWARFRange getRange() { public String getDescriptiveName() {
return new DWARFRange(address.getOffset(), return "%s@%s".formatted(name.getName(), address);
highAddress != null ? highAddress.getOffset() : address.getOffset() + 1); }
public DWARFRangeList getRangeList() {
return dwarfBody;
} }
public String getCallingConventionName() { public String getCallingConventionName() {
return callingConventionName; 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. * Returns the DWARFVariable that starts at the specified stack offset.
* *
@@ -264,18 +279,16 @@ public class DWARFFunction {
try { try {
varStorage = dvar.getVariableStorage(); varStorage = dvar.getVariableStorage();
if (hasConflictWithParamStorage(dvar)) { if (hasConflictWithParamStorage(dvar)) {
appendComment(function.getEntryPoint(), CodeUnit.PLATE_COMMENT, getProgram().logWarningAt(function.getEntryPoint(), function.getName(),
"Local variable %s[%s] conflicts with parameter, skipped." "Local variable %s[%s] conflicts with parameter, skipped."
.formatted(dvar.getDeclInfoString(), varStorage), .formatted(dvar.getDeclInfoString(), varStorage));
"\n");
return; return;
} }
if (hasConflictWithExistingLocalVariableStorage(dvar)) { if (hasConflictWithExistingLocalVariableStorage(dvar)) {
appendComment(function.getEntryPoint().add(dvar.lexicalOffset), getProgram().logWarningAt(function.getEntryPoint().add(dvar.lexicalOffset),
CodeUnit.EOL_COMMENT, "Local omitted variable %s[%s] scope starts here" function.getName(), "Local omitted variable %s[%s] scope starts here"
.formatted(dvar.getDeclInfoString(), varStorage), .formatted(dvar.getDeclInfoString(), varStorage));
"; ");
return; return;
} }
@@ -301,55 +314,41 @@ public class DWARFFunction {
function.addLocalVariable(var, SourceType.IMPORTED); function.addLocalVariable(var, SourceType.IMPORTED);
} }
catch (InvalidInputException | DuplicateNameException e) { 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( "Local omitted variable %s[%s] scope starts here".formatted(
dvar.getDeclInfoString(), 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 { public static AddressRange getFuncBody(DIEAggregate diea, boolean flattenDisjoint)
// TODO: dw_at_entry_pc is also sometimes available, typically in things like inlined_subroutines throws IOException {
// TODO: dw_at_entry_pc is also sometimes available, typically in things like inlined_subroutines
DWARFProgram dprog = diea.getProgram(); DWARFProgram dprog = diea.getProgram();
DWARFNumericAttribute lowpcAttr = DWARFRangeList bodyRangeList = getFuncBodyRanges(diea);
diea.getAttribute(DW_AT_low_pc, DWARFNumericAttribute.class); if (bodyRangeList != null && !bodyRangeList.isEmpty()) {
if (lowpcAttr != null && lowpcAttr.getUnsignedValue() != 0) { DWARFRange bodyRange =
return dprog.getCodeAddress( flattenDisjoint ? bodyRangeList.getFlattenedRange() : bodyRangeList.getFirst();
lowpcAttr.getUnsignedValue() + dprog.getProgramBaseAddressFixup()); return dprog.getAddressRange(bodyRange, true);
}
if (diea.hasAttribute(DW_AT_ranges)) {
List<DWARFRange> bodyRanges = diea.readRange(DW_AT_ranges);
if (!bodyRanges.isEmpty()) {
return dprog.getCodeAddress(bodyRanges.get(0).getFrom());
}
} }
return null; 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) { public boolean syncWithExistingGhidraFunction(boolean createIfMissing) {
try { try {
Program currentProgram = getProgram().getGhidraProgram(); Program currentProgram = getProgram().getGhidraProgram();
@@ -13,21 +13,14 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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 java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import ghidra.app.cmd.label.SetLabelPrimaryCmd; import ghidra.app.util.bin.format.dwarf.DWARFFunction.CommitMode;
import ghidra.app.util.bin.format.dwarf4.*; import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException;
import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException; import ghidra.program.model.address.*;
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.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.data.DataUtilities.ClearDataMode; import ghidra.program.model.data.DataUtilities.ClearDataMode;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
@@ -126,6 +119,9 @@ public class DWARFFunctionImporter {
case DW_TAG_label: case DW_TAG_label:
processLabel(diea); processLabel(diea);
break; break;
default:
// do nothing
break;
} }
} }
catch (OutOfMemoryError oom) { catch (OutOfMemoryError oom) {
@@ -206,10 +202,8 @@ public class DWARFFunctionImporter {
dfunc.updateFunctionSignature(); dfunc.updateFunctionSignature();
} }
else { else {
Msg.warn(this, prog.logWarningAt(dfunc.function.getEntryPoint(), dfunc.function.getName(),
"Failed to get DWARF function signature information, leaving undefined: %s@%s" "Failed to get DWARF function signature information, leaving undefined");
.formatted(dfunc.function.getName(), dfunc.function.getEntryPoint()));
//Msg.debug(this, "DIE info: " + diea.toString());
} }
for (DWARFVariable localVar : dfunc.localVars) { for (DWARFVariable localVar : dfunc.localVars) {
@@ -255,9 +249,8 @@ public class DWARFFunctionImporter {
FunctionDefinition origFuncDef) { FunctionDefinition origFuncDef) {
if (dfunc.sourceInfo != null) { if (dfunc.sourceInfo != null) {
// Move the function into the program tree of the file // Move the function into the program tree of the file
moveIntoFragment(dfunc.function.getName(), dfunc.address, moveIntoFragment(dfunc.function.getName(), dfunc.getBody(),
dfunc.highAddress != null ? dfunc.highAddress : dfunc.address.add(1), dfunc.sourceInfo.filename());
dfunc.sourceInfo.getFilename());
if (importOptions.isOutputSourceLocationInfo()) { if (importOptions.isOutputSourceLocationInfo()) {
appendPlateComment(dfunc.address, "", dfunc.sourceInfo.getDescriptionStr()); appendPlateComment(dfunc.address, "", dfunc.sourceInfo.getDescriptionStr());
@@ -323,7 +316,9 @@ public class DWARFFunctionImporter {
DIEAggregate partDIEA = DIEAggregate.createSkipHead(diea); DIEAggregate partDIEA = DIEAggregate.createSkipHead(diea);
processSubprogram(partDIEA); processSubprogram(partDIEA);
break; break;
default:
// do nothing
break;
} }
} }
} }
@@ -397,8 +392,9 @@ public class DWARFFunctionImporter {
Data varData = DataUtilities.createData(currentProgram, address, dataType, -1, Data varData = DataUtilities.createData(currentProgram, address, dataType, -1,
ClearDataMode.CLEAR_ALL_CONFLICT_DATA); ClearDataMode.CLEAR_ALL_CONFLICT_DATA);
if (varData != null && globalVar.sourceInfo != null) { if (varData != null && globalVar.sourceInfo != null) {
moveIntoFragment(name, varData.getMinAddress(), varData.getMaxAddress(), AddressSet dataRng =
globalVar.sourceInfo.getFilename()); new AddressSet(varData.getMinAddress(), varData.getMaxAddress());
moveIntoFragment(name, dataRng, globalVar.sourceInfo.filename());
} }
variablesProcesesed.add(address); variablesProcesesed.add(address);
importSummary.globalVarsAdded++; importSummary.globalVarsAdded++;
@@ -426,33 +422,16 @@ public class DWARFFunctionImporter {
return; return;
} }
Number lowPC = null; DWARFRangeList blockRanges =
boolean disjoint = false; 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()) { 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, appendComment(blockStart, CodeUnit.PRE_COMMENT,
"Begin: " + dni.getName() + (disjoint ? " - Disjoint" : ""), "\n"); "Begin: %s%s".formatted(dni.getName(), disjoint ? " - Disjoint" : ""), "\n");
} }
processFuncChildren(diea, dfunc, processFuncChildren(diea, dfunc,
@@ -465,54 +444,30 @@ public class DWARFFunctionImporter {
return; return;
} }
Number lowPC = null; AddressRange body = DWARFFunction.getFuncBody(diea, true);
Number highPC = null; if (body != null) {
if (importOptions.isOutputInlineFuncComments()) {
// Process low and high pc if it exists addCommentsForInlineFunc(diea, body);
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;
} }
processFuncChildren(diea, dfunc, body.getMinAddress().subtract(dfunc.address));
lowPC = ranges.get(0).getFrom();
highPC = ranges.get(ranges.size() - 1).getTo();
} }
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 * 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. * 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); FunctionDefinition funcDef = dwarfDTM.getFunctionSignature(diea);
if (funcDef != null) { if (funcDef != null) {
long inlineFuncLen = blockEnd.subtract(blockStart); long inlineFuncLen = range.getLength();
boolean isShort = inlineFuncLen < INLINE_FUNC_SHORT_LEN; boolean isShort = inlineFuncLen < INLINE_FUNC_SHORT_LEN;
if (isShort) { if (isShort) {
appendComment(blockStart, CodeUnit.EOL_COMMENT, appendComment(range.getMinAddress(), CodeUnit.EOL_COMMENT,
"inline " + funcDef.getPrototypeString(), "; "); "inline " + funcDef.getPrototypeString(), "; ");
} }
else { else {
appendComment(blockStart, CodeUnit.PRE_COMMENT, appendComment(range.getMinAddress(), CodeUnit.PRE_COMMENT,
"Begin: inline " + funcDef.getPrototypeString(), "\n"); "Begin: inline " + funcDef.getPrototypeString(), "\n");
} }
} }
@@ -550,7 +505,7 @@ public class DWARFFunctionImporter {
* @param end end address of the fragment * @param end end address of the fragment
* @param fileID offset of the file name in the debug_line section * @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) { if (fileName != null) {
ProgramModule module = null; ProgramModule module = null;
int index = rootModule.getIndex(fileName); int index = rootModule.getIndex(fileName);
@@ -559,8 +514,8 @@ public class DWARFFunctionImporter {
module = rootModule.createModule(fileName); module = rootModule.createModule(fileName);
} }
catch (DuplicateNameException e) { catch (DuplicateNameException e) {
Msg.error(this, Msg.error(this, "Error while moving fragment %s (%s)".formatted(name, range),
"Error while moving fragment " + name + " from " + start + " to " + end, e); e);
return; return;
} }
} }
@@ -579,10 +534,11 @@ public class DWARFFunctionImporter {
Group[] children = module.getChildren();//TODO add a getChildAt(index) method... Group[] children = module.getChildren();//TODO add a getChildAt(index) method...
frag = (ProgramFragment) children[index]; frag = (ProgramFragment) children[index];
} }
frag.move(start, end); frag.move(range.getMinAddress(), range.getMaxAddress());
} }
catch (NotFoundException e) { 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; return;
} }
catch (DuplicateNameException e) { 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) { private void processLabel(DIEAggregate diea) {
if (!shouldProcess(diea)) { if (!shouldProcess(diea)) {
return; return;
} }
String name = prog.getEntryName(diea); String name = prog.getEntryName(diea);
if (name != null && diea.hasAttribute(DW_AT_low_pc)) { DWARFRange labelPc;
Address address = prog.getCodeAddress(diea.getLowPC(0)); if (name != null && (labelPc = diea.getPCRange()) != null) {
Address address = prog.getCodeAddress(labelPc.getFrom());
if (address.getOffset() != 0) { if (address.getOffset() != 0) {
try { try {
SymbolTable symbolTable = currentProgram.getSymbolTable(); SymbolTable symbolTable = currentProgram.getSymbolTable();
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.HashMap;
import java.util.Map; import java.util.Map;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.AnalysisOptionsUpdater;
import ghidra.app.plugin.core.analysis.DWARFAnalyzer; import ghidra.app.plugin.core.analysis.DWARFAnalyzer;
@@ -97,6 +97,7 @@ public class DWARFImportOptions {
private boolean tryPackStructs = true; private boolean tryPackStructs = true;
private boolean specialCaseSizedBaseTypes = true; private boolean specialCaseSizedBaseTypes = true;
private boolean importLocalVariables = true; private boolean importLocalVariables = true;
private boolean useBookmarks = true;
/** /**
* Create new instance * Create new instance
@@ -355,6 +356,10 @@ public class DWARFImportOptions {
this.importLocalVariables = importLocalVariables; this.importLocalVariables = importLocalVariables;
} }
public boolean isUseBookmarks() {
return useBookmarks;
}
/** /**
* See {@link Analyzer#registerOptions(Options, ghidra.program.model.listing.Program)} * See {@link Analyzer#registerOptions(Options, ghidra.program.model.listing.Program)}
* *
@@ -13,10 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.*;
import java.util.Set;
import ghidra.util.Msg; import ghidra.util.Msg;
@@ -41,13 +40,23 @@ public class DWARFImportSummary {
int varDWARFExpressionValue; int varDWARFExpressionValue;
int exprReadError; int exprReadError;
Set<String> typeRemappings = new HashSet<>(); 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. * Writes summary information to the {@link Msg} log.
*/ */
public void logSummaryResults() { public void logSummaryResults() {
if (totalElapsedMS > 0) { 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) { if (dataTypeElapsedMS > 0) {
Msg.info(this, Msg.info(this,
@@ -67,6 +76,18 @@ public class DWARFImportSummary {
Msg.info(this, Msg.info(this,
String.format("DWARF function signatures added: %d", funcSignaturesAdded)); 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()) { if (!typeRemappings.isEmpty()) {
Msg.error(this, Msg.error(this,
@@ -108,8 +129,40 @@ public class DWARFImportSummary {
"DWARF variable definitions that failed because they are computed pseudo variables: " + "DWARF variable definitions that failed because they are computed pseudo variables: " +
varDWARFExpressionValue); varDWARFExpressionValue);
} }
if (paramZeroLenDataType > 0) {
Msg.error(this, "DWARF zero-length parameters: " + paramZeroLenDataType);
}
if (exprReadError > 0) { if (exprReadError > 0) {
Msg.error(this, "DWARF expression failed to read: " + exprReadError); 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));
}
}
}
} }
@@ -13,14 +13,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.util.bin.format.dwarf4.DWARFException;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.Swing; 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 * Performs a DWARF datatype import and a DWARF function import, under the control of the
* {@link DWARFImportOptions}. * {@link DWARFImportOptions}.
*/ */
public class DWARFParser { public class DWARFImporter {
private DWARFProgram prog; private DWARFProgram prog;
private DWARFDataTypeManager dwarfDTM; private DWARFDataTypeManager dwarfDTM;
private TaskMonitor monitor; private TaskMonitor monitor;
public DWARFParser(DWARFProgram prog, TaskMonitor monitor) { public DWARFImporter(DWARFProgram prog, TaskMonitor monitor) {
this.prog = prog; this.prog = prog;
this.monitor = monitor; this.monitor = monitor;
this.dwarfDTM = prog.getDwarfDTM(); this.dwarfDTM = prog.getDwarfDTM();
@@ -87,9 +86,9 @@ public class DWARFParser {
prog.getGhidraProgram().getDataTypeManager().getDataType(dataTypePath); prog.getGhidraProgram().getDataTypeManager().getDataType(dataTypePath);
if (dataType != null && !(dataType instanceof Pointer || dataType instanceof Array)) { if (dataType != null && !(dataType instanceof Pointer || dataType instanceof Array)) {
DWARFSourceInfo dsi = dwarfDTM.getSourceInfo(dataType); DWARFSourceInfo dsi = dwarfDTM.getSourceInfo(dataType);
if (dsi != null && dsi.getFilename() != null) { if (dsi != null && dsi.filename() != null) {
CategoryPath dataTypeOrigCP = dataType.getCategoryPath(); CategoryPath dataTypeOrigCP = dataType.getCategoryPath();
CategoryPath newRoot = new CategoryPath(rootCP, dsi.getFilename()); CategoryPath newRoot = new CategoryPath(rootCP, dsi.filename());
CategoryPath newCP = CategoryPath newCP =
rehomeCategoryPathSubTree(unCatRootCp, newRoot, dataTypeOrigCP); rehomeCategoryPathSubTree(unCatRootCp, newRoot, dataTypeOrigCP);
if (newCP != null) { if (newCP != null) {
@@ -181,7 +180,7 @@ public class DWARFParser {
* @throws DWARFException * @throws DWARFException
* @throws CancelledException * @throws CancelledException
*/ */
public DWARFImportSummary parse() throws IOException, DWARFException, CancelledException { public DWARFImportSummary performImport() throws IOException, DWARFException, CancelledException {
monitor.setIndeterminate(false); monitor.setIndeterminate(false);
monitor.setShowProgressValue(true); monitor.setShowProgressValue(true);
@@ -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();
}
}
@@ -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;
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.HashMap;
import java.util.Map; import java.util.Map;
@@ -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;
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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));
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.List;
import java.util.Objects; import java.util.Objects;
@@ -28,9 +28,9 @@ import ghidra.program.model.symbol.SymbolType;
* {@link Namespace namespaces} or {@link CategoryPath categorypaths}. * {@link Namespace namespaces} or {@link CategoryPath categorypaths}.
* <p> * <p>
*/ */
public class DWARFNameInfo { public class DWARFName {
private final DWARFNameInfo parent; private final DWARFName parent;
private final CategoryPath organizationalCategoryPath; private final CategoryPath organizationalCategoryPath;
private final NamespacePath namespacePath; private final NamespacePath namespacePath;
private final String originalName; private final String originalName;
@@ -40,42 +40,42 @@ public class DWARFNameInfo {
* *
* @param rootCategory {@link CategoryPath} in the data type manager that will contain * @param rootCategory {@link CategoryPath} in the data type manager that will contain
* any sub-categories that represent namespaces * any sub-categories that represent namespaces
* @return a new {@link DWARFNameInfo} instance * @return a new {@link DWARFName} instance
*/ */
public static DWARFNameInfo createRoot(CategoryPath rootCategory) { public static DWARFName createRoot(CategoryPath rootCategory) {
return new DWARFNameInfo(null, rootCategory, NamespacePath.ROOT, null); 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} * @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) { public static DWARFName fromDataType(DataType dataType) {
return new DWARFNameInfo(null, dataType.getCategoryPath(), return new DWARFName(null, dataType.getCategoryPath(),
NamespacePath.create(null, dataType.getName(), null), dataType.getName()); 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> * <p>
* Example:<br> * Example:<br>
* <pre>fromList(parent, List.of("name1", "name2")) &rarr; parent_name/name1/name2</pre> * <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 * @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) { for (String s : names) {
DWARFNameInfo tmp = new DWARFNameInfo(parent, s, s, SymbolType.NAMESPACE); DWARFName tmp = new DWARFName(parent, s, s, SymbolType.NAMESPACE);
parent = tmp; parent = tmp;
} }
return parent; return parent;
} }
private DWARFNameInfo(DWARFNameInfo parent, CategoryPath organizationalCategoryPath, private DWARFName(DWARFName parent, CategoryPath organizationalCategoryPath,
NamespacePath namespacePath, String originalName) { NamespacePath namespacePath, String originalName) {
this.parent = parent; this.parent = parent;
this.organizationalCategoryPath = this.organizationalCategoryPath =
@@ -84,7 +84,7 @@ public class DWARFNameInfo {
this.originalName = originalName; 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.parent = parent;
this.organizationalCategoryPath = parent.getOrganizationalCategoryPath(); this.organizationalCategoryPath = parent.getOrganizationalCategoryPath();
this.namespacePath = NamespacePath.create(parent.getNamespacePath(), name, type); this.namespacePath = NamespacePath.create(parent.getNamespacePath(), name, type);
@@ -96,7 +96,7 @@ public class DWARFNameInfo {
* *
* @return parent * @return parent
*/ */
public DWARFNameInfo getParent() { public DWARFName getParent() {
return parent; return parent;
} }
@@ -154,8 +154,8 @@ public class DWARFNameInfo {
* @param newOriginalName originalName for the new instance * @param newOriginalName originalName for the new instance
* @return new instance with new name * @return new instance with new name
*/ */
public DWARFNameInfo replaceName(String newName, String newOriginalName) { public DWARFName replaceName(String newName, String newOriginalName) {
return new DWARFNameInfo(getParent(), newOriginalName, newName, getType()); return new DWARFName(getParent(), newOriginalName, newName, getType());
} }
/** /**
@@ -165,8 +165,8 @@ public class DWARFNameInfo {
* @param newType new SymbolType value * @param newType new SymbolType value
* @return new instance with the specified SymbolType * @return new instance with the specified SymbolType
*/ */
public DWARFNameInfo replaceType(SymbolType newType) { public DWARFName replaceType(SymbolType newType) {
return new DWARFNameInfo(parent, originalName, getName(), 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. * this instance's namespace, using the specified name and symbol type.
* *
* @param childOriginalName the unmodified name * @param childOriginalName the unmodified name
@@ -263,9 +263,9 @@ public class DWARFNameInfo {
* @param childType the type of the object being named * @param childType the type of the object being named
* @return new DWARFNameInfo instance * @return new DWARFNameInfo instance
*/ */
public DWARFNameInfo createChild(String childOriginalName, String childName, public DWARFName createChild(String childOriginalName, String childName,
SymbolType childType) { SymbolType childType) {
return new DWARFNameInfo(this, childOriginalName, childName, childType); return new DWARFName(this, childOriginalName, childName, childType);
} }
@Override @Override
@@ -288,10 +288,10 @@ public class DWARFNameInfo {
if (obj == null) { if (obj == null) {
return false; return false;
} }
if (!(obj instanceof DWARFNameInfo)) { if (!(obj instanceof DWARFName)) {
return false; return false;
} }
DWARFNameInfo other = (DWARFNameInfo) obj; DWARFName other = (DWARFName) obj;
if (namespacePath == null) { if (namespacePath == null) {
if (other.namespacePath != null) { if (other.namespacePath != null) {
return false; return false;
@@ -13,13 +13,23 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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 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 start;
private final long end; private final long end;
@@ -31,9 +41,9 @@ public class DWARFRange implements Comparable<DWARFRange> {
* @param end long ending address, exclusive * @param end long ending address, exclusive
*/ */
public DWARFRange(long start, long end) { public DWARFRange(long start, long end) {
if (end < start) { if (Long.compareUnsigned(end, start) < 0) {
throw new IllegalArgumentException( 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.start = start;
this.end = end; this.end = end;
@@ -41,18 +51,26 @@ public class DWARFRange implements Comparable<DWARFRange> {
@Override @Override
public String toString() { public String toString() {
return "(" + this.start + "," + this.end + ")"; return "[%x,%x)".formatted(start, end);
} }
@Override @Override
public int compareTo(DWARFRange other) { public int compareTo(DWARFRange other) {
int tmp = Long.compare(start, other.start); int tmp = Long.compareUnsigned(start, other.start);
if (tmp == 0) { if (tmp == 0) {
tmp = Long.compare(end, other.end); tmp = Long.compareUnsigned(end, other.end);
} }
return tmp; 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. * Returns starting address.
* *
@@ -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;
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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; import ghidra.program.model.lang.Register;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@@ -26,7 +26,6 @@ import org.jdom.*;
import org.jdom.input.SAXBuilder; import org.jdom.input.SAXBuilder;
import generic.jar.ResourceFile; import generic.jar.ResourceFile;
import ghidra.app.util.bin.format.dwarf4.DWARFUtil;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.xml.XmlUtilities; import ghidra.util.xml.XmlUtilities;
@@ -13,22 +13,20 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.dwarf.DWARFTag.*;
import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.DW_TAG_formal_parameter; import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*;
import ghidra.app.util.bin.format.dwarf4.DIEAggregate; import ghidra.app.util.bin.format.dwarf.attribs.DWARFNumericAttribute;
import ghidra.app.util.bin.format.dwarf4.DebugInfoEntry;
import ghidra.app.util.bin.format.dwarf4.attribs.DWARFNumericAttribute;
/** /**
* Small class to hold the filename and line number info values from * Represents the filename and line number info values from DWARF {@link DebugInfoEntry DIEs}.
* 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} * Creates a new {@link DWARFSourceInfo} instance from the supplied {@link DIEAggregate}
* if the info is present, otherwise returns null; * if the info is present, otherwise returns null;
@@ -88,23 +86,6 @@ public class DWARFSourceInfo {
return sourceInfo != null ? sourceInfo.getDescriptionStr() : null; 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" * Returns the source location info as a string formatted as "filename:linenum"
* *
@@ -114,50 +95,6 @@ public class DWARFSourceInfo {
return filename + ":" + lineNum; 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 @Override
public String toString() { public String toString() {
return "DWARFSourceInfo [filename=" + filename + ", lineNum=" + lineNum + "]"; return "DWARFSourceInfo [filename=" + filename + ", lineNum=" + lineNum + "]";
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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. * DWARF source lang consts from www.dwarfstd.org/doc/DWARF4.pdf.
@@ -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);
}
}
@@ -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);
}
@@ -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;
}
}
@@ -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;
}
@@ -13,7 +13,10 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@@ -24,13 +27,9 @@ import java.util.regex.Pattern;
import generic.jar.ResourceFile; import generic.jar.ResourceFile;
import ghidra.app.cmd.comments.AppendCommentCmd; import ghidra.app.cmd.comments.AppendCommentCmd;
import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.format.dwarf.attribs.DWARFAttributeValue;
import ghidra.app.util.bin.format.dwarf4.attribs.DWARFAttributeValue; import ghidra.app.util.bin.format.dwarf.attribs.DWARFNumericAttribute;
import ghidra.app.util.bin.format.dwarf4.attribs.DWARFNumericAttribute; import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException;
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.program.database.data.DataTypeUtilities; import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace; import ghidra.program.model.address.AddressSpace;
@@ -38,8 +37,6 @@ import ghidra.program.model.data.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.SymbolType;
import ghidra.util.Conv;
public class DWARFUtil { public class DWARFUtil {
/** /**
@@ -53,7 +50,7 @@ public class DWARFUtil {
* @return the String name of the matching field. * @return the String name of the matching field.
*/ */
public static String toString(Class<?> clazz, int value) { 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]+.*)"); private static Pattern MANGLED_NESTING_REGEX = Pattern.compile("(.*_Z)?N([0-9]+.*)");
@@ -223,9 +152,9 @@ public class DWARFUtil {
DWARFProgram prog = die.getProgram(); DWARFProgram prog = die.getProgram();
for (DebugInfoEntry childDIE : die.getChildren(DWARFTag.DW_TAG_subprogram)) { for (DebugInfoEntry childDIE : die.getChildren(DWARFTag.DW_TAG_subprogram)) {
DIEAggregate childDIEA = prog.getAggregate(childDIE); 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) { 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) { 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()) { for (DebugInfoEntry childDIE : parent.getChildren()) {
DIEAggregate childDIEA = prog.getAggregate(childDIE); DIEAggregate childDIEA = prog.getAggregate(childDIE);
if (diea == childDIEA || diea.getOffset() == childDIEA.getOffset()) { 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++; typeDefCount++;
} }
} }
@@ -328,7 +258,7 @@ public class DWARFUtil {
sb.append(childName); 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) * @return formatted string, example "80_5_73dc6de9" (80 bytes, 5 fields, hex hash of field names)
*/ */
public static String getStructLayoutFingerprint(DIEAggregate diea) { 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; int memberCount = 0;
List<String> memberNames = new ArrayList<>(); List<String> memberNames = new ArrayList<>();
for (DebugInfoEntry childEntry : diea.getHeadFragment().getChildren()) { for (DebugInfoEntry childEntry : diea.getHeadFragment().getChildren()) {
@@ -348,7 +278,7 @@ public class DWARFUtil {
continue; continue;
} }
DIEAggregate childDIEA = diea.getProgram().getAggregate(childEntry); DIEAggregate childDIEA = diea.getProgram().getAggregate(childEntry);
if (childDIEA.hasAttribute(DWARFAttribute.DW_AT_external)) { if (childDIEA.hasAttribute(DW_AT_external)) {
continue; continue;
} }
memberCount++; memberCount++;
@@ -357,7 +287,7 @@ public class DWARFUtil {
int memberOffset = 0; int memberOffset = 0;
try { try {
memberOffset = memberOffset =
childDIEA.parseDataMemberOffset(DWARFAttribute.DW_AT_data_member_location, 0); childDIEA.parseDataMemberOffset(DW_AT_data_member_location, 0);
} }
catch (DWARFExpressionException | IOException e) { catch (DWARFExpressionException | IOException e) {
// ignore, leave as default value 0 // ignore, leave as default value 0
@@ -372,26 +302,6 @@ public class DWARFUtil {
return String.format("%d_%d_%08x", structSize, memberCount, memberNames.hashCode()); 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. * Append a string to a {@link DataType}'s description.
* *
@@ -468,100 +378,6 @@ public class DWARFUtil {
return cu; 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) { public static boolean isThisParam(DIEAggregate paramDIEA) {
// DWARF has multiple ways of indicating a DW_TAG_formal_parameter is // DWARF has multiple ways of indicating a DW_TAG_formal_parameter is
// the "this" parameter, and different versions of different toolchains // 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"). // referencing the param that points to the object instance (ie. "this").
// //
String paramName = paramDIEA.getName(); 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)) { Function.THIS_PARAM_NAME.equals(paramName)) {
return true; return true;
} }
DIEAggregate funcDIEA = paramDIEA.getParent(); DIEAggregate funcDIEA = paramDIEA.getParent();
DWARFAttributeValue dwATObjectPointer = DWARFAttributeValue dwATObjectPointer =
funcDIEA.getAttribute(DWARFAttribute.DW_AT_object_pointer); funcDIEA.getAttribute(DW_AT_object_pointer);
if (dwATObjectPointer != null && dwATObjectPointer instanceof DWARFNumericAttribute dnum && if (dwATObjectPointer != null && dwATObjectPointer instanceof DWARFNumericAttribute dnum &&
paramDIEA.hasOffset(dnum.getUnsignedValue())) { paramDIEA.hasOffset(dnum.getUnsignedValue())) {
return true; return true;
@@ -592,7 +408,7 @@ public class DWARFUtil {
// If the variable is not named, check to see if the parent of the function // 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 // is a struct/class, and the parameter points to it
DIEAggregate classDIEA = funcDIEA.getParent(); 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 // Check to see if the parent data type equals the parameters' data type
return isPointerTo(classDIEA, paramDIEA.getTypeRef()); return isPointerTo(classDIEA, paramDIEA.getTypeRef());
} }
@@ -601,94 +417,15 @@ public class DWARFUtil {
} }
public static boolean isPointerTo(DIEAggregate targetDIEA, DIEAggregate testDIEA) { 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; testDIEA.getTypeRef() == targetDIEA;
} }
public static boolean isPointerDataType(DIEAggregate diea) { public static boolean isPointerDataType(DIEAggregate diea) {
while (diea.getTag() == DWARFTag.DW_TAG_typedef) { while (diea.getTag() == DW_TAG_typedef) {
diea = diea.getTypeRef(); diea = diea.getTypeRef();
} }
return diea.getTag() == DWARFTag.DW_TAG_pointer_type; return diea.getTag() == 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);
} }
/** /**
@@ -700,7 +437,7 @@ public class DWARFUtil {
* @param lang {@link Language} to query * @param lang {@link Language} to query
* @param name name of the option in the ldefs file * @param name name of the option in the ldefs file
* @return file pointed to by the specified external_name tool entry * @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) public static ResourceFile getLanguageExternalFile(Language lang, String name)
throws IOException { throws IOException {
@@ -715,14 +452,13 @@ public class DWARFUtil {
* *
* @param lang {@link Language} to get base definition directory * @param lang {@link Language} to get base definition directory
* @return base directory for language definition files * @return base directory for language definition files
* @throws IOException * @throws IOException if not a sleigh lang
*/ */
public static ResourceFile getLanguageDefinitionDirectory(Language lang) throws IOException { public static ResourceFile getLanguageDefinitionDirectory(Language lang) throws IOException {
LanguageDescription langDesc = lang.getLanguageDescription(); LanguageDescription langDesc = lang.getLanguageDescription();
if (!(langDesc instanceof SleighLanguageDescription)) { if (!(langDesc instanceof SleighLanguageDescription sld)) {
throw new IOException("Not a Sleigh Language: " + lang.getLanguageID()); throw new IOException("Not a Sleigh Language: " + lang.getLanguageID());
} }
SleighLanguageDescription sld = (SleighLanguageDescription) langDesc;
ResourceFile defsFile = sld.getDefsFile(); ResourceFile defsFile = sld.getDefsFile();
ResourceFile parentFile = defsFile.getParentFile(); ResourceFile parentFile = defsFile.getParentFile();
return parentFile; return parentFile;
@@ -13,16 +13,15 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.io.IOException;
import java.util.*; import java.util.*;
import ghidra.app.util.bin.format.dwarf4.*; import ghidra.app.util.bin.format.dwarf.expression.*;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag;
import ghidra.app.util.bin.format.dwarf4.expression.*;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
@@ -34,7 +33,7 @@ import ghidra.util.Msg;
import ghidra.util.exception.InvalidInputException; 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 { public class DWARFVariable {
/** /**
@@ -102,7 +101,7 @@ public class DWARFVariable {
private final DWARFProgram program; private final DWARFProgram program;
private final DWARFFunction dfunc; private final DWARFFunction dfunc;
public DWARFNameInfo name; public DWARFName name;
public DataType type; public DataType type;
public long lexicalOffset; // offset inside function where variable storage becomes valid public long lexicalOffset; // offset inside function where variable storage becomes valid
public boolean isOutputParameter; // changes to parameter value escape back to the calling location 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) { private boolean readParamStorage(DIEAggregate diea) {
try { try {
if (DataTypeComponent.usesZeroLengthComponent(type)) { if (DataTypeComponent.usesZeroLengthComponent(type)) {
Msg.warn(this, "DWARF: zero-length function parameter %s:%s in %s@%s".formatted( program.getImportSummary().paramZeroLenDataType++;
name.getName(), type.getName(), dfunc.name.getName(), dfunc.address)); program.logWarningAt(dfunc.address, dfunc.name.getName(),
"Zero-length function parameter: %s : %s".formatted(name.getName(),
type.getName()));
return false; return false;
} }
DWARFLocation topLocation = DWARFLocation.getTopLocation( DWARFLocation paramLoc = diea.getLocation(DW_AT_location, dfunc.getEntryPc());
diea.getAsLocation(DW_AT_location, dfunc.getRange()), dfunc.address.getOffset()); if (paramLoc == null) {
if (topLocation == null) {
return false; return false;
} }
return readStorage(diea, topLocation); return readStorage(diea, paramLoc);
} }
catch (IOException e) { catch (IOException e) {
diea.getProgram().getImportSummary().exprReadError++; diea.getProgram().getImportSummary().exprReadError++;
@@ -264,8 +264,7 @@ public class DWARFVariable {
private boolean readLocalVariableStorage(DIEAggregate diea) { private boolean readLocalVariableStorage(DIEAggregate diea) {
try { try {
DWARFLocation location = DWARFLocation DWARFLocation location = diea.getLocation(DW_AT_location, dfunc.getEntryPc());
.getFirstLocation(diea.getAsLocation(DW_AT_location, dfunc.getRange()));
if (location == null) { if (location == null) {
return false; return false;
} }
@@ -274,7 +273,7 @@ public class DWARFVariable {
// with the address from the dwarf range. This gives slightly better results with // 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. // 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. // 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); return readStorage(diea, location);
@@ -289,16 +288,16 @@ public class DWARFVariable {
DWARFProgram prog = diea.getProgram(); DWARFProgram prog = diea.getProgram();
try { try {
DWARFLocation location = DWARFLocation DWARFLocationList locList = diea.getLocationList(DW_AT_location);
.getFirstLocation(diea.getAsLocation(DW_AT_location, DWARFRange.EMPTY)); DWARFLocation location = locList.getFirstLocation();
if (location == null) { if (location == null) {
return false; return false;
} }
DWARFExpressionEvaluator exprEvaluator = 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); exprEvaluator.evaluate(expr);
if (exprEvaluator.getRawLastRegister() != -1) { if (exprEvaluator.getRawLastRegister() != -1) {
@@ -333,17 +332,17 @@ public class DWARFVariable {
return false; return false;
} }
lexicalOffset = location.getRange().getFrom() - dfunc.address.getOffset(); lexicalOffset = location.getOffset(dfunc.address.getOffset());
DWARFProgram prog = diea.getProgram(); DWARFProgram prog = diea.getProgram();
DWARFImportSummary importSummary = prog.getImportSummary(); DWARFImportSummary importSummary = prog.getImportSummary();
try { try {
DWARFExpressionEvaluator exprEvaluator = DWARFExpressionEvaluator exprEvaluator =
DWARFExpressionEvaluator.create(diea.getHeadFragment()); new DWARFExpressionEvaluator(diea.getCompilationUnit());
exprEvaluator.setFrameBase(dfunc.frameBase); exprEvaluator.setFrameBase(dfunc.frameBase);
DWARFExpression expr = exprEvaluator.readExpr(location.getLocation()); DWARFExpression expr = exprEvaluator.readExpr(location.getExpr());
exprEvaluator.evaluate(expr); exprEvaluator.evaluate(expr);
long res = exprEvaluator.pop(); long res = exprEvaluator.pop();
@@ -391,12 +390,10 @@ public class DWARFVariable {
} }
if ((type.getLength() > reg.getMinimumByteSize())) { if ((type.getLength() > reg.getMinimumByteSize())) {
importSummary.varFitError++; importSummary.varFitError++;
program.logWarningAt(dfunc.address, dfunc.name.getName(),
Msg.warn(this, "%s %s [%s, size=%d] can not fit into specified register %s, size=%d"
"%s %s [%s, size=%d] for function %s@%s can not fit into specified register %s, size=%d, skipping. DWARF DIE: %s"
.formatted(getVarTypeName(diea), name.getName(), type.getName(), .formatted(getVarTypeName(diea), name.getName(), type.getName(),
type.getLength(), dfunc.name.getName(), dfunc.address, type.getLength(), reg.getName(), reg.getMinimumByteSize()));
reg.getName(), reg.getMinimumByteSize(), diea.getHexOffset()));
return false; return false;
} }
setRegisterStorage(List.of(reg)); 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." "%s location error for function %s@%s, %s: %s, DWARF DIE: %s, unsupported location information."
.formatted(getVarTypeName(diea), dfunc.name.getName(), dfunc.address, .formatted(getVarTypeName(diea), dfunc.name.getName(), dfunc.address,
name.getName(), name.getName(),
DWARFExpression.exprToString(location.getLocation(), diea), DWARFExpression.exprToString(location.getExpr(), diea),
diea.getHexOffset())); diea.getHexOffset()));
return false; return false;
} }
@@ -425,7 +422,7 @@ public class DWARFVariable {
} }
private String getVarTypeName(DIEAggregate diea) { 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() { public int getStorageSize() {
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.*;
@@ -13,16 +13,14 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.app.util.bin.format.dwarf4; package ghidra.app.util.bin.format.dwarf;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.dwarf4.attribs.*; import ghidra.app.util.bin.format.dwarf.attribs.*;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute; import ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.AttrDef;
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.program.model.data.LEB128;
import ghidra.util.datastruct.IntArrayList; import ghidra.util.datastruct.IntArrayList;
@@ -36,17 +34,10 @@ import ghidra.util.datastruct.IntArrayList;
*/ */
public class DebugInfoEntry { 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 DWARFCompilationUnit compilationUnit;
private final DWARFAbbreviation abbreviation; private final DWARFAbbreviation abbreviation;
private final DWARFAttributeValue[] attributes; private final DWARFAttributeValue[] attributes;
private final int[] attrOffsets;
private final long offset; private final long offset;
private final int dieIndex; private final int dieIndex;
@@ -54,71 +45,75 @@ public class DebugInfoEntry {
* Read a DIE record. * Read a DIE record.
* *
* @param reader {@link BinaryReader} positioned at the start of 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 dieIndex the index of the DIE
* @param attributeFactory the {@link DWARFAttributeFactory} to use to deserialize attribute
* values
* @return new DIE instance * @return new DIE instance
* @throws IOException if error reading data, or bad DWARF * @throws IOException if error reading data, or bad DWARF
*/ */
public static DebugInfoEntry read(BinaryReader reader, DWARFCompilationUnit unit, public static DebugInfoEntry read(BinaryReader reader, DWARFCompilationUnit cu, int dieIndex)
int dieIndex, DWARFAttributeFactory attributeFactory) throws IOException { throws IOException {
long offset = reader.getPointerIndex(); long offset = reader.getPointerIndex();
int abbreviationCode = reader.readNextUnsignedVarIntExact(LEB128::unsigned); int ac = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
// Check for terminator DIE // Check for terminator DIE
if (abbreviationCode == 0) { if (ac == 0) {
return new DebugInfoEntry(unit, offset, -1, null); return new DebugInfoEntry(cu, offset);
} }
DWARFAbbreviation abbreviation = unit.getCodeToAbbreviationMap().get(abbreviationCode); DWARFAbbreviation abbreviation = cu.getAbbreviation(ac);
if (abbreviation == null) { if (abbreviation == null) {
throw new IOException("Abbreviation code " + abbreviationCode + throw new IOException("Abbreviation code %d not found in compunit %d at 0x%x"
" not found in the abbreviation map for compunit " + unit); .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 // 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++) { for (int i = 0; i < attributeSpecs.length; i++) {
DWARFAttributeSpecification attributeSpec = attributeSpecs[i]; attrOffsets[i] = currentAttrOffset;
result.attributes[i] =
attributeFactory.read(reader, unit, attributeSpec.getAttributeForm());
if (ATTRIBUTES_TO_SKIP.contains(attributeSpec.getAttribute())) { AttrDef attributeSpec = attributeSpecs[i];
// throw away the object holding the value and replace it with DWARFFormContext context = new DWARFFormContext(reader, cu, attributeSpec);
// the static boolean true value object to hold its place in long attrSize = attributeSpec.getAttributeForm().getSize(context);
// the list. This saves a little memory if (attrSize < 0 || attrSize > Integer.MAX_VALUE) {
result.attributes[i] = DWARFBooleanAttribute.TRUE; 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 * Creates a DIE.
* {@link #read(BinaryReader, DWARFCompilationUnit, DWARFAttributeFactory) static read()} and
* junit tests.
* *
* @param unit compunit containing the DIE * @param cu compunit containing the DIE
* @param offset offset of the DIE * @param offset offset of the DIE
* @param dieIndex index of the DIE * @param dieIndex index of the DIE
* @param abbreviation that defines the schema of this DIE record * @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, public DebugInfoEntry(DWARFCompilationUnit cu, long offset, int dieIndex,
DWARFAbbreviation abbreviation) { DWARFAbbreviation abbreviation, int[] attrOffsets) {
this.compilationUnit = unit; this.compilationUnit = cu;
this.offset = offset; this.offset = offset;
this.dieIndex = dieIndex; this.dieIndex = dieIndex;
this.abbreviation = abbreviation; this.abbreviation = abbreviation;
this.attributes = abbreviation != null this.attrOffsets = attrOffsets;
? new DWARFAttributeValue[abbreviation.getAttributeCount()] this.attributes = attrOffsets != null ? new DWARFAttributeValue[attrOffsets.length] : null;
: 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 * @return index of this DIE
*/ */
@@ -141,9 +136,10 @@ public class DebugInfoEntry {
* @param childTag DIE tag used to filter the child DIEs * @param childTag DIE tag used to filter the child DIEs
* @return list of matching child DIE records * @return list of matching child DIE records
*/ */
public List<DebugInfoEntry> getChildren(int childTag) { public List<DebugInfoEntry> getChildren(DWARFTag childTag) {
List<DebugInfoEntry> result = new ArrayList<>(); List<DebugInfoEntry> children = getChildren();
for (DebugInfoEntry child : getChildren()) { List<DebugInfoEntry> result = new ArrayList<>(children.size());
for (DebugInfoEntry child : children) {
if (child.getTag() == childTag) { if (child.getTag() == childTag) {
result.add(child); result.add(child);
} }
@@ -154,7 +150,7 @@ public class DebugInfoEntry {
/** /**
* Get the parent DIE of this DIE. * 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() { public DebugInfoEntry getParent() {
return getProgram().getParentOf(dieIndex); return getProgram().getParentOf(dieIndex);
@@ -172,30 +168,65 @@ public class DebugInfoEntry {
* Get the DWARFTag value of this DIE. * Get the DWARFTag value of this DIE.
* @return the DWARFTag value of this DIE * @return the DWARFTag value of this DIE
*/ */
public int getTag() { public DWARFTag getTag() {
return (abbreviation != null) ? abbreviation.getTag() : 0; return (abbreviation != null) ? abbreviation.getTag() : null;
}
public DWARFAttributeValue[] getAttributes() {
return attributes;
} }
/** /**
* Check to see if this DIE has the given attribute key. * Returns the number of attributes in this DIE.
* @param attribute the attribute key *
* @return true if the DIE contains the attribute and false otherwise * @return number of attribute values in this DIE
*/ */
public boolean hasAttribute(int attribute) { public int getAttributeCount() {
if (abbreviation == null) { return attrOffsets.length;
return false; }
}
for (DWARFAttributeSpecification as : abbreviation.getAttributes()) { /**
if (as.getAttribute() == attribute) { * Returns the indexed attribute value.
return true; *
* @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 @Override
public String toString() { public String toString() {
StringBuilder buffer = new StringBuilder(); 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 abbrNum = abbreviation != null ? abbreviation.getAbbreviationCode() : 0;
int childCount = getProgram().getDIEChildIndexes(dieIndex).size(); int childCount = getProgram().getDIEChildIndexes(dieIndex).size();
buffer.append("<%d><%x>: %s [abbrev %d, tag %d, index %d, children %d]\n".formatted( 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, getDepth(), offset, tag, abbrNum, tagNum, dieIndex, childCount));
childCount));
if (isTerminator()) { if (isTerminator()) {
return buffer.toString(); return buffer.toString();
} }
DWARFAttributeSpecification[] attributeSpecs = abbreviation.getAttributes(); for (int i = 0; i < attributes.length; i++) {
for (int i = 0; i < attributeSpecs.length; i++) { buffer.append("\t\t");
DWARFAttributeSpecification attributeSpec = attributeSpecs[i]; DWARFAttributeValue attribVal = getAttributeValueUnchecked(i);
buffer.append("\t\tAttribute: %s %s %s\n".formatted( buffer.append(attribVal != null ? attribVal.toString(compilationUnit) : "-missing-");
DWARFUtil.toString(DWARFAttribute.class, attributeSpec.getAttribute()), buffer.append("\n");
attributes[i], attributeSpec.getAttributeForm().toString()));
} }
return buffer.toString(); return buffer.toString();

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