Merge remote-tracking branch 'origin/GP-5221_dwarf_debug_relative_paths--SQUASHED' into Ghidra_11.3

This commit is contained in:
Ryan Kurtz 2025-01-07 20:20:19 -05:00
commit 8e7f0034e5
5 changed files with 274 additions and 69 deletions

View file

@ -14,12 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
// Adds DWARF source file line number info to the current program as source map entries. // Adds DWARF source file line number info to the current program as source map entries.
// A source file that is relative after path normalization will have all leading "."
// and "/../" entries stripped and then be placed under an artificial directory.
// Note that you can run this script on a program that has already been analyzed by the // Note that you can run this script on a program that has already been analyzed by the
// DWARF analyzer. // DWARF analyzer.
//@category DWARF //@category DWARF
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import ghidra.app.script.GhidraScript; import ghidra.app.script.GhidraScript;
import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.BinaryReader;
@ -30,14 +31,19 @@ import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProviderFact
import ghidra.framework.store.LockException; import ghidra.framework.store.LockException;
import ghidra.program.database.sourcemap.SourceFile; import ghidra.program.database.sourcemap.SourceFile;
import ghidra.program.database.sourcemap.SourceFileIdType; import ghidra.program.database.sourcemap.SourceFileIdType;
import ghidra.program.model.address.Address; import ghidra.program.model.address.*;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.sourcemap.SourceFileManager; import ghidra.program.model.sourcemap.SourceFileManager;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.SourceFileUtils;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
public class DWARFLineInfoSourceMapScript extends GhidraScript { public class DWARFLineInfoSourceMapScript extends GhidraScript {
public static final int ENTRY_MAX_LENGTH = 1000; public static final int ENTRY_MAX_LENGTH = 1000;
private static final int MAX_ERROR_MSGS_TO_DISPLAY = 25;
private static final int MAX_WARNING_MSGS_TO_DISPLAY = 25;
private static final String COMPILATION_ROOT_DIRECTORY = DWARFImporter.DEFAULT_COMPILATION_DIR;
private int numErrors;
private int numWarnings;
@Override @Override
protected void run() throws Exception { protected void run() throws Exception {
@ -69,71 +75,111 @@ public class DWARFLineInfoSourceMapScript extends GhidraScript {
return; return;
} }
int entryCount = 0; int entryCount = 0;
monitor.initialize(reader.length(), "DWARF Source Map Info");
List<DWARFCompilationUnit> compUnits = dprog.getCompilationUnits(); List<DWARFCompilationUnit> compUnits = dprog.getCompilationUnits();
SourceFileManager sourceManager = currentProgram.getSourceFileManager(); SourceFileManager sourceManager = currentProgram.getSourceFileManager();
List<SourceFileAddr> sourceInfo = new ArrayList<>(); List<SourceFileAddr> sourceInfo = new ArrayList<>();
monitor.initialize(compUnits.size(), "DWARF: Reading Source Map Info");
for (DWARFCompilationUnit cu : compUnits) { for (DWARFCompilationUnit cu : compUnits) {
monitor.increment();
sourceInfo.addAll(cu.getLine().getAllSourceFileAddrInfo(cu, reader)); sourceInfo.addAll(cu.getLine().getAllSourceFileAddrInfo(cu, reader));
} }
monitor.setIndeterminate(true);
monitor.setMessage("Sorting " + sourceInfo.size() + " entries");
sourceInfo.sort((i, j) -> Long.compareUnsigned(i.address(), j.address())); sourceInfo.sort((i, j) -> Long.compareUnsigned(i.address(), j.address()));
monitor.initialize(sourceInfo.size()); monitor.setIndeterminate(false);
monitor.initialize(sourceInfo.size(), "DWARF: Applying Source Map Info");
Map<SourceFileAddr, SourceFile> sfasToSourceFiles = new HashMap<>();
Set<SourceFileAddr> badSfas = new HashSet<>();
AddressSet warnedAddresses = new AddressSet();
for (int i = 0; i < sourceInfo.size(); i++) { for (int i = 0; i < sourceInfo.size(); i++) {
monitor.checkCancelled();
monitor.increment(1); monitor.increment(1);
SourceFileAddr sourceFileAddr = sourceInfo.get(i); SourceFileAddr sourceFileAddr = sourceInfo.get(i);
if (sourceFileAddr.isEndSequence()) { if (sourceFileAddr.isEndSequence()) {
continue; continue;
} }
if (sourceFileAddr.fileName() == null) {
continue;
}
if (badSfas.contains(sourceFileAddr)) {
continue;
}
Address addr = dprog.getCodeAddress(sourceFileAddr.address()); Address addr = dprog.getCodeAddress(sourceFileAddr.address());
if (warnedAddresses.contains(addr)) {
continue; // only warn once per address
}
if (!currentProgram.getMemory().getExecuteSet().contains(addr)) { if (!currentProgram.getMemory().getExecuteSet().contains(addr)) {
if (numWarnings++ < MAX_WARNING_MSGS_TO_DISPLAY) {
printerr( printerr(
"entry for non-executable address; skipping: file %s line %d address: %s %x" "entry for non-executable address; skipping: file %s line %d address: %s %x"
.formatted(sourceFileAddr.fileName(), sourceFileAddr.lineNum(), .formatted(sourceFileAddr.fileName(), sourceFileAddr.lineNum(),
addr.toString(), sourceFileAddr.address())); addr.toString(), sourceFileAddr.address()));
}
warnedAddresses.add(addr);
continue; continue;
} }
long length = getLength(i, sourceInfo); long length = getLength(i, sourceInfo);
if (length < 0) { if (length < 0) {
if (numWarnings++ < MAX_WARNING_MSGS_TO_DISPLAY) {
println( println(
"Error computing entry length for file %s line %d address %s %x; replacing" + "Error computing entry length for file %s line %d address %s %x; replacing" +
" with length 0 entry".formatted(sourceFileAddr.fileName(), " with length 0 entry".formatted(sourceFileAddr.fileName(),
sourceFileAddr.lineNum(), addr.toString(), sourceFileAddr.address())); sourceFileAddr.lineNum(), addr.toString(),
length = 0; sourceFileAddr.address()));
}
} }
if (length > ENTRY_MAX_LENGTH) { if (length > ENTRY_MAX_LENGTH) {
if (numWarnings++ < MAX_WARNING_MSGS_TO_DISPLAY) {
println( println(
("entry for file %s line %d address: %s %x length %d too large, replacing " + ("entry for file %s line %d address: %s %x length %d too large, replacing " +
"with length 0 entry").formatted(sourceFileAddr.fileName(), "with length 0 entry").formatted(sourceFileAddr.fileName(),
sourceFileAddr.lineNum(), addr.toString(), sourceFileAddr.address(), sourceFileAddr.lineNum(), addr.toString(), sourceFileAddr.address(),
length)); length));
length = 0;
} }
if (sourceFileAddr.fileName() == null) {
continue;
} }
SourceFile source = null;
SourceFile source = sfasToSourceFiles.get(sourceFileAddr);
if (source == null) {
String path = SourceFileUtils.fixDwarfRelativePath(sourceFileAddr.fileName(),
COMPILATION_ROOT_DIRECTORY);
try { try {
SourceFileIdType type = SourceFileIdType type =
sourceFileAddr.md5() == null ? SourceFileIdType.NONE : SourceFileIdType.MD5; sourceFileAddr.md5() == null ? SourceFileIdType.NONE : SourceFileIdType.MD5;
source = new SourceFile(sourceFileAddr.fileName(), type, sourceFileAddr.md5()); source = new SourceFile(path, type, sourceFileAddr.md5());
sourceManager.addSourceFile(source); sourceManager.addSourceFile(source);
sfasToSourceFiles.put(sourceFileAddr, source);
} }
catch (IllegalArgumentException e) { catch (IllegalArgumentException e) {
if (numErrors++ < MAX_ERROR_MSGS_TO_DISPLAY) {
printerr("Exception creating source file %s".formatted(e.getMessage())); printerr("Exception creating source file %s".formatted(e.getMessage()));
}
badSfas.add(sourceFileAddr);
continue; continue;
} }
}
try { try {
sourceManager.addSourceMapEntry(source, sourceFileAddr.lineNum(), addr, length); sourceManager.addSourceMapEntry(source, sourceFileAddr.lineNum(), addr, length);
} }
catch (IllegalArgumentException e) { catch (IllegalArgumentException e) {
if (numErrors++ < MAX_ERROR_MSGS_TO_DISPLAY) {
printerr(e.getMessage()); printerr(e.getMessage());
}
continue; continue;
} }
entryCount++; entryCount++;
} }
if (numWarnings >= MAX_WARNING_MSGS_TO_DISPLAY) {
println("Additional warning messages suppressed");
}
if (numErrors >= MAX_ERROR_MSGS_TO_DISPLAY) {
println("Additional error messages suppressed");
}
println("Added " + entryCount + " source map entries"); println("Added " + entryCount + " source map entries");
printf("There were %d errors and %d warnings\n", numErrors,numWarnings);
} }
private long getLength(int i, List<SourceFileAddr> allSFA) { private long getLength(int i, List<SourceFileAddr> allSFA) {

View file

@ -27,13 +27,13 @@ import ghidra.app.util.bin.format.dwarf.line.DWARFLineProgramExecutor;
import ghidra.app.util.bin.format.golang.GoConstants; import ghidra.app.util.bin.format.golang.GoConstants;
import ghidra.framework.store.LockException; import ghidra.framework.store.LockException;
import ghidra.program.database.sourcemap.SourceFile; import ghidra.program.database.sourcemap.SourceFile;
import ghidra.program.model.address.Address; import ghidra.program.database.sourcemap.SourceFileIdType;
import ghidra.program.model.address.AddressOverflowException; import ghidra.program.model.address.*;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.BookmarkType;
import ghidra.program.model.listing.Program;
import ghidra.program.model.sourcemap.SourceFileManager; import ghidra.program.model.sourcemap.SourceFileManager;
import ghidra.util.Msg; import ghidra.util.*;
import ghidra.util.Swing;
import ghidra.util.exception.*; import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
import utility.function.Dummy; import utility.function.Dummy;
@ -49,6 +49,13 @@ public class DWARFImporter {
private DWARFProgram prog; private DWARFProgram prog;
private DWARFDataTypeManager dwarfDTM; private DWARFDataTypeManager dwarfDTM;
private TaskMonitor monitor; private TaskMonitor monitor;
private static final int MAX_NUM_SOURCE_LINE_ERROR_REPORTS = 200;
private static final int MAX_NUM_SOURCE_LINE_WARNING_REPORTS = 200;
private int numSourceLineErrorReports = 0;
private int numSourceLineWarningReports = 0;
// TODO: consider making this an analyzer option
public static final String DEFAULT_COMPILATION_DIR = "DWARF_DEFAULT_COMP_DIR";
public DWARFImporter(DWARFProgram prog, TaskMonitor monitor) { public DWARFImporter(DWARFProgram prog, TaskMonitor monitor) {
this.prog = prog; this.prog = prog;
@ -183,6 +190,17 @@ public class DWARFImporter {
return new CategoryPath(newRoot, cpParts.subList(origRootParts.size(), cpParts.size())); return new CategoryPath(newRoot, cpParts.subList(origRootParts.size(), cpParts.size()));
} }
/**
* Reads the dwarf source line info and applies it via the program's {@link SourceFileManager}.
* Note that source file paths which are relative after normalization will have all leading
* "." and "/../" entries stripped and then be placed under artificial directories based on
* {@code DEFAULT_COMPILATION_DIR}.
*
* @param reader reader
* @throws CancelledException if cancelled by user
* @throws IOException if error during reading
* @throws LockException if invoked without exclusive access
*/
private void addSourceLineInfo(BinaryReader reader) private void addSourceLineInfo(BinaryReader reader)
throws CancelledException, IOException, LockException { throws CancelledException, IOException, LockException {
if (reader == null) { if (reader == null) {
@ -191,21 +209,25 @@ public class DWARFImporter {
} }
int entryCount = 0; int entryCount = 0;
Program ghidraProgram = prog.getGhidraProgram(); Program ghidraProgram = prog.getGhidraProgram();
BookmarkManager bookmarkManager = ghidraProgram.getBookmarkManager();
long maxLength = prog.getImportOptions().getMaxSourceMapEntryLength(); long maxLength = prog.getImportOptions().getMaxSourceMapEntryLength();
boolean errorBookmarks = prog.getImportOptions().isUseBookmarks();
List<DWARFCompilationUnit> compUnits = prog.getCompilationUnits(); List<DWARFCompilationUnit> compUnits = prog.getCompilationUnits();
monitor.initialize(compUnits.size(), "Reading DWARF Source Map Info"); monitor.initialize(compUnits.size(), "DWARF: Reading Source Map Info");
SourceFileManager sourceManager = ghidraProgram.getSourceFileManager(); SourceFileManager sourceManager = ghidraProgram.getSourceFileManager();
List<SourceFileAddr> sourceInfo = new ArrayList<>(); List<SourceFileAddr> sourceInfo = new ArrayList<>();
for (DWARFCompilationUnit cu : compUnits) { for (DWARFCompilationUnit cu : compUnits) {
monitor.increment(); monitor.increment();
sourceInfo.addAll(cu.getLine().getAllSourceFileAddrInfo(cu, reader)); sourceInfo.addAll(cu.getLine().getAllSourceFileAddrInfo(cu, reader));
} }
monitor.setIndeterminate(true);
monitor.setMessage("Sorting " + sourceInfo.size() + " entries");
sourceInfo.sort((i, j) -> Long.compareUnsigned(i.address(), j.address())); sourceInfo.sort((i, j) -> Long.compareUnsigned(i.address(), j.address()));
monitor.initialize(sourceInfo.size(), "Applying DWARF Source Map Info"); monitor.setIndeterminate(false);
monitor.initialize(sourceInfo.size(), "DWARF: Applying Source Map Info");
Map<SourceFileAddr, SourceFile> sfasToSourceFiles = new HashMap<>();
Set<SourceFileAddr> badSfas = new HashSet<>();
AddressSet warnedAddresses = new AddressSet();
for (int i = 0; i < sourceInfo.size() - 1; i++) { for (int i = 0; i < sourceInfo.size() - 1; i++) {
monitor.checkCancelled();
monitor.increment(1); monitor.increment(1);
SourceFileAddr sfa = sourceInfo.get(i); SourceFileAddr sfa = sourceInfo.get(i);
if (SOURCEFILENAMES_IGNORE.contains(sfa.fileName()) || if (SOURCEFILENAMES_IGNORE.contains(sfa.fileName()) ||
@ -213,43 +235,73 @@ public class DWARFImporter {
sfa.isEndSequence()) { sfa.isEndSequence()) {
continue; continue;
} }
if (sfa.fileName() == null) {
continue;
}
if (badSfas.contains(sfa)) {
continue;
}
Address addr = prog.getCodeAddress(sfa.address()); Address addr = prog.getCodeAddress(sfa.address());
if (warnedAddresses.contains(addr)) {
continue; // only warn once per address
}
if (!ghidraProgram.getMemory().getExecuteSet().contains(addr)) { if (!ghidraProgram.getMemory().getExecuteSet().contains(addr)) {
String errorString = String warningString =
"entry for non-executable address; skipping: file %s line %d address: %s %x" "entry for non-executable address; skipping: file %s line %d address: %s %x"
.formatted(sfa.fileName(), sfa.lineNum(), addr.toString(), .formatted(sfa.fileName(), sfa.lineNum(), addr.toString(),
sfa.address()); sfa.address());
reportError(bookmarkManager, errorString, addr, errorBookmarks); if (numSourceLineWarningReports++ < MAX_NUM_SOURCE_LINE_WARNING_REPORTS) {
prog.logWarningAt(addr, addr.toString(), warningString);
}
warnedAddresses.add(addr);
continue; continue;
} }
long length = getLength(i, sourceInfo); long length = getLength(i, sourceInfo);
if (length < 0) { if (length < 0) {
length = 0; length = 0;
String errorString = String warningString =
"Error calculating entry length for file %s line %d address %s %x; replacing " + "Error calculating entry length for file %s line %d address %s %x; replacing " +
"with length 0 entry".formatted(sfa.fileName(), sfa.lineNum(), "with length 0 entry".formatted(sfa.fileName(), sfa.lineNum(),
addr.toString(), sfa.address()); addr.toString(), sfa.address());
reportError(bookmarkManager, errorString, addr, errorBookmarks); if (numSourceLineWarningReports++ < MAX_NUM_SOURCE_LINE_WARNING_REPORTS) {
prog.logWarningAt(addr, addr.toString(), warningString);
}
} }
if (length > maxLength) { if (length > maxLength) {
String errorString = ("entry for file %s line %d address: %s %x length %d too" + String warningString = ("entry for file %s line %d address: %s %x length %d too" +
" large, replacing with length 0 entry").formatted(sfa.fileName(), " large, replacing with length 0 entry").formatted(sfa.fileName(),
sfa.lineNum(), addr.toString(), sfa.address(), length); sfa.lineNum(), addr.toString(), sfa.address(), length);
length = 0; length = 0;
reportError(bookmarkManager, errorString, addr, errorBookmarks); if (numSourceLineWarningReports++ < MAX_NUM_SOURCE_LINE_WARNING_REPORTS) {
prog.logWarningAt(addr, addr.toString(), warningString);
}
} }
SourceFile source = null; SourceFile source = sfasToSourceFiles.get(sfa);
if (source == null) {
String path = SourceFileUtils.fixDwarfRelativePath(sfa.fileName(),
DEFAULT_COMPILATION_DIR);
try { try {
source = new SourceFile(sfa.fileName()); SourceFileIdType type =
sfa.md5() == null ? SourceFileIdType.NONE : SourceFileIdType.MD5;
source = new SourceFile(path, type, sfa.md5());
sourceManager.addSourceFile(source); sourceManager.addSourceFile(source);
sfasToSourceFiles.put(sfa, source);
} }
catch (IllegalArgumentException e) { catch (IllegalArgumentException e) {
Msg.error(this, "Exception creating source file: %s".formatted(e.getMessage())); String errorString = "Exception creating source file: " + e.getMessage();
if (numSourceLineErrorReports++ < MAX_NUM_SOURCE_LINE_ERROR_REPORTS) {
reportError(errorString, addr);
}
badSfas.add(sfa);
continue; continue;
} }
}
try { try {
sourceManager.addSourceMapEntry(source, sfa.lineNum(), addr, length); sourceManager.addSourceMapEntry(source, sfa.lineNum(), addr, length);
} }
@ -257,18 +309,29 @@ public class DWARFImporter {
String errorString = "AddressOverflowException for source map entry %s %d %s %x %d" String errorString = "AddressOverflowException for source map entry %s %d %s %x %d"
.formatted(source.getFilename(), sfa.lineNum(), addr.toString(), .formatted(source.getFilename(), sfa.lineNum(), addr.toString(),
sfa.address(), length); sfa.address(), length);
reportError(bookmarkManager, errorString, addr, errorBookmarks); if (numSourceLineErrorReports++ < MAX_NUM_SOURCE_LINE_ERROR_REPORTS) {
reportError(errorString, addr);
}
continue; continue;
} }
entryCount++; entryCount++;
} }
if (numSourceLineWarningReports >= MAX_NUM_SOURCE_LINE_WARNING_REPORTS) {
Msg.warn(this, "Additional warnings suppressed (%d total warnings)"
.formatted(numSourceLineWarningReports));
}
if (numSourceLineErrorReports >= MAX_NUM_SOURCE_LINE_ERROR_REPORTS) {
Msg.error(this, "Additional errors suppressed (%d total errors)"
.formatted(numSourceLineErrorReports));
}
Msg.info(this, "Added %d source map entries".formatted(entryCount)); Msg.info(this, "Added %d source map entries".formatted(entryCount));
} }
private void reportError(BookmarkManager bManager, String errorString, Address addr, private void reportError(String errorString, Address addr) {
boolean errorBookmarks) { if (prog.getImportOptions().isUseBookmarks()) {
if (errorBookmarks) { prog.getGhidraProgram()
bManager.setBookmark(addr, BookmarkType.ERROR, DWARFProgram.DWARF_BOOKMARK_CAT, .getBookmarkManager()
.setBookmark(addr, BookmarkType.ERROR, DWARFProgram.DWARF_BOOKMARK_CAT,
errorString); errorString);
} }
else { else {

View file

@ -16,8 +16,11 @@
package ghidra.util; package ghidra.util;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException;
import java.util.HexFormat; import java.util.HexFormat;
import java.util.List; import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -33,6 +36,7 @@ import ghidra.program.model.sourcemap.SourceMapEntry;
public class SourceFileUtils { public class SourceFileUtils {
private static HexFormat hexFormat = HexFormat.of(); private static HexFormat hexFormat = HexFormat.of();
private static Pattern dirNameValidator = Pattern.compile("\\W");
private SourceFileUtils() { private SourceFileUtils() {
// singleton class // singleton class
@ -147,6 +151,58 @@ public class SourceFileUtils {
return new SourceLineBounds(min, max); return new SourceLineBounds(min, max);
} }
/**
* Corrects potentially relative paths encountered in DWARF debug info.
* Relative paths are based at /{@code baseDir}/. If normalization of "/../" subpaths
* results in a path "above" /{@code baseDir}/, the returned path will be based at "baseDir_i"
* where i is the count of initial "/../" in the normalized path.
* @param path path to normalize
* @param baseDir name of artificial root directory
* @return normalized path
* @throws IllegalArgumentException if the path is not valid or if baseDir contains a
* non-alphanumeric, non-underscore character
*/
public static String fixDwarfRelativePath(String path, String baseDir) {
if (StringUtils.isEmpty(baseDir)) {
throw new IllegalArgumentException("baseDir cannot be empty");
}
Matcher matcher = dirNameValidator.matcher(baseDir);
if (matcher.find()) {
throw new IllegalArgumentException(
"baseDir must consist of alphanumeric characters or underscores");
}
boolean based = false;
if (path.startsWith("./")) {
path = "/" + baseDir + path.substring(1);
based = true;
}
try {
URI uri = new URI("file", null, path, null).normalize();
path = uri.getPath();
}
catch (URISyntaxException e) {
throw new IllegalArgumentException("path not valid: " + e.getMessage());
}
int numDotDots = 0;
while (path.startsWith("/..")) {
path = path.substring(3);
numDotDots += 1;
}
if (numDotDots == 0) {
if (!based) {
return path; // baseDir not necessary: path normalizes to absolute path without it
}
if (path.startsWith("/" + baseDir)) {
return path; // adding initial /baseDir was sufficient
}
}
if (based) {
numDotDots += 1; //initial baseDir was consumed by interior /../ during normalization
}
String count = numDotDots == 0 ? "" : "_" + Integer.toString(numDotDots);
return "/" + baseDir + count + path;
}
/** /**
* A record containing the minimum and maximum mapped line numbers * A record containing the minimum and maximum mapped line numbers
* @param min minimum line number * @param min minimum line number

View file

@ -104,6 +104,9 @@ public final class SourceFile implements Comparable<SourceFile> {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"SourceFile URI must represent a file (not a directory)"); "SourceFile URI must represent a file (not a directory)");
} }
if (path.startsWith("/../")) {
throw new IllegalArgumentException("path must be absolute after normalization");
}
} }
catch (URISyntaxException e) { catch (URISyntaxException e) {
throw new IllegalArgumentException("path not valid: " + e.getMessage()); throw new IllegalArgumentException("path not valid: " + e.getMessage());

View file

@ -52,11 +52,48 @@ public class SourceFileTest extends AbstractSourceFileTest {
} }
@Test @Test
public void testPathNormalization() { public void testPathNormalizationLinux() {
assertEquals("/src/dir1/dir2/file.c", assertEquals("/src/dir1/dir2/file.c",
new SourceFile("/src/test/../dir1/test/../dir2/file.c").getPath()); new SourceFile("/src/test/../dir1/test/../dir2/file.c").getPath());
} }
@Test(expected = IllegalArgumentException.class)
public void testInteriorPathNormalizationLinux() {
new SourceFile("/src/../../../file.c");
}
@Test(expected = IllegalArgumentException.class)
public void testInteriorPathNormalizationLinuxUtilsMethod() {
SourceFileUtils.getSourceFileFromPathString("/src/../../../file.c");
}
@Test(expected = IllegalArgumentException.class)
public void testInteriorPathNormalizationWindows() {
new SourceFile("/c:/src/../../../file.c");
}
@Test
public void testFixDwarfRelativePath() {
String baseDirName = "root_dir";
assertEquals("/src/file.c",
SourceFileUtils.fixDwarfRelativePath("/src/file.c", baseDirName));
assertEquals("/file.c",
SourceFileUtils.fixDwarfRelativePath("/src/../file.c", baseDirName));
assertEquals("/root_dir/file.c",
SourceFileUtils.fixDwarfRelativePath("./file.c", baseDirName));
assertEquals("/root_dir_1/file.c",
SourceFileUtils.fixDwarfRelativePath("/../file.c", baseDirName));
assertEquals("/root_dir_2/file.c",
SourceFileUtils.fixDwarfRelativePath("/.././../file.c", baseDirName));
assertEquals("/root_dir_1/file.c",
SourceFileUtils.fixDwarfRelativePath("./../file.c", baseDirName));
}
@Test(expected = IllegalArgumentException.class)
public void testInteriorPathNormalizationWindowsUtilsMethod() {
SourceFileUtils.getSourceFileFromPathString("c:\\src\\..\\..\\..\\file.c");
}
@Test @Test
public void testGetFilename() { public void testGetFilename() {
assertEquals("file.c", new SourceFile("/src/test/file.c").getFilename()); assertEquals("file.c", new SourceFile("/src/test/file.c").getFilename());