mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-22 04:05:39 +00:00
Merge remote-tracking branch 'origin/GP-3826_ryanmkurtz_DefLoader' into
Ghidra_10.4 (Closes #5676)
This commit is contained in:
commit
7bffc47b81
@ -15,67 +15,162 @@
|
||||
*/
|
||||
package ghidra.app.util.opinion;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import ghidra.util.exception.AssertException;
|
||||
import java.io.IOException;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* An object to parse a line from a ".def" file.
|
||||
* An object to parse an EXPORTS line from a ".def" file.
|
||||
*
|
||||
* @see <a href="https://learn.microsoft.com/en-us/cpp/build/reference/exports?view=msvc-170">EXPORTS</a>
|
||||
*
|
||||
*/
|
||||
class DefExportLine {
|
||||
|
||||
private Pattern EXPORT_LINE_PATTERN = Pattern.compile("\\s*(\\w+)(\\s@\\d+)?(\\s\\w+)?");
|
||||
|
||||
private String name;
|
||||
private int ordinal;
|
||||
private String type;
|
||||
private String internalName;
|
||||
private String otherModuleName;
|
||||
private String otherModuleExportedName;
|
||||
private Integer otherModuleOrdinal;
|
||||
private Integer ordinal;
|
||||
private boolean isNoName;
|
||||
private boolean isPrivate;
|
||||
private boolean isData;
|
||||
|
||||
DefExportLine(String exportLine) {
|
||||
|
||||
//
|
||||
// Format: FunctionName [@1] [PRIVATE]
|
||||
//
|
||||
|
||||
Matcher matcher = EXPORT_LINE_PATTERN.matcher(exportLine);
|
||||
if (!matcher.matches()) {
|
||||
throw new AssertException("Unexpected '.def' file line format. " +
|
||||
"Expected 'Name [@number] [PRIVATE]';" + " found " + exportLine);
|
||||
/**
|
||||
* Parses the given export line into a new {@link DefExportLine}
|
||||
*
|
||||
* @param exportLine The export line
|
||||
* @throws IOException if there was a problem parsing
|
||||
*/
|
||||
DefExportLine(String exportLine) throws IOException {
|
||||
StringTokenizer st = new StringTokenizer(exportLine);
|
||||
if (!st.hasMoreTokens()) {
|
||||
throw new IOException("Line is empty");
|
||||
}
|
||||
while (st.hasMoreTokens()) {
|
||||
String token = st.nextToken();
|
||||
if (name == null) {
|
||||
String[] equalsParts = token.split("=", 2);
|
||||
name = equalsParts[0];
|
||||
if (equalsParts.length > 1) {
|
||||
String[] dotParts = equalsParts[1].split("\\.", 2);
|
||||
if (dotParts.length == 1) {
|
||||
internalName = equalsParts[1];
|
||||
}
|
||||
else {
|
||||
otherModuleName = dotParts[0];
|
||||
if (dotParts[1].startsWith("#")) {
|
||||
otherModuleOrdinal = parseInt(dotParts[1].substring(1));
|
||||
}
|
||||
else {
|
||||
otherModuleExportedName = dotParts[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ordinal == null && token.startsWith("@")) {
|
||||
if (!token.equals("@")) {
|
||||
ordinal = parseInt(token.substring(1));
|
||||
}
|
||||
else if (st.hasMoreTokens()) {
|
||||
ordinal = parseInt(st.nextToken());
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (token) {
|
||||
case "NONAME":
|
||||
isNoName = true;
|
||||
break;
|
||||
case "PRIVATE":
|
||||
isPrivate = true;
|
||||
break;
|
||||
case "DATA":
|
||||
isData = true;
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Invalid type: " + token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
name = matcher.group(1);
|
||||
String ordinalString = matcher.group(2);
|
||||
if (ordinalString != null) { // this is optional
|
||||
ordinalString = ordinalString.trim().substring(1); // strip off '@'
|
||||
ordinal = Integer.parseInt(ordinalString);
|
||||
}
|
||||
|
||||
String privateString = matcher.group(3);
|
||||
if (privateString != null) {
|
||||
type = privateString.trim();
|
||||
}
|
||||
}
|
||||
|
||||
int getOrdinal() {
|
||||
return ordinal;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the name}
|
||||
*/
|
||||
String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
String getType() {
|
||||
return type;
|
||||
/**
|
||||
* {@return the internal name, or null if there is no internal name}
|
||||
*/
|
||||
String getInternalName() {
|
||||
return internalName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
//@formatter:off
|
||||
return "{\n" +
|
||||
"\tname: " + name + ",\n" +
|
||||
"\tordinal: " + ordinal + ",\n" +
|
||||
"\ttype: " + type + "\n" +
|
||||
"}";
|
||||
//@formatter:on
|
||||
/**
|
||||
* {@return the other module name, or null if there is no other module}
|
||||
*/
|
||||
String getOtherModuleName() {
|
||||
return otherModuleName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the other module exported name, or null if there is no other module exported name}
|
||||
*/
|
||||
String getOtherModuleExportedName() {
|
||||
return otherModuleExportedName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the other module ordinal, or null if there is no other module ordinal}
|
||||
*/
|
||||
Integer getOtherModuleOrdinal() {
|
||||
return otherModuleOrdinal;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the ordinal value, or null if there is no ordinal}
|
||||
*/
|
||||
Integer getOrdinal() {
|
||||
return ordinal;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return true if the export has no name; otherwise, false}
|
||||
*/
|
||||
boolean isNoName() {
|
||||
return isNoName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return true if the export is private; otherwise, false}
|
||||
*/
|
||||
boolean isPrivate() {
|
||||
return isPrivate;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return true if the export is data; otherwise, false}
|
||||
*/
|
||||
boolean isData() {
|
||||
return isData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@link String} argument as a signed decimal integer
|
||||
*
|
||||
* @param str The {@link String} to parse
|
||||
* @return The integer value represented by the argument in decimal
|
||||
* @throws IOException if the {@link String} does not contain a parseable integer
|
||||
*/
|
||||
private int parseInt(String str) throws IOException {
|
||||
try {
|
||||
return Integer.parseInt(str);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,10 +88,14 @@ public class DefLoader extends AbstractProgramWrapperLoader {
|
||||
}
|
||||
|
||||
SymbolTable symtab = prog.getSymbolTable();
|
||||
Consumer<String> errorConsumer = err -> log.error("DefLoader", err);
|
||||
Consumer<String> errorConsumer = err -> log.appendMsg("DefLoader", err);
|
||||
for (DefExportLine def : parseExports(provider)) {
|
||||
Integer ordinal = def.getOrdinal();
|
||||
if (ordinal == null) {
|
||||
continue;
|
||||
}
|
||||
Symbol symbol = SymbolUtilities.getLabelOrFunctionSymbol(prog,
|
||||
SymbolUtilities.ORDINAL_PREFIX + def.getOrdinal(), errorConsumer);
|
||||
SymbolUtilities.ORDINAL_PREFIX + ordinal, errorConsumer);
|
||||
if (symbol == null) {
|
||||
continue;
|
||||
}
|
||||
@ -110,4 +114,9 @@ public class DefLoader extends AbstractProgramWrapperLoader {
|
||||
public String getName() {
|
||||
return DEF_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLoadIntoProgram(Program program) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -15,70 +15,183 @@
|
||||
*/
|
||||
package ghidra.app.util.opinion;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
public class DefExportLineTest {
|
||||
|
||||
@Test
|
||||
public void testExportLineWithOrdinal() {
|
||||
|
||||
//
|
||||
// Format: FunctionName @1 PRIVATE
|
||||
//
|
||||
DefExportLine line = new DefExportLine("BobsHouse @1 PRIVATE");
|
||||
assertEquals("BobsHouse", line.getName());
|
||||
assertEquals(1, line.getOrdinal());
|
||||
assertEquals("PRIVATE", line.getType());
|
||||
public void testExportLineNameOnly() throws IOException {
|
||||
DefExportLine export = new DefExportLine("func");
|
||||
assertEquals("func", export.getName());
|
||||
assertEquals(null, export.getInternalName());
|
||||
assertEquals(null, export.getOtherModuleName());
|
||||
assertEquals(null, export.getOtherModuleExportedName());
|
||||
assertEquals(null, export.getOtherModuleOrdinal());
|
||||
assertEquals(null, export.getOrdinal());
|
||||
assertEquals(false, export.isNoName());
|
||||
assertEquals(false, export.isPrivate());
|
||||
assertEquals(false, export.isData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportLineWithoutOrdinal() {
|
||||
|
||||
//
|
||||
// Format: FunctionName PRIVATE
|
||||
//
|
||||
DefExportLine line = new DefExportLine("BobsHouse PRIVATE");
|
||||
assertEquals("BobsHouse", line.getName());
|
||||
assertEquals(0, line.getOrdinal());
|
||||
assertEquals("PRIVATE", line.getType());
|
||||
public void testExportLineInternalName() throws IOException {
|
||||
DefExportLine export = new DefExportLine("func2=func1");
|
||||
assertEquals("func2", export.getName());
|
||||
assertEquals("func1", export.getInternalName());
|
||||
assertEquals(null, export.getOtherModuleName());
|
||||
assertEquals(null, export.getOtherModuleExportedName());
|
||||
assertEquals(null, export.getOtherModuleOrdinal());
|
||||
assertEquals(null, export.getOrdinal());
|
||||
assertEquals(false, export.isNoName());
|
||||
assertEquals(false, export.isPrivate());
|
||||
assertEquals(false, export.isData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportLineWithoutPrivateKeyword() {
|
||||
|
||||
//
|
||||
// Format: FunctionName PRIVATE
|
||||
//
|
||||
DefExportLine line = new DefExportLine("BobsHouse @1");
|
||||
assertEquals("BobsHouse", line.getName());
|
||||
assertEquals(1, line.getOrdinal());
|
||||
assertEquals(null, line.getType());
|
||||
public void testExportLineOtherModuleExportedName() throws IOException {
|
||||
DefExportLine export = new DefExportLine("func2=other_module.func1");
|
||||
assertEquals("func2", export.getName());
|
||||
assertEquals(null, export.getInternalName());
|
||||
assertEquals("other_module", export.getOtherModuleName());
|
||||
assertEquals("func1", export.getOtherModuleExportedName());
|
||||
assertEquals(null, export.getOtherModuleOrdinal());
|
||||
assertEquals(null, export.getOrdinal());
|
||||
assertEquals(false, export.isNoName());
|
||||
assertEquals(false, export.isPrivate());
|
||||
assertEquals(false, export.isData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportLineWithoutOrdinalOrPrivateKeyword() {
|
||||
|
||||
//
|
||||
// Format: FunctionName PRIVATE
|
||||
//
|
||||
DefExportLine line = new DefExportLine("BobsHouse");
|
||||
assertEquals("BobsHouse", line.getName());
|
||||
assertEquals(0, line.getOrdinal());
|
||||
assertEquals(null, line.getType());
|
||||
public void testExportLineOtherModuleOrdinal() throws IOException {
|
||||
DefExportLine export = new DefExportLine("func2=other_module.#42");
|
||||
assertEquals("func2", export.getName());
|
||||
assertEquals(null, export.getInternalName());
|
||||
assertEquals("other_module", export.getOtherModuleName());
|
||||
assertEquals(null, export.getOtherModuleExportedName());
|
||||
assertEquals(42, (int) export.getOtherModuleOrdinal());
|
||||
assertEquals(null, export.getOrdinal());
|
||||
assertEquals(false, export.isNoName());
|
||||
assertEquals(false, export.isPrivate());
|
||||
assertEquals(false, export.isData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportLineWithInvalidFormat() {
|
||||
public void testExportLineOrdinal() throws IOException {
|
||||
DefExportLine export = new DefExportLine("func @1");
|
||||
assertEquals("func", export.getName());
|
||||
assertEquals(null, export.getInternalName());
|
||||
assertEquals(null, export.getOtherModuleName());
|
||||
assertEquals(null, export.getOtherModuleExportedName());
|
||||
assertEquals(null, export.getOtherModuleOrdinal());
|
||||
assertEquals(1, (int) export.getOrdinal());
|
||||
assertEquals(false, export.isNoName());
|
||||
assertEquals(false, export.isPrivate());
|
||||
assertEquals(false, export.isData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportLineOrdinalSpaces() throws IOException {
|
||||
DefExportLine export = new DefExportLine("func @ 1");
|
||||
assertEquals("func", export.getName());
|
||||
assertEquals(null, export.getInternalName());
|
||||
assertEquals(null, export.getOtherModuleName());
|
||||
assertEquals(null, export.getOtherModuleExportedName());
|
||||
assertEquals(null, export.getOtherModuleOrdinal());
|
||||
assertEquals(1, (int) export.getOrdinal());
|
||||
assertEquals(false, export.isNoName());
|
||||
assertEquals(false, export.isPrivate());
|
||||
assertEquals(false, export.isData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportLineOrdinalNoName() throws IOException {
|
||||
DefExportLine export = new DefExportLine("func @1 NONAME");
|
||||
assertEquals("func", export.getName());
|
||||
assertEquals(null, export.getInternalName());
|
||||
assertEquals(null, export.getOtherModuleName());
|
||||
assertEquals(null, export.getOtherModuleExportedName());
|
||||
assertEquals(null, export.getOtherModuleOrdinal());
|
||||
assertEquals(1, (int) export.getOrdinal());
|
||||
assertEquals(true, export.isNoName());
|
||||
assertEquals(false, export.isPrivate());
|
||||
assertEquals(false, export.isData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportLineData() throws IOException {
|
||||
DefExportLine export = new DefExportLine("exported_global DATA");
|
||||
assertEquals("exported_global", export.getName());
|
||||
assertEquals(null, export.getInternalName());
|
||||
assertEquals(null, export.getOtherModuleName());
|
||||
assertEquals(null, export.getOtherModuleExportedName());
|
||||
assertEquals(null, export.getOtherModuleOrdinal());
|
||||
assertEquals(null, export.getOrdinal());
|
||||
assertEquals(false, export.isNoName());
|
||||
assertEquals(false, export.isPrivate());
|
||||
assertEquals(true, export.isData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportLinePrivate() throws IOException {
|
||||
DefExportLine export = new DefExportLine("func PRIVATE");
|
||||
assertEquals("func", export.getName());
|
||||
assertEquals(null, export.getInternalName());
|
||||
assertEquals(null, export.getOtherModuleName());
|
||||
assertEquals(null, export.getOtherModuleExportedName());
|
||||
assertEquals(null, export.getOtherModuleOrdinal());
|
||||
assertEquals(null, export.getOrdinal());
|
||||
assertEquals(false, export.isNoName());
|
||||
assertEquals(true, export.isPrivate());
|
||||
assertEquals(false, export.isData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportLineAll() throws IOException {
|
||||
DefExportLine export = new DefExportLine("func2=other_module.#42 @ 1 NONAME PRIVATE");
|
||||
assertEquals("func2", export.getName());
|
||||
assertEquals(null, export.getInternalName());
|
||||
assertEquals("other_module", export.getOtherModuleName());
|
||||
assertEquals(null, export.getOtherModuleExportedName());
|
||||
assertEquals(42, (int) export.getOtherModuleOrdinal());
|
||||
assertEquals(1, (int) export.getOrdinal());
|
||||
assertEquals(true, export.isNoName());
|
||||
assertEquals(true, export.isPrivate());
|
||||
assertEquals(false, export.isData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportLineWithNoName() {
|
||||
try {
|
||||
new DefExportLine("one two three four");
|
||||
new DefExportLine(" ");
|
||||
fail("Did not get a parsing exception with an invalid format");
|
||||
}
|
||||
catch (AssertException e) {
|
||||
catch (IOException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportLineWithInvalidOrdinal() {
|
||||
try {
|
||||
new DefExportLine("func @ff");
|
||||
fail("Did not get a parsing exception with an invalid format");
|
||||
}
|
||||
catch (IOException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportLineWithInvalidType() {
|
||||
try {
|
||||
new DefExportLine("func @ 1 INVALID_TYPE");
|
||||
fail("Did not get a parsing exception with an invalid format");
|
||||
}
|
||||
catch (IOException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user