Skip to content

Commit

Permalink
Initial implementation
Browse files Browse the repository at this point in the history
Features:
* creates symbols for global variables
* creates symbols for functions
  • Loading branch information
rafalh committed Mar 14, 2021
0 parents commit e9d8c6c
Show file tree
Hide file tree
Showing 22 changed files with 829 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
build
dist
bin
.gradle
.project
.classpath
.settings
.antProperties.xml
Empty file added Module.manifest
Empty file.
33 changes: 33 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Builds a Ghidra Extension for a given Ghidra installation.
//
// An absolute path to the Ghidra installation directory must be supplied either by setting the
// GHIDRA_INSTALL_DIR environment variable or Gradle project property:
//
// > export GHIDRA_INSTALL_DIR=<Absolute path to Ghidra>
// > gradle
//
// or
//
// > gradle -PGHIDRA_INSTALL_DIR=<Absolute path to Ghidra>
//
// Gradle should be invoked from the directory of the project to build. Please see the
// application.gradle.version property in <GHIDRA_INSTALL_DIR>/Ghidra/application.properties
// for the correction version of Gradle to use for the Ghidra installation you specify.

//----------------------START "DO NOT MODIFY" SECTION------------------------------
def ghidraInstallDir

if (System.env.GHIDRA_INSTALL_DIR) {
ghidraInstallDir = System.env.GHIDRA_INSTALL_DIR
}
else if (project.hasProperty("GHIDRA_INSTALL_DIR")) {
ghidraInstallDir = project.getProperty("GHIDRA_INSTALL_DIR")
}

if (ghidraInstallDir) {
apply from: new File(ghidraInstallDir).getCanonicalPath() + "/support/buildExtension.gradle"
}
else {
throw new GradleException("GHIDRA_INSTALL_DIR is not defined!")
}
//----------------------END "DO NOT MODIFY" SECTION-------------------------------
5 changes: 5 additions & 0 deletions extension.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name=@extname@
description=Analyser for DWARF1 debug information
author=Rafal Harabien
createdOn=
version=@extversion@
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* ###
* 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 com.github.rafalh.ghidra.dwarfone;

import ghidra.app.services.AbstractAnalyzer;
import ghidra.app.services.AnalyzerType;
import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.ElfSectionProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;

/**
* Analyzer of DWARF1 debug information.
*/
public class DWARF1Analyzer extends AbstractAnalyzer {

public DWARF1Analyzer() {
super("DWARF1 Analyzer", "Analyzer of DWARF debug data in version 1", AnalyzerType.BYTE_ANALYZER);
setDefaultEnablement(false);
setSupportsOneTimeAnalysis();
}

@Override
public boolean canAnalyze(Program program) {
var sectionProvider = ElfSectionProvider.createSectionProviderFor(program);
return sectionProvider.hasSection(SectionNames.DEBUG);
}

@Override
public void registerOptions(Options options, Program program) {

options.registerOption("Option name goes here", false, null,
"Option description goes here");
}

@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {

var programAnalyzer = new DWARF1ProgramAnalyzer(program, monitor, log);
programAnalyzer.process();
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package com.github.rafalh.ghidra.dwarfone;

import java.io.IOException;
import java.util.Optional;

import com.github.rafalh.ghidra.dwarfone.model.AddrAttributeValue;
import com.github.rafalh.ghidra.dwarfone.model.AttributeName;
import com.github.rafalh.ghidra.dwarfone.model.BlockAttributeValue;
import com.github.rafalh.ghidra.dwarfone.model.DebugInfoEntry;
import com.github.rafalh.ghidra.dwarfone.model.LocationAtomOp;
import com.github.rafalh.ghidra.dwarfone.model.LocationDescription;
import com.github.rafalh.ghidra.dwarfone.model.StringAttributeValue;

import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteArrayProvider;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.ElfSectionProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;

public class DWARF1ProgramAnalyzer {
private final Program program;
private final TaskMonitor monitor;
private final MessageLog log;

public DWARF1ProgramAnalyzer(Program program, TaskMonitor monitor, MessageLog log) {
this.program = program;
this.monitor = monitor;
this.log = log;
}

public boolean process() {
var sectionProvider = ElfSectionProvider.createSectionProviderFor(program);
try {
var debug = sectionProvider.getSectionAsByteProvider(SectionNames.DEBUG);
processDebugSection(debug);
//log.appendMsg("Finished parsing DWARF1");
return true;
} catch (IOException e) {
log.appendException(e);
return false;
}
}

private boolean isLittleEndian() {
return !program.getLanguage().isBigEndian();
}

private void processDebugSection(ByteProvider bp) throws IOException {
BinaryReader br = new BinaryReader(bp, isLittleEndian());
while (br.getPointerIndex() < bp.length() && !monitor.isCancelled()) {
var die = new DebugInfoEntry(br);
processDebugInfoEntry(die);
}
}

private void processDebugInfoEntry(DebugInfoEntry die) throws IOException {
//log.appendMsg(die.toString());
switch (die.getTag()) {
case GLOBAL_VARIABLE:
processGlobalVariable(die);
break;
case GLOBAL_SUBROUTINE:
processGlobalSubrountine(die);
break;
default:
// skip other tags
}
}

private LocationDescription decodeLocation(byte[] encodedLocation) throws IOException {
var bp = new ByteArrayProvider(encodedLocation);
return LocationDescription.read(bp, isLittleEndian());
}

private Long offsetFromLocation(LocationDescription location) {
var locationAtoms = location.getAtoms();
if (locationAtoms.size() == 1 && locationAtoms.get(0).getOp() == LocationAtomOp.ADDR) {
return locationAtoms.get(0).getArg();
}
log.appendMsg("Complex location not supported: " + locationAtoms);
return null;
}

private void processGlobalVariable(DebugInfoEntry die) throws IOException {
Optional<StringAttributeValue> nameAttributeOptional = die.getAttribute(AttributeName.NAME);
Optional<BlockAttributeValue> locationAttributeOptional = die.getAttribute(AttributeName.LOCATION);
if (nameAttributeOptional.isEmpty() || locationAttributeOptional.isEmpty()) {
return;
}
String name = nameAttributeOptional.get().get();
byte[] encodedLocation = locationAttributeOptional.get().get();
LocationDescription location = decodeLocation(encodedLocation);
Long offset = offsetFromLocation(location);
//log.appendMsg(name + " " + Long.toHexString(offset));
Address addr = toAddr(offset);
try {
program.getSymbolTable().createLabel(addr, name, SourceType.IMPORTED);
} catch (InvalidInputException e) {
log.appendException(e);
}
}

private void processGlobalSubrountine(DebugInfoEntry die) {
Optional<StringAttributeValue> nameAttributeOptional = die.getAttribute(AttributeName.NAME);
Optional<AddrAttributeValue> lowPcAttributeOptional = die.getAttribute(AttributeName.LOW_PC);
Optional<AddrAttributeValue> highPcAttributeOptional = die.getAttribute(AttributeName.HIGH_PC);
if (nameAttributeOptional.isEmpty() || lowPcAttributeOptional.isEmpty() || highPcAttributeOptional.isEmpty()) {
return;
}
String name = nameAttributeOptional.get().get();
long lowPc = lowPcAttributeOptional.get().get();
//long highPc = highPcAttributeOptional.get().get();
//log.appendMsg(name + " " + Long.toHexString(lowPc.longValue()));

Address addr = toAddr(lowPc);
try {
program.getSymbolTable().createLabel(addr, name, SourceType.IMPORTED);
} catch (InvalidInputException e) {
log.appendException(e);
}
}

private final Address toAddr(Number offset) {
return program.getAddressFactory().getDefaultAddressSpace().getAddress(
offset.longValue(), true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.github.rafalh.ghidra.dwarfone;

public class SectionNames {
public static final String DEBUG = "debug";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.github.rafalh.ghidra.dwarfone.model;

public class AddrAttributeValue implements AttributeValue {
private final long addr;

public AddrAttributeValue(long addr) {
this.addr = addr;
}

public long get() {
return addr;
}

@Override
public String toString() {
return "addr(" + Long.toString(addr) + ")";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.github.rafalh.ghidra.dwarfone.model;

import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public enum AttributeName {
SIBLING(0x0010),
LOCATION(0x0020),
NAME(0x0030),
FUND_TYPE(0x0050),
MOD_FUND_TYPE(0x0060),
USER_DEF_TYPE(0x0070),
MOD_U_D_TYPE(0x0080),
ORDERING(0x0090),
SUBSCR_DATA(0x00A0),
BYTE_SIZE(0x00B0),
BIT_OFFSET(0x00C0),
BIT_SIZE(0x00D0),
ELEMENT_LIST(0x00F0),
STMT_LIST(0x0100),
LOW_PC(0x0110),
HIGH_PC(0x0120),
LANGUAGE(0x0130),
MEMBER(0x0140),
DISCR(0x0150),
DISCR_VALUE(0x0160),
STRING_LENGTH(0x0190),
COMMON_REFERENCE(0x01A0),
COMP_DIR(0x01B0),
CONST_VALUE(0x01C0),
CONTAINING_TYPE(0x01D0),
DEFAULT_VALUE(0x01E0),
FRIENDS(0x01F0),
INLINE(0x0200),
IS_OPTIONAL(0x0210),
LOWER_BOUND(0x0220),
PROGRAM(0x0230),
PRIVATE(0x0240),
PRODUCER(0x0250),
PROTECTED(0x0260),
PROTOTYPED(0x0270),
PUBLIC(0x0280),
PURE_VIRTUAL(0x0290),
RETURN_ADDRBLOCK2(0x02A0),
SPECIFICATIONREFERENCE(0x02B0),
START_SCOPE(0x02C0),
STRIDE_SIZE(0x02E0),
UPPER_BOUND(0x02F0),
VIRTUAL(0x0300),
USER(null);

public static final int MASK = 0xFFF0;
private static final int LO_USER = 0x2000;
private static final int HI_USER = 0x3ff0;
private static final Map<Integer, AttributeName> VALUE_MAP;

private Integer value;

static {
VALUE_MAP = Stream.of(AttributeName.values())
.filter(at -> at.value != null)
.collect(Collectors.toUnmodifiableMap(at -> at.value, Function.identity()));
}

AttributeName(Integer value) {
this.value = value;
}

public static AttributeName decode(int value) {
if (value >= LO_USER && value <= HI_USER) {
return USER;
}
AttributeName at = VALUE_MAP.get(value);
if (at == null) {
throw new IllegalArgumentException("invalid attribute value " + value);
}
return at;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.github.rafalh.ghidra.dwarfone.model;

public interface AttributeValue {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.github.rafalh.ghidra.dwarfone.model;

public class BlockAttributeValue implements AttributeValue {
private final byte[] block;

public BlockAttributeValue(byte[] block) {
this.block = block;
}

public byte[] get() {
return block;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.github.rafalh.ghidra.dwarfone.model;

import java.util.Objects;

public class ConstAttributeValue implements AttributeValue {
private final Number value;

public ConstAttributeValue(Number value) {
this.value = value;
}

public Number get() {
return value;
}

@Override
public String toString() {
return Objects.toString(value);
}
}
Loading

0 comments on commit e9d8c6c

Please sign in to comment.