mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-01 10:25:03 +08:00
Merge remote-tracking branch 'origin/GP-4193_dev747368_dwarfline_source_info--SQUASHED'
This commit is contained in:
@@ -0,0 +1,76 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
// Adds DWARF source file line number info to the current binary
|
||||||
|
//@category DWARF
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
|
import ghidra.app.util.bin.format.dwarf.*;
|
||||||
|
import ghidra.app.util.bin.format.dwarf.line.DWARFLine.SourceFileAddr;
|
||||||
|
import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProvider;
|
||||||
|
import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProviderFactory;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.CodeUnit;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
|
||||||
|
public class DWARFLineInfoScript extends GhidraScript {
|
||||||
|
@Override
|
||||||
|
protected void run() throws Exception {
|
||||||
|
DWARFSectionProvider dsp =
|
||||||
|
DWARFSectionProviderFactory.createSectionProviderFor(currentProgram, monitor);
|
||||||
|
if (dsp == null) {
|
||||||
|
printerr("Unable to find DWARF information");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWARFImportOptions importOptions = new DWARFImportOptions();
|
||||||
|
try (DWARFProgram dprog = new DWARFProgram(currentProgram, importOptions, monitor, dsp)) {
|
||||||
|
dprog.init(monitor);
|
||||||
|
addSourceLineInfo(dprog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSourceLineInfo(DWARFProgram dprog) throws CancelledException, IOException {
|
||||||
|
BinaryReader reader = dprog.getDebugLineBR();
|
||||||
|
if (reader == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int count = 0;
|
||||||
|
monitor.initialize(reader.length(), "DWARF Source Line Info");
|
||||||
|
List<DWARFCompilationUnit> compUnits = dprog.getCompilationUnits();
|
||||||
|
for (DWARFCompilationUnit cu : compUnits) {
|
||||||
|
try {
|
||||||
|
monitor.checkCancelled();
|
||||||
|
monitor.setProgress(cu.getLine().getStartOffset());
|
||||||
|
List<SourceFileAddr> allSFA = cu.getLine().getAllSourceFileAddrInfo(cu, reader);
|
||||||
|
for (SourceFileAddr sfa : allSFA) {
|
||||||
|
Address addr = dprog.getCodeAddress(sfa.address());
|
||||||
|
DWARFUtil.appendComment(currentProgram, addr, CodeUnit.EOL_COMMENT, "",
|
||||||
|
"%s:%d".formatted(sfa.fileName(), sfa.lineNum()), ";");
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
Msg.error(this,
|
||||||
|
"Failed to read DWARF line info for cu %d".formatted(cu.getUnitNumber()), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println("Marked up " + count + " locations with source info");
|
||||||
|
}
|
||||||
|
}
|
||||||
-176
@@ -1,176 +0,0 @@
|
|||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package ghidra.app.plugin.core.analysis;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import ghidra.app.services.*;
|
|
||||||
import ghidra.app.util.bin.*;
|
|
||||||
import ghidra.app.util.bin.format.dwarf.DwarfSectionNames;
|
|
||||||
import ghidra.app.util.bin.format.dwarf.line.*;
|
|
||||||
import ghidra.app.util.bin.format.macho.*;
|
|
||||||
import ghidra.app.util.importer.MessageLog;
|
|
||||||
import ghidra.app.util.opinion.ElfLoader;
|
|
||||||
import ghidra.app.util.opinion.MachoLoader;
|
|
||||||
import ghidra.program.model.address.*;
|
|
||||||
import ghidra.program.model.listing.CodeUnit;
|
|
||||||
import ghidra.program.model.listing.Program;
|
|
||||||
import ghidra.program.model.mem.MemoryBlock;
|
|
||||||
import ghidra.util.Msg;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
|
||||||
|
|
||||||
public class DwarfLineNumberAnalyzer extends AbstractAnalyzer {
|
|
||||||
private static final String NAME = "DWARF Line Number";
|
|
||||||
private static final String DESCRIPTION = "Extracts DWARF debug line number information.";
|
|
||||||
|
|
||||||
public DwarfLineNumberAnalyzer() {
|
|
||||||
super(NAME, DESCRIPTION, AnalyzerType.BYTE_ANALYZER);
|
|
||||||
setPriority(AnalysisPriority.FORMAT_ANALYSIS.after().after().after());
|
|
||||||
setPrototype();
|
|
||||||
setSupportsOneTimeAnalysis();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
|
||||||
throws CancelledException {
|
|
||||||
AddressSpace space = program.getAddressFactory().getDefaultAddressSpace();
|
|
||||||
|
|
||||||
DwarfSectionNames sectionNames = new DwarfSectionNames(program);
|
|
||||||
try {
|
|
||||||
ByteProvider provider = getByteProvider(program, sectionNames);
|
|
||||||
if (provider == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
BinaryReader reader = new BinaryReader(provider, !program.getLanguage().isBigEndian());
|
|
||||||
|
|
||||||
while (!monitor.isCancelled() && reader.hasNext()) {
|
|
||||||
long startIndex = reader.getPointerIndex();
|
|
||||||
|
|
||||||
StatementProgramPrologue prologue = new StatementProgramPrologue(reader);
|
|
||||||
|
|
||||||
StateMachine machine = new StateMachine();
|
|
||||||
machine.reset(prologue.isDefaultIsStatement());
|
|
||||||
|
|
||||||
StatementProgramInstructions instructions =
|
|
||||||
new StatementProgramInstructions(reader, machine, prologue);
|
|
||||||
|
|
||||||
while (!monitor.isCancelled()) {
|
|
||||||
instructions.execute();
|
|
||||||
//machine.print();
|
|
||||||
|
|
||||||
FileEntry entry = prologue.getFileNameByIndex(machine.file);
|
|
||||||
String directory = prologue.getDirectoryByIndex(entry.getDirectoryIndex());
|
|
||||||
|
|
||||||
Address address = space.getAddress(machine.address);
|
|
||||||
CodeUnit cu = program.getListing().getCodeUnitContaining(address);
|
|
||||||
if (cu != null) {
|
|
||||||
cu.setProperty("Source Path",
|
|
||||||
directory + File.separator + entry.getFileName());
|
|
||||||
cu.setProperty("Source File", entry.getFileName());
|
|
||||||
cu.setProperty("Source Line", machine.line);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reader.getPointerIndex() - startIndex >= prologue.getTotalLength() +
|
|
||||||
StatementProgramPrologue.TOTAL_LENGTH_FIELD_LEN) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ByteProvider getByteProvider(Program program, DwarfSectionNames sectionNames)
|
|
||||||
throws IOException {
|
|
||||||
File exePath = new File(program.getExecutablePath());
|
|
||||||
if (MachoLoader.MACH_O_NAME.equals(program.getExecutableFormat())) {
|
|
||||||
File parent = exePath.getParentFile();
|
|
||||||
File dSymFile =
|
|
||||||
new File(parent, exePath.getName() + ".dSYM/Contents/Resources/DWARF/" +
|
|
||||||
exePath.getName());
|
|
||||||
if (!dSymFile.exists()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
RandomAccessByteProvider provider = new RandomAccessByteProvider(dSymFile);
|
|
||||||
try {
|
|
||||||
MachHeader header = new MachHeader(provider);
|
|
||||||
header.parse();
|
|
||||||
List<Section> allSections = header.getAllSections();
|
|
||||||
for (Section section : allSections) {
|
|
||||||
if (section.getSectionName().equals(sectionNames.SECTION_NAME_LINE())) {
|
|
||||||
return new InputStreamByteProvider(section.getDataStream(header),
|
|
||||||
section.getSize());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
catch (MachException e) {
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
provider.close();
|
|
||||||
}
|
|
||||||
return null;//no line number section existed!
|
|
||||||
}
|
|
||||||
else if (ElfLoader.ELF_NAME.equals(program.getExecutableFormat())) {
|
|
||||||
// We now load the .debug section as an overlay block, no need for the
|
|
||||||
// original file
|
|
||||||
MemoryBlock block = program.getMemory().getBlock(sectionNames.SECTION_NAME_LINE());
|
|
||||||
if (block != null) {
|
|
||||||
return MemoryByteProvider.createMemoryBlockByteProvider(program.getMemory(), block);
|
|
||||||
}
|
|
||||||
// TODO: this will not handle the case where the .debug section is
|
|
||||||
// in a separate file. Can the file in a separate location?
|
|
||||||
return null; // no line number section existed!
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Unrecognized program format: " +
|
|
||||||
program.getExecutableFormat());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canAnalyze(Program program) {
|
|
||||||
|
|
||||||
return isElfOrMacho(program);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasDebugInfo(Program program) {
|
|
||||||
DwarfSectionNames sectionNames = new DwarfSectionNames(program);
|
|
||||||
|
|
||||||
MemoryBlock block = null;
|
|
||||||
block = program.getMemory().getBlock(sectionNames.SECTION_NAME_LINE());
|
|
||||||
|
|
||||||
return block != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isElfOrMacho(Program program) {
|
|
||||||
String format = program.getExecutableFormat();
|
|
||||||
if (ElfLoader.ELF_NAME.equals(format)) {
|
|
||||||
return hasDebugInfo(program);
|
|
||||||
}
|
|
||||||
if (MachoLoader.MACH_O_NAME.equals(format)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+17
-12
@@ -25,6 +25,7 @@ import org.apache.commons.lang3.ArrayUtils;
|
|||||||
|
|
||||||
import ghidra.app.util.bin.format.dwarf.attribs.*;
|
import ghidra.app.util.bin.format.dwarf.attribs.*;
|
||||||
import ghidra.app.util.bin.format.dwarf.expression.*;
|
import ghidra.app.util.bin.format.dwarf.expression.*;
|
||||||
|
import ghidra.app.util.bin.format.dwarf.line.DWARFLine;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -470,9 +471,15 @@ public class DIEAggregate {
|
|||||||
if (attr == null) {
|
if (attr == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
int fileNum = (int) attr.getUnsignedValue();
|
try {
|
||||||
DWARFCompilationUnit cu = attrInfo.die.getCompilationUnit();
|
int fileNum = attr.getUnsignedIntExact();
|
||||||
return cu.isValidFileIndex(fileNum) ? cu.getFileByIndex(fileNum) : null;
|
DWARFCompilationUnit cu = attrInfo.die.getCompilationUnit();
|
||||||
|
DWARFLine line = cu.getLine();
|
||||||
|
return line.getFilePath(fileNum, false);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -710,7 +717,8 @@ public class DIEAggregate {
|
|||||||
/**
|
/**
|
||||||
* Return the range specified by the low_pc...high_pc attribute values.
|
* Return the range specified by the low_pc...high_pc attribute values.
|
||||||
*
|
*
|
||||||
* @return {@link DWARFRange} containing low_pc - high_pc, or null if the low_pc is not present
|
* @return {@link DWARFRange} containing low_pc - high_pc, or empty range if the low_pc is
|
||||||
|
* not present
|
||||||
*/
|
*/
|
||||||
public DWARFRange getPCRange() {
|
public DWARFRange getPCRange() {
|
||||||
DWARFNumericAttribute lowPc = getAttribute(DW_AT_low_pc, DWARFNumericAttribute.class);
|
DWARFNumericAttribute lowPc = getAttribute(DW_AT_low_pc, DWARFNumericAttribute.class);
|
||||||
@@ -720,29 +728,26 @@ public class DIEAggregate {
|
|||||||
long rawLowPc = lowPc.getUnsignedValue();
|
long rawLowPc = lowPc.getUnsignedValue();
|
||||||
long lowPcOffset = getProgram().getAddress(lowPc.getAttributeForm(), rawLowPc,
|
long lowPcOffset = getProgram().getAddress(lowPc.getAttributeForm(), rawLowPc,
|
||||||
getCompilationUnit());
|
getCompilationUnit());
|
||||||
long highPcOffset = lowPcOffset + 1;
|
long highPcOffset = lowPcOffset;
|
||||||
|
|
||||||
DWARFNumericAttribute highPc =
|
DWARFNumericAttribute highPc =
|
||||||
getAttribute(DW_AT_high_pc, DWARFNumericAttribute.class);
|
getAttribute(DW_AT_high_pc, DWARFNumericAttribute.class);
|
||||||
if (highPc != null) {
|
if (highPc != null) {
|
||||||
if (highPc.getAttributeForm() == DWARFForm.DW_FORM_addr) {
|
if (highPc.getAttributeForm() == DWARFForm.DW_FORM_addr) {
|
||||||
long baseAddrFixup = getProgram().getProgramBaseAddressFixup();
|
highPcOffset = highPc.getUnsignedValue();
|
||||||
highPcOffset = highPc.getUnsignedValue() + baseAddrFixup;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
highPcOffset = highPc.getUnsignedValue();
|
highPcOffset = highPc.getUnsignedValue();
|
||||||
if (highPcOffset != 0) {
|
highPcOffset = lowPcOffset + highPcOffset;
|
||||||
highPcOffset = lowPcOffset + highPcOffset;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new DWARFRange(lowPcOffset, highPcOffset);
|
return new DWARFRange(lowPcOffset, highPcOffset);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
// fall thru, return null
|
// fall thru, return empty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return DWARFRange.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+1
-3
@@ -15,8 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.dwarf;
|
package ghidra.app.util.bin.format.dwarf;
|
||||||
|
|
||||||
import static ghidra.app.util.bin.format.dwarf.DWARFTag.*;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -152,7 +150,7 @@ public class DWARFAbbreviation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getTagName() {
|
public String getTagName() {
|
||||||
return tag != DW_TAG_UNKNOWN ? tag.name() : "DW_TAG_??? %d".formatted(tagId);
|
return tag.name(tagId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+13
-47
@@ -22,6 +22,7 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
|
import ghidra.app.util.bin.format.dwarf.line.DWARFLine;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
@@ -170,7 +171,6 @@ public class DWARFCompilationUnit extends DWARFUnitHeader {
|
|||||||
* @param dwarfProgram {@link DWARFProgram}
|
* @param dwarfProgram {@link DWARFProgram}
|
||||||
* @param startOffset offset in provider where it starts
|
* @param startOffset offset in provider where it starts
|
||||||
* @param endOffset offset in provider where it ends
|
* @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 intSize 4 (DWARF_32) or 8 (DWARF_64)
|
||||||
* @param dwarfVersion 2-5
|
* @param dwarfVersion 2-5
|
||||||
* @param pointerSize default size of pointers
|
* @param pointerSize default size of pointers
|
||||||
@@ -179,9 +179,9 @@ public class DWARFCompilationUnit extends DWARFUnitHeader {
|
|||||||
* @param codeToAbbreviationMap map of abbreviation numbers to {@link DWARFAbbreviation} instances
|
* @param codeToAbbreviationMap map of abbreviation numbers to {@link DWARFAbbreviation} instances
|
||||||
*/
|
*/
|
||||||
public DWARFCompilationUnit(DWARFProgram dwarfProgram, long startOffset, long endOffset,
|
public DWARFCompilationUnit(DWARFProgram dwarfProgram, long startOffset, long endOffset,
|
||||||
long length, int intSize, short dwarfVersion, byte pointerSize, int unitNumber,
|
int intSize, short dwarfVersion, byte pointerSize, int unitNumber,
|
||||||
long firstDIEOffset, Map<Integer, DWARFAbbreviation> codeToAbbreviationMap) {
|
long firstDIEOffset, Map<Integer, DWARFAbbreviation> codeToAbbreviationMap) {
|
||||||
super(dwarfProgram, startOffset, endOffset, length, intSize, dwarfVersion, unitNumber);
|
super(dwarfProgram, startOffset, endOffset, intSize, dwarfVersion, unitNumber);
|
||||||
this.pointerSize = pointerSize;
|
this.pointerSize = pointerSize;
|
||||||
this.firstDIEOffset = firstDIEOffset;
|
this.firstDIEOffset = firstDIEOffset;
|
||||||
this.codeToAbbreviationMap =
|
this.codeToAbbreviationMap =
|
||||||
@@ -230,6 +230,10 @@ public class DWARFCompilationUnit extends DWARFUnitHeader {
|
|||||||
return firstDIEOffset;
|
return firstDIEOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DWARFLine getLine() {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the filename that produced the compile unit
|
* Get the filename that produced the compile unit
|
||||||
*
|
*
|
||||||
@@ -239,50 +243,6 @@ public class DWARFCompilationUnit extends DWARFUnitHeader {
|
|||||||
return diea.getString(DW_AT_name, null);
|
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
|
* Get the producer of the compile unit
|
||||||
* @return the producer of the compile unit
|
* @return the producer of the compile unit
|
||||||
@@ -330,6 +290,12 @@ public class DWARFCompilationUnit extends DWARFUnitHeader {
|
|||||||
return diea.getUnsignedLong(DW_AT_str_offsets_base, 0);
|
return diea.getUnsignedLong(DW_AT_str_offsets_base, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the range covered by this CU, as defined by the lo_pc and high_pc attribute values,
|
||||||
|
* defaulting to (0,0] if missing.
|
||||||
|
*
|
||||||
|
* @return {@link DWARFRange} that this CU covers, never null
|
||||||
|
*/
|
||||||
public DWARFRange getPCRange() {
|
public DWARFRange getPCRange() {
|
||||||
return diea.getPCRange();
|
return diea.getPCRange();
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-7
@@ -78,7 +78,7 @@ public class DWARFFunction {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
DWARFRangeList bodyRanges = getFuncBodyRanges(diea);
|
DWARFRangeList bodyRanges = getFuncBodyRanges(diea);
|
||||||
if (bodyRanges == null || bodyRanges.isEmpty()) {
|
if (bodyRanges.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,17 +330,17 @@ public class DWARFFunction {
|
|||||||
// TODO: dw_at_entry_pc is also sometimes available, typically in things like inlined_subroutines
|
// TODO: dw_at_entry_pc is also sometimes available, typically in things like inlined_subroutines
|
||||||
DWARFProgram dprog = diea.getProgram();
|
DWARFProgram dprog = diea.getProgram();
|
||||||
DWARFRangeList bodyRangeList = getFuncBodyRanges(diea);
|
DWARFRangeList bodyRangeList = getFuncBodyRanges(diea);
|
||||||
if (bodyRangeList != null && !bodyRangeList.isEmpty()) {
|
if (bodyRangeList.isEmpty()) {
|
||||||
DWARFRange bodyRange =
|
return null;
|
||||||
flattenDisjoint ? bodyRangeList.getFlattenedRange() : bodyRangeList.getFirst();
|
|
||||||
return dprog.getAddressRange(bodyRange, true);
|
|
||||||
}
|
}
|
||||||
return null;
|
DWARFRange bodyRange =
|
||||||
|
flattenDisjoint ? bodyRangeList.getFlattenedRange() : bodyRangeList.getFirst();
|
||||||
|
return dprog.getAddressRange(bodyRange, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DWARFRangeList getFuncBodyRanges(DIEAggregate diea) throws IOException {
|
public static DWARFRangeList getFuncBodyRanges(DIEAggregate diea) throws IOException {
|
||||||
DWARFRange body = diea.getPCRange();
|
DWARFRange body = diea.getPCRange();
|
||||||
if (body != null && !body.isEmpty()) {
|
if (!body.isEmpty()) {
|
||||||
return new DWARFRangeList(body);
|
return new DWARFRangeList(body);
|
||||||
}
|
}
|
||||||
if (diea.hasAttribute(DW_AT_ranges)) {
|
if (diea.hasAttribute(DW_AT_ranges)) {
|
||||||
|
|||||||
+22
-25
@@ -422,16 +422,16 @@ public class DWARFFunctionImporter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWARFRangeList blockRanges =
|
Address blockStart = null;
|
||||||
Objects.requireNonNullElse(DWARFFunction.getFuncBodyRanges(diea), DWARFRangeList.EMTPY);
|
DWARFRangeList blockRanges = DWARFFunction.getFuncBodyRanges(diea);
|
||||||
Address blockStart =
|
if (!blockRanges.isEmpty()) {
|
||||||
!blockRanges.isEmpty() ? prog.getCodeAddress(blockRanges.getFirst().getFrom()) : null;
|
blockStart = prog.getCodeAddress(blockRanges.getFirst().getFrom());
|
||||||
|
if (importOptions.isOutputLexicalBlockComments()) {
|
||||||
if (blockStart != null && importOptions.isOutputLexicalBlockComments()) {
|
boolean disjoint = blockRanges.getListCount() > 1;
|
||||||
boolean disjoint = blockRanges.getListCount() > 1;
|
DWARFName dni = prog.getName(diea);
|
||||||
DWARFName dni = prog.getName(diea);
|
appendComment(blockStart, CodeUnit.PRE_COMMENT,
|
||||||
appendComment(blockStart, CodeUnit.PRE_COMMENT,
|
"Begin: %s%s".formatted(dni.getName(), disjoint ? " - Disjoint" : ""), "\n");
|
||||||
"Begin: %s%s".formatted(dni.getName(), disjoint ? " - Disjoint" : ""), "\n");
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processFuncChildren(diea, dfunc,
|
processFuncChildren(diea, dfunc,
|
||||||
@@ -554,25 +554,22 @@ public class DWARFFunctionImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String name = prog.getEntryName(diea);
|
String name = prog.getEntryName(diea);
|
||||||
DWARFRange labelPc;
|
DWARFRange labelPc = diea.getPCRange();
|
||||||
if (name != null && (labelPc = diea.getPCRange()) != null) {
|
if (name != null && !labelPc.isEmpty() && labelPc.getFrom() != 0) {
|
||||||
Address address = prog.getCodeAddress(labelPc.getFrom());
|
Address address = prog.getCodeAddress(labelPc.getFrom());
|
||||||
if (address.getOffset() != 0) {
|
try {
|
||||||
try {
|
SymbolTable symbolTable = currentProgram.getSymbolTable();
|
||||||
SymbolTable symbolTable = currentProgram.getSymbolTable();
|
symbolTable.createLabel(address, name, currentProgram.getGlobalNamespace(),
|
||||||
symbolTable.createLabel(address, name, currentProgram.getGlobalNamespace(),
|
SourceType.IMPORTED);
|
||||||
SourceType.IMPORTED);
|
|
||||||
|
|
||||||
String locationInfo = DWARFSourceInfo.getDescriptionStr(diea);
|
String locationInfo = DWARFSourceInfo.getDescriptionStr(diea);
|
||||||
if (locationInfo != null) {
|
if (locationInfo != null) {
|
||||||
appendComment(address, CodeUnit.EOL_COMMENT, locationInfo, "; ");
|
appendComment(address, CodeUnit.EOL_COMMENT, locationInfo, "; ");
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (InvalidInputException e) {
|
|
||||||
Msg.error(this, "Problem creating label at " + address + " with name " + name,
|
|
||||||
e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (InvalidInputException e) {
|
||||||
|
Msg.error(this, "Problem creating label at " + address + " with name " + name, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
@@ -38,6 +38,11 @@ public class DWARFImportOptions {
|
|||||||
"Include source code location info (filename:linenumber) in comments attached to the " +
|
"Include source code location info (filename:linenumber) in comments attached to the " +
|
||||||
"Ghidra datatype or function or variable created.";
|
"Ghidra datatype or function or variable created.";
|
||||||
|
|
||||||
|
private static final String OPTION_SOURCE_LINEINFO = "Output Source Line Info";
|
||||||
|
private static final String OPTION_SOURCE_LINEINFO_DESC =
|
||||||
|
"Place end-of-line comments containg the source code filename and line number at " +
|
||||||
|
"each location provided in the DWARF data";
|
||||||
|
|
||||||
private static final String OPTION_OUTPUT_DWARF_DIE_INFO = "Output DWARF DIE Info";
|
private static final String OPTION_OUTPUT_DWARF_DIE_INFO = "Output DWARF DIE Info";
|
||||||
private static final String OPTION_OUTPUT_DWARF_DIE_INFO_DESC =
|
private static final String OPTION_OUTPUT_DWARF_DIE_INFO_DESC =
|
||||||
"Include DWARF DIE offset info in comments attached to the Ghidra datatype or function " +
|
"Include DWARF DIE offset info in comments attached to the Ghidra datatype or function " +
|
||||||
@@ -98,6 +103,7 @@ public class DWARFImportOptions {
|
|||||||
private boolean specialCaseSizedBaseTypes = true;
|
private boolean specialCaseSizedBaseTypes = true;
|
||||||
private boolean importLocalVariables = true;
|
private boolean importLocalVariables = true;
|
||||||
private boolean useBookmarks = true;
|
private boolean useBookmarks = true;
|
||||||
|
private boolean outputSourceLineInfo = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new instance
|
* Create new instance
|
||||||
@@ -360,6 +366,14 @@ public class DWARFImportOptions {
|
|||||||
return useBookmarks;
|
return useBookmarks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isOutputSourceLineInfo() {
|
||||||
|
return outputSourceLineInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputSourceLineInfo(boolean outputSourceLineInfo) {
|
||||||
|
this.outputSourceLineInfo = outputSourceLineInfo;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See {@link Analyzer#registerOptions(Options, ghidra.program.model.listing.Program)}
|
* See {@link Analyzer#registerOptions(Options, ghidra.program.model.listing.Program)}
|
||||||
*
|
*
|
||||||
@@ -392,6 +406,9 @@ public class DWARFImportOptions {
|
|||||||
|
|
||||||
options.registerOption(OPTION_IMPORT_LOCAL_VARS, isImportLocalVariables(), null,
|
options.registerOption(OPTION_IMPORT_LOCAL_VARS, isImportLocalVariables(), null,
|
||||||
OPTION_IMPORT_LOCAL_VARS_DESC);
|
OPTION_IMPORT_LOCAL_VARS_DESC);
|
||||||
|
|
||||||
|
options.registerOption(OPTION_SOURCE_LINEINFO, isOutputSourceLineInfo(), null,
|
||||||
|
OPTION_SOURCE_LINEINFO_DESC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -414,5 +431,8 @@ public class DWARFImportOptions {
|
|||||||
setTryPackDataTypes(options.getBoolean(OPTION_TRY_PACK_STRUCTS, isTryPackStructs()));
|
setTryPackDataTypes(options.getBoolean(OPTION_TRY_PACK_STRUCTS, isTryPackStructs()));
|
||||||
setImportLocalVariables(
|
setImportLocalVariables(
|
||||||
options.getBoolean(OPTION_IMPORT_LOCAL_VARS, isImportLocalVariables()));
|
options.getBoolean(OPTION_IMPORT_LOCAL_VARS, isImportLocalVariables()));
|
||||||
|
setOutputSourceLineInfo(
|
||||||
|
options.getBoolean(OPTION_SOURCE_LINEINFO, isOutputSourceLineInfo()));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
@@ -20,7 +20,11 @@ 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.BinaryReader;
|
||||||
|
import ghidra.app.util.bin.format.dwarf.line.DWARFLine.SourceFileAddr;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
|
import ghidra.program.model.listing.CodeUnit;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.Swing;
|
import ghidra.util.Swing;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
@@ -170,6 +174,30 @@ public class DWARFImporter {
|
|||||||
return new CategoryPath(newRoot, cpParts.subList(origRootParts.size(), cpParts.size()));
|
return new CategoryPath(newRoot, cpParts.subList(origRootParts.size(), cpParts.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addSourceLineInfo(BinaryReader reader) throws CancelledException, IOException {
|
||||||
|
if ( reader == null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
monitor.initialize(reader.length(), "DWARF Source Line Info");
|
||||||
|
List<DWARFCompilationUnit> compUnits = prog.getCompilationUnits();
|
||||||
|
for (DWARFCompilationUnit cu : compUnits) {
|
||||||
|
try {
|
||||||
|
monitor.checkCancelled();
|
||||||
|
monitor.setProgress(cu.getLine().getStartOffset());
|
||||||
|
List<SourceFileAddr> allSFA = cu.getLine().getAllSourceFileAddrInfo(cu, reader);
|
||||||
|
for (SourceFileAddr sfa : allSFA) {
|
||||||
|
Address addr = prog.getCodeAddress(sfa.address());
|
||||||
|
DWARFUtil.appendComment(prog.getGhidraProgram(), addr, CodeUnit.EOL_COMMENT, "",
|
||||||
|
"%s:%d".formatted(sfa.fileName(), sfa.lineNum()), ";");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
Msg.error(this,
|
||||||
|
"Failed to read DWARF line info for cu %d".formatted(cu.getUnitNumber()), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports DWARF information according to the {@link DWARFImportOptions} set.
|
* Imports DWARF information according to the {@link DWARFImportOptions} set.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -205,6 +233,10 @@ public class DWARFImporter {
|
|||||||
moveTypesIntoSourceFolders();
|
moveTypesIntoSourceFolders();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (importOptions.isOutputSourceLineInfo()) {
|
||||||
|
addSourceLineInfo(prog.getDebugLineBR());
|
||||||
|
}
|
||||||
|
|
||||||
importSummary.totalElapsedMS = System.currentTimeMillis() - start_ts;
|
importSummary.totalElapsedMS = System.currentTimeMillis() - start_ts;
|
||||||
|
|
||||||
return importSummary;
|
return importSummary;
|
||||||
|
|||||||
@@ -1,424 +0,0 @@
|
|||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package ghidra.app.util.bin.format.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+25
-31
@@ -58,43 +58,35 @@ public class DWARFLocationList {
|
|||||||
List<DWARFLocation> results = new ArrayList<>();
|
List<DWARFLocation> results = new ArrayList<>();
|
||||||
|
|
||||||
byte pointerSize = cu.getPointerSize();
|
byte pointerSize = cu.getPointerSize();
|
||||||
|
long baseAddress = cu.getPCRange().getFrom();
|
||||||
|
long maxAddrVal = pointerSize == 4 ? NumericUtilities.MAX_UNSIGNED_INT32_AS_LONG : -1;
|
||||||
|
|
||||||
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()) {
|
while (reader.hasNext()) {
|
||||||
|
// Read the beginning and ending addresses
|
||||||
long beginning = reader.readNextUnsignedValue(pointerSize);
|
long beginning = reader.readNextUnsignedValue(pointerSize);
|
||||||
long ending = reader.readNextUnsignedValue(pointerSize);
|
long ending = reader.readNextUnsignedValue(pointerSize); // dwarf end addrs are exclusive
|
||||||
|
|
||||||
|
// End of the list
|
||||||
if (beginning == 0 && ending == 0) {
|
if (beginning == 0 && ending == 0) {
|
||||||
// List end
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (beginning == ending) {
|
|
||||||
// don't add empty range
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check to see if this is a base address entry
|
// Check to see if this is a base address entry
|
||||||
if (beginning == eolVal) {
|
if (beginning == maxAddrVal) {
|
||||||
baseAddrOffset = ending + baseFixup;
|
baseAddress = ending;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
beginning += baseAddrOffset;
|
|
||||||
ending += baseAddrOffset;
|
|
||||||
|
|
||||||
// byte array size is 2 bytes
|
int size = reader.readNextUnsignedShort();
|
||||||
int size = reader.readNextUnsignedShort();
|
byte[] expr = reader.readNextByteArray(size);
|
||||||
|
|
||||||
// Read the exprloc bytes
|
if (beginning == ending) {
|
||||||
byte[] expr = reader.readNextByteArray(size);
|
// skip adding empty ranges because Ghidra can't use them
|
||||||
|
continue;
|
||||||
// TODO: verify end addr calc with DWARFstd.pdf, inclusive vs exclusive
|
|
||||||
results.add(new DWARFLocation(new DWARFRange(beginning, ending), expr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DWARFRange range = new DWARFRange(baseAddress + beginning, baseAddress + ending);
|
||||||
|
results.add(new DWARFLocation(range, expr));
|
||||||
}
|
}
|
||||||
return new DWARFLocationList(results);
|
return new DWARFLocationList(results);
|
||||||
}
|
}
|
||||||
@@ -109,8 +101,7 @@ public class DWARFLocationList {
|
|||||||
*/
|
*/
|
||||||
public static DWARFLocationList readV5(BinaryReader reader, DWARFCompilationUnit cu)
|
public static DWARFLocationList readV5(BinaryReader reader, DWARFCompilationUnit cu)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
long baseAddrFixup = cu.getProgram().getProgramBaseAddressFixup();
|
long baseAddr = cu.getPCRange().getFrom();
|
||||||
long baseAddr = baseAddrFixup;
|
|
||||||
DWARFProgram dprog = cu.getProgram();
|
DWARFProgram dprog = cu.getProgram();
|
||||||
|
|
||||||
List<DWARFLocation> list = new ArrayList<>();
|
List<DWARFLocation> list = new ArrayList<>();
|
||||||
@@ -149,24 +140,27 @@ public class DWARFLocationList {
|
|||||||
list.add(new DWARFLocation(baseAddr + startOfs, baseAddr + endOfs, expr));
|
list.add(new DWARFLocation(baseAddr + startOfs, baseAddr + endOfs, expr));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case DW_LLE_default_location: {
|
||||||
|
byte[] expr = reader.readNext(DWARFLocationList::uleb128SizedByteArray);
|
||||||
|
list.add(new DWARFLocation(DWARFRange.EMPTY, expr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
case DW_LLE_base_address: {
|
case DW_LLE_base_address: {
|
||||||
baseAddr = reader.readNextUnsignedValue(cu.getPointerSize()) + baseAddrFixup;
|
baseAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DW_LLE_start_end: {
|
case DW_LLE_start_end: {
|
||||||
long startAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
long startAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
||||||
long endAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
long endAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
||||||
byte[] expr = reader.readNext(DWARFLocationList::uleb128SizedByteArray);
|
byte[] expr = reader.readNext(DWARFLocationList::uleb128SizedByteArray);
|
||||||
list.add(new DWARFLocation(startAddr + baseAddrFixup, endAddr + baseAddrFixup,
|
list.add(new DWARFLocation(startAddr, endAddr, expr));
|
||||||
expr));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DW_LLE_start_length: {
|
case DW_LLE_start_length: {
|
||||||
long startAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
long startAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
||||||
int len = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
|
int len = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
|
||||||
byte[] expr = reader.readNext(DWARFLocationList::uleb128SizedByteArray);
|
byte[] expr = reader.readNext(DWARFLocationList::uleb128SizedByteArray);
|
||||||
list.add(new DWARFLocation(startAddr + baseAddrFixup,
|
list.add(new DWARFLocation(startAddr, startAddr + len, expr));
|
||||||
startAddr + baseAddrFixup + len, expr));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
+25
-27
@@ -30,6 +30,7 @@ import ghidra.app.util.bin.format.dwarf.attribs.*;
|
|||||||
import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException;
|
import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException;
|
||||||
import ghidra.app.util.bin.format.dwarf.external.ExternalDebugInfo;
|
import ghidra.app.util.bin.format.dwarf.external.ExternalDebugInfo;
|
||||||
import ghidra.app.util.bin.format.dwarf.funcfixup.DWARFFunctionFixup;
|
import ghidra.app.util.bin.format.dwarf.funcfixup.DWARFFunctionFixup;
|
||||||
|
import ghidra.app.util.bin.format.dwarf.line.DWARFLine;
|
||||||
import ghidra.app.util.bin.format.dwarf.sectionprovider.*;
|
import ghidra.app.util.bin.format.dwarf.sectionprovider.*;
|
||||||
import ghidra.app.util.bin.format.golang.rtti.GoSymbolName;
|
import ghidra.app.util.bin.format.golang.rtti.GoSymbolName;
|
||||||
import ghidra.app.util.opinion.*;
|
import ghidra.app.util.opinion.*;
|
||||||
@@ -533,6 +534,10 @@ public class DWARFProgram implements Closeable {
|
|||||||
return dwarfDTM;
|
return dwarfDTM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<DWARFCompilationUnit> getCompilationUnits() {
|
||||||
|
return compUnits;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isBigEndian() {
|
public boolean isBigEndian() {
|
||||||
return program.getLanguage().isBigEndian();
|
return program.getLanguage().isBigEndian();
|
||||||
}
|
}
|
||||||
@@ -541,6 +546,10 @@ public class DWARFProgram implements Closeable {
|
|||||||
return !program.getLanguage().isBigEndian();
|
return !program.getLanguage().isBigEndian();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BinaryReader getDebugLineBR() {
|
||||||
|
return debugLineBR;
|
||||||
|
}
|
||||||
|
|
||||||
private BinaryReader getBinaryReaderFor(String sectionName, TaskMonitor monitor)
|
private BinaryReader getBinaryReaderFor(String sectionName, TaskMonitor monitor)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
ByteProvider bp = sectionProvider.getSectionAsByteProvider(sectionName, monitor);
|
ByteProvider bp = sectionProvider.getSectionAsByteProvider(sectionName, monitor);
|
||||||
@@ -1137,7 +1146,7 @@ public class DWARFProgram implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an address value, corrected for any Ghidra load offset shenanigans.
|
* Returns an address value.
|
||||||
*
|
*
|
||||||
* @param form the format of the numeric value
|
* @param form the format of the numeric value
|
||||||
* @param value raw offset or indirect address index (depending on the DWARFForm)
|
* @param value raw offset or indirect address index (depending on the DWARFForm)
|
||||||
@@ -1149,14 +1158,14 @@ public class DWARFProgram implements Closeable {
|
|||||||
switch (form) {
|
switch (form) {
|
||||||
case DW_FORM_addr:
|
case DW_FORM_addr:
|
||||||
case DW_FORM_udata:
|
case DW_FORM_udata:
|
||||||
return value + programBaseAddressFixup;
|
return value;
|
||||||
case DW_FORM_addrx:
|
case DW_FORM_addrx:
|
||||||
case DW_FORM_addrx1:
|
case DW_FORM_addrx1:
|
||||||
case DW_FORM_addrx2:
|
case DW_FORM_addrx2:
|
||||||
case DW_FORM_addrx3:
|
case DW_FORM_addrx3:
|
||||||
case DW_FORM_addrx4: {
|
case DW_FORM_addrx4: {
|
||||||
long addr = addressListTable.getOffset((int) value, cu);
|
long addr = addressListTable.getOffset((int) value, cu);
|
||||||
return addr + programBaseAddressFixup;
|
return addr;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new IOException("Unsupported form %s".formatted(form));
|
throw new IOException("Unsupported form %s".formatted(form));
|
||||||
@@ -1221,31 +1230,18 @@ public class DWARFProgram implements Closeable {
|
|||||||
*
|
*
|
||||||
* @param diea {@link DIEAggregate}
|
* @param diea {@link DIEAggregate}
|
||||||
* @param attribute attribute id that points to the line info
|
* @param attribute attribute id that points to the line info
|
||||||
* @return {@link DWARFLine}, or null if attribute
|
* @return {@link DWARFLine}, never null, see {@link DWARFLine#empty()}
|
||||||
* @throws IOException if error reading line data
|
* @throws IOException if error reading line data
|
||||||
*/
|
*/
|
||||||
public DWARFLine getLine(DIEAggregate diea, DWARFAttribute attribute) throws IOException {
|
public DWARFLine getLine(DIEAggregate diea, DWARFAttribute attribute) throws IOException {
|
||||||
DWARFNumericAttribute attrib = diea.getAttribute(attribute, DWARFNumericAttribute.class);
|
DWARFNumericAttribute attrib = diea.getAttribute(attribute, DWARFNumericAttribute.class);
|
||||||
if (attrib == null || debugLineBR == null) {
|
if (attrib == null || debugLineBR == null) {
|
||||||
return null;
|
return DWARFLine.empty();
|
||||||
}
|
}
|
||||||
long stmtListOffset = attrib.getUnsignedValue();
|
long stmtListOffset = attrib.getUnsignedValue();
|
||||||
debugLineBR.setPointerIndex(stmtListOffset);
|
DWARFLine result = DWARFLine.read(debugLineBR.clone(stmtListOffset), getDefaultIntSize(),
|
||||||
|
diea.getCompilationUnit());
|
||||||
// probe for the DWARFLine version number
|
return result;
|
||||||
// length : dwarf_length
|
|
||||||
// version : 2 bytes
|
|
||||||
DWARFLengthValue lengthInfo = DWARFLengthValue.read(debugLineBR, getDefaultIntSize());
|
|
||||||
if (lengthInfo == null) {
|
|
||||||
throw new DWARFException("Invalid DWARFLine length at 0x%x".formatted(stmtListOffset));
|
|
||||||
}
|
|
||||||
|
|
||||||
int version = debugLineBR.readNextUnsignedShort();
|
|
||||||
|
|
||||||
return version < 5
|
|
||||||
? DWARFLine.readV4(this, debugLineBR, lengthInfo, version)
|
|
||||||
: DWARFLine.readV5(this, debugLineBR, lengthInfo, version,
|
|
||||||
diea.getCompilationUnit());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1337,21 +1333,23 @@ public class DWARFProgram implements Closeable {
|
|||||||
|
|
||||||
public AddressRange getAddressRange(DWARFRange range, boolean isCode) {
|
public AddressRange getAddressRange(DWARFRange range, boolean isCode) {
|
||||||
AddressSpace defAS = program.getAddressFactory().getDefaultAddressSpace();
|
AddressSpace defAS = program.getAddressFactory().getDefaultAddressSpace();
|
||||||
Address start = defAS.getAddress(range.getFrom(), true /* TODO check this */);
|
Address start =
|
||||||
Address end = defAS.getAddress(range.getTo() - 1, true /* TODO check this */);
|
defAS.getAddress(range.getFrom() + programBaseAddressFixup, true /* TODO check this */);
|
||||||
|
Address end = defAS.getAddress(range.getTo() - 1 + programBaseAddressFixup,
|
||||||
|
true /* TODO check this */);
|
||||||
return new AddressRangeImpl(start, end);
|
return new AddressRangeImpl(start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Address getCodeAddress(Number offset) {
|
public Address getCodeAddress(long offset) {
|
||||||
return program.getAddressFactory()
|
return program.getAddressFactory()
|
||||||
.getDefaultAddressSpace()
|
.getDefaultAddressSpace()
|
||||||
.getAddress(offset.longValue(), true);
|
.getAddress(offset + programBaseAddressFixup, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Address getDataAddress(Number offset) {
|
public Address getDataAddress(long offset) {
|
||||||
return program.getAddressFactory()
|
return program.getAddressFactory()
|
||||||
.getDefaultAddressSpace()
|
.getDefaultAddressSpace()
|
||||||
.getAddress(offset.longValue(), true);
|
.getAddress(offset + programBaseAddressFixup, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean stackGrowsNegative() {
|
public boolean stackGrowsNegative() {
|
||||||
|
|||||||
+7
-10
@@ -45,8 +45,8 @@ public class DWARFRangeList {
|
|||||||
byte pointerSize = cu.getPointerSize();
|
byte pointerSize = cu.getPointerSize();
|
||||||
List<DWARFRange> ranges = new ArrayList<>();
|
List<DWARFRange> ranges = new ArrayList<>();
|
||||||
|
|
||||||
DWARFRange cuRange = cu.getPCRange();
|
long baseAddress = cu.getPCRange().getFrom();
|
||||||
long baseAddress = cuRange != null ? cuRange.getFrom() : 0;
|
long maxAddrVal = pointerSize == 4 ? NumericUtilities.MAX_UNSIGNED_INT32_AS_LONG : -1;
|
||||||
|
|
||||||
while (reader.hasNext()) {
|
while (reader.hasNext()) {
|
||||||
// Read the beginning and ending addresses
|
// Read the beginning and ending addresses
|
||||||
@@ -59,8 +59,7 @@ public class DWARFRangeList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check to see if this is a base address entry
|
// Check to see if this is a base address entry
|
||||||
if (beginning == -1 ||
|
if (beginning == maxAddrVal) {
|
||||||
(pointerSize == 4 && beginning == NumericUtilities.MAX_UNSIGNED_INT32_AS_LONG)) {
|
|
||||||
baseAddress = ending;
|
baseAddress = ending;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -85,8 +84,7 @@ public class DWARFRangeList {
|
|||||||
List<DWARFRange> list = new ArrayList<>();
|
List<DWARFRange> list = new ArrayList<>();
|
||||||
|
|
||||||
DWARFProgram dprog = cu.getProgram();
|
DWARFProgram dprog = cu.getProgram();
|
||||||
long baseAddrFixup = dprog.getProgramBaseAddressFixup();
|
long baseAddr = cu.getPCRange().getFrom();
|
||||||
long baseAddr = baseAddrFixup;
|
|
||||||
|
|
||||||
while (reader.hasNext()) {
|
while (reader.hasNext()) {
|
||||||
int rleId = reader.readNextUnsignedByte();
|
int rleId = reader.readNextUnsignedByte();
|
||||||
@@ -121,20 +119,19 @@ public class DWARFRangeList {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DW_RLE_base_address: {
|
case DW_RLE_base_address: {
|
||||||
baseAddr = reader.readNextUnsignedValue(cu.getPointerSize()) + baseAddrFixup;
|
baseAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DW_RLE_start_end: {
|
case DW_RLE_start_end: {
|
||||||
long startAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
long startAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
||||||
long endAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
long endAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
||||||
list.add(new DWARFRange(startAddr + baseAddrFixup, endAddr + baseAddrFixup));
|
list.add(new DWARFRange(startAddr, endAddr));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DW_RLE_start_length: {
|
case DW_RLE_start_length: {
|
||||||
long startAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
long startAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
||||||
int len = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
|
int len = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
|
||||||
list.add(
|
list.add(new DWARFRange(startAddr, startAddr + len));
|
||||||
new DWARFRange(startAddr + baseAddrFixup, startAddr + baseAddrFixup + len));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
+8
-26
@@ -29,7 +29,7 @@ public class DWARFUnitHeader {
|
|||||||
* Reads the initial fields found in a unit header.
|
* Reads the initial fields found in a unit header.
|
||||||
*
|
*
|
||||||
* @param dprog {@link DWARFProgram}
|
* @param dprog {@link DWARFProgram}
|
||||||
* @param reader {@link BinaryReader} .debug_info stream
|
* @param reader {@link BinaryReader} stream
|
||||||
* @param abbrReader {@link BinaryReader} .debug_abbr stream
|
* @param abbrReader {@link BinaryReader} .debug_abbr stream
|
||||||
* @param unitNumber ordinal of this item
|
* @param unitNumber ordinal of this item
|
||||||
* @param monitor {@link TaskMonitor}
|
* @param monitor {@link TaskMonitor}
|
||||||
@@ -58,7 +58,7 @@ public class DWARFUnitHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DWARFUnitHeader partial = new DWARFUnitHeader(dprog, startOffset, endOffset,
|
DWARFUnitHeader partial = new DWARFUnitHeader(dprog, startOffset, endOffset,
|
||||||
lengthInfo.length(), lengthInfo.intSize(), version, unitNumber);
|
lengthInfo.intSize(), version, unitNumber);
|
||||||
|
|
||||||
if (2 <= version && version <= 4) {
|
if (2 <= version && version <= 4) {
|
||||||
return DWARFCompilationUnit.readV4(partial, reader, abbrReader, monitor);
|
return DWARFCompilationUnit.readV4(partial, reader, abbrReader, monitor);
|
||||||
@@ -84,28 +84,23 @@ public class DWARFUnitHeader {
|
|||||||
protected final DWARFProgram dprog;
|
protected final DWARFProgram dprog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Offset in the debug_info section of this compUnit's header
|
* Offset in the section of this header
|
||||||
*/
|
*/
|
||||||
protected final long startOffset;
|
protected final long startOffset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Offset in the debug_info section of the end of this compUnit. (right after
|
* Offset in the section of the end of this header. (exclusive)
|
||||||
* the last DIE record)
|
|
||||||
*/
|
*/
|
||||||
protected final long endOffset;
|
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
|
* size of integers, 4=int32 or 8=int64
|
||||||
*/
|
*/
|
||||||
protected final int intSize;
|
protected final int intSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DWARF ver number, as read from the compunit structure, currently not used but being kept.
|
* Version number, as read from the header. Note: Some header types use version numbers that do
|
||||||
|
* not match the general dwarfVersion.
|
||||||
*/
|
*/
|
||||||
protected final short dwarfVersion;
|
protected final short dwarfVersion;
|
||||||
|
|
||||||
@@ -118,18 +113,16 @@ public class DWARFUnitHeader {
|
|||||||
this.dprog = other.dprog;
|
this.dprog = other.dprog;
|
||||||
this.startOffset = other.startOffset;
|
this.startOffset = other.startOffset;
|
||||||
this.endOffset = other.endOffset;
|
this.endOffset = other.endOffset;
|
||||||
this.length = other.length;
|
|
||||||
this.intSize = other.intSize;
|
this.intSize = other.intSize;
|
||||||
this.dwarfVersion = other.dwarfVersion;
|
this.dwarfVersion = other.dwarfVersion;
|
||||||
this.unitNumber = other.unitNumber;
|
this.unitNumber = other.unitNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DWARFUnitHeader(DWARFProgram dprog, long startOffset, long endOffset, long length,
|
protected DWARFUnitHeader(DWARFProgram dprog, long startOffset, long endOffset, int intSize,
|
||||||
int intSize, short version, int unitNumber) {
|
short version, int unitNumber) {
|
||||||
this.dprog = dprog;
|
this.dprog = dprog;
|
||||||
this.startOffset = startOffset;
|
this.startOffset = startOffset;
|
||||||
this.endOffset = endOffset;
|
this.endOffset = endOffset;
|
||||||
this.length = length;
|
|
||||||
this.intSize = intSize;
|
this.intSize = intSize;
|
||||||
this.dwarfVersion = version;
|
this.dwarfVersion = version;
|
||||||
this.unitNumber = unitNumber;
|
this.unitNumber = unitNumber;
|
||||||
@@ -143,17 +136,6 @@ public class DWARFUnitHeader {
|
|||||||
return dwarfVersion;
|
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.
|
* Returns the byte offset to the start of this unit.
|
||||||
* @return the byte offset to the start of this unit
|
* @return the byte offset to the start of this unit
|
||||||
|
|||||||
+2
-2
@@ -322,7 +322,7 @@ public class DWARFVariable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setRamStorage(res + prog.getProgramBaseAddressFixup());
|
setRamStorage(res);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (DWARFExpressionException | UnsupportedOperationException
|
catch (DWARFExpressionException | UnsupportedOperationException
|
||||||
@@ -406,7 +406,7 @@ public class DWARFVariable {
|
|||||||
}
|
}
|
||||||
else if (exprEvaluator.getRawLastRegister() == -1 && res != 0) {
|
else if (exprEvaluator.getRawLastRegister() == -1 && res != 0) {
|
||||||
// static global variable location
|
// static global variable location
|
||||||
setRamStorage(res + prog.getProgramBaseAddressFixup());
|
setRamStorage(res);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Msg.error(this,
|
Msg.error(this,
|
||||||
|
|||||||
-98
@@ -1,98 +0,0 @@
|
|||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package ghidra.app.util.bin.format.dwarf;
|
|
||||||
|
|
||||||
import ghidra.app.plugin.core.analysis.DwarfLineNumberAnalyzer;
|
|
||||||
import ghidra.app.util.opinion.ElfLoader;
|
|
||||||
import ghidra.app.util.opinion.MachoLoader;
|
|
||||||
import ghidra.program.model.listing.Program;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Section name logic for the obsolete {@link DwarfLineNumberAnalyzer}
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true)
|
|
||||||
public final class DwarfSectionNames {
|
|
||||||
private final static String MACHO_PREFIX = "__";
|
|
||||||
private final static String ELF_PREFIX = ".";
|
|
||||||
|
|
||||||
private String prefix = "";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new Dwarf Section Names for the specific program.
|
|
||||||
* @param program the program containing dwarf debug information.
|
|
||||||
* @throws IllegalArgumentException if the program's format is not handled.
|
|
||||||
*/
|
|
||||||
public DwarfSectionNames(Program program) {
|
|
||||||
if (MachoLoader.MACH_O_NAME.equals(program.getExecutableFormat())) {
|
|
||||||
prefix = MACHO_PREFIX;
|
|
||||||
}
|
|
||||||
else if (ElfLoader.ELF_NAME.equals(program.getExecutableFormat())) {
|
|
||||||
prefix = ELF_PREFIX;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalArgumentException("Unrecognized program format: "+program.getExecutableFormat());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds tag, attribute names, and attribute forms encodings
|
|
||||||
*/
|
|
||||||
public String SECTION_NAME_ABBREV() { return prefix+"debug_abbrev"; }
|
|
||||||
/**
|
|
||||||
* A mapping between memory address and compilation
|
|
||||||
*/
|
|
||||||
public String SECTION_NAME_ARANGES() { return prefix+"debug_aranges"; }
|
|
||||||
/**
|
|
||||||
* Holds information about call frame activations
|
|
||||||
*/
|
|
||||||
public String SECTION_NAME_FRAME() { return prefix+"debug_frame"; }
|
|
||||||
/**
|
|
||||||
* Debugging information entries for DWARF v2
|
|
||||||
*/
|
|
||||||
public String SECTION_NAME_INFO() { return prefix+"debug_info"; }
|
|
||||||
/**
|
|
||||||
* Line Number Program
|
|
||||||
*/
|
|
||||||
public String SECTION_NAME_LINE() { return prefix+"debug_line"; }
|
|
||||||
/**
|
|
||||||
* Location lists are used in place of location expressions whenever the object whose location is
|
|
||||||
* being described can change location during its lifetime. Location lists are contained in a separate
|
|
||||||
* object file section called .debug_loc. A location list is indicated by a location attribute
|
|
||||||
* whose value is represented as a constant offset from the beginning of the .debug_loc section
|
|
||||||
* to the first byte of the list for the object in question.
|
|
||||||
*/
|
|
||||||
public String SECTION_NAME_LOC() { return prefix+"debug_loc"; }
|
|
||||||
/**
|
|
||||||
* A lookup table for global objects and functions
|
|
||||||
*/
|
|
||||||
public String SECTION_NAME_MACINFO() { return prefix+"debug_macinfo"; }
|
|
||||||
/**
|
|
||||||
* A lookup table for global objects and functions
|
|
||||||
*/
|
|
||||||
public String SECTION_NAME_PUBNAMES() { return prefix+"debug_pubnames"; }
|
|
||||||
/**
|
|
||||||
* A lookup table for global types
|
|
||||||
*/
|
|
||||||
public String SECTION_NAME_PUBTYPES() { return prefix+"debug_pubtypes"; }
|
|
||||||
/**
|
|
||||||
* Address ranges referenced by DIEs
|
|
||||||
*/
|
|
||||||
public String SECTION_NAME_RANGES() { return prefix+"debug_ranges"; }
|
|
||||||
/**
|
|
||||||
* String table used by .debug_info
|
|
||||||
*/
|
|
||||||
public String SECTION_NAME_STR() { return prefix+"debug_str"; }
|
|
||||||
}
|
|
||||||
+2
-2
@@ -39,7 +39,7 @@ public enum DWARFAttribute {
|
|||||||
DW_AT_bit_offset(0xc), // dwarf-3
|
DW_AT_bit_offset(0xc), // dwarf-3
|
||||||
DW_AT_bit_size(0xd, constant, exprloc, reference),
|
DW_AT_bit_size(0xd, constant, exprloc, reference),
|
||||||
//DW_AT_element_list(0xf),
|
//DW_AT_element_list(0xf),
|
||||||
DW_AT_stmt_list(0x10, lineptr),
|
DW_AT_stmt_list(0x10, lineptr, constant),
|
||||||
DW_AT_low_pc(0x11, address),
|
DW_AT_low_pc(0x11, address),
|
||||||
DW_AT_high_pc(0x12, address, constant),
|
DW_AT_high_pc(0x12, address, constant),
|
||||||
DW_AT_language(0x13, constant),
|
DW_AT_language(0x13, constant),
|
||||||
@@ -252,7 +252,7 @@ public enum DWARFAttribute {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getRawAttributeIdDescription() {
|
protected String getRawAttributeIdDescription() {
|
||||||
return "DW_AT_???? %d (0x%x)".formatted(attributeId, attributeId);
|
return "DW_AT_???? %d (0x%x)".formatted(rawAttributeId, rawAttributeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+157
@@ -0,0 +1,157 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.line;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
|
import ghidra.app.util.bin.format.dwarf.DWARFCompilationUnit;
|
||||||
|
import ghidra.app.util.bin.format.dwarf.attribs.*;
|
||||||
|
import ghidra.program.model.data.LEB128;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DWARFFile is used to store file or directory entries in the DWARFLine.
|
||||||
|
*/
|
||||||
|
public 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int directory_index = reader.readNextUnsignedVarIntExact(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;
|
||||||
|
int 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.getUnsignedIntExact()
|
||||||
|
: -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 final String name;
|
||||||
|
private final int directory_index;
|
||||||
|
private final long modification_time;
|
||||||
|
private final long length;
|
||||||
|
private final 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, int 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 DWARFFile withName(String newName) {
|
||||||
|
return new DWARFFile(newName, directory_index, modification_time, length, md5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDirectoryIndex() {
|
||||||
|
return this.directory_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getModificationTime() {
|
||||||
|
return this.modification_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getMD5() {
|
||||||
|
return md5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Filename: %s, Length: 0x%x, Time: 0x%x, DirIndex: %d".formatted(name, length,
|
||||||
|
modification_time, directory_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
+354
@@ -0,0 +1,354 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.line;
|
||||||
|
|
||||||
|
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.*;
|
||||||
|
import ghidra.app.util.bin.format.dwarf.line.DWARFLineContentType.Def;
|
||||||
|
import ghidra.formats.gfilesystem.FSUtilities;
|
||||||
|
import ghidra.program.model.data.LEB128;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A structure read from .debug_line, contains indexed source filenames as well as a mapping between
|
||||||
|
* addresses and source filename and linenumbers.
|
||||||
|
* <p>
|
||||||
|
* TODO: refactor this and other similar classes to derive from DWARFUnitHeader and simplify
|
||||||
|
*/
|
||||||
|
public class DWARFLine {
|
||||||
|
/**
|
||||||
|
* Returns a dummy DWARFLine instance that contains no information.
|
||||||
|
*
|
||||||
|
* @return {@link DWARFLine} instance with no info
|
||||||
|
*/
|
||||||
|
public static DWARFLine empty() {
|
||||||
|
return new DWARFLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DWARFLine read(BinaryReader reader, int defaultIntSize, DWARFCompilationUnit cu)
|
||||||
|
throws IOException {
|
||||||
|
// probe for the DWARFLine version number
|
||||||
|
// length : dwarf_length
|
||||||
|
// version : 2 bytes
|
||||||
|
DWARFLine result = new DWARFLine();
|
||||||
|
result.dprog = cu.getProgram();
|
||||||
|
result.startOffset = reader.getPointerIndex();
|
||||||
|
DWARFLengthValue lengthInfo = DWARFLengthValue.read(reader, defaultIntSize);
|
||||||
|
if (lengthInfo == null) {
|
||||||
|
throw new DWARFException(
|
||||||
|
"Invalid DWARFLine length at 0x%x".formatted(result.startOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
result.length = lengthInfo.length();
|
||||||
|
result.intSize = lengthInfo.intSize();
|
||||||
|
result.endOffset = reader.getPointerIndex() + lengthInfo.length();
|
||||||
|
|
||||||
|
result.dwarfVersion = reader.readNextUnsignedShort();
|
||||||
|
if (result.dwarfVersion < 5) {
|
||||||
|
DWARFLine.readV4(result, reader, cu);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DWARFLine.readV5(result, reader, cu);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void readV4(DWARFLine result, BinaryReader reader, DWARFCompilationUnit cu)
|
||||||
|
throws IOException, DWARFException {
|
||||||
|
|
||||||
|
// length : dwarf_length (already)
|
||||||
|
// version : 2 bytes (already)
|
||||||
|
// header_len : dwarf_intsize
|
||||||
|
// min_instr_len : 1 byte
|
||||||
|
// ....
|
||||||
|
long header_length = reader.readNextUnsignedValue(result.intSize);
|
||||||
|
result.opcodes_start = reader.getPointerIndex() + header_length;
|
||||||
|
|
||||||
|
result.minimum_instruction_length = reader.readNextUnsignedByte();
|
||||||
|
|
||||||
|
if (result.dwarfVersion >= 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() != 0;
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the cu's compDir as element 0 of the dir table
|
||||||
|
String defaultCompDir = cu.getCompileDirectory();
|
||||||
|
if (defaultCompDir == null || defaultCompDir.isBlank()) {
|
||||||
|
defaultCompDir = "";
|
||||||
|
}
|
||||||
|
result.directories.add(new DWARFFile(defaultCompDir));
|
||||||
|
|
||||||
|
// Read all include directories, which are only a list of names in v4
|
||||||
|
String dirName = reader.readNextAsciiString();
|
||||||
|
while (dirName.length() != 0) {
|
||||||
|
DWARFFile dir = new DWARFFile(dirName);
|
||||||
|
dir = fixupDir(dir, defaultCompDir);
|
||||||
|
|
||||||
|
result.directories.add(dir);
|
||||||
|
dirName = reader.readNextAsciiString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read all files, ending when null (hit empty filename)
|
||||||
|
DWARFFile file;
|
||||||
|
while ((file = DWARFFile.readV4(reader)) != null) {
|
||||||
|
result.files.add(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void readV5(DWARFLine result, BinaryReader reader, DWARFCompilationUnit cu)
|
||||||
|
throws IOException, DWARFException {
|
||||||
|
|
||||||
|
// length : dwarf_length (already)
|
||||||
|
// version : 2 bytes (already)
|
||||||
|
// address_size : 1 byte
|
||||||
|
// segment_selector_size : 1 byte
|
||||||
|
// header_len : dwarf_intsize
|
||||||
|
// min_instr_len : 1 byte
|
||||||
|
// ...
|
||||||
|
result.address_size = reader.readNextUnsignedByte();
|
||||||
|
result.segment_selector_size = reader.readNextUnsignedByte();
|
||||||
|
|
||||||
|
long header_length = reader.readNextUnsignedValue(result.intSize);
|
||||||
|
result.opcodes_start = reader.getPointerIndex() + header_length;
|
||||||
|
|
||||||
|
result.minimum_instruction_length = reader.readNextUnsignedByte();
|
||||||
|
result.maximum_operations_per_instruction = reader.readNextUnsignedByte();
|
||||||
|
result.default_is_stmt = reader.readNextUnsignedByte() != 0;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
String defaultCompDir = cu.getCompileDirectory();
|
||||||
|
if (defaultCompDir == null || defaultCompDir.isBlank()) {
|
||||||
|
defaultCompDir = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the directories, which are defined the same way files are
|
||||||
|
int directories_count = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
|
||||||
|
for (int i = 0; i < directories_count; i++) {
|
||||||
|
DWARFFile dir = DWARFFile.readV5(reader, dirFormatDefs, cu);
|
||||||
|
dir = fixupDir(dir, defaultCompDir);
|
||||||
|
result.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.files.add(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DWARFFile fixupDir(DWARFFile dir, String defaultCompDir) {
|
||||||
|
// fix relative dir names using the compiledir string from the CU
|
||||||
|
if (!defaultCompDir.isEmpty()) {
|
||||||
|
if (dir.getName().equals(".")) {
|
||||||
|
return dir.withName(defaultCompDir);
|
||||||
|
}
|
||||||
|
else if (!isAbsolutePath(dir.getName())) {
|
||||||
|
return dir.withName(FSUtilities.appendPath(defaultCompDir, dir.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isAbsolutePath(String s) {
|
||||||
|
return s.startsWith("/") || s.startsWith("\\") ||
|
||||||
|
(s.length() > 3 && s.charAt(1) == ':' && (s.charAt(2) == '/' || s.charAt(2) == '\\'));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 DWARFProgram dprog;
|
||||||
|
|
||||||
|
private long startOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset in the section of the end of this header. (exclusive)
|
||||||
|
*/
|
||||||
|
private long endOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length in bytes of this header.
|
||||||
|
*/
|
||||||
|
private long length;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* size of integers, 4=int32 or 8=int64
|
||||||
|
*/
|
||||||
|
private int intSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version number, as read from the header.
|
||||||
|
*/
|
||||||
|
private int dwarfVersion;
|
||||||
|
|
||||||
|
private int minimum_instruction_length;
|
||||||
|
private int maximum_operations_per_instruction;
|
||||||
|
private boolean default_is_stmt;
|
||||||
|
private int line_base;
|
||||||
|
private int line_range;
|
||||||
|
private int opcode_base;
|
||||||
|
private int[] standard_opcode_length;
|
||||||
|
private List<DWARFFile> directories = new ArrayList<>();
|
||||||
|
private List<DWARFFile> files = new ArrayList<>();
|
||||||
|
private int address_size;
|
||||||
|
private int segment_selector_size;
|
||||||
|
|
||||||
|
private long opcodes_start = -1; // offset where line number program opcodes start
|
||||||
|
|
||||||
|
private DWARFLine() {
|
||||||
|
// empty, use #read()
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getStartOffset() {
|
||||||
|
return startOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getEndOffset() {
|
||||||
|
return endOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DWARFLineProgramExecutor getLineProgramexecutor(DWARFCompilationUnit cu,
|
||||||
|
BinaryReader reader) {
|
||||||
|
DWARFLineProgramExecutor lpe = new DWARFLineProgramExecutor(reader.clone(opcodes_start),
|
||||||
|
endOffset, cu.getPointerSize(), opcode_base, line_base, line_range,
|
||||||
|
minimum_instruction_length, default_is_stmt);
|
||||||
|
|
||||||
|
return lpe;
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SourceFileAddr(long address, String fileName, int lineNum) {}
|
||||||
|
|
||||||
|
public List<SourceFileAddr> getAllSourceFileAddrInfo(DWARFCompilationUnit cu,
|
||||||
|
BinaryReader reader) throws IOException {
|
||||||
|
try (DWARFLineProgramExecutor lpe = getLineProgramexecutor(cu, reader)) {
|
||||||
|
List<SourceFileAddr> results = new ArrayList<>();
|
||||||
|
for (DWARFLineProgramState row : lpe.allRows()) {
|
||||||
|
results.add(new SourceFileAddr(row.address, getFilePath(row.file, true), row.line));
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DWARFFile getDir(int index) throws IOException {
|
||||||
|
if (0 <= index && index < directories.size()) {
|
||||||
|
return directories.get(index);
|
||||||
|
}
|
||||||
|
throw new IOException(
|
||||||
|
"Invalid dir index %d for line table at 0x%x: ".formatted(index, startOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a file name given a file index.
|
||||||
|
*
|
||||||
|
* @param index index of the file
|
||||||
|
* @return file {@link DWARFFile}
|
||||||
|
* @throws IOException if invalid index
|
||||||
|
*/
|
||||||
|
public DWARFFile getFile(int index) throws IOException {
|
||||||
|
if (dwarfVersion < 5) {
|
||||||
|
if (0 < index && index <= files.size()) {
|
||||||
|
// Retrieve the file by index (index starts at 1)
|
||||||
|
return files.get(index - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dwarfVersion >= 5) {
|
||||||
|
if (0 <= index && index < files.size()) {
|
||||||
|
return files.get(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IOException(
|
||||||
|
"Invalid file index %d for line table at 0x%x: ".formatted(index, startOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilePath(int index, boolean includePath) {
|
||||||
|
try {
|
||||||
|
DWARFFile f = getFile(index);
|
||||||
|
if (!includePath) {
|
||||||
|
return f.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
String dir = f.getDirectoryIndex() >= 0
|
||||||
|
? getDir(f.getDirectoryIndex()).getName()
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return FSUtilities.appendPath(dir, f.getName());
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer buffer = new StringBuffer();
|
||||||
|
buffer.append("Line Entry");
|
||||||
|
buffer.append(" Include Directories: [");
|
||||||
|
for (DWARFFile dir : this.directories) {
|
||||||
|
buffer.append(dir);
|
||||||
|
buffer.append(", ");
|
||||||
|
}
|
||||||
|
buffer.append("] File Names: [");
|
||||||
|
for (DWARFFile file : this.files) {
|
||||||
|
buffer.append(file.toString());
|
||||||
|
buffer.append(", ");
|
||||||
|
}
|
||||||
|
buffer.append("]");
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+1
-1
@@ -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.dwarf;
|
package ghidra.app.util.bin.format.dwarf.line;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.line;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class DWARFLineException extends IOException {
|
||||||
|
|
||||||
|
public DWARFLineException() {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
|
||||||
|
public DWARFLineException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DWARFLineException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DWARFLineException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.line;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.format.dwarf.DWARFUtil;
|
||||||
|
|
||||||
|
public class DWARFLineNumberExtendedOpcodes {
|
||||||
|
public final static int DW_LNE_end_sequence = 1;
|
||||||
|
public final static int DW_LNE_set_address = 2;
|
||||||
|
public final static int DW_LNE_define_file = 3; // v2-v4, v5=reserved
|
||||||
|
public final static int DW_LNE_set_discriminator = 4;
|
||||||
|
|
||||||
|
public final static int DW_LNE_lo_user = 0x80;
|
||||||
|
public final static int DW_LNE_hi_user = 0xff;
|
||||||
|
|
||||||
|
public static String toString(int value) {
|
||||||
|
return DWARFUtil.toString(DWARFLineNumberExtendedOpcodes.class, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.line;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.format.dwarf.DWARFUtil;
|
||||||
|
|
||||||
|
public class DWARFLineNumberStandardOpcodes {
|
||||||
|
public final static int DW_LNS_copy = 1;
|
||||||
|
public final static int DW_LNS_advance_pc = 2;
|
||||||
|
public final static int DW_LNS_advance_line = 3;
|
||||||
|
public final static int DW_LNS_set_file = 4;
|
||||||
|
public final static int DW_LNS_set_column = 5;
|
||||||
|
public final static int DW_LNS_negate_statement = 6;
|
||||||
|
public final static int DW_LNS_set_basic_block = 7;
|
||||||
|
public final static int DW_LNS_const_add_pc = 8;
|
||||||
|
public final static int DW_LNS_fixed_advanced_pc = 9;
|
||||||
|
public final static int DW_LNS_set_prologue_end = 10;
|
||||||
|
public final static int DW_LNS_set_epilog_begin = 11;
|
||||||
|
public final static int DW_LNS_set_isa = 12;
|
||||||
|
|
||||||
|
public static String toString(int value) {
|
||||||
|
return DWARFUtil.toString(DWARFLineNumberStandardOpcodes.class, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
+280
@@ -0,0 +1,280 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.line;
|
||||||
|
|
||||||
|
import static ghidra.app.util.bin.format.dwarf.line.DWARFLineNumberExtendedOpcodes.*;
|
||||||
|
import static ghidra.app.util.bin.format.dwarf.line.DWARFLineNumberStandardOpcodes.*;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
|
import ghidra.program.model.data.LEB128;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles executing, step-by-step, the address-to-sourcefile mapping instructions found at the
|
||||||
|
* end of a DWARFLine structure.
|
||||||
|
*/
|
||||||
|
public final class DWARFLineProgramExecutor implements Closeable {
|
||||||
|
private DWARFLineProgramState state;
|
||||||
|
private BinaryReader reader;
|
||||||
|
private final int pointerSize;
|
||||||
|
private final long streamEnd;
|
||||||
|
private final int opcodeBase;
|
||||||
|
private final int lineRange;
|
||||||
|
private final int lineBase;
|
||||||
|
private final int minInstrLen;
|
||||||
|
private final boolean defaultIsStatement;
|
||||||
|
|
||||||
|
public DWARFLineProgramExecutor(BinaryReader reader, long streamEnd, int pointerSize,
|
||||||
|
int opcodeBase, int lineBase, int lineRange, int minInstrLen,
|
||||||
|
boolean defaultIsStatement) {
|
||||||
|
this.reader = reader;
|
||||||
|
this.streamEnd = streamEnd;
|
||||||
|
this.pointerSize = pointerSize;
|
||||||
|
this.opcodeBase = opcodeBase;
|
||||||
|
this.lineBase = lineBase;
|
||||||
|
this.lineRange = lineRange;
|
||||||
|
this.minInstrLen = minInstrLen;
|
||||||
|
this.defaultIsStatement = defaultIsStatement;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
reader = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
return reader.getPointerIndex() < streamEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DWARFLineProgramState currentState() {
|
||||||
|
return new DWARFLineProgramState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DWARFLineProgramState nextRow() throws IOException {
|
||||||
|
while (hasNext()) {
|
||||||
|
DWARFLineProgramInstruction instr = step();
|
||||||
|
if (instr.row() != null) {
|
||||||
|
return instr.row();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DWARFLineProgramState> allRows() throws IOException {
|
||||||
|
List<DWARFLineProgramState> results = new ArrayList<>();
|
||||||
|
|
||||||
|
DWARFLineProgramState row;
|
||||||
|
while ((row = nextRow()) != null) {
|
||||||
|
results.add(row);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the next instruction and executes it
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @throws IOException if an i/o error occurs
|
||||||
|
*/
|
||||||
|
public DWARFLineProgramInstruction step() throws IOException {
|
||||||
|
DWARFLineProgramInstruction instr = stepInstr();
|
||||||
|
return instr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DWARFLineProgramInstruction stepInstr() throws IOException {
|
||||||
|
if (state == null) {
|
||||||
|
state = new DWARFLineProgramState(defaultIsStatement);
|
||||||
|
}
|
||||||
|
|
||||||
|
long instrOffset = reader.getPointerIndex();
|
||||||
|
|
||||||
|
int opcode = reader.readNextUnsignedByte();
|
||||||
|
|
||||||
|
if (opcode == 0) {
|
||||||
|
return executeExtended(instrOffset);
|
||||||
|
}
|
||||||
|
else if (opcode >= opcodeBase) {
|
||||||
|
return executeSpecial(instrOffset, opcode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return executeStandard(instrOffset, opcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DWARFLineProgramInstruction executeSpecial(long instrOffset, int specialOpcodeValue) {
|
||||||
|
int adjustedOpcode = (specialOpcodeValue & 0xff) - opcodeBase;
|
||||||
|
int addressIncrement = adjustedOpcode / lineRange;
|
||||||
|
int lineIncrement = lineBase + (adjustedOpcode % lineRange);
|
||||||
|
|
||||||
|
addressIncrement &= 0xff;
|
||||||
|
lineIncrement &= 0xff;
|
||||||
|
|
||||||
|
state.line += (byte) lineIncrement;
|
||||||
|
state.address += (addressIncrement * minInstrLen);
|
||||||
|
|
||||||
|
DWARFLineProgramState row = currentState();
|
||||||
|
|
||||||
|
state.isBasicBlock = false;
|
||||||
|
state.prologueEnd = false;
|
||||||
|
state.epilogueBegin = false;
|
||||||
|
state.discriminator = 0;
|
||||||
|
|
||||||
|
return new DWARFLineProgramInstruction(instrOffset, "DW_LN_special_" + specialOpcodeValue,
|
||||||
|
List.of(addressIncrement, lineIncrement), row);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DWARFLineProgramInstruction executeExtended(long instrOffset) throws IOException {
|
||||||
|
int length = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
|
||||||
|
|
||||||
|
long oldIndex = reader.getPointerIndex();
|
||||||
|
int extendedOpcode = reader.readNextByte();
|
||||||
|
|
||||||
|
String instr = DWARFLineNumberExtendedOpcodes.toString(extendedOpcode);
|
||||||
|
List<Number> operands = List.of();
|
||||||
|
DWARFLineProgramState row = null;
|
||||||
|
|
||||||
|
switch (extendedOpcode) {
|
||||||
|
case DW_LNE_end_sequence:
|
||||||
|
// end_seq is a special marker, and by definition specifies a row that is one byte
|
||||||
|
// after the last instruction of the sequence.
|
||||||
|
state.isEndSequence = true;
|
||||||
|
row = currentState();
|
||||||
|
row.address--; // tweak backwards 1 byte
|
||||||
|
state = new DWARFLineProgramState(defaultIsStatement);
|
||||||
|
break;
|
||||||
|
case DW_LNE_set_address:
|
||||||
|
state.address = reader.readNextUnsignedValue(pointerSize);
|
||||||
|
operands = List.of(state.address);
|
||||||
|
break;
|
||||||
|
case DW_LNE_define_file: {
|
||||||
|
// this instruction is deprecated in v5+, and not fully supported in this
|
||||||
|
// impl
|
||||||
|
String sourceFilename = reader.readNextUtf8String();
|
||||||
|
int dirIndex = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
|
||||||
|
long lastMod = reader.readNext(LEB128::unsigned);
|
||||||
|
long fileLen = reader.readNext(LEB128::unsigned);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DW_LNE_set_discriminator:
|
||||||
|
state.discriminator = reader.readNext(LEB128::unsigned);
|
||||||
|
operands = List.of(state.discriminator);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new DWARFLineException("Unknown extended instruction: " + instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldIndex + length != reader.getPointerIndex()) {
|
||||||
|
throw new DWARFLineException("Bad extended opcode decoding, length mismatch @0x%x: %s"
|
||||||
|
.formatted(oldIndex, instr));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DWARFLineProgramInstruction(instrOffset, instr, operands, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DWARFLineProgramInstruction executeStandard(long instrOffset, int opcode)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
String instr = DWARFLineNumberStandardOpcodes.toString(opcode);
|
||||||
|
List<Number> operands = List.of();
|
||||||
|
DWARFLineProgramState row = null;
|
||||||
|
|
||||||
|
switch (opcode) {
|
||||||
|
case DW_LNS_copy: {
|
||||||
|
row = currentState();
|
||||||
|
|
||||||
|
state.discriminator = 0;
|
||||||
|
state.isBasicBlock = false;
|
||||||
|
state.prologueEnd = false;
|
||||||
|
state.epilogueBegin = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DW_LNS_advance_pc: {
|
||||||
|
long value = reader.readNext(LEB128::unsigned);
|
||||||
|
operands = List.of(value);
|
||||||
|
|
||||||
|
state.address += (value * minInstrLen);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DW_LNS_advance_line: {
|
||||||
|
int value = reader.readNextVarInt(LEB128::signed);
|
||||||
|
operands = List.of(value);
|
||||||
|
|
||||||
|
state.line += value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DW_LNS_set_file: {
|
||||||
|
int value = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
|
||||||
|
operands = List.of(value);
|
||||||
|
|
||||||
|
state.file = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DW_LNS_set_column: {
|
||||||
|
int value = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
|
||||||
|
operands = List.of(value);
|
||||||
|
|
||||||
|
state.column = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DW_LNS_negate_statement: {
|
||||||
|
state.isStatement = !state.isStatement;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DW_LNS_set_basic_block: {
|
||||||
|
state.isBasicBlock = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DW_LNS_const_add_pc: {
|
||||||
|
int adjustedOpcode = 255 - opcodeBase;
|
||||||
|
int addressIncrement = adjustedOpcode / lineRange;
|
||||||
|
state.address += (addressIncrement & 0xff);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DW_LNS_fixed_advanced_pc: {
|
||||||
|
int value = reader.readNextUnsignedShort();
|
||||||
|
operands = List.of(value);
|
||||||
|
|
||||||
|
state.address += value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DW_LNS_set_prologue_end:
|
||||||
|
state.prologueEnd = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_LNS_set_epilog_begin:
|
||||||
|
state.epilogueBegin = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_LNS_set_isa: {
|
||||||
|
long value = reader.readNext(LEB128::unsigned);
|
||||||
|
operands = List.of(value);
|
||||||
|
|
||||||
|
state.isa = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new DWARFLineException("Unsupported standard opcode: " + instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DWARFLineProgramInstruction(instrOffset, instr, operands, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
+34
@@ -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.line;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record DWARFLineProgramInstruction(long offset, String instr, List<Number> operands,
|
||||||
|
DWARFLineProgramState row) {
|
||||||
|
|
||||||
|
public String getDesc() {
|
||||||
|
if (row != null) {
|
||||||
|
String flags = (row.isBasicBlock ? " basic block " : "") +
|
||||||
|
(row.isEndSequence ? " end-of-seq " : "") + (row.isStatement ? " statement " : "") +
|
||||||
|
(row.prologueEnd ? " prologue-end " : "");
|
||||||
|
return "[%04x] %s %s - 0x%x, file: %d, line: %d, %s".formatted(offset, instr, operands,
|
||||||
|
row.address, row.file, row.line, flags);
|
||||||
|
}
|
||||||
|
return "[%04x] %s %s".formatted(offset, instr, operands);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+39
-26
@@ -1,6 +1,5 @@
|
|||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,25 +15,24 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.dwarf.line;
|
package ghidra.app.util.bin.format.dwarf.line;
|
||||||
|
|
||||||
import ghidra.util.Msg;
|
public class DWARFLineProgramState {
|
||||||
|
|
||||||
public class StateMachine {
|
|
||||||
/**
|
/**
|
||||||
* The program-counter value corresponding to a machine instruction
|
* The program-counter value corresponding to a machine instruction
|
||||||
* generated by the compiler.
|
* generated by the compiler.
|
||||||
*/
|
*/
|
||||||
public long address;
|
public long address;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An unsigned integer indicating the identity of the source file
|
* An unsigned integer indicating the identity of the source file
|
||||||
* corresponding to a machine instruction.
|
* corresponding to a machine instruction.
|
||||||
*/
|
*/
|
||||||
public int file;
|
public int file = 1;
|
||||||
/**
|
/**
|
||||||
* An unsigned integer indicating a source line number. Lines are
|
* An unsigned integer indicating a source line number. Lines are
|
||||||
* numbered beginning at 1. The compiler may emit the value 0 in cases
|
* numbered beginning at 1. The compiler may emit the value 0 in cases
|
||||||
* where an instruction cannot be attributed to any source line.
|
* where an instruction cannot be attributed to any source line.
|
||||||
*/
|
*/
|
||||||
public int line;
|
public int line = 1;
|
||||||
/**
|
/**
|
||||||
* An unsigned integer indicating a column number within a source line.
|
* An unsigned integer indicating a column number within a source line.
|
||||||
* Columns are numbered beginning at 1. The value 0 is reserved to
|
* Columns are numbered beginning at 1. The value 0 is reserved to
|
||||||
@@ -57,27 +55,42 @@ public class StateMachine {
|
|||||||
*/
|
*/
|
||||||
public boolean isEndSequence;
|
public boolean isEndSequence;
|
||||||
|
|
||||||
public void reset(boolean defaultIsStatement) {
|
public boolean prologueEnd;
|
||||||
address = 0;
|
|
||||||
file = 1;
|
public boolean epilogueBegin;
|
||||||
line = 1;
|
|
||||||
column = 0;
|
public long isa;
|
||||||
isStatement = defaultIsStatement;
|
|
||||||
isBasicBlock = false;
|
public long discriminator;
|
||||||
isEndSequence = false;
|
|
||||||
|
public DWARFLineProgramState(boolean defaultIsStatement) {
|
||||||
|
this.isStatement = defaultIsStatement;
|
||||||
}
|
}
|
||||||
|
|
||||||
void print() {
|
public DWARFLineProgramState(DWARFLineProgramState other) {
|
||||||
Msg.info(this, "ADDR=" + Long.toHexString(address));
|
this.address = other.address;
|
||||||
Msg.info(this, " ");
|
this.file = other.file;
|
||||||
Msg.info(this, "FILE=" + Long.toHexString(file));
|
this.line = other.line;
|
||||||
Msg.info(this, " ");
|
this.column = other.column;
|
||||||
Msg.info(this, "LINE=" + Long.toHexString(line));
|
this.isStatement = other.isStatement;
|
||||||
Msg.info(this, " ");
|
this.isBasicBlock = other.isBasicBlock;
|
||||||
Msg.info(this, "LINE=" + line);
|
this.isEndSequence = other.isEndSequence;
|
||||||
Msg.info(this, " ");
|
this.prologueEnd = other.prologueEnd;
|
||||||
Msg.info(this, "COL=" + Long.toHexString(column));
|
this.epilogueBegin = other.epilogueBegin;
|
||||||
Msg.info(this, " ");
|
this.isa = other.isa;
|
||||||
Msg.info(this, "");
|
this.discriminator = other.discriminator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSameFileLine(DWARFLineProgramState other) {
|
||||||
|
return file == other.file && line == other.line;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format(
|
||||||
|
"DWARFLineProgramState [address=%s, file=%s, line=%s, column=%s, isStatement=%s, isBasicBlock=%s, isEndSequence=%s, prologueEnd=%s, epilogueBegin=%s, isa=%s, discriminator=%s]",
|
||||||
|
address, file, line, column, isStatement, isBasicBlock, isEndSequence, prologueEnd,
|
||||||
|
epilogueBegin, isa, discriminator);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
-56
@@ -1,56 +0,0 @@
|
|||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package ghidra.app.util.bin.format.dwarf.line;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
|
||||||
import ghidra.program.model.data.LEB128;
|
|
||||||
|
|
||||||
public class FileEntry {
|
|
||||||
private String fileName;
|
|
||||||
private long directoryIndex;
|
|
||||||
private long lastModifiedTime;
|
|
||||||
private long fileLengthInBytes;
|
|
||||||
|
|
||||||
FileEntry(BinaryReader reader) throws IOException {
|
|
||||||
fileName = reader.readNextAsciiString();
|
|
||||||
if (fileName.length() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
directoryIndex = reader.readNext(LEB128::unsigned);
|
|
||||||
lastModifiedTime = reader.readNext(LEB128::unsigned);
|
|
||||||
fileLengthInBytes = reader.readNext(LEB128::unsigned);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFileName() {
|
|
||||||
return fileName;
|
|
||||||
}
|
|
||||||
public long getDirectoryIndex() {
|
|
||||||
return directoryIndex;
|
|
||||||
}
|
|
||||||
public long getLastModifiedTime() {
|
|
||||||
return lastModifiedTime;
|
|
||||||
}
|
|
||||||
public long getFileLengthInBytes() {
|
|
||||||
return fileLengthInBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return fileName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-166
@@ -1,166 +0,0 @@
|
|||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package ghidra.app.util.bin.format.dwarf.line;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
|
||||||
import ghidra.program.model.data.LEB128;
|
|
||||||
|
|
||||||
public final class StatementProgramInstructions {
|
|
||||||
|
|
||||||
//Standard Opcodes
|
|
||||||
|
|
||||||
public final static int DW_LNS_copy = 1;
|
|
||||||
public final static int DW_LNS_advance_pc = 2;
|
|
||||||
public final static int DW_LNS_advance_line = 3;
|
|
||||||
public final static int DW_LNS_set_file = 4;
|
|
||||||
public final static int DW_LNS_set_column = 5;
|
|
||||||
public final static int DW_LNS_negate_statement = 6;
|
|
||||||
public final static int DW_LNS_set_basic_block = 7;
|
|
||||||
public final static int DW_LNS_const_add_pc = 8;
|
|
||||||
public final static int DW_LNS_fixed_advanced_pc = 9;
|
|
||||||
|
|
||||||
public final static int DW_LNS_set_prologue_end = 10;
|
|
||||||
public final static int DW_LNS_set_epilog_begin = 11;
|
|
||||||
public final static int DW_LNS_set_isa = 12;
|
|
||||||
|
|
||||||
//Extended Opcodes
|
|
||||||
|
|
||||||
public final static int DW_LNE_end_sequence = 1;
|
|
||||||
public final static int DW_LNE_set_address = 2;
|
|
||||||
public final static int DW_LNE_define_file = 3;
|
|
||||||
|
|
||||||
private BinaryReader reader;
|
|
||||||
private StateMachine machine;
|
|
||||||
private StatementProgramPrologue prologue;
|
|
||||||
|
|
||||||
public StatementProgramInstructions(BinaryReader reader, StateMachine machine,
|
|
||||||
StatementProgramPrologue prologue) {
|
|
||||||
this.reader = reader;
|
|
||||||
this.machine = machine;
|
|
||||||
this.prologue = prologue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispose() {
|
|
||||||
reader = null;
|
|
||||||
machine = null;
|
|
||||||
prologue = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the next instruction and executes it
|
|
||||||
* on the given state machine.
|
|
||||||
* @throws IOException if an i/o error occurs
|
|
||||||
*/
|
|
||||||
public void execute() throws IOException {
|
|
||||||
int opcode = reader.readNextByte() & 0xff;
|
|
||||||
if (opcode == 0) {
|
|
||||||
executeExtended(opcode);
|
|
||||||
}
|
|
||||||
else if (opcode >= prologue.getOpcodeBase()) {
|
|
||||||
executeSpecial(opcode);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
executeStandard(opcode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void executeSpecial(int specialOpcodeValue) {
|
|
||||||
int adjustedOpcode = (specialOpcodeValue & 0xff) - prologue.getOpcodeBase();
|
|
||||||
int addressIncrement = adjustedOpcode / prologue.getLineRange();
|
|
||||||
int lineIncrement = prologue.getLineBase() + (adjustedOpcode % prologue.getLineRange());
|
|
||||||
|
|
||||||
addressIncrement &= 0xff;
|
|
||||||
lineIncrement &= 0xff;
|
|
||||||
|
|
||||||
machine.line += (byte) lineIncrement;
|
|
||||||
machine.address += (addressIncrement * prologue.getMinimumInstructionLength());
|
|
||||||
machine.isBasicBlock = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void executeExtended(int opcode) throws IOException {
|
|
||||||
long length = reader.readNext(LEB128::unsigned);
|
|
||||||
|
|
||||||
long oldIndex = reader.getPointerIndex();
|
|
||||||
int extendedOpcode = reader.readNextByte();
|
|
||||||
|
|
||||||
switch (extendedOpcode) {
|
|
||||||
case DW_LNE_end_sequence:
|
|
||||||
machine.isEndSequence = true;
|
|
||||||
machine.reset(prologue.isDefaultIsStatement());
|
|
||||||
break;
|
|
||||||
case DW_LNE_set_address:
|
|
||||||
machine.address = reader.readNextInt();
|
|
||||||
break;
|
|
||||||
case DW_LNE_define_file://TODO
|
|
||||||
//break;
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldIndex + length != reader.getPointerIndex()) {
|
|
||||||
throw new IllegalStateException("Index values do not match!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void executeStandard(int opcode) throws IOException {
|
|
||||||
switch (opcode) {
|
|
||||||
case DW_LNS_copy: {
|
|
||||||
machine.isBasicBlock = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DW_LNS_advance_pc: {
|
|
||||||
long value = reader.readNext(LEB128::unsigned);
|
|
||||||
machine.address += (value * prologue.getMinimumInstructionLength());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DW_LNS_advance_line: {
|
|
||||||
long value = reader.readNext(LEB128::unsigned);
|
|
||||||
machine.line += value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DW_LNS_set_file: {
|
|
||||||
long value = reader.readNext(LEB128::unsigned);
|
|
||||||
machine.file = (int) value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DW_LNS_set_column: {
|
|
||||||
long value = reader.readNext(LEB128::unsigned);
|
|
||||||
machine.column = (int) value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DW_LNS_negate_statement: {
|
|
||||||
machine.isStatement = !machine.isStatement;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DW_LNS_set_basic_block: {
|
|
||||||
machine.isBasicBlock = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DW_LNS_const_add_pc: {
|
|
||||||
int adjustedOpcode = 255 - prologue.getOpcodeBase();
|
|
||||||
int addressIncrement = adjustedOpcode / prologue.getLineRange();
|
|
||||||
machine.address += (addressIncrement & 0xff);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DW_LNS_fixed_advanced_pc: {
|
|
||||||
short value = reader.readNextShort();
|
|
||||||
machine.address += value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-177
@@ -1,177 +0,0 @@
|
|||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package ghidra.app.util.bin.format.dwarf.line;
|
|
||||||
|
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class StatementProgramPrologue {
|
|
||||||
public final static int TOTAL_LENGTH_FIELD_LEN = 4;
|
|
||||||
public final static int PRE_PROLOGUE_LEN = 4 + 2 + 4;
|
|
||||||
|
|
||||||
private int totalLength;
|
|
||||||
private short version;
|
|
||||||
private int prologueLength;
|
|
||||||
private byte minimumInstructionLength;
|
|
||||||
private boolean defaultIsStatement;
|
|
||||||
private byte lineBase;
|
|
||||||
private byte lineRange;
|
|
||||||
private byte opcodeBase;
|
|
||||||
private byte [] standardOpcodeLengths;
|
|
||||||
|
|
||||||
private List<String> includeDirectories = new ArrayList<String>();
|
|
||||||
private List<FileEntry> fileNames = new ArrayList<FileEntry>();
|
|
||||||
|
|
||||||
public StatementProgramPrologue(BinaryReader reader) throws IOException {
|
|
||||||
totalLength = reader.readNextInt();
|
|
||||||
version = reader.readNextShort();
|
|
||||||
|
|
||||||
if (version != 2) {
|
|
||||||
throw new IllegalStateException("Only DWARF v2 is supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
prologueLength = reader.readNextInt();
|
|
||||||
minimumInstructionLength = reader.readNextByte();
|
|
||||||
defaultIsStatement = reader.readNextByte() != 0;
|
|
||||||
lineBase = reader.readNextByte();
|
|
||||||
lineRange = reader.readNextByte();
|
|
||||||
opcodeBase = reader.readNextByte();
|
|
||||||
standardOpcodeLengths = reader.readNextByteArray(opcodeBase - 1);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
String dir = reader.readNextAsciiString();
|
|
||||||
if (dir.length() == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
includeDirectories.add(dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
FileEntry entry = new FileEntry(reader);
|
|
||||||
if (entry.getFileName().length() == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fileNames.add(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the size in bytes of the statement information for this
|
|
||||||
* compilation unit (not including the total_length field itself).
|
|
||||||
* @return size in bytes of the statement information
|
|
||||||
*/
|
|
||||||
public int getTotalLength() {
|
|
||||||
return totalLength;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns the version identifier for the statement information format.
|
|
||||||
* @return the version identifier for the statement information format
|
|
||||||
*/
|
|
||||||
public int getVersion() {
|
|
||||||
return version & 0xffff;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns the number of bytes following the prologue_length field to the
|
|
||||||
* beginning of the first byte of the statement program itself.
|
|
||||||
* @return the number of bytes following the prologue_length
|
|
||||||
*/
|
|
||||||
public int getPrologueLength() {
|
|
||||||
return prologueLength;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns the size in bytes of the smallest target machine instruction.
|
|
||||||
* Statement program opcodes that alter the address register first
|
|
||||||
* multiply their operands by this value.
|
|
||||||
* @return the size in bytes of the smallest target machine instruction
|
|
||||||
*/
|
|
||||||
public int getMinimumInstructionLength() {
|
|
||||||
return minimumInstructionLength & 0xff;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns the initial value of the is_stmt register.
|
|
||||||
* @return the initial value of the is_stmt register
|
|
||||||
*/
|
|
||||||
public boolean isDefaultIsStatement() {
|
|
||||||
return defaultIsStatement;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns the line base value.
|
|
||||||
* This parameter affects the meaning of the special opcodes. See below.
|
|
||||||
* @return the line base value
|
|
||||||
*/
|
|
||||||
public int getLineBase() {
|
|
||||||
return lineBase & 0xff;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns the line range value.
|
|
||||||
* This parameter affects the meaning of the special opcodes. See below.
|
|
||||||
* @return the line range value
|
|
||||||
*/
|
|
||||||
public int getLineRange() {
|
|
||||||
return lineRange & 0xff;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns the number assigned to the first special opcode.
|
|
||||||
* @return the number assigned to the first special opcode
|
|
||||||
*/
|
|
||||||
public int getOpcodeBase() {
|
|
||||||
return opcodeBase & 0xff;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* return the array for each of the standard opcodes
|
|
||||||
* @return the array for each of the standard opcodes
|
|
||||||
*/
|
|
||||||
public byte [] getStandardOpcodeLengths() {
|
|
||||||
return standardOpcodeLengths;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @return each path that was searched for included source files
|
|
||||||
*/
|
|
||||||
public List<String> getIncludeDirectories() {
|
|
||||||
return includeDirectories;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @return an entry for each source file that contributed to the statement
|
|
||||||
*/
|
|
||||||
public List<FileEntry> getFileNames() {
|
|
||||||
return fileNames;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns the file entry at the given index.
|
|
||||||
* @param fileIndex the file index
|
|
||||||
* @return the file entry at the given index
|
|
||||||
*/
|
|
||||||
public FileEntry getFileNameByIndex(int fileIndex) {
|
|
||||||
return fileNames.get(fileIndex - 1);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* The directory index represents an entry in the
|
|
||||||
* include directories section. If the directoryIndex
|
|
||||||
* is LEB128(0), then the file was found in the current
|
|
||||||
* directory.
|
|
||||||
* @param directoryIndex the directory index
|
|
||||||
* @return the directory or current directory
|
|
||||||
*/
|
|
||||||
public String getDirectoryByIndex(long directoryIndex) {
|
|
||||||
if (directoryIndex == 0) {
|
|
||||||
return ".";
|
|
||||||
}
|
|
||||||
return includeDirectories.get((int)directoryIndex - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-2
@@ -24,8 +24,8 @@ public class MockDWARFCompilationUnit extends DWARFCompilationUnit {
|
|||||||
private int dieCount;
|
private int dieCount;
|
||||||
|
|
||||||
public MockDWARFCompilationUnit(MockDWARFProgram dwarfProgram, long startOffset, long endOffset,
|
public MockDWARFCompilationUnit(MockDWARFProgram dwarfProgram, long startOffset, long endOffset,
|
||||||
long length, int intSize, short version, byte pointerSize, int compUnitNumber) {
|
int intSize, short version, byte pointerSize, int compUnitNumber) {
|
||||||
super(dwarfProgram, startOffset, endOffset, length, intSize, version, pointerSize,
|
super(dwarfProgram, startOffset, endOffset, intSize, version, pointerSize,
|
||||||
compUnitNumber, startOffset, null);
|
compUnitNumber, startOffset, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -68,8 +68,8 @@ public class MockDWARFProgram extends DWARFProgram {
|
|||||||
compUnitDieIndex.put(dieOffsets.length - 1, currentCompUnit);
|
compUnitDieIndex.put(dieOffsets.length - 1, currentCompUnit);
|
||||||
}
|
}
|
||||||
long start = compUnits.size() * 0x1000;
|
long start = compUnits.size() * 0x1000;
|
||||||
currentCompUnit = new MockDWARFCompilationUnit(this, start, start + 0x1000, 0,
|
currentCompUnit = new MockDWARFCompilationUnit(this, start, start + 0x1000, dwarfIntSize,
|
||||||
dwarfIntSize, (short) 4, (byte) 8, 0);
|
(short) 4, (byte) 8, 0);
|
||||||
compUnits.add(currentCompUnit);
|
compUnits.add(currentCompUnit);
|
||||||
compUnitDieIndex.put(dieOffsets.length - 1, currentCompUnit);
|
compUnitDieIndex.put(dieOffsets.length - 1, currentCompUnit);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user