mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Merge remote-tracking branch 'origin/GP-3826_ryanmkurtz_DefLoader' into
Ghidra_10.4 (Closes #5676)
This commit is contained in:
commit
7bffc47b81
3 changed files with 307 additions and 90 deletions
|
@ -15,67 +15,162 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.opinion;
|
package ghidra.app.util.opinion;
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
import java.io.IOException;
|
||||||
import java.util.regex.Pattern;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import ghidra.util.exception.AssertException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 {
|
class DefExportLine {
|
||||||
|
|
||||||
private Pattern EXPORT_LINE_PATTERN = Pattern.compile("\\s*(\\w+)(\\s@\\d+)?(\\s\\w+)?");
|
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
private int ordinal;
|
private String internalName;
|
||||||
private String type;
|
private String otherModuleName;
|
||||||
|
private String otherModuleExportedName;
|
||||||
|
private Integer otherModuleOrdinal;
|
||||||
|
private Integer ordinal;
|
||||||
|
private boolean isNoName;
|
||||||
|
private boolean isPrivate;
|
||||||
|
private boolean isData;
|
||||||
|
|
||||||
DefExportLine(String exportLine) {
|
/**
|
||||||
|
* Parses the given export line into a new {@link DefExportLine}
|
||||||
//
|
*
|
||||||
// Format: FunctionName [@1] [PRIVATE]
|
* @param exportLine The export line
|
||||||
//
|
* @throws IOException if there was a problem parsing
|
||||||
|
*/
|
||||||
Matcher matcher = EXPORT_LINE_PATTERN.matcher(exportLine);
|
DefExportLine(String exportLine) throws IOException {
|
||||||
if (!matcher.matches()) {
|
StringTokenizer st = new StringTokenizer(exportLine);
|
||||||
throw new AssertException("Unexpected '.def' file line format. " +
|
if (!st.hasMoreTokens()) {
|
||||||
"Expected 'Name [@number] [PRIVATE]';" + " found " + exportLine);
|
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() {
|
String getName() {
|
||||||
return name;
|
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() {
|
* {@return the other module name, or null if there is no other module}
|
||||||
//@formatter:off
|
*/
|
||||||
return "{\n" +
|
String getOtherModuleName() {
|
||||||
"\tname: " + name + ",\n" +
|
return otherModuleName;
|
||||||
"\tordinal: " + ordinal + ",\n" +
|
}
|
||||||
"\ttype: " + type + "\n" +
|
|
||||||
"}";
|
/**
|
||||||
//@formatter:on
|
* {@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();
|
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)) {
|
for (DefExportLine def : parseExports(provider)) {
|
||||||
|
Integer ordinal = def.getOrdinal();
|
||||||
|
if (ordinal == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
Symbol symbol = SymbolUtilities.getLabelOrFunctionSymbol(prog,
|
Symbol symbol = SymbolUtilities.getLabelOrFunctionSymbol(prog,
|
||||||
SymbolUtilities.ORDINAL_PREFIX + def.getOrdinal(), errorConsumer);
|
SymbolUtilities.ORDINAL_PREFIX + ordinal, errorConsumer);
|
||||||
if (symbol == null) {
|
if (symbol == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -110,4 +114,9 @@ public class DefLoader extends AbstractProgramWrapperLoader {
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return DEF_NAME;
|
return DEF_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsLoadIntoProgram(Program program) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,70 +15,183 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.opinion;
|
package ghidra.app.util.opinion;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ghidra.util.exception.AssertException;
|
|
||||||
|
|
||||||
public class DefExportLineTest {
|
public class DefExportLineTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExportLineWithOrdinal() {
|
public void testExportLineNameOnly() throws IOException {
|
||||||
|
DefExportLine export = new DefExportLine("func");
|
||||||
//
|
assertEquals("func", export.getName());
|
||||||
// Format: FunctionName @1 PRIVATE
|
assertEquals(null, export.getInternalName());
|
||||||
//
|
assertEquals(null, export.getOtherModuleName());
|
||||||
DefExportLine line = new DefExportLine("BobsHouse @1 PRIVATE");
|
assertEquals(null, export.getOtherModuleExportedName());
|
||||||
assertEquals("BobsHouse", line.getName());
|
assertEquals(null, export.getOtherModuleOrdinal());
|
||||||
assertEquals(1, line.getOrdinal());
|
assertEquals(null, export.getOrdinal());
|
||||||
assertEquals("PRIVATE", line.getType());
|
assertEquals(false, export.isNoName());
|
||||||
|
assertEquals(false, export.isPrivate());
|
||||||
|
assertEquals(false, export.isData());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExportLineWithoutOrdinal() {
|
public void testExportLineInternalName() throws IOException {
|
||||||
|
DefExportLine export = new DefExportLine("func2=func1");
|
||||||
//
|
assertEquals("func2", export.getName());
|
||||||
// Format: FunctionName PRIVATE
|
assertEquals("func1", export.getInternalName());
|
||||||
//
|
assertEquals(null, export.getOtherModuleName());
|
||||||
DefExportLine line = new DefExportLine("BobsHouse PRIVATE");
|
assertEquals(null, export.getOtherModuleExportedName());
|
||||||
assertEquals("BobsHouse", line.getName());
|
assertEquals(null, export.getOtherModuleOrdinal());
|
||||||
assertEquals(0, line.getOrdinal());
|
assertEquals(null, export.getOrdinal());
|
||||||
assertEquals("PRIVATE", line.getType());
|
assertEquals(false, export.isNoName());
|
||||||
|
assertEquals(false, export.isPrivate());
|
||||||
|
assertEquals(false, export.isData());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExportLineWithoutPrivateKeyword() {
|
public void testExportLineOtherModuleExportedName() throws IOException {
|
||||||
|
DefExportLine export = new DefExportLine("func2=other_module.func1");
|
||||||
//
|
assertEquals("func2", export.getName());
|
||||||
// Format: FunctionName PRIVATE
|
assertEquals(null, export.getInternalName());
|
||||||
//
|
assertEquals("other_module", export.getOtherModuleName());
|
||||||
DefExportLine line = new DefExportLine("BobsHouse @1");
|
assertEquals("func1", export.getOtherModuleExportedName());
|
||||||
assertEquals("BobsHouse", line.getName());
|
assertEquals(null, export.getOtherModuleOrdinal());
|
||||||
assertEquals(1, line.getOrdinal());
|
assertEquals(null, export.getOrdinal());
|
||||||
assertEquals(null, line.getType());
|
assertEquals(false, export.isNoName());
|
||||||
|
assertEquals(false, export.isPrivate());
|
||||||
|
assertEquals(false, export.isData());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExportLineWithoutOrdinalOrPrivateKeyword() {
|
public void testExportLineOtherModuleOrdinal() throws IOException {
|
||||||
|
DefExportLine export = new DefExportLine("func2=other_module.#42");
|
||||||
//
|
assertEquals("func2", export.getName());
|
||||||
// Format: FunctionName PRIVATE
|
assertEquals(null, export.getInternalName());
|
||||||
//
|
assertEquals("other_module", export.getOtherModuleName());
|
||||||
DefExportLine line = new DefExportLine("BobsHouse");
|
assertEquals(null, export.getOtherModuleExportedName());
|
||||||
assertEquals("BobsHouse", line.getName());
|
assertEquals(42, (int) export.getOtherModuleOrdinal());
|
||||||
assertEquals(0, line.getOrdinal());
|
assertEquals(null, export.getOrdinal());
|
||||||
assertEquals(null, line.getType());
|
assertEquals(false, export.isNoName());
|
||||||
|
assertEquals(false, export.isPrivate());
|
||||||
|
assertEquals(false, export.isData());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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 {
|
try {
|
||||||
new DefExportLine("one two three four");
|
new DefExportLine(" ");
|
||||||
fail("Did not get a parsing exception with an invalid format");
|
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
|
// expected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue