mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
Merge remote-tracking branch 'origin/GP-5792_dev747368_DWARF_macro_info'
This commit is contained in:
commit
13ffa3a4a8
19 changed files with 947 additions and 15 deletions
67
Ghidra/Features/Base/ghidra_scripts/DWARFMacroScript.java
Normal file
67
Ghidra/Features/Base/ghidra_scripts/DWARFMacroScript.java
Normal file
|
@ -0,0 +1,67 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
// Parses the .debug_macro section of a file with DWARF debug info (version 5+) and
|
||||
// prints the result to the ghidra console.
|
||||
// @category DWARF
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.app.util.bin.format.dwarf.*;
|
||||
import ghidra.app.util.bin.format.dwarf.macro.DWARFMacroHeader;
|
||||
import ghidra.app.util.bin.format.dwarf.macro.entry.*;
|
||||
import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProvider;
|
||||
import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProviderFactory;
|
||||
|
||||
public class DWARFMacroScript extends GhidraScript {
|
||||
|
||||
@Override
|
||||
protected void run() throws Exception {
|
||||
DWARFSectionProvider dsp =
|
||||
DWARFSectionProviderFactory.createSectionProviderFor(currentProgram, monitor);
|
||||
if (dsp == null) {
|
||||
printerr("Unable to find DWARF information");
|
||||
return;
|
||||
}
|
||||
|
||||
try (DWARFProgram dprog =
|
||||
new DWARFProgram(currentProgram, new DWARFImportOptions(), monitor, dsp)) {
|
||||
dprog.init(monitor);
|
||||
for (DWARFCompilationUnit cu : dprog.getCompilationUnits()) {
|
||||
dumpMacros(cu.getMacros(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dumpMacros(DWARFMacroHeader macroHeader, int indent) throws IOException {
|
||||
for (DWARFMacroInfoEntry macroEntry : macroHeader.getEntries()) {
|
||||
print(macroEntry.toString().indent(indent));
|
||||
switch (macroEntry) {
|
||||
case DWARFMacroImport macroImport:
|
||||
dumpMacros(macroImport.getImportedMacroHeader(), indent + 2);
|
||||
break;
|
||||
case DWARFMacroStartFile macroStartFile:
|
||||
indent += 2;
|
||||
break;
|
||||
case DWARFMacroEndFile macroEndFile:
|
||||
indent -= 2;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -23,6 +23,7 @@ import java.util.Map;
|
|||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.dwarf.line.DWARFLine;
|
||||
import ghidra.app.util.bin.format.dwarf.macro.DWARFMacroHeader;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
|
@ -233,6 +234,13 @@ public class DWARFCompilationUnit extends DWARFUnitHeader {
|
|||
return line;
|
||||
}
|
||||
|
||||
public DWARFMacroHeader getMacros() {
|
||||
long macrosOffset = diea.getUnsignedLong(DW_AT_macros, -1);
|
||||
return macrosOffset != -1
|
||||
? diea.getProgram().getMacroHeader(macrosOffset, this)
|
||||
: DWARFMacroHeader.EMTPY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filename that produced the compile unit
|
||||
*
|
||||
|
|
|
@ -34,6 +34,8 @@ 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.macro.DWARFMacroHeader;
|
||||
import ghidra.app.util.bin.format.dwarf.macro.entry.DWARFMacroInfoEntry;
|
||||
import ghidra.app.util.bin.format.dwarf.sectionprovider.*;
|
||||
import ghidra.app.util.bin.format.golang.rtti.GoSymbolName;
|
||||
import ghidra.app.util.opinion.*;
|
||||
|
@ -158,6 +160,7 @@ public class DWARFProgram implements Closeable {
|
|||
private BinaryReader debugAbbrBR;
|
||||
private BinaryReader debugAddr; // v5+
|
||||
private BinaryReader debugStrOffsets; // v5+
|
||||
private BinaryReader debugMacros; // v5+
|
||||
|
||||
// dieOffsets, siblingIndexes, parentIndexes contain for each DIE the information needed
|
||||
// to read each DIE and to navigate to parent / child / sibling elements.
|
||||
|
@ -195,6 +198,8 @@ public class DWARFProgram implements Closeable {
|
|||
// In other words, a map of inbound links to a DIEA.
|
||||
private ListValuedMap<Long, Long> typeReferers = new ArrayListValuedHashMap<>();
|
||||
|
||||
private Map<Long, DWARFLine> cachedDWARFLines = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Main constructor for DWARFProgram.
|
||||
* <p>
|
||||
|
@ -250,6 +255,8 @@ public class DWARFProgram implements Closeable {
|
|||
this.debugAddr = getBinaryReaderFor(DWARFSectionNames.DEBUG_ADDR, monitor);
|
||||
this.debugStrOffsets = getBinaryReaderFor(DWARFSectionNames.DEBUG_STROFFSETS, monitor);
|
||||
|
||||
this.debugMacros = getBinaryReaderFor(DWARFSectionNames.DEBUG_MACRO, monitor);
|
||||
|
||||
this.rangeListTable =
|
||||
new DWARFIndirectTable(this.debugRngLists, DWARFCompilationUnit::getRangeListsBase);
|
||||
this.addressListTable =
|
||||
|
@ -374,7 +381,7 @@ public class DWARFProgram implements Closeable {
|
|||
typeReferers.put(typeRef.getOffset(), diea.getOffset());
|
||||
}
|
||||
}
|
||||
|
||||
monitor.initialize(0, "");
|
||||
}
|
||||
|
||||
protected void indexDIEAggregates(LongArrayList aggrTargets, TaskMonitor monitor)
|
||||
|
@ -1281,11 +1288,41 @@ public class DWARFProgram implements Closeable {
|
|||
return DWARFLine.empty();
|
||||
}
|
||||
long stmtListOffset = attrib.getUnsignedValue();
|
||||
DWARFLine result = DWARFLine.read(debugLineBR.clone(stmtListOffset), getDefaultIntSize(),
|
||||
diea.getCompilationUnit());
|
||||
return getLine(stmtListOffset, diea.getCompilationUnit(), true);
|
||||
}
|
||||
|
||||
public DWARFLine getLine(long offset, DWARFCompilationUnit cu, boolean readIfMissing)
|
||||
throws IOException {
|
||||
DWARFLine result = cachedDWARFLines.get(offset);
|
||||
if (result == null && readIfMissing) {
|
||||
result = DWARFLine.read(debugLineBR.clone(offset), getDefaultIntSize(), cu);
|
||||
cachedDWARFLines.put(offset, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public DWARFMacroHeader getMacroHeader(long offset, DWARFCompilationUnit cu) {
|
||||
if (debugMacros != null) {
|
||||
try {
|
||||
return DWARFMacroHeader.readV5(debugMacros.clone(offset), cu);
|
||||
}
|
||||
catch (IOException e) {
|
||||
// ignore, fall thru return emtpy
|
||||
}
|
||||
}
|
||||
return DWARFMacroHeader.EMTPY;
|
||||
}
|
||||
|
||||
public List<DWARFMacroInfoEntry> getMacroEntries(DWARFMacroHeader macroHeader)
|
||||
throws IOException {
|
||||
if (debugMacros == null) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
return DWARFMacroHeader.readMacroEntries(
|
||||
debugMacros.clone(macroHeader.getEntriesStartOffset()), macroHeader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns iterable that traverses all {@link DIEAggregate}s in the program.
|
||||
*
|
||||
|
|
|
@ -82,7 +82,7 @@ public class StringTable {
|
|||
*/
|
||||
public String getStringAtOffset(long offset) throws IOException {
|
||||
if (!isValid(offset)) {
|
||||
throw new IOException("Invalid offset requested " + offset);
|
||||
throw new IOException("Invalid offset requested %d [0x%x]".formatted(offset, offset));
|
||||
}
|
||||
|
||||
String s = cache.get(offset);
|
||||
|
|
|
@ -36,6 +36,10 @@ public abstract class DWARFAttributeValue {
|
|||
return def.getAttributeName();
|
||||
}
|
||||
|
||||
public String getValueString(DWARFCompilationUnit cu) {
|
||||
return toString();
|
||||
}
|
||||
|
||||
public String toString(DWARFCompilationUnit compilationUnit) {
|
||||
return toString();
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.util.bin.format.dwarf.attribs;
|
||||
|
||||
import ghidra.app.util.bin.format.dwarf.DWARFCompilationUnit;
|
||||
import ghidra.util.NumericUtilities;
|
||||
|
||||
/**
|
||||
|
@ -36,6 +37,11 @@ public class DWARFBlobAttribute extends DWARFAttributeValue {
|
|||
return bytes.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueString(DWARFCompilationUnit cu) {
|
||||
return NumericUtilities.convertBytesToString(bytes, " ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "%s : %s = [%d]%s".formatted(getAttributeName(), getAttributeForm(), bytes.length,
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package ghidra.app.util.bin.format.dwarf.attribs;
|
||||
|
||||
import ghidra.app.util.bin.format.dwarf.DWARFCompilationUnit;
|
||||
|
||||
/**
|
||||
* DWARF boolean attribute.
|
||||
*/
|
||||
|
@ -30,6 +32,11 @@ public class DWARFBooleanAttribute extends DWARFAttributeValue {
|
|||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueString(DWARFCompilationUnit cu) {
|
||||
return "%b".formatted(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "%s : %s = %s".formatted(getAttributeName(), getAttributeForm(), getValue());
|
||||
|
|
|
@ -95,6 +95,12 @@ public class DWARFNumericAttribute extends DWARFAttributeValue {
|
|||
return value.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueString(DWARFCompilationUnit cu) {
|
||||
long v = getValue();
|
||||
return "%d [0x%x]".formatted(v, v);
|
||||
}
|
||||
|
||||
public long getUnsignedValue() {
|
||||
return value.getUnsignedValue();
|
||||
}
|
||||
|
|
|
@ -32,6 +32,11 @@ public class DWARFStringAttribute extends DWARFAttributeValue {
|
|||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueString(DWARFCompilationUnit cu) {
|
||||
return getValue(cu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "%s : %s = \"%s\"".formatted(getAttributeName(), getAttributeForm(), value);
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
/* ###
|
||||
* 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.macro;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.dwarf.DWARFCompilationUnit;
|
||||
import ghidra.app.util.bin.format.dwarf.attribs.DWARFForm;
|
||||
import ghidra.app.util.bin.format.dwarf.line.DWARFLine;
|
||||
import ghidra.app.util.bin.format.dwarf.macro.entry.DWARFMacroInfoEntry;
|
||||
import ghidra.program.model.data.LEB128;
|
||||
|
||||
/**
|
||||
* Represents a DWARF Macro Header
|
||||
*/
|
||||
public class DWARFMacroHeader {
|
||||
|
||||
private static final int OFFSET_SIZE_FLAG_MASK = 0x1;
|
||||
private static final int DEBUG_LINE_OFFSET_FLAG_MASK = 0x2;
|
||||
private static final int OPCODE_OPERANDS_TABLE_FLAG_MASK = 0x4;
|
||||
|
||||
/**
|
||||
* Reads a {@code DWARFMacroHeader} from a stream.
|
||||
*
|
||||
* @param reader source of bytes
|
||||
* @param cu {@link DWARFCompilationUnit} that pointed to this macro header
|
||||
* @return macro header, never null
|
||||
* @throws IOException if reading fails
|
||||
*/
|
||||
public static DWARFMacroHeader readV5(BinaryReader reader, DWARFCompilationUnit cu)
|
||||
throws IOException {
|
||||
long startOffset = reader.getPointerIndex();
|
||||
int version = reader.readNextUnsignedShort();
|
||||
if (version != 5) {
|
||||
throw new IllegalArgumentException("Unsupported DWARF Macro version: " + version);
|
||||
}
|
||||
|
||||
int flags = reader.readNextUnsignedByte();
|
||||
int intSize = (flags & OFFSET_SIZE_FLAG_MASK) == OFFSET_SIZE_FLAG_MASK ? 8 : 4;
|
||||
|
||||
DWARFLine line = null;
|
||||
long debug_line_offset = -1;
|
||||
if ((flags & DEBUG_LINE_OFFSET_FLAG_MASK) != 0) {
|
||||
debug_line_offset = reader.readNextUnsignedValue(intSize);
|
||||
line = cu.getProgram().getLine(debug_line_offset, cu, false);
|
||||
}
|
||||
Map<Integer, List<DWARFForm>> opcodeMap = DWARFMacroOpcode.defaultOpcodeOperandMap;
|
||||
if ((flags & OPCODE_OPERANDS_TABLE_FLAG_MASK) != 0) {
|
||||
opcodeMap = new HashMap<>(opcodeMap);
|
||||
readMacroOpcodeTable(reader, opcodeMap);
|
||||
}
|
||||
return new DWARFMacroHeader(startOffset, version, flags, debug_line_offset, intSize,
|
||||
reader.getPointerIndex(), cu, line, opcodeMap);
|
||||
}
|
||||
|
||||
private static void readMacroOpcodeTable(BinaryReader reader,
|
||||
Map<Integer, List<DWARFForm>> opcodeMap) throws IOException {
|
||||
// TODO: needs testing with actual data emitted from toolchain
|
||||
int numOpcodes = reader.readNextUnsignedByte();
|
||||
for (int i = 0; i < numOpcodes; i++) {
|
||||
int opcode = reader.readNextUnsignedByte();
|
||||
int numOperands = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
|
||||
DWARFForm[] operandForms = new DWARFForm[numOperands];
|
||||
for (int formIndex = 0; formIndex < numOperands; formIndex++) {
|
||||
int formInt = reader.readNextUnsignedByte();
|
||||
operandForms[formIndex] = DWARFForm.of(formInt);
|
||||
}
|
||||
opcodeMap.put(opcode, List.of(operandForms));
|
||||
}
|
||||
}
|
||||
|
||||
public static List<DWARFMacroInfoEntry> readMacroEntries(BinaryReader reader,
|
||||
DWARFMacroHeader macroHeader) throws IOException {
|
||||
List<DWARFMacroInfoEntry> results = new ArrayList<>();
|
||||
DWARFMacroInfoEntry entry;
|
||||
while ((entry = DWARFMacroInfoEntry.read(reader, macroHeader)) != null) {
|
||||
results.add(entry);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private long startOffset;
|
||||
private int version;
|
||||
private int flags;
|
||||
private long debug_line_offset;
|
||||
private int intSize;
|
||||
private long entriesStartOffset;
|
||||
private Map<Integer, List<DWARFForm>> opcodeMap;
|
||||
private DWARFCompilationUnit cu;
|
||||
private DWARFLine line;
|
||||
|
||||
public DWARFMacroHeader(long startOffset, int version, int flags, long debug_line_offset,
|
||||
int intSize, long entriesStartOffset, DWARFCompilationUnit cu, DWARFLine line,
|
||||
Map<Integer, List<DWARFForm>> opcodeMap) {
|
||||
this.startOffset = startOffset;
|
||||
this.version = version;
|
||||
this.flags = flags;
|
||||
this.debug_line_offset = debug_line_offset;
|
||||
this.intSize = intSize;
|
||||
this.entriesStartOffset = entriesStartOffset;
|
||||
this.cu = cu;
|
||||
this.line = line;
|
||||
this.opcodeMap = opcodeMap;
|
||||
}
|
||||
|
||||
public DWARFLine getLine() {
|
||||
return line;
|
||||
}
|
||||
|
||||
public long getDebug_line_offset() {
|
||||
return debug_line_offset;
|
||||
}
|
||||
|
||||
public int getIntSize() {
|
||||
return intSize;
|
||||
}
|
||||
|
||||
public long getEntriesStartOffset() {
|
||||
return entriesStartOffset;
|
||||
}
|
||||
|
||||
public List<DWARFMacroInfoEntry> getEntries() throws IOException {
|
||||
return cu.getProgram().getMacroEntries(this);
|
||||
}
|
||||
|
||||
public DWARFCompilationUnit getCompilationUnit() {
|
||||
return cu;
|
||||
}
|
||||
|
||||
public Map<Integer, List<DWARFForm>> getOpcodeMap() {
|
||||
return opcodeMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DWARFMacroHeader: startOffset=0x%x, debug_line_offset=0x%x, intSize=%d"
|
||||
.formatted(startOffset, debug_line_offset, intSize);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
public static final DWARFMacroHeader EMTPY =
|
||||
new DWARFMacroHeader(0, 0, 0, 0, 0, 0, null, DWARFLine.empty(), null) {
|
||||
@Override
|
||||
public List<DWARFMacroInfoEntry> getEntries() throws IOException {
|
||||
return List.of();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/* ###
|
||||
* 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.macro;
|
||||
|
||||
import static ghidra.app.util.bin.format.dwarf.attribs.DWARFForm.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.bin.format.dwarf.attribs.DWARFAttributeDef;
|
||||
import ghidra.app.util.bin.format.dwarf.attribs.DWARFForm;
|
||||
|
||||
/**
|
||||
* DWARF macro entry opcodes and their expected operand types.
|
||||
* <p>
|
||||
* DWARF5
|
||||
*/
|
||||
public enum DWARFMacroOpcode {
|
||||
|
||||
/** This is not an official opcode in the DWARF standard, but represents the
|
||||
* entry with opcode 0 that terminates a macro unit.
|
||||
*/
|
||||
MACRO_UNIT_TERMINATOR(0, "unknown"),
|
||||
|
||||
DW_MACRO_define(0x1, "#define", DW_FORM_udata, DW_FORM_string),
|
||||
DW_MACRO_undef(0x2, "#undef", DW_FORM_udata, DW_FORM_string),
|
||||
DW_MACRO_start_file(0x3, "startfile", DW_FORM_udata, DW_FORM_udata),
|
||||
DW_MACRO_end_file(0x4, "endfile"),
|
||||
DW_MACRO_define_strp(0x5, "#define", DW_FORM_udata, DW_FORM_strp),
|
||||
DW_MACRO_undef_strp(0x6, "#undef", DW_FORM_udata, DW_FORM_strp),
|
||||
DW_MACRO_import(0x7, "#include", DW_FORM_sec_offset),
|
||||
DW_MACRO_define_sup(0x8, "#define", DW_FORM_udata, DW_FORM_strp_sup),
|
||||
DW_MACRO_undef_sup(0x9, "#undef", DW_FORM_udata, DW_FORM_strp_sup),
|
||||
DW_MACRO_import_sup(0xa, "#include", DW_FORM_sec_offset),
|
||||
DW_MACRO_define_strx(0xb, "#define", DW_FORM_udata, DW_FORM_strx),
|
||||
DW_MACRO_undef_strx(0xc, "#undef", DW_FORM_udata, DW_FORM_strx);
|
||||
//DW_MACRO_lo_user(0xe0),
|
||||
//DW_MACRO_hi_user(0xff);
|
||||
|
||||
private final int rawOpcode;
|
||||
private final String description;
|
||||
private final DWARFForm[] operandForms;
|
||||
|
||||
// enum is small enough that linear search is probably fast enough
|
||||
private static DWARFMacroOpcode[] lookupValues = values();
|
||||
|
||||
DWARFMacroOpcode(int rawOpcode, String description, DWARFForm... operandForms) {
|
||||
this.rawOpcode = rawOpcode;
|
||||
this.description = description;
|
||||
this.operandForms = operandForms;
|
||||
}
|
||||
|
||||
public int getRawOpcode() {
|
||||
return rawOpcode;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public DWARFForm[] getOperandForms() {
|
||||
return operandForms;
|
||||
}
|
||||
|
||||
public static DWARFMacroOpcode of(int opcodeVal) {
|
||||
for (DWARFMacroOpcode opcode : lookupValues) {
|
||||
if (opcode.rawOpcode == opcodeVal) {
|
||||
return opcode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static final Map<Integer, List<DWARFForm>> defaultOpcodeOperandMap =
|
||||
getDefaultOpcodeOperandMap();
|
||||
|
||||
private static Map<Integer, List<DWARFForm>> getDefaultOpcodeOperandMap() {
|
||||
Map<Integer, List<DWARFForm>> results = new HashMap<>();
|
||||
for (DWARFMacroOpcode opcode : DWARFMacroOpcode.values()) {
|
||||
results.put(opcode.getRawOpcode(), List.of(opcode.getOperandForms()));
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(results);
|
||||
}
|
||||
|
||||
public static class Def extends DWARFAttributeDef<DWARFMacroOpcode> {
|
||||
|
||||
public Def(DWARFMacroOpcode opcode, int rawOpcode, DWARFForm form) {
|
||||
super(opcode, rawOpcode, form, -1 /* NA */);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/* ###
|
||||
* 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.macro.entry;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import ghidra.app.util.bin.format.dwarf.attribs.DWARFNumericAttribute;
|
||||
import ghidra.app.util.bin.format.dwarf.attribs.DWARFStringAttribute;
|
||||
import ghidra.app.util.bin.format.dwarf.macro.DWARFMacroHeader;
|
||||
import ghidra.app.util.bin.format.dwarf.macro.DWARFMacroOpcode;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
/**
|
||||
* Represents a "#define ...." macro element.
|
||||
*/
|
||||
public class DWARFMacroDefine extends DWARFMacroInfoEntry {
|
||||
//@formatter:off
|
||||
private static final Pattern PARSEMACROREGEX = Pattern.compile(
|
||||
"([^( ]+)" + // "NAME" group=1
|
||||
"|([^( ]+) ([^ ()]+)" + // "NAME VALUE" group=2,3
|
||||
"|([^( ]+)\\(([^)]+)\\) (.*)"); // "NAME(arg, arg) BODY" group=4,5,6
|
||||
//@formatter:on
|
||||
|
||||
public record MacroInfo(String macro, String symbolName, List<String> parameters,
|
||||
boolean isFunctionLike, String definition) {
|
||||
@Override
|
||||
public String toString() {
|
||||
if (macro.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder("Macro Symbol: ");
|
||||
sb.append(symbolName);
|
||||
sb.append(" ");
|
||||
if (isFunctionLike) {
|
||||
sb.append("(function-like) ");
|
||||
sb.append("parameters[%d]: ".formatted(parameters.size()));
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
sb.append(parameters.get(i));
|
||||
if (i != parameters.size() - 1) {
|
||||
sb.append(",");
|
||||
}
|
||||
}
|
||||
sb.append(" ");
|
||||
}
|
||||
else {
|
||||
sb.append("(object-like) ");
|
||||
}
|
||||
sb.append("definition: ");
|
||||
sb.append(definition.isEmpty() ? "-none-" : "\"%s\"".formatted(definition));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static MacroInfo parseMacro(String macroString) {
|
||||
Matcher m = PARSEMACROREGEX.matcher(macroString);
|
||||
if (!m.matches() || m.group(1) != null) {
|
||||
return new MacroInfo(macroString, macroString, List.of(), false, "");
|
||||
}
|
||||
if (m.group(2) != null) {
|
||||
return new MacroInfo(macroString, m.group(2), List.of(), false, m.group(3));
|
||||
}
|
||||
if (m.group(4) != null) {
|
||||
return new MacroInfo(macroString, m.group(4), Arrays.asList(m.group(5).split(",")),
|
||||
true, m.group(6));
|
||||
}
|
||||
throw new AssertException();
|
||||
}
|
||||
|
||||
public DWARFMacroDefine(int lineNumber, String defineString, DWARFMacroHeader parent) {
|
||||
super(DWARFMacroOpcode.DW_MACRO_define, parent);
|
||||
operandValues[0] = new DWARFNumericAttribute(lineNumber, operandDef(0));
|
||||
operandValues[1] = new DWARFStringAttribute(defineString, operandDef(1));
|
||||
}
|
||||
|
||||
public DWARFMacroDefine(DWARFMacroInfoEntry other){
|
||||
super(other);
|
||||
}
|
||||
|
||||
public int getLineNumber() throws IOException {
|
||||
return getOperand(0, DWARFNumericAttribute.class).getUnsignedIntExact();
|
||||
}
|
||||
|
||||
public String getMacro() throws IOException {
|
||||
return getOperand(1, DWARFStringAttribute.class).getValue(macroHeader.getCompilationUnit());
|
||||
}
|
||||
|
||||
public MacroInfo getMacroInfo() throws IOException {
|
||||
return parseMacro(getMacro());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return "%s: line: %d, %s".formatted(getName(), getLineNumber(), getMacroInfo());
|
||||
}
|
||||
catch (IOException e) {
|
||||
return super.toString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/* ###
|
||||
* 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.macro.entry;
|
||||
|
||||
/**
|
||||
* Represents the end of an included source file.
|
||||
*/
|
||||
public class DWARFMacroEndFile extends DWARFMacroInfoEntry {
|
||||
|
||||
public DWARFMacroEndFile(DWARFMacroInfoEntry other) {
|
||||
super(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.dwarf.macro.entry;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.format.dwarf.DWARFCompilationUnit;
|
||||
import ghidra.app.util.bin.format.dwarf.attribs.DWARFNumericAttribute;
|
||||
import ghidra.app.util.bin.format.dwarf.macro.DWARFMacroHeader;
|
||||
|
||||
/**
|
||||
* Represents the inclusion of macro entries from another macro header.
|
||||
*/
|
||||
public class DWARFMacroImport extends DWARFMacroInfoEntry {
|
||||
|
||||
public DWARFMacroImport(DWARFMacroInfoEntry other) {
|
||||
super(other);
|
||||
}
|
||||
|
||||
public long getOffset() throws IOException {
|
||||
return getOperand(0, DWARFNumericAttribute.class).getUnsignedValue();
|
||||
}
|
||||
|
||||
public DWARFMacroHeader getImportedMacroHeader() throws IOException {
|
||||
long offset = getOffset();
|
||||
DWARFCompilationUnit cu = macroHeader.getCompilationUnit();
|
||||
return cu.getProgram().getMacroHeader(offset, cu);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.dwarf.macro.entry;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.dwarf.attribs.*;
|
||||
import ghidra.app.util.bin.format.dwarf.macro.DWARFMacroHeader;
|
||||
import ghidra.app.util.bin.format.dwarf.macro.DWARFMacroOpcode;
|
||||
import ghidra.app.util.bin.format.dwarf.macro.DWARFMacroOpcode.Def;
|
||||
|
||||
/**
|
||||
* Represents a generic macro info entry, and can contain any macro entry element.
|
||||
* <p>
|
||||
* Specific macro entry classes are derived from this and provide getters to ease fetching
|
||||
* values that are expected for that class. These classes are expected to implement a copy-ctor
|
||||
* that accepts a DWARFMacroInfoEntry containing the raw data to be wrapped, and must be registered
|
||||
* in {@link #toSpecializedForm(DWARFMacroInfoEntry)} method's switch() statement.
|
||||
*/
|
||||
public class DWARFMacroInfoEntry {
|
||||
|
||||
/**
|
||||
* Reads a DWARF macro info entry from the stream.
|
||||
*
|
||||
* @param reader {@link BinaryReader} stream
|
||||
* @param macroHeader the parent {@link DWARFMacroHeader}
|
||||
* @return a {@link DWARFMacroInfoEntry}, or subclass if element is a known opcode, or
|
||||
* {@code null} if the element was the end-of-list marker
|
||||
* @throws IOException if error reading or unknown opcode
|
||||
*/
|
||||
public static DWARFMacroInfoEntry read(BinaryReader reader, DWARFMacroHeader macroHeader)
|
||||
throws IOException {
|
||||
|
||||
Map<Integer, List<DWARFForm>> opcodeMap = macroHeader.getOpcodeMap();
|
||||
|
||||
long startOffset = reader.getPointerIndex();
|
||||
int rawOpcode = reader.readNextUnsignedByte();
|
||||
DWARFMacroOpcode opcode = DWARFMacroOpcode.of(rawOpcode);
|
||||
List<DWARFForm> operandForms = opcodeMap.get(rawOpcode);
|
||||
if (operandForms == null) {
|
||||
throw new IOException("Unknown DW_MACRO opcode %x at position %d [0x%x]"
|
||||
.formatted(rawOpcode, startOffset, startOffset));
|
||||
}
|
||||
|
||||
DWARFAttributeValue[] operandValues = new DWARFAttributeValue[operandForms.size()];
|
||||
for (int i = 0; i < operandForms.size(); i++) {
|
||||
DWARFForm form = operandForms.get(i);
|
||||
Def opcodeDef = new DWARFMacroOpcode.Def(opcode, rawOpcode, form);
|
||||
DWARFFormContext readContext = new DWARFFormContext(reader,
|
||||
macroHeader.getCompilationUnit(), opcodeDef, macroHeader.getIntSize());
|
||||
operandValues[i] = form.readValue(readContext);
|
||||
}
|
||||
DWARFMacroInfoEntry genericEntry =
|
||||
new DWARFMacroInfoEntry(opcode, rawOpcode, operandValues, macroHeader);
|
||||
|
||||
return toSpecializedForm(genericEntry);
|
||||
}
|
||||
|
||||
public static DWARFMacroInfoEntry toSpecializedForm(DWARFMacroInfoEntry genericEntry) {
|
||||
return switch (genericEntry.getOpcode()) {
|
||||
case MACRO_UNIT_TERMINATOR -> null;
|
||||
case DW_MACRO_define, DW_MACRO_define_strp, DW_MACRO_define_sup, DW_MACRO_define_strx -> new DWARFMacroDefine(
|
||||
genericEntry);
|
||||
case DW_MACRO_undef, DW_MACRO_undef_strp, DW_MACRO_undef_sup, DW_MACRO_undef_strx -> new DWARFMacroUndef(
|
||||
genericEntry);
|
||||
case DW_MACRO_start_file -> new DWARFMacroStartFile(genericEntry);
|
||||
case DW_MACRO_end_file -> new DWARFMacroEndFile(genericEntry);
|
||||
case DW_MACRO_import, DW_MACRO_import_sup -> new DWARFMacroImport(genericEntry);
|
||||
default -> genericEntry;
|
||||
};
|
||||
}
|
||||
|
||||
protected DWARFMacroOpcode opcode;
|
||||
protected int rawOpcode;
|
||||
protected DWARFAttributeValue[] operandValues;
|
||||
protected DWARFMacroHeader macroHeader;
|
||||
|
||||
protected DWARFMacroInfoEntry(DWARFMacroOpcode opcode, DWARFMacroHeader macroHeader) {
|
||||
this.opcode = opcode;
|
||||
this.rawOpcode = opcode.getRawOpcode();
|
||||
this.macroHeader = macroHeader;
|
||||
this.operandValues = new DWARFAttributeValue[opcode.getOperandForms().length];
|
||||
}
|
||||
|
||||
public DWARFMacroInfoEntry(DWARFMacroOpcode opcode, int rawOpcode,
|
||||
DWARFAttributeValue[] operandValues, DWARFMacroHeader macroHeader) {
|
||||
this.opcode = opcode;
|
||||
this.rawOpcode = rawOpcode;
|
||||
this.operandValues = operandValues;
|
||||
this.macroHeader = macroHeader;
|
||||
}
|
||||
|
||||
protected DWARFMacroInfoEntry(DWARFMacroInfoEntry other) {
|
||||
this.opcode = other.opcode;
|
||||
this.rawOpcode = other.rawOpcode;
|
||||
this.operandValues = other.operandValues;
|
||||
this.macroHeader = other.macroHeader;
|
||||
}
|
||||
|
||||
public DWARFMacroOpcode getOpcode() {
|
||||
return opcode;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return opcode != null
|
||||
? opcode.getDescription()
|
||||
: "DW_MACRO_unknown[%x]".formatted(rawOpcode);
|
||||
}
|
||||
|
||||
public <T extends DWARFAttributeValue> T getOperand(int index, Class<T> valueClass)
|
||||
throws IOException {
|
||||
DWARFAttributeValue val = operandValues[index];
|
||||
if (valueClass.isInstance(val)) {
|
||||
return valueClass.cast(val);
|
||||
}
|
||||
throw new IOException("Incompatible operand type %s for %s"
|
||||
.formatted(valueClass.getSimpleName(), toString()));
|
||||
}
|
||||
|
||||
protected DWARFAttributeDef<DWARFMacroOpcode> operandDef(int operandIndex) {
|
||||
// TODO: we are re-using the opcode's enum as the identity of each operand value, which
|
||||
// isn't technically correct.
|
||||
return new DWARFMacroOpcode.Def(opcode, rawOpcode, opcode.getOperandForms()[operandIndex]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getName());
|
||||
if (operandValues.length > 0) {
|
||||
sb.append(": ");
|
||||
for (int i = 0; i < operandValues.length; i++) {
|
||||
if (i != 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(operandValues[i].getValueString(macroHeader.getCompilationUnit()));
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/* ###
|
||||
* 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.macro.entry;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.format.dwarf.DWARFImporter;
|
||||
import ghidra.app.util.bin.format.dwarf.attribs.DWARFNumericAttribute;
|
||||
import ghidra.app.util.bin.format.dwarf.line.DWARFFile;
|
||||
import ghidra.app.util.bin.format.dwarf.line.DWARFLine;
|
||||
import ghidra.program.database.sourcemap.SourceFile;
|
||||
import ghidra.program.database.sourcemap.SourceFileIdType;
|
||||
import ghidra.util.SourceFileUtils;
|
||||
|
||||
/**
|
||||
* Represents the start of a source file.
|
||||
*/
|
||||
public class DWARFMacroStartFile extends DWARFMacroInfoEntry {
|
||||
|
||||
public DWARFMacroStartFile(DWARFMacroInfoEntry other) {
|
||||
super(other);
|
||||
}
|
||||
|
||||
public int getLineNumber() throws IOException {
|
||||
return getOperand(0, DWARFNumericAttribute.class).getUnsignedIntExact();
|
||||
}
|
||||
|
||||
public int getFileNumber() throws IOException {
|
||||
return getOperand(1, DWARFNumericAttribute.class).getUnsignedIntExact();
|
||||
}
|
||||
|
||||
public SourceFile getSourceFile() throws IOException {
|
||||
int fileIndex = getFileNumber();
|
||||
|
||||
DWARFLine dLine = macroHeader.getLine();
|
||||
DWARFFile dFile = dLine.getFile(fileIndex);
|
||||
String normalizedPath = SourceFileUtils.normalizeDwarfPath(dFile.getPathName(dLine),
|
||||
DWARFImporter.DEFAULT_COMPILATION_DIR);
|
||||
byte[] md5 = dFile.getMD5();
|
||||
SourceFileIdType type = md5 == null ? SourceFileIdType.NONE : SourceFileIdType.MD5;
|
||||
return new SourceFile(normalizedPath, type, md5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return "%s: line: %d, filenum: %d %s".formatted(getName(), getLineNumber(),
|
||||
getFileNumber(), getSourceFile());
|
||||
}
|
||||
catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
return super.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/* ###
|
||||
* 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.macro.entry;
|
||||
|
||||
/**
|
||||
* Represents a "#undef" macro element.
|
||||
*/
|
||||
public class DWARFMacroUndef extends DWARFMacroDefine {
|
||||
|
||||
public DWARFMacroUndef(DWARFMacroInfoEntry other) {
|
||||
super(other);
|
||||
}
|
||||
|
||||
}
|
|
@ -32,6 +32,7 @@ public final class DWARFSectionNames {
|
|||
public static final String DEBUG_PUBNAMES = "debug_pubnames";
|
||||
public static final String DEBUG_PUBTYPES = "debug_pubtypes";
|
||||
public static final String DEBUG_MACINFO = "debug_macinfo";
|
||||
public static final String DEBUG_MACRO = "debug_macro"; // v5+
|
||||
public static final String DEBUG_ADDR = "debug_addr";
|
||||
|
||||
public static final String[] MINIMAL_DWARF_SECTIONS = { DEBUG_INFO, DEBUG_ABBREV };
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.dwarf.macro;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.app.util.bin.format.dwarf.DWARFTestBase;
|
||||
import ghidra.app.util.bin.format.dwarf.line.DWARFLine;
|
||||
import ghidra.app.util.bin.format.dwarf.macro.entry.DWARFMacroDefine;
|
||||
import ghidra.app.util.bin.format.dwarf.macro.entry.DWARFMacroDefine.MacroInfo;
|
||||
|
||||
public class DWARFMacroParsingTests extends DWARFTestBase {
|
||||
DWARFMacroHeader header;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
ensureCompUnit();
|
||||
header = new DWARFMacroHeader(0, 5, 0, 0, 4, 0, cu, DWARFLine.empty(),
|
||||
DWARFMacroOpcode.defaultOpcodeOperandMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefinedOnly() throws IOException {
|
||||
DWARFMacroDefine entry = new DWARFMacroDefine(1, "TEST", header);
|
||||
MacroInfo macroInfo = entry.getMacroInfo();
|
||||
assertEquals(1, entry.getLineNumber());
|
||||
assertFalse(macroInfo.isFunctionLike());
|
||||
assertTrue(macroInfo.parameters().isEmpty());
|
||||
assertEquals(macroInfo.definition(), StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObjectLikeMacro() throws IOException {
|
||||
DWARFMacroDefine entry = new DWARFMacroDefine(2, "TEST 0x4", header);
|
||||
MacroInfo macroInfo = entry.getMacroInfo();
|
||||
assertEquals(2, entry.getLineNumber());
|
||||
assertFalse(macroInfo.isFunctionLike());
|
||||
assertTrue(macroInfo.parameters().isEmpty());
|
||||
assertEquals("0x4", macroInfo.definition());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionLikeMacro() throws IOException {
|
||||
DWARFMacroDefine entry = new DWARFMacroDefine(3, "SUM(A,B) (A+B)", header);
|
||||
MacroInfo macroInfo = entry.getMacroInfo();
|
||||
assertEquals(3, entry.getLineNumber());
|
||||
assertTrue(macroInfo.isFunctionLike());
|
||||
assertEquals(2, macroInfo.parameters().size());
|
||||
assertEquals("A", macroInfo.parameters().get(0));
|
||||
assertEquals("B", macroInfo.parameters().get(1));
|
||||
assertEquals("(A+B)", macroInfo.definition());
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue