mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Merge remote-tracking branch 'origin/GP-4193_dev747368_dwarfline_source_info--SQUASHED'
This commit is contained in:
commit
6439d61594
31 changed files with 1234 additions and 1320 deletions
76
Ghidra/Features/Base/ghidra_scripts/DWARFLineInfoScript.java
Normal file
76
Ghidra/Features/Base/ghidra_scripts/DWARFLineInfoScript.java
Normal file
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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.expression.*;
|
||||
import ghidra.app.util.bin.format.dwarf.line.DWARFLine;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
|
@ -470,9 +471,15 @@ public class DIEAggregate {
|
|||
if (attr == null) {
|
||||
return null;
|
||||
}
|
||||
int fileNum = (int) attr.getUnsignedValue();
|
||||
DWARFCompilationUnit cu = attrInfo.die.getCompilationUnit();
|
||||
return cu.isValidFileIndex(fileNum) ? cu.getFileByIndex(fileNum) : null;
|
||||
try {
|
||||
int fileNum = attr.getUnsignedIntExact();
|
||||
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 {@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() {
|
||||
DWARFNumericAttribute lowPc = getAttribute(DW_AT_low_pc, DWARFNumericAttribute.class);
|
||||
|
@ -720,29 +728,26 @@ public class DIEAggregate {
|
|||
long rawLowPc = lowPc.getUnsignedValue();
|
||||
long lowPcOffset = getProgram().getAddress(lowPc.getAttributeForm(), rawLowPc,
|
||||
getCompilationUnit());
|
||||
long highPcOffset = lowPcOffset + 1;
|
||||
long highPcOffset = lowPcOffset;
|
||||
|
||||
DWARFNumericAttribute highPc =
|
||||
getAttribute(DW_AT_high_pc, DWARFNumericAttribute.class);
|
||||
if (highPc != null) {
|
||||
if (highPc.getAttributeForm() == DWARFForm.DW_FORM_addr) {
|
||||
long baseAddrFixup = getProgram().getProgramBaseAddressFixup();
|
||||
highPcOffset = highPc.getUnsignedValue() + baseAddrFixup;
|
||||
highPcOffset = highPc.getUnsignedValue();
|
||||
}
|
||||
else {
|
||||
highPcOffset = highPc.getUnsignedValue();
|
||||
if (highPcOffset != 0) {
|
||||
highPcOffset = lowPcOffset + highPcOffset;
|
||||
}
|
||||
highPcOffset = lowPcOffset + highPcOffset;
|
||||
}
|
||||
}
|
||||
return new DWARFRange(lowPcOffset, highPcOffset);
|
||||
}
|
||||
catch (IOException e) {
|
||||
// fall thru, return null
|
||||
// fall thru, return empty
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return DWARFRange.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package ghidra.app.util.bin.format.dwarf;
|
||||
|
||||
import static ghidra.app.util.bin.format.dwarf.DWARFTag.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
|
@ -152,7 +150,7 @@ public class DWARFAbbreviation {
|
|||
}
|
||||
|
||||
public String getTagName() {
|
||||
return tag != DW_TAG_UNKNOWN ? tag.name() : "DW_TAG_??? %d".formatted(tagId);
|
||||
return tag.name(tagId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.dwarf.line.DWARFLine;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
|
@ -170,7 +171,6 @@ public class DWARFCompilationUnit extends DWARFUnitHeader {
|
|||
* @param dwarfProgram {@link DWARFProgram}
|
||||
* @param startOffset offset in provider where it starts
|
||||
* @param endOffset offset in provider where it ends
|
||||
* @param length how many bytes following the header the DIEs of this unit take
|
||||
* @param intSize 4 (DWARF_32) or 8 (DWARF_64)
|
||||
* @param dwarfVersion 2-5
|
||||
* @param pointerSize default size of pointers
|
||||
|
@ -179,9 +179,9 @@ public class DWARFCompilationUnit extends DWARFUnitHeader {
|
|||
* @param codeToAbbreviationMap map of abbreviation numbers to {@link DWARFAbbreviation} instances
|
||||
*/
|
||||
public DWARFCompilationUnit(DWARFProgram dwarfProgram, long startOffset, long endOffset,
|
||||
long length, int intSize, short dwarfVersion, byte pointerSize, int unitNumber,
|
||||
int intSize, short dwarfVersion, byte pointerSize, int unitNumber,
|
||||
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.firstDIEOffset = firstDIEOffset;
|
||||
this.codeToAbbreviationMap =
|
||||
|
@ -230,6 +230,10 @@ public class DWARFCompilationUnit extends DWARFUnitHeader {
|
|||
return firstDIEOffset;
|
||||
}
|
||||
|
||||
public DWARFLine getLine() {
|
||||
return line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filename that produced the compile unit
|
||||
*
|
||||
|
@ -239,50 +243,6 @@ public class DWARFCompilationUnit extends DWARFUnitHeader {
|
|||
return diea.getString(DW_AT_name, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file name with the full path included based on a file index.
|
||||
* @param index index of the file
|
||||
* @return file name with full path or null if line information does not exist
|
||||
* @throws IllegalArgumentException if a negative or invalid file index is given
|
||||
*/
|
||||
public String getFullFileByIndex(int index) {
|
||||
if (index < 0) {
|
||||
throw new IllegalArgumentException("Negative file index was given.");
|
||||
}
|
||||
if (this.line == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.line.getFullFile(index, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file name based on a file index.
|
||||
* @param index index of the file
|
||||
* @return file name or null if line information does not exist
|
||||
* @throws IllegalArgumentException if a negative or invalid file index is given
|
||||
*/
|
||||
public String getFileByIndex(int index) {
|
||||
if (index < 0) {
|
||||
throw new IllegalArgumentException("Negative file index was given.");
|
||||
}
|
||||
if (this.line == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.line.getFile(index, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks validity of a file index number.
|
||||
*
|
||||
* @param index file number, 1..N
|
||||
* @return boolean true if index is a valid file number, false otherwise
|
||||
*/
|
||||
public boolean isValidFileIndex(int index) {
|
||||
return line.isValidFileIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the producer of the compile unit
|
||||
* @return the producer of the compile unit
|
||||
|
@ -330,6 +290,12 @@ public class DWARFCompilationUnit extends DWARFUnitHeader {
|
|||
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() {
|
||||
return diea.getPCRange();
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ public class DWARFFunction {
|
|||
return null;
|
||||
}
|
||||
DWARFRangeList bodyRanges = getFuncBodyRanges(diea);
|
||||
if (bodyRanges == null || bodyRanges.isEmpty()) {
|
||||
if (bodyRanges.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -330,17 +330,17 @@ public class DWARFFunction {
|
|||
// TODO: dw_at_entry_pc is also sometimes available, typically in things like inlined_subroutines
|
||||
DWARFProgram dprog = diea.getProgram();
|
||||
DWARFRangeList bodyRangeList = getFuncBodyRanges(diea);
|
||||
if (bodyRangeList != null && !bodyRangeList.isEmpty()) {
|
||||
DWARFRange bodyRange =
|
||||
flattenDisjoint ? bodyRangeList.getFlattenedRange() : bodyRangeList.getFirst();
|
||||
return dprog.getAddressRange(bodyRange, true);
|
||||
if (bodyRangeList.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
DWARFRange bodyRange =
|
||||
flattenDisjoint ? bodyRangeList.getFlattenedRange() : bodyRangeList.getFirst();
|
||||
return dprog.getAddressRange(bodyRange, true);
|
||||
}
|
||||
|
||||
public static DWARFRangeList getFuncBodyRanges(DIEAggregate diea) throws IOException {
|
||||
DWARFRange body = diea.getPCRange();
|
||||
if (body != null && !body.isEmpty()) {
|
||||
if (!body.isEmpty()) {
|
||||
return new DWARFRangeList(body);
|
||||
}
|
||||
if (diea.hasAttribute(DW_AT_ranges)) {
|
||||
|
|
|
@ -422,16 +422,16 @@ public class DWARFFunctionImporter {
|
|||
return;
|
||||
}
|
||||
|
||||
DWARFRangeList blockRanges =
|
||||
Objects.requireNonNullElse(DWARFFunction.getFuncBodyRanges(diea), DWARFRangeList.EMTPY);
|
||||
Address blockStart =
|
||||
!blockRanges.isEmpty() ? prog.getCodeAddress(blockRanges.getFirst().getFrom()) : null;
|
||||
|
||||
if (blockStart != null && importOptions.isOutputLexicalBlockComments()) {
|
||||
boolean disjoint = blockRanges.getListCount() > 1;
|
||||
DWARFName dni = prog.getName(diea);
|
||||
appendComment(blockStart, CodeUnit.PRE_COMMENT,
|
||||
"Begin: %s%s".formatted(dni.getName(), disjoint ? " - Disjoint" : ""), "\n");
|
||||
Address blockStart = null;
|
||||
DWARFRangeList blockRanges = DWARFFunction.getFuncBodyRanges(diea);
|
||||
if (!blockRanges.isEmpty()) {
|
||||
blockStart = prog.getCodeAddress(blockRanges.getFirst().getFrom());
|
||||
if (importOptions.isOutputLexicalBlockComments()) {
|
||||
boolean disjoint = blockRanges.getListCount() > 1;
|
||||
DWARFName dni = prog.getName(diea);
|
||||
appendComment(blockStart, CodeUnit.PRE_COMMENT,
|
||||
"Begin: %s%s".formatted(dni.getName(), disjoint ? " - Disjoint" : ""), "\n");
|
||||
}
|
||||
}
|
||||
|
||||
processFuncChildren(diea, dfunc,
|
||||
|
@ -554,25 +554,22 @@ public class DWARFFunctionImporter {
|
|||
}
|
||||
|
||||
String name = prog.getEntryName(diea);
|
||||
DWARFRange labelPc;
|
||||
if (name != null && (labelPc = diea.getPCRange()) != null) {
|
||||
DWARFRange labelPc = diea.getPCRange();
|
||||
if (name != null && !labelPc.isEmpty() && labelPc.getFrom() != 0) {
|
||||
Address address = prog.getCodeAddress(labelPc.getFrom());
|
||||
if (address.getOffset() != 0) {
|
||||
try {
|
||||
SymbolTable symbolTable = currentProgram.getSymbolTable();
|
||||
symbolTable.createLabel(address, name, currentProgram.getGlobalNamespace(),
|
||||
SourceType.IMPORTED);
|
||||
try {
|
||||
SymbolTable symbolTable = currentProgram.getSymbolTable();
|
||||
symbolTable.createLabel(address, name, currentProgram.getGlobalNamespace(),
|
||||
SourceType.IMPORTED);
|
||||
|
||||
String locationInfo = DWARFSourceInfo.getDescriptionStr(diea);
|
||||
if (locationInfo != null) {
|
||||
appendComment(address, CodeUnit.EOL_COMMENT, locationInfo, "; ");
|
||||
}
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
Msg.error(this, "Problem creating label at " + address + " with name " + name,
|
||||
e);
|
||||
String locationInfo = DWARFSourceInfo.getDescriptionStr(diea);
|
||||
if (locationInfo != null) {
|
||||
appendComment(address, CodeUnit.EOL_COMMENT, locationInfo, "; ");
|
||||
}
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
Msg.error(this, "Problem creating label at " + address + " with name " + name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,11 @@ public class DWARFImportOptions {
|
|||
"Include source code location info (filename:linenumber) in comments attached to the " +
|
||||
"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_DESC =
|
||||
"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 importLocalVariables = true;
|
||||
private boolean useBookmarks = true;
|
||||
private boolean outputSourceLineInfo = false;
|
||||
|
||||
/**
|
||||
* Create new instance
|
||||
|
@ -360,6 +366,14 @@ public class DWARFImportOptions {
|
|||
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)}
|
||||
*
|
||||
|
@ -392,6 +406,9 @@ public class DWARFImportOptions {
|
|||
|
||||
options.registerOption(OPTION_IMPORT_LOCAL_VARS, isImportLocalVariables(), null,
|
||||
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()));
|
||||
setImportLocalVariables(
|
||||
options.getBoolean(OPTION_IMPORT_LOCAL_VARS, isImportLocalVariables()));
|
||||
setOutputSourceLineInfo(
|
||||
options.getBoolean(OPTION_SOURCE_LINEINFO, isOutputSourceLineInfo()));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,11 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
|
||||
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.listing.CodeUnit;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
@ -170,6 +174,30 @@ public class DWARFImporter {
|
|||
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.
|
||||
* <p>
|
||||
|
@ -205,6 +233,10 @@ public class DWARFImporter {
|
|||
moveTypesIntoSourceFolders();
|
||||
}
|
||||
|
||||
if (importOptions.isOutputSourceLineInfo()) {
|
||||
addSourceLineInfo(prog.getDebugLineBR());
|
||||
}
|
||||
|
||||
importSummary.totalElapsedMS = System.currentTimeMillis() - start_ts;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -58,43 +58,35 @@ public class DWARFLocationList {
|
|||
List<DWARFLocation> results = new ArrayList<>();
|
||||
|
||||
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()) {
|
||||
// Read the beginning and ending addresses
|
||||
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) {
|
||||
// List end
|
||||
break;
|
||||
}
|
||||
else if (beginning == ending) {
|
||||
// don't add empty range
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check to see if this is a base address entry
|
||||
if (beginning == eolVal) {
|
||||
baseAddrOffset = ending + baseFixup;
|
||||
if (beginning == maxAddrVal) {
|
||||
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
|
||||
byte[] expr = reader.readNextByteArray(size);
|
||||
|
||||
// TODO: verify end addr calc with DWARFstd.pdf, inclusive vs exclusive
|
||||
results.add(new DWARFLocation(new DWARFRange(beginning, ending), expr));
|
||||
if (beginning == ending) {
|
||||
// skip adding empty ranges because Ghidra can't use them
|
||||
continue;
|
||||
}
|
||||
|
||||
DWARFRange range = new DWARFRange(baseAddress + beginning, baseAddress + ending);
|
||||
results.add(new DWARFLocation(range, expr));
|
||||
}
|
||||
return new DWARFLocationList(results);
|
||||
}
|
||||
|
@ -109,8 +101,7 @@ public class DWARFLocationList {
|
|||
*/
|
||||
public static DWARFLocationList readV5(BinaryReader reader, DWARFCompilationUnit cu)
|
||||
throws IOException {
|
||||
long baseAddrFixup = cu.getProgram().getProgramBaseAddressFixup();
|
||||
long baseAddr = baseAddrFixup;
|
||||
long baseAddr = cu.getPCRange().getFrom();
|
||||
DWARFProgram dprog = cu.getProgram();
|
||||
|
||||
List<DWARFLocation> list = new ArrayList<>();
|
||||
|
@ -149,24 +140,27 @@ public class DWARFLocationList {
|
|||
list.add(new DWARFLocation(baseAddr + startOfs, baseAddr + endOfs, expr));
|
||||
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: {
|
||||
baseAddr = reader.readNextUnsignedValue(cu.getPointerSize()) + baseAddrFixup;
|
||||
baseAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
||||
break;
|
||||
}
|
||||
case DW_LLE_start_end: {
|
||||
long startAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
||||
long endAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
||||
byte[] expr = reader.readNext(DWARFLocationList::uleb128SizedByteArray);
|
||||
list.add(new DWARFLocation(startAddr + baseAddrFixup, endAddr + baseAddrFixup,
|
||||
expr));
|
||||
list.add(new DWARFLocation(startAddr, endAddr, expr));
|
||||
break;
|
||||
}
|
||||
case DW_LLE_start_length: {
|
||||
long startAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
||||
int len = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
|
||||
byte[] expr = reader.readNext(DWARFLocationList::uleb128SizedByteArray);
|
||||
list.add(new DWARFLocation(startAddr + baseAddrFixup,
|
||||
startAddr + baseAddrFixup + len, expr));
|
||||
list.add(new DWARFLocation(startAddr, startAddr + len, expr));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -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.external.ExternalDebugInfo;
|
||||
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.golang.rtti.GoSymbolName;
|
||||
import ghidra.app.util.opinion.*;
|
||||
|
@ -533,6 +534,10 @@ public class DWARFProgram implements Closeable {
|
|||
return dwarfDTM;
|
||||
}
|
||||
|
||||
public List<DWARFCompilationUnit> getCompilationUnits() {
|
||||
return compUnits;
|
||||
}
|
||||
|
||||
public boolean isBigEndian() {
|
||||
return program.getLanguage().isBigEndian();
|
||||
}
|
||||
|
@ -541,6 +546,10 @@ public class DWARFProgram implements Closeable {
|
|||
return !program.getLanguage().isBigEndian();
|
||||
}
|
||||
|
||||
public BinaryReader getDebugLineBR() {
|
||||
return debugLineBR;
|
||||
}
|
||||
|
||||
private BinaryReader getBinaryReaderFor(String sectionName, TaskMonitor monitor)
|
||||
throws IOException {
|
||||
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 value raw offset or indirect address index (depending on the DWARFForm)
|
||||
|
@ -1149,14 +1158,14 @@ public class DWARFProgram implements Closeable {
|
|||
switch (form) {
|
||||
case DW_FORM_addr:
|
||||
case DW_FORM_udata:
|
||||
return value + programBaseAddressFixup;
|
||||
return value;
|
||||
case DW_FORM_addrx:
|
||||
case DW_FORM_addrx1:
|
||||
case DW_FORM_addrx2:
|
||||
case DW_FORM_addrx3:
|
||||
case DW_FORM_addrx4: {
|
||||
long addr = addressListTable.getOffset((int) value, cu);
|
||||
return addr + programBaseAddressFixup;
|
||||
return addr;
|
||||
}
|
||||
default:
|
||||
throw new IOException("Unsupported form %s".formatted(form));
|
||||
|
@ -1221,31 +1230,18 @@ public class DWARFProgram implements Closeable {
|
|||
*
|
||||
* @param diea {@link DIEAggregate}
|
||||
* @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
|
||||
*/
|
||||
public DWARFLine getLine(DIEAggregate diea, DWARFAttribute attribute) throws IOException {
|
||||
DWARFNumericAttribute attrib = diea.getAttribute(attribute, DWARFNumericAttribute.class);
|
||||
if (attrib == null || debugLineBR == null) {
|
||||
return null;
|
||||
return DWARFLine.empty();
|
||||
}
|
||||
long stmtListOffset = attrib.getUnsignedValue();
|
||||
debugLineBR.setPointerIndex(stmtListOffset);
|
||||
|
||||
// probe for the DWARFLine version number
|
||||
// length : dwarf_length
|
||||
// version : 2 bytes
|
||||
DWARFLengthValue lengthInfo = DWARFLengthValue.read(debugLineBR, getDefaultIntSize());
|
||||
if (lengthInfo == null) {
|
||||
throw new DWARFException("Invalid DWARFLine length at 0x%x".formatted(stmtListOffset));
|
||||
}
|
||||
|
||||
int version = debugLineBR.readNextUnsignedShort();
|
||||
|
||||
return version < 5
|
||||
? DWARFLine.readV4(this, debugLineBR, lengthInfo, version)
|
||||
: DWARFLine.readV5(this, debugLineBR, lengthInfo, version,
|
||||
diea.getCompilationUnit());
|
||||
DWARFLine result = DWARFLine.read(debugLineBR.clone(stmtListOffset), getDefaultIntSize(),
|
||||
diea.getCompilationUnit());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1337,21 +1333,23 @@ public class DWARFProgram implements Closeable {
|
|||
|
||||
public AddressRange getAddressRange(DWARFRange range, boolean isCode) {
|
||||
AddressSpace defAS = program.getAddressFactory().getDefaultAddressSpace();
|
||||
Address start = defAS.getAddress(range.getFrom(), true /* TODO check this */);
|
||||
Address end = defAS.getAddress(range.getTo() - 1, true /* TODO check this */);
|
||||
Address start =
|
||||
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);
|
||||
}
|
||||
|
||||
public Address getCodeAddress(Number offset) {
|
||||
public Address getCodeAddress(long offset) {
|
||||
return program.getAddressFactory()
|
||||
.getDefaultAddressSpace()
|
||||
.getAddress(offset.longValue(), true);
|
||||
.getAddress(offset + programBaseAddressFixup, true);
|
||||
}
|
||||
|
||||
public Address getDataAddress(Number offset) {
|
||||
public Address getDataAddress(long offset) {
|
||||
return program.getAddressFactory()
|
||||
.getDefaultAddressSpace()
|
||||
.getAddress(offset.longValue(), true);
|
||||
.getAddress(offset + programBaseAddressFixup, true);
|
||||
}
|
||||
|
||||
public boolean stackGrowsNegative() {
|
||||
|
|
|
@ -45,8 +45,8 @@ public class DWARFRangeList {
|
|||
byte pointerSize = cu.getPointerSize();
|
||||
List<DWARFRange> ranges = new ArrayList<>();
|
||||
|
||||
DWARFRange cuRange = cu.getPCRange();
|
||||
long baseAddress = cuRange != null ? cuRange.getFrom() : 0;
|
||||
long baseAddress = cu.getPCRange().getFrom();
|
||||
long maxAddrVal = pointerSize == 4 ? NumericUtilities.MAX_UNSIGNED_INT32_AS_LONG : -1;
|
||||
|
||||
while (reader.hasNext()) {
|
||||
// Read the beginning and ending addresses
|
||||
|
@ -59,8 +59,7 @@ public class DWARFRangeList {
|
|||
}
|
||||
|
||||
// Check to see if this is a base address entry
|
||||
if (beginning == -1 ||
|
||||
(pointerSize == 4 && beginning == NumericUtilities.MAX_UNSIGNED_INT32_AS_LONG)) {
|
||||
if (beginning == maxAddrVal) {
|
||||
baseAddress = ending;
|
||||
continue;
|
||||
}
|
||||
|
@ -85,8 +84,7 @@ public class DWARFRangeList {
|
|||
List<DWARFRange> list = new ArrayList<>();
|
||||
|
||||
DWARFProgram dprog = cu.getProgram();
|
||||
long baseAddrFixup = dprog.getProgramBaseAddressFixup();
|
||||
long baseAddr = baseAddrFixup;
|
||||
long baseAddr = cu.getPCRange().getFrom();
|
||||
|
||||
while (reader.hasNext()) {
|
||||
int rleId = reader.readNextUnsignedByte();
|
||||
|
@ -121,20 +119,19 @@ public class DWARFRangeList {
|
|||
break;
|
||||
}
|
||||
case DW_RLE_base_address: {
|
||||
baseAddr = reader.readNextUnsignedValue(cu.getPointerSize()) + baseAddrFixup;
|
||||
baseAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
||||
break;
|
||||
}
|
||||
case DW_RLE_start_end: {
|
||||
long startAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
||||
long endAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
||||
list.add(new DWARFRange(startAddr + baseAddrFixup, endAddr + baseAddrFixup));
|
||||
list.add(new DWARFRange(startAddr, endAddr));
|
||||
break;
|
||||
}
|
||||
case DW_RLE_start_length: {
|
||||
long startAddr = reader.readNextUnsignedValue(cu.getPointerSize());
|
||||
int len = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
|
||||
list.add(
|
||||
new DWARFRange(startAddr + baseAddrFixup, startAddr + baseAddrFixup + len));
|
||||
list.add(new DWARFRange(startAddr, startAddr + len));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -29,7 +29,7 @@ public class DWARFUnitHeader {
|
|||
* Reads the initial fields found in a unit header.
|
||||
*
|
||||
* @param dprog {@link DWARFProgram}
|
||||
* @param reader {@link BinaryReader} .debug_info stream
|
||||
* @param reader {@link BinaryReader} stream
|
||||
* @param abbrReader {@link BinaryReader} .debug_abbr stream
|
||||
* @param unitNumber ordinal of this item
|
||||
* @param monitor {@link TaskMonitor}
|
||||
|
@ -58,7 +58,7 @@ public class DWARFUnitHeader {
|
|||
}
|
||||
|
||||
DWARFUnitHeader partial = new DWARFUnitHeader(dprog, startOffset, endOffset,
|
||||
lengthInfo.length(), lengthInfo.intSize(), version, unitNumber);
|
||||
lengthInfo.intSize(), version, unitNumber);
|
||||
|
||||
if (2 <= version && version <= 4) {
|
||||
return DWARFCompilationUnit.readV4(partial, reader, abbrReader, monitor);
|
||||
|
@ -84,28 +84,23 @@ public class DWARFUnitHeader {
|
|||
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;
|
||||
|
||||
/**
|
||||
* Offset in the debug_info section of the end of this compUnit. (right after
|
||||
* the last DIE record)
|
||||
* Offset in the section of the end of this header. (exclusive)
|
||||
*/
|
||||
protected final long endOffset;
|
||||
|
||||
/**
|
||||
* Length in bytes of this compUnit header and DIE records.
|
||||
*/
|
||||
protected final long length;
|
||||
|
||||
/**
|
||||
* size of integers, 4=int32 or 8=int64
|
||||
*/
|
||||
protected final int intSize;
|
||||
|
||||
/**
|
||||
* DWARF ver number, as read from the compunit structure, currently not used but being kept.
|
||||
* 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;
|
||||
|
||||
|
@ -118,18 +113,16 @@ public class DWARFUnitHeader {
|
|||
this.dprog = other.dprog;
|
||||
this.startOffset = other.startOffset;
|
||||
this.endOffset = other.endOffset;
|
||||
this.length = other.length;
|
||||
this.intSize = other.intSize;
|
||||
this.dwarfVersion = other.dwarfVersion;
|
||||
this.unitNumber = other.unitNumber;
|
||||
}
|
||||
|
||||
protected DWARFUnitHeader(DWARFProgram dprog, long startOffset, long endOffset, long length,
|
||||
int intSize, short version, int unitNumber) {
|
||||
protected DWARFUnitHeader(DWARFProgram dprog, long startOffset, long endOffset, int intSize,
|
||||
short version, int unitNumber) {
|
||||
this.dprog = dprog;
|
||||
this.startOffset = startOffset;
|
||||
this.endOffset = endOffset;
|
||||
this.length = length;
|
||||
this.intSize = intSize;
|
||||
this.dwarfVersion = version;
|
||||
this.unitNumber = unitNumber;
|
||||
|
@ -143,17 +136,6 @@ public class DWARFUnitHeader {
|
|||
return dwarfVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* An unsigned long (4 bytes in 32-bit or 8 bytes in 64-bit format) representing
|
||||
* the length of the .debug_info contribution for this unit, not including the length
|
||||
* field itself.
|
||||
*
|
||||
* @return the length in bytes of this unit
|
||||
*/
|
||||
public long getLength() {
|
||||
return this.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the byte offset to the start of this unit.
|
||||
* @return the byte offset to the start of this unit
|
||||
|
|
|
@ -322,7 +322,7 @@ public class DWARFVariable {
|
|||
return false;
|
||||
}
|
||||
|
||||
setRamStorage(res + prog.getProgramBaseAddressFixup());
|
||||
setRamStorage(res);
|
||||
return true;
|
||||
}
|
||||
catch (DWARFExpressionException | UnsupportedOperationException
|
||||
|
@ -406,7 +406,7 @@ public class DWARFVariable {
|
|||
}
|
||||
else if (exprEvaluator.getRawLastRegister() == -1 && res != 0) {
|
||||
// static global variable location
|
||||
setRamStorage(res + prog.getProgramBaseAddressFixup());
|
||||
setRamStorage(res);
|
||||
}
|
||||
else {
|
||||
Msg.error(this,
|
||||
|
|
|
@ -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"; }
|
||||
}
|
|
@ -39,7 +39,7 @@ public enum DWARFAttribute {
|
|||
DW_AT_bit_offset(0xc), // dwarf-3
|
||||
DW_AT_bit_size(0xd, constant, exprloc, reference),
|
||||
//DW_AT_element_list(0xf),
|
||||
DW_AT_stmt_list(0x10, lineptr),
|
||||
DW_AT_stmt_list(0x10, lineptr, constant),
|
||||
DW_AT_low_pc(0x11, address),
|
||||
DW_AT_high_pc(0x12, address, constant),
|
||||
DW_AT_language(0x13, constant),
|
||||
|
@ -252,7 +252,7 @@ public enum DWARFAttribute {
|
|||
|
||||
@Override
|
||||
protected String getRawAttributeIdDescription() {
|
||||
return "DW_AT_???? %d (0x%x)".formatted(attributeId, attributeId);
|
||||
return "DW_AT_???? %d (0x%x)".formatted(rawAttributeId, rawAttributeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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.util.HashMap;
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,25 +15,24 @@
|
|||
*/
|
||||
package ghidra.app.util.bin.format.dwarf.line;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class StateMachine {
|
||||
public class DWARFLineProgramState {
|
||||
/**
|
||||
* The program-counter value corresponding to a machine instruction
|
||||
* generated by the compiler.
|
||||
*/
|
||||
public long address;
|
||||
|
||||
/**
|
||||
* An unsigned integer indicating the identity of the source file
|
||||
* corresponding to a machine instruction.
|
||||
*/
|
||||
public int file;
|
||||
public int file = 1;
|
||||
/**
|
||||
* An unsigned integer indicating a source line number. Lines are
|
||||
* numbered beginning at 1. The compiler may emit the value 0 in cases
|
||||
* 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.
|
||||
* Columns are numbered beginning at 1. The value 0 is reserved to
|
||||
|
@ -57,27 +55,42 @@ public class StateMachine {
|
|||
*/
|
||||
public boolean isEndSequence;
|
||||
|
||||
public void reset(boolean defaultIsStatement) {
|
||||
address = 0;
|
||||
file = 1;
|
||||
line = 1;
|
||||
column = 0;
|
||||
isStatement = defaultIsStatement;
|
||||
isBasicBlock = false;
|
||||
isEndSequence = false;
|
||||
public boolean prologueEnd;
|
||||
|
||||
public boolean epilogueBegin;
|
||||
|
||||
public long isa;
|
||||
|
||||
public long discriminator;
|
||||
|
||||
public DWARFLineProgramState(boolean defaultIsStatement) {
|
||||
this.isStatement = defaultIsStatement;
|
||||
}
|
||||
|
||||
void print() {
|
||||
Msg.info(this, "ADDR=" + Long.toHexString(address));
|
||||
Msg.info(this, " ");
|
||||
Msg.info(this, "FILE=" + Long.toHexString(file));
|
||||
Msg.info(this, " ");
|
||||
Msg.info(this, "LINE=" + Long.toHexString(line));
|
||||
Msg.info(this, " ");
|
||||
Msg.info(this, "LINE=" + line);
|
||||
Msg.info(this, " ");
|
||||
Msg.info(this, "COL=" + Long.toHexString(column));
|
||||
Msg.info(this, " ");
|
||||
Msg.info(this, "");
|
||||
public DWARFLineProgramState(DWARFLineProgramState other) {
|
||||
this.address = other.address;
|
||||
this.file = other.file;
|
||||
this.line = other.line;
|
||||
this.column = other.column;
|
||||
this.isStatement = other.isStatement;
|
||||
this.isBasicBlock = other.isBasicBlock;
|
||||
this.isEndSequence = other.isEndSequence;
|
||||
this.prologueEnd = other.prologueEnd;
|
||||
this.epilogueBegin = other.epilogueBegin;
|
||||
this.isa = other.isa;
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -24,8 +24,8 @@ public class MockDWARFCompilationUnit extends DWARFCompilationUnit {
|
|||
private int dieCount;
|
||||
|
||||
public MockDWARFCompilationUnit(MockDWARFProgram dwarfProgram, long startOffset, long endOffset,
|
||||
long length, int intSize, short version, byte pointerSize, int compUnitNumber) {
|
||||
super(dwarfProgram, startOffset, endOffset, length, intSize, version, pointerSize,
|
||||
int intSize, short version, byte pointerSize, int compUnitNumber) {
|
||||
super(dwarfProgram, startOffset, endOffset, intSize, version, pointerSize,
|
||||
compUnitNumber, startOffset, null);
|
||||
}
|
||||
|
||||
|
|
|
@ -68,8 +68,8 @@ public class MockDWARFProgram extends DWARFProgram {
|
|||
compUnitDieIndex.put(dieOffsets.length - 1, currentCompUnit);
|
||||
}
|
||||
long start = compUnits.size() * 0x1000;
|
||||
currentCompUnit = new MockDWARFCompilationUnit(this, start, start + 0x1000, 0,
|
||||
dwarfIntSize, (short) 4, (byte) 8, 0);
|
||||
currentCompUnit = new MockDWARFCompilationUnit(this, start, start + 0x1000, dwarfIntSize,
|
||||
(short) 4, (byte) 8, 0);
|
||||
compUnits.add(currentCompUnit);
|
||||
compUnitDieIndex.put(dieOffsets.length - 1, currentCompUnit);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue