diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DefExportLine.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DefExportLine.java
index 483745999b..2dd847cb01 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DefExportLine.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DefExportLine.java
@@ -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 EXPORTS
+ *
*/
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);
+ }
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DefLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DefLoader.java
index 3d8699d1d2..01dcf7e6e4 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DefLoader.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DefLoader.java
@@ -88,10 +88,14 @@ public class DefLoader extends AbstractProgramWrapperLoader {
}
SymbolTable symtab = prog.getSymbolTable();
- Consumer errorConsumer = err -> log.error("DefLoader", err);
+ Consumer 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;
+ }
}
diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/opinion/DefExportLineTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/opinion/DefExportLineTest.java
index 095f97f050..1103bbd914 100644
--- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/opinion/DefExportLineTest.java
+++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/opinion/DefExportLineTest.java
@@ -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
}
}