mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-25 17:00:36 -09:00
Merge remote-tracking branch 'origin/Ghidra_12.2'
This commit is contained in:
@@ -76,8 +76,7 @@ public class DWARFLineInfoSourceMapScript extends GhidraScript {
|
||||
popup("Unable to get reader for debug line info");
|
||||
return;
|
||||
}
|
||||
ExternalDebugInfo extDebugInfo = ExternalDebugInfo.fromProgram(dprog.getGhidraProgram());
|
||||
boolean hasBuildId = extDebugInfo != null && extDebugInfo.hasBuildId();
|
||||
BuildIdDebugInfo buildId = BuildIdDebugInfo.fromProgram(dprog.getGhidraProgram());
|
||||
ExternalDebugFilesService edfs =
|
||||
ExternalDebugFilesService.forProgram(dprog.getGhidraProgram());
|
||||
|
||||
@@ -108,9 +107,9 @@ public class DWARFLineInfoSourceMapScript extends GhidraScript {
|
||||
SourceFile sFile = new SourceFile(path, type, sfi.md5());
|
||||
sourceManager.addSourceFile(sFile);
|
||||
sourceFileInfoToSourceFile.put(sfi, sFile);
|
||||
if (hasBuildId) {
|
||||
ExternalDebugInfo srcFileDebugInfo =
|
||||
extDebugInfo.withType(ObjectType.SOURCE, path);
|
||||
if (buildId != null) {
|
||||
BuildIdDebugInfo srcFileDebugInfo =
|
||||
buildId.withType(ObjectType.SOURCE, path);
|
||||
File srcFile = edfs.find(srcFileDebugInfo, monitor);
|
||||
if (srcFile != null) {
|
||||
println("Source file: " + srcFile);
|
||||
|
||||
@@ -89,14 +89,17 @@ public class BuildIdDebugFileProvider implements DebugFileProvider {
|
||||
@Override
|
||||
public File getFile(ExternalDebugInfo debugInfo, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
String buildId = debugInfo.getBuildId();
|
||||
if (buildId == null || buildId.length() < 4 /* 2 bytes = 4 hex digits */ ) {
|
||||
if (!(debugInfo instanceof BuildIdDebugInfo buildIdInfo)) {
|
||||
return null;
|
||||
}
|
||||
File bucketDir = new File(rootDir, buildId.substring(0, 2));
|
||||
File file = new File(bucketDir, buildId.substring(2) + ".debug");
|
||||
String buildIdStr = buildIdInfo.getBuildIdHexString();
|
||||
if (buildIdStr == null || buildIdStr.length() < 4 /* 2 bytes = 4 hex digits */ ) {
|
||||
return null;
|
||||
}
|
||||
File bucketDir = new File(rootDir, buildIdStr.substring(0, 2));
|
||||
File file = new File(bucketDir, buildIdStr.substring(2) + ".debug");
|
||||
if (!rootDir.equals(bucketDir.getParentFile()) || !bucketDir.equals(file.getParentFile())) {
|
||||
throw new IOException("Bad buildid: " + buildId);
|
||||
throw new IOException("Bad buildid: " + buildIdStr);
|
||||
}
|
||||
return file.isFile() ? file : null;
|
||||
}
|
||||
|
||||
129
Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/BuildIdDebugInfo.java
vendored
Normal file
129
Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/external/BuildIdDebugInfo.java
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
/* ###
|
||||
* 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 ghidra.app.util.bin.format.dwarf.external;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.bin.format.elf.info.NoteGnuBuildId;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* Represents {@link ExternalDebugInfo} found in a build-id property embedded in an ELF file.
|
||||
*/
|
||||
public class BuildIdDebugInfo implements ExternalDebugInfo {
|
||||
private static final int MIN_BUILDID_HASH_LENGTH = 20;
|
||||
|
||||
public static BuildIdDebugInfo fromProgram(Program program) {
|
||||
NoteGnuBuildId buildId = NoteGnuBuildId.fromProgram(program);
|
||||
return buildId != null && buildId.getDescription().length >= MIN_BUILDID_HASH_LENGTH
|
||||
? new BuildIdDebugInfo(buildId.getDescription())
|
||||
: null;
|
||||
}
|
||||
|
||||
private final byte[] buildId;
|
||||
private final ObjectType objectType;
|
||||
private final String extra;
|
||||
|
||||
/**
|
||||
* Creates a new {@link BuildIdDebugInfo} from the bytes of its hash digest
|
||||
*
|
||||
* @param buildId bytes of the hash digest
|
||||
*/
|
||||
public BuildIdDebugInfo(byte[] buildId) {
|
||||
this(buildId, ObjectType.DEBUGINFO, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link BuildIdDebugInfo} from the bytes of its hash digest, a specifier for the
|
||||
* type of the object being pointed to, and an optional path used ObjectType.SOURCE instances.
|
||||
*
|
||||
* @param buildId build-id hash digest found in ".note.gnu.build-id" section
|
||||
* @param objectType {@link ObjectType} specifies what kind of debug file is specified by the
|
||||
* other info
|
||||
* @param extra additional information used by {@link ObjectType#SOURCE}
|
||||
*/
|
||||
public BuildIdDebugInfo(byte[] buildId, ObjectType objectType, String extra) {
|
||||
Objects.requireNonNull(buildId);
|
||||
|
||||
this.buildId = buildId;
|
||||
this.objectType = objectType;
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the build-id.
|
||||
*
|
||||
* @return build-id hash string
|
||||
*/
|
||||
public String getBuildIdHexString() {
|
||||
return HexFormat.of().formatHex(buildId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return ObjectType that this build-id specifier points to}
|
||||
*/
|
||||
public ObjectType getObjectType() {
|
||||
return objectType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return extra info used by ObjectTypes that need it}
|
||||
*/
|
||||
public String getExtra() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a new BuildIdDebugInfo instance, pointing to the same build-id hash but an
|
||||
* alternate object associated with the base debug file}
|
||||
* @param newObjectType the new {@link ObjectType}
|
||||
* @param newExtra extra information used by the ObjectType to find the target file
|
||||
*/
|
||||
public BuildIdDebugInfo withType(ObjectType newObjectType, String newExtra) {
|
||||
return new BuildIdDebugInfo(buildId, newObjectType, newExtra);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Arrays.hashCode(buildId);
|
||||
result = prime * result + Objects.hash(extra, objectType);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof BuildIdDebugInfo)) {
|
||||
return false;
|
||||
}
|
||||
BuildIdDebugInfo other = (BuildIdDebugInfo) obj;
|
||||
return Arrays.equals(buildId, other.buildId) && Objects.equals(extra, other.extra) &&
|
||||
objectType == other.objectType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BuildIdDebugInfo [" +
|
||||
(buildId != null ? "buildId=" + Arrays.toString(buildId) + ", " : "") +
|
||||
(objectType != null ? "objectType=" + objectType + ", " : "") +
|
||||
(extra != null ? "extra=" + extra : "") + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/* ###
|
||||
* 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 ghidra.app.util.bin.format.dwarf.external;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import ghidra.app.util.bin.format.elf.info.GnuDebugLink;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* Represents {@link ExternalDebugInfo} found in a debug-link property embedded in an ELF file.
|
||||
*/
|
||||
public class DebugLinkDebugInfo implements ExternalDebugInfo {
|
||||
|
||||
public static DebugLinkDebugInfo fromProgram(Program program) {
|
||||
GnuDebugLink debugLink = GnuDebugLink.fromProgram(program);
|
||||
return debugLink != null && !debugLink.getFilename().isEmpty()
|
||||
? new DebugLinkDebugInfo(debugLink.getFilename(), debugLink.getCrc())
|
||||
: null;
|
||||
}
|
||||
|
||||
private final String filename;
|
||||
private final int crc;
|
||||
|
||||
/**
|
||||
* @param filename filename of external debug file
|
||||
* @param crc crc32 of external debug file
|
||||
*/
|
||||
public DebugLinkDebugInfo(String filename, int crc) {
|
||||
this.filename = filename;
|
||||
this.crc = crc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the filename of the external debug file
|
||||
*
|
||||
* @return String filename of external debug file
|
||||
*/
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the crc of the external debug file.
|
||||
*
|
||||
* @return int crc32 of external debug file.
|
||||
*/
|
||||
public int getCrc() {
|
||||
return crc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(Integer.valueOf(crc), filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof DebugLinkDebugInfo)) {
|
||||
return false;
|
||||
}
|
||||
DebugLinkDebugInfo other = (DebugLinkDebugInfo) obj;
|
||||
return crc == other.crc && Objects.equals(filename, other.filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("DebugLinkDebugInfo [filename=%s, crc=%s]", filename, crc);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -75,6 +75,24 @@ public class ExternalDebugFilesService {
|
||||
providers.add(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the specified external debug file.
|
||||
*
|
||||
* @param debugInfos list of information about the external debug file
|
||||
* @param monitor {@link TaskMonitor}
|
||||
* @return first found file, or {@code null} if not found
|
||||
* @throws IOException if error
|
||||
*/
|
||||
public File find(List<ExternalDebugInfo> debugInfos, TaskMonitor monitor) throws IOException {
|
||||
for (ExternalDebugInfo debugInfo : debugInfos) {
|
||||
File result = find(debugInfo, monitor);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the specified external debug file.
|
||||
*
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.dwarf.external;
|
||||
|
||||
import ghidra.app.util.bin.format.elf.info.GnuDebugLink;
|
||||
import ghidra.app.util.bin.format.elf.info.NoteGnuBuildId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.NumericUtilities;
|
||||
|
||||
/**
|
||||
* Metadata needed to find an ELF/DWARF external debug file, retrieved from an ELF binary's
|
||||
@@ -27,134 +27,28 @@ import ghidra.util.NumericUtilities;
|
||||
* The debuglink can provide a filename and crc of the external debug file, while the build-id
|
||||
* can provide a hash that is converted to a filename that identifies the external debug file.
|
||||
*/
|
||||
public class ExternalDebugInfo {
|
||||
public interface ExternalDebugInfo {
|
||||
|
||||
/**
|
||||
* Create a new {@link ExternalDebugInfo} from information found in the specified program.
|
||||
*
|
||||
* @param program {@link Program} to query
|
||||
* @return new {@link ExternalDebugInfo} or null if no external debug metadata found in
|
||||
* program
|
||||
* @return List of {@link ExternalDebugInfo} instances that were found in the program.
|
||||
*/
|
||||
public static ExternalDebugInfo fromProgram(Program program) {
|
||||
GnuDebugLink debugLink = GnuDebugLink.fromProgram(program);
|
||||
NoteGnuBuildId buildId = NoteGnuBuildId.fromProgram(program);
|
||||
if (debugLink == null && buildId == null) {
|
||||
return null;
|
||||
public static List<ExternalDebugInfo> fromProgram(Program program) {
|
||||
List<ExternalDebugInfo> results = new ArrayList<>(2);
|
||||
|
||||
BuildIdDebugInfo buildId = BuildIdDebugInfo.fromProgram(program);
|
||||
if (buildId != null) {
|
||||
results.add(buildId);
|
||||
}
|
||||
|
||||
String filename = debugLink != null ? debugLink.getFilename() : null;
|
||||
int crc = debugLink != null ? debugLink.getCrc() : 0;
|
||||
String hash = buildId != null
|
||||
? NumericUtilities.convertBytesToString(buildId.getDescription())
|
||||
: null;
|
||||
DebugLinkDebugInfo debugLink = DebugLinkDebugInfo.fromProgram(program);
|
||||
if (debugLink != null) {
|
||||
results.add(debugLink);
|
||||
}
|
||||
|
||||
return new ExternalDebugInfo(filename, crc, hash, ObjectType.DEBUGINFO, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a new ExternalDebugInfo instance created using the specified Build-Id value}
|
||||
* @param buildId hex string
|
||||
*/
|
||||
public static ExternalDebugInfo forBuildId(String buildId) {
|
||||
return new ExternalDebugInfo(null, 0, buildId, ObjectType.DEBUGINFO, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a new ExternalDebugInfo instance created using the specified debuglink values}
|
||||
* @param debugLinkFilename filename from debuglink section
|
||||
* @param crc crc32 from debuglink section
|
||||
*/
|
||||
public static ExternalDebugInfo forDebugLink(String debugLinkFilename, int crc) {
|
||||
return new ExternalDebugInfo(debugLinkFilename, crc, null, ObjectType.DEBUGINFO, null);
|
||||
}
|
||||
|
||||
private final String filename;
|
||||
private final int crc;
|
||||
private final String buildId;
|
||||
private final ObjectType objectType;
|
||||
private final String extra;
|
||||
|
||||
/**
|
||||
* Constructor to create an {@link ExternalDebugInfo} instance.
|
||||
*
|
||||
* @param filename filename of external debug file, or null
|
||||
* @param crc crc32 of external debug file, or 0 if no filename
|
||||
* @param buildId build-id hash digest found in ".note.gnu.build-id" section, or null if
|
||||
* not present
|
||||
* @param objectType {@link ObjectType} specifies what kind of debug file is specified by the
|
||||
* other info
|
||||
* @param extra additional information used by {@link ObjectType#SOURCE}
|
||||
*/
|
||||
public ExternalDebugInfo(String filename, int crc, String buildId, ObjectType objectType,
|
||||
String extra) {
|
||||
this.filename = filename;
|
||||
this.crc = crc;
|
||||
this.buildId = buildId;
|
||||
this.objectType = objectType;
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is a filename
|
||||
*
|
||||
* @return boolean true if filename is available, false if not
|
||||
*/
|
||||
public boolean hasDebugLink() {
|
||||
return filename != null && !filename.isBlank();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the filename of the external debug file, or null if not specified.
|
||||
*
|
||||
* @return String filename of external debug file, or null if not specified
|
||||
*/
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the crc of the external debug file. Not valid if filename is missing.
|
||||
*
|
||||
* @return int crc32 of external debug file.
|
||||
*/
|
||||
public int getCrc() {
|
||||
return crc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the build-id.
|
||||
*
|
||||
* @return build-id hash string
|
||||
*/
|
||||
public String getBuildId() {
|
||||
return buildId;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return true if buildId is available, false if not}
|
||||
*/
|
||||
public boolean hasBuildId() {
|
||||
return buildId != null && !buildId.isBlank();
|
||||
}
|
||||
|
||||
public ObjectType getObjectType() {
|
||||
return objectType;
|
||||
}
|
||||
|
||||
public String getExtra() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
public ExternalDebugInfo withType(ObjectType newObjectType, String newExtra) {
|
||||
return new ExternalDebugInfo(extra, crc, buildId, newObjectType, newExtra);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"ExternalDebugInfo [filename=%s, crc=%s, hash=%s, objectType=%s, extra=%s]", filename,
|
||||
crc, buildId, objectType, extra);
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -91,13 +91,13 @@ public class HttpDebugInfoDProvider implements DebugStreamProvider {
|
||||
return DebugInfoProviderStatus.UNKNOWN;
|
||||
}
|
||||
|
||||
private HttpRequest.Builder request(ExternalDebugInfo id) throws IOException {
|
||||
private HttpRequest.Builder request(BuildIdDebugInfo id) throws IOException {
|
||||
try {
|
||||
String extra = "";
|
||||
if (id.getObjectType() == ObjectType.SOURCE) {
|
||||
extra = "/" + Objects.requireNonNullElse(id.getExtra(), "");
|
||||
}
|
||||
String requestPath = "buildid/%s/%s%s".formatted(id.getBuildId(),
|
||||
String requestPath = "buildid/%s/%s%s".formatted(id.getBuildIdHexString(),
|
||||
id.getObjectType().getPathString(), extra);
|
||||
return HttpRequest.newBuilder(serverURI.resolve(requestPath))
|
||||
.setHeader("User-Agent", GHIDRA_USER_AGENT);
|
||||
@@ -110,14 +110,14 @@ public class HttpDebugInfoDProvider implements DebugStreamProvider {
|
||||
@Override
|
||||
public StreamInfo getStream(ExternalDebugInfo id, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
if (!id.hasBuildId()) {
|
||||
if (!(id instanceof BuildIdDebugInfo buildIdInfo)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
monitor.setIndeterminate(true);
|
||||
monitor.setMessage("Connecting to " + serverURI);
|
||||
|
||||
HttpRequest request = request(id).GET().build();
|
||||
HttpRequest request = request(buildIdInfo).GET().build();
|
||||
|
||||
retryLoop: for (int retryNum = 0; retryNum < maxRetryCount; retryNum++) {
|
||||
if (retryNum > 0) {
|
||||
|
||||
@@ -154,12 +154,12 @@ public class LocalDirDebugInfoDProvider implements DebugFileStorage {
|
||||
@Override
|
||||
public File getFile(ExternalDebugInfo debugInfo, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
if (!isValid() || !debugInfo.hasBuildId()) {
|
||||
if (!isValid() || !(debugInfo instanceof BuildIdDebugInfo buildIdInfo)) {
|
||||
return null;
|
||||
}
|
||||
performInitMaintIfNeeded();
|
||||
|
||||
File f = getCachePath(debugInfo);
|
||||
File f = getCachePath(buildIdInfo);
|
||||
if (f.isFile()) {
|
||||
f.setLastModified(System.currentTimeMillis());
|
||||
return f;
|
||||
@@ -175,26 +175,26 @@ public class LocalDirDebugInfoDProvider implements DebugFileStorage {
|
||||
return dir;
|
||||
}
|
||||
|
||||
private File getCachePath(ExternalDebugInfo id) throws IOException {
|
||||
private File getCachePath(BuildIdDebugInfo id) throws IOException {
|
||||
String suffix = "";
|
||||
if (id.getObjectType() == ObjectType.SOURCE) {
|
||||
suffix = "-" + escapePath(Objects.requireNonNullElse(id.getExtra(), ""));
|
||||
}
|
||||
|
||||
return new File(getBuildidDir(id.getBuildId()),
|
||||
return new File(getBuildidDir(id.getBuildIdHexString()),
|
||||
id.getObjectType().getPathString() + suffix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File putStream(ExternalDebugInfo id, StreamInfo stream, TaskMonitor monitor)
|
||||
public File putStream(ExternalDebugInfo debugInfo, StreamInfo stream, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
assertValid();
|
||||
if (!id.hasBuildId()) {
|
||||
throw new IOException("Can't store debug file without BuildId value: " + id);
|
||||
if (!(debugInfo instanceof BuildIdDebugInfo buildId)) {
|
||||
throw new IOException("Can't store debug file without BuildId value");
|
||||
}
|
||||
performInitMaintIfNeeded();
|
||||
|
||||
File f = getCachePath(id);
|
||||
File f = getCachePath(buildId);
|
||||
File tmpF = new File(f.getParentFile(), ".tmp_" + f.getName());
|
||||
FileUtilities.checkedMkdirs(f.getParentFile());
|
||||
try (stream; FileOutputStream fos = new FileOutputStream(tmpF)) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package ghidra.app.util.bin.format.dwarf.external;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
@@ -24,7 +25,8 @@ import ghidra.util.task.TaskMonitor;
|
||||
import utilities.util.FileUtilities;
|
||||
|
||||
/**
|
||||
* Searches for DWARF external debug files specified via a debug-link filename / crc in a directory.
|
||||
* Searches for DWARF external debug files specified via a debug-link filename / crc
|
||||
* recursively in a directory.
|
||||
*/
|
||||
public class LocalDirDebugLinkProvider implements DebugFileProvider {
|
||||
|
||||
@@ -89,10 +91,10 @@ public class LocalDirDebugLinkProvider implements DebugFileProvider {
|
||||
@Override
|
||||
public File getFile(ExternalDebugInfo debugInfo, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
if (!debugInfo.hasDebugLink() || !isValid()) {
|
||||
if (!(debugInfo instanceof DebugLinkDebugInfo debugLink) || !isValid()) {
|
||||
return null;
|
||||
}
|
||||
ensureSafeFilename(debugInfo.getFilename());
|
||||
ensureSafeFilename(debugLink.getFilename()); // ensures there are no shenanigans in debugLink filename
|
||||
return findFile(searchDir, debugInfo, monitor);
|
||||
}
|
||||
|
||||
@@ -105,13 +107,13 @@ public class LocalDirDebugLinkProvider implements DebugFileProvider {
|
||||
|
||||
File findFile(File dir, ExternalDebugInfo debugInfo, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
if (!debugInfo.hasDebugLink()) {
|
||||
if (!(debugInfo instanceof DebugLinkDebugInfo debugLink)) {
|
||||
return null;
|
||||
}
|
||||
File file = new File(dir, debugInfo.getFilename());
|
||||
File file = new File(dir, debugLink.getFilename());
|
||||
if (file.isFile()) {
|
||||
int fileCRC = calcCRC(file);
|
||||
if (fileCRC == debugInfo.getCrc()) {
|
||||
if (fileCRC == debugLink.getCrc()) {
|
||||
return file; // success
|
||||
}
|
||||
Msg.info(this,
|
||||
@@ -119,8 +121,8 @@ public class LocalDirDebugLinkProvider implements DebugFileProvider {
|
||||
.formatted(file, fileCRC));
|
||||
}
|
||||
File[] subDirs;
|
||||
if ((subDirs = dir.listFiles(f -> f.isDirectory())) != null) {
|
||||
// TODO: prevent recursing into symlinks?
|
||||
if ((subDirs =
|
||||
dir.listFiles(f -> f.isDirectory() && !Files.isSymbolicLink(f.toPath()))) != null) {
|
||||
for (File subDir : subDirs) {
|
||||
File result = findFile(subDir, debugInfo, monitor);
|
||||
if (result != null) {
|
||||
|
||||
@@ -17,6 +17,7 @@ package ghidra.app.util.bin.format.dwarf.external;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
@@ -28,6 +29,12 @@ import utilities.util.FileUtilities;
|
||||
/**
|
||||
* A {@link DebugFileProvider} that only looks in the program's original import directory for
|
||||
* matching debug files.
|
||||
* <p>
|
||||
* A debug-link (filename+crc) specified debug file must exist in the binary's original import
|
||||
* directory and must have a matching crc.
|
||||
* <p>
|
||||
* A build-id (20'ish byte hash) specified debug file must exist in the binary's original import
|
||||
* directory, named "aabbcc...ff.debug"
|
||||
*/
|
||||
public class SameDirDebugInfoProvider implements DebugFileProvider {
|
||||
|
||||
@@ -92,13 +99,13 @@ public class SameDirDebugInfoProvider implements DebugFileProvider {
|
||||
@Override
|
||||
public File getFile(ExternalDebugInfo debugInfo, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
if (debugInfo.hasDebugLink()) {
|
||||
if (debugInfo instanceof DebugLinkDebugInfo debugLink) {
|
||||
// This differs from the LocalDirDebugLinkProvider in that it does NOT recursively search
|
||||
// for the file
|
||||
File debugFile = ensureSafeFilename(debugInfo.getFilename());
|
||||
File debugFile = ensureSafeFilename(debugLink.getFilename());
|
||||
if (debugFile.isFile()) {
|
||||
int fileCRC = LocalDirDebugLinkProvider.calcCRC(debugFile);
|
||||
if (fileCRC == debugInfo.getCrc()) {
|
||||
if (fileCRC == debugLink.getCrc()) {
|
||||
return debugFile; // success
|
||||
}
|
||||
Msg.info(this,
|
||||
@@ -107,10 +114,10 @@ public class SameDirDebugInfoProvider implements DebugFileProvider {
|
||||
}
|
||||
}
|
||||
|
||||
if (debugInfo.hasBuildId()) {
|
||||
if (debugInfo instanceof BuildIdDebugInfo buildId) {
|
||||
// this probe is a w.a.g for what people might do when co-locating a build-id debug
|
||||
// file with the original binary
|
||||
File debugFile = ensureSafeFilename(debugInfo.getBuildId() + ".debug");
|
||||
File debugFile = ensureSafeFilename(buildId.getBuildIdHexString() + ".debug");
|
||||
if (debugFile.isFile()) {
|
||||
return debugFile;
|
||||
}
|
||||
@@ -124,6 +131,9 @@ public class SameDirDebugInfoProvider implements DebugFileProvider {
|
||||
if (!progDir.equals(testFile.getParentFile())) {
|
||||
throw new IOException("Unsupported path specified in debug file: " + filename);
|
||||
}
|
||||
if (Files.isSymbolicLink(testFile.toPath())) {
|
||||
throw new IOException("Unsupported symlink specified as debug file: " + filename);
|
||||
}
|
||||
return testFile;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,14 +55,14 @@ public class ExternalDebugFileSectionProvider extends BaseSectionProvider {
|
||||
public static DWARFSectionProvider createExternalSectionProviderFor(Program program,
|
||||
TaskMonitor monitor) {
|
||||
try {
|
||||
ExternalDebugInfo extDebugInfo = ExternalDebugInfo.fromProgram(program);
|
||||
if (extDebugInfo == null) {
|
||||
List<ExternalDebugInfo> extDebugInfos = ExternalDebugInfo.fromProgram(program);
|
||||
if (extDebugInfos.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Msg.info(ExternalDebugFileSectionProvider.class,
|
||||
"DWARF external debug information found: " + extDebugInfo);
|
||||
"DWARF external debug information found: " + extDebugInfos);
|
||||
ExternalDebugFilesService edfs = ExternalDebugFilesService.forProgram(program);
|
||||
File extDebugFile = edfs.find(extDebugInfo, monitor);
|
||||
File extDebugFile = edfs.find(extDebugInfos, monitor);
|
||||
if (extDebugFile == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ public class ArrayValuesFieldFactory extends FieldFactory {
|
||||
|
||||
List<FieldElement> elements = new ArrayList<>();
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
Data child = parent.getComponent(index);
|
||||
Data child = parent.getComponent(index++);
|
||||
boolean isLastItem = isLastLine && (i == itemCount - 1);
|
||||
String value = getDisplayValue(child, !isLastItem);
|
||||
AttributedString as =
|
||||
|
||||
@@ -172,7 +172,7 @@ public class FSRL {
|
||||
String params = path.substring(paramStart + 1);
|
||||
path = path.substring(0, paramStart);
|
||||
Map<String, String> paramMap = getParamMapFromString(params);
|
||||
md5 = paramMap.get(FSRL.PARAM_MD5);
|
||||
md5 = getValidatedMD5(paramMap);
|
||||
}
|
||||
|
||||
FSRLRoot fsRoot = FSRLRoot.nestedFS(containerFile, proto);
|
||||
@@ -200,6 +200,21 @@ public class FSRL {
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
private static final int MD5_DIGEST_LEN = 16;
|
||||
private static String getValidatedMD5(Map<String, String> params) {
|
||||
String md5 = params.get(FSRL.PARAM_MD5);
|
||||
if (md5 != null && md5.length() == MD5_DIGEST_LEN * 2) {
|
||||
try {
|
||||
byte[] digestBytes = HexFormat.of().parseHex(md5);
|
||||
return HexFormat.of().formatHex(digestBytes);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// not a valid hex string
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected final FSRL parent;
|
||||
protected final String path;
|
||||
private final String md5;
|
||||
|
||||
@@ -35,6 +35,8 @@ import ghidra.util.Msg;
|
||||
public class FileSystemIndexHelper<METADATATYPE> {
|
||||
|
||||
private static final int MAX_SYMLINK_RECURSE_DEPTH = 10;
|
||||
public static final int MAX_FILEENTRY_COUNT = 500_000;
|
||||
|
||||
private FileData<METADATATYPE> rootDir;
|
||||
|
||||
static class FileData<METADATATYPE> {
|
||||
@@ -336,9 +338,10 @@ public class FileSystemIndexHelper<METADATATYPE> {
|
||||
* @param metadata opaque blob that will be stored and associated with the new
|
||||
* GFile instance
|
||||
* @return new GFile instance
|
||||
* @throws IOException if error adding files (too many files)
|
||||
*/
|
||||
public synchronized GFile storeFile(String path, long fileIndex, boolean isDirectory,
|
||||
long length, METADATATYPE metadata) {
|
||||
long length, METADATATYPE metadata) throws IOException {
|
||||
|
||||
String[] nameparts = FSUtilities.splitPath(path);
|
||||
if (nameparts.length == 0) {
|
||||
@@ -370,9 +373,10 @@ public class FileSystemIndexHelper<METADATATYPE> {
|
||||
* @param metadata opaque blob that will be stored and associated with the new
|
||||
* GFile instance
|
||||
* @return new GFile instance
|
||||
* @throws IOException if error adding files (too many files)
|
||||
*/
|
||||
public synchronized GFile storeFileWithParent(String filename, GFile parent, long fileIndex,
|
||||
boolean isDirectory, long length, METADATATYPE metadata) {
|
||||
boolean isDirectory, long length, METADATATYPE metadata) throws IOException {
|
||||
FileData<METADATATYPE> fileData =
|
||||
doStoreFile(filename, parent, fileIndex, isDirectory, length, null, metadata);
|
||||
return fileData.file;
|
||||
@@ -397,9 +401,10 @@ public class FileSystemIndexHelper<METADATATYPE> {
|
||||
* @param metadata opaque blob that will be stored and associated with the new
|
||||
* GFile instance
|
||||
* @return new GFile instance
|
||||
* @throws IOException if error adding files (too many files)
|
||||
*/
|
||||
public synchronized GFile storeSymlink(String path, long fileIndex, String symlinkPath,
|
||||
long length, METADATATYPE metadata) {
|
||||
long length, METADATATYPE metadata) throws IOException {
|
||||
String[] nameparts = FSUtilities.splitPath(path);
|
||||
if (nameparts.length == 0) {
|
||||
Msg.warn(this,
|
||||
@@ -434,9 +439,10 @@ public class FileSystemIndexHelper<METADATATYPE> {
|
||||
* @param metadata opaque blob that will be stored and associated with the new
|
||||
* GFile instance
|
||||
* @return new GFile instance
|
||||
* @throws IOException if error adding files (too many files)
|
||||
*/
|
||||
public synchronized GFile storeSymlinkWithParent(String filename, GFile parent, long fileIndex,
|
||||
String symlinkPath, long length, METADATATYPE metadata) {
|
||||
String symlinkPath, long length, METADATATYPE metadata) throws IOException {
|
||||
length = length != 0 ? length : symlinkPath.length();
|
||||
FileData<METADATATYPE> fileData =
|
||||
doStoreFile(filename, parent, fileIndex, false, length, symlinkPath, metadata);
|
||||
@@ -458,7 +464,13 @@ public class FileSystemIndexHelper<METADATATYPE> {
|
||||
}
|
||||
|
||||
private FileData<METADATATYPE> doStoreFile(String filename, GFile parent, long fileIndex,
|
||||
boolean isDirectory, long length, String symlinkPath, METADATATYPE metadata) {
|
||||
boolean isDirectory, long length, String symlinkPath, METADATATYPE metadata)
|
||||
throws IOException {
|
||||
|
||||
if (fileToEntryMap.size() > MAX_FILEENTRY_COUNT) {
|
||||
throw new IOException("Too many file entries: " + fileToEntryMap.size());
|
||||
}
|
||||
|
||||
parent = (parent == null) ? rootDir.file : parent;
|
||||
long fileNum = (fileIndex != -1) ? fileIndex : fileToEntryMap.size();
|
||||
if (fileIndexToEntryMap.containsKey(fileNum)) {
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/* ###
|
||||
* 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 ghidra.formats.gfilesystem;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class FileSystemIndexHelperTest {
|
||||
|
||||
@Test
|
||||
public void testMaxFiles() throws IOException {
|
||||
FSRLRoot fsFSRL = FSRLRoot.makeRoot("dummy");
|
||||
FileSystemIndexHelper<Object> fsih =
|
||||
new FileSystemIndexHelper<Object>(new DummyFileSystem(), fsFSRL);
|
||||
|
||||
for (int i = 0; i < FileSystemIndexHelper.MAX_FILEENTRY_COUNT; i++) {
|
||||
fsih.storeFile("file" + i, -1, false, 1, null);
|
||||
}
|
||||
|
||||
try {
|
||||
fsih.storeFile("toomuch", -1, false, 1, null);
|
||||
fail("Should not get here");
|
||||
}
|
||||
catch (IOException e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
private static class DummyFileSystem implements GFileSystem {
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
// empty dummy
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) throws IOException {
|
||||
// empty dummy
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
// empty dummy
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystemRefManager getRefManager() {
|
||||
// empty dummy
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
// empty dummy
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GFile> getListing(GFile directory) throws IOException {
|
||||
// empty dummy
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSRLRoot getFSRL() {
|
||||
// empty dummy
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteProvider getByteProvider(GFile file, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
// empty dummy
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import utilities.util.FileUtilities;
|
||||
public class BuildIdDebugFileProviderTest extends AbstractGenericTest {
|
||||
private TaskMonitor monitor = TaskMonitor.DUMMY;
|
||||
private File tmpDir;
|
||||
BuildIdDebugInfo id = new BuildIdDebugInfo(new byte[20] /* all 00's */);
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@@ -42,14 +43,13 @@ public class BuildIdDebugFileProviderTest extends AbstractGenericTest {
|
||||
public void testGet() throws IOException, CancelledException {
|
||||
BuildIdDebugFileProvider provider = new BuildIdDebugFileProvider(tmpDir);
|
||||
|
||||
String buildId = "0000000000000000000000000000000000000000";
|
||||
|
||||
String buildIdStr = id.getBuildIdHexString();
|
||||
File f = new File(tmpDir,
|
||||
"%s/%s.debug".formatted(buildId.substring(0, 2), buildId.substring(2)));
|
||||
"%s/%s.debug".formatted(buildIdStr.substring(0, 2), buildIdStr.substring(2)));
|
||||
FileUtilities.checkedMkdirs(f.getParentFile());
|
||||
FileUtilities.writeStringToFile(f, "test1");
|
||||
|
||||
File result = provider.getFile(ExternalDebugInfo.forBuildId(buildId), monitor);
|
||||
File result = provider.getFile(id, monitor);
|
||||
|
||||
assertEquals("test1", Files.readString(result.toPath()));
|
||||
assertEquals(5, result.length());
|
||||
|
||||
@@ -30,31 +30,32 @@ import com.sun.net.httpserver.HttpServer;
|
||||
import generic.hash.HashUtilities;
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.app.util.bin.format.dwarf.external.DebugStreamProvider.StreamInfo;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
|
||||
private TaskMonitor monitor = TaskMonitor.DUMMY;
|
||||
|
||||
BuildIdDebugInfo id = new BuildIdDebugInfo(new byte[20] /* all 00's */);
|
||||
|
||||
@Test
|
||||
public void testNoConnect() throws IOException, CancelledException {
|
||||
InetSocketAddress unusedAddr = nextLoopbackServerAddr();
|
||||
HttpDebugInfoDProvider httpProvider = new HttpDebugInfoDProvider(getURI(unusedAddr));
|
||||
StreamInfo stream = httpProvider.getStream(
|
||||
ExternalDebugInfo.forBuildId("0000000000000000000000000000000000000000"), monitor);
|
||||
StreamInfo stream = httpProvider.getStream(id, monitor);
|
||||
assertNull(stream);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGet() throws IOException, CancelledException {
|
||||
String buildId = "0000000000000000000000000000000000000000";
|
||||
|
||||
HttpServer server = createMockHttpServer();
|
||||
server.createContext("/buildid/" + buildId + "/debuginfo",
|
||||
server.createContext("/buildid/" + id.getBuildIdHexString() + "/debuginfo",
|
||||
createStaticResponseHandler("application/octet-stream", "result1".getBytes()));
|
||||
server.createContext("/buildid/" + buildId + "/executable",
|
||||
server.createContext("/buildid/" + id.getBuildIdHexString() + "/executable",
|
||||
createStaticResponseHandler("application/octet-stream", "result2".getBytes()));
|
||||
server.createContext("/buildid/" + buildId + "/source/usr/include/stdio.h",
|
||||
server.createContext("/buildid/" + id.getBuildIdHexString() + "/source/usr/include/stdio.h",
|
||||
createStaticResponseHandler("application/octet-stream", "result3".getBytes()));
|
||||
|
||||
HttpDebugInfoDProvider httpProvider =
|
||||
@@ -62,7 +63,6 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
|
||||
try {
|
||||
server.start();
|
||||
|
||||
ExternalDebugInfo id = ExternalDebugInfo.forBuildId(buildId);
|
||||
assertStreamResult("result1", httpProvider.getStream(id, monitor));
|
||||
assertStreamResult("result2",
|
||||
httpProvider.getStream(id.withType(ObjectType.EXECUTABLE, null), monitor));
|
||||
@@ -80,10 +80,9 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
|
||||
|
||||
@Test
|
||||
public void testGetWithRetry() throws IOException, CancelledException {
|
||||
String buildId = "0000000000000000000000000000000000000000";
|
||||
|
||||
HttpServer server = createMockHttpServer();
|
||||
server.createContext("/buildid/" + buildId + "/debuginfo",
|
||||
server.createContext("/buildid/" + id.getBuildIdHexString() + "/debuginfo",
|
||||
wrapHandlerWithRetryError(
|
||||
createStaticResponseHandler("application/octet-stream", "result1".getBytes()), 3,
|
||||
HTTP_INTERNAL_ERROR));
|
||||
@@ -93,7 +92,6 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
|
||||
try {
|
||||
server.start();
|
||||
|
||||
ExternalDebugInfo id = ExternalDebugInfo.forBuildId(buildId);
|
||||
assertStreamResult("result1", httpProvider.getStream(id, monitor));
|
||||
assertEquals(3, httpProvider.getRetriedCount());
|
||||
}
|
||||
@@ -104,10 +102,9 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
|
||||
|
||||
@Test
|
||||
public void testTimeout() throws IOException, CancelledException {
|
||||
String buildId = "0000000000000000000000000000000000000000";
|
||||
|
||||
HttpServer server = createMockHttpServer();
|
||||
server.createContext("/buildid/" + buildId + "/debuginfo", wrapHandlerWithDelay(
|
||||
server.createContext("/buildid/" + id.getBuildIdHexString() + "/debuginfo", wrapHandlerWithDelay(
|
||||
createStaticResponseHandler("application/octet-stream", "result1".getBytes()), 3000));
|
||||
|
||||
HttpDebugInfoDProvider httpProvider =
|
||||
@@ -118,9 +115,8 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
|
||||
server.start();
|
||||
|
||||
long startms = System.currentTimeMillis();
|
||||
ExternalDebugInfo id = ExternalDebugInfo.forBuildId(buildId);
|
||||
long elapsed = System.currentTimeMillis() - startms;
|
||||
assertNull(httpProvider.getStream(id, monitor));
|
||||
long elapsed = System.currentTimeMillis() - startms;
|
||||
assertTrue("Request took too long", elapsed < (1000 * 2)); // make sure request time was approx same as timeout setting
|
||||
}
|
||||
finally {
|
||||
@@ -130,6 +126,7 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
|
||||
|
||||
@Test
|
||||
public void testGetNotFound() throws IOException, CancelledException {
|
||||
|
||||
HttpServer server = createMockHttpServer();
|
||||
|
||||
HttpDebugInfoDProvider httpProvider =
|
||||
@@ -137,8 +134,6 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
|
||||
try {
|
||||
server.start();
|
||||
|
||||
ExternalDebugInfo id =
|
||||
ExternalDebugInfo.forBuildId("0000000000000000000000000000000000000000");
|
||||
assertNull(httpProvider.getStream(id, monitor));
|
||||
assertEquals(0, httpProvider.getRetriedCount());
|
||||
assertEquals(1, httpProvider.getNotFoundCount());
|
||||
@@ -150,9 +145,9 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
|
||||
|
||||
@Test
|
||||
public void testServerError() throws IOException, CancelledException {
|
||||
String buildId = "0000000000000000000000000000000000000000";
|
||||
|
||||
HttpServer server = createMockHttpServer();
|
||||
server.createContext("/buildid/" + buildId + "/debuginfo",
|
||||
server.createContext("/buildid/" + id.getBuildIdHexString() + "/debuginfo",
|
||||
createStaticResponseHandler(HTTP_INTERNAL_ERROR, "text/plain", "".getBytes()));
|
||||
|
||||
HttpDebugInfoDProvider httpProvider =
|
||||
@@ -160,8 +155,6 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
|
||||
try {
|
||||
server.start();
|
||||
|
||||
ExternalDebugInfo id =
|
||||
ExternalDebugInfo.forBuildId("0000000000000000000000000000000000000000");
|
||||
assertNull(httpProvider.getStream(id, monitor));
|
||||
assertEquals(4, httpProvider.getRetriedCount());
|
||||
assertEquals(0, httpProvider.getNotFoundCount());
|
||||
@@ -178,10 +171,10 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
|
||||
// The specified buildId may stop being present at some point of time in the future
|
||||
HttpDebugInfoDProvider httpProvider =
|
||||
new HttpDebugInfoDProvider(URI.create("https://debuginfod.elfutils.org/"));
|
||||
ExternalDebugInfo id =
|
||||
ExternalDebugInfo.forBuildId("421e1abd8faf1cb290df755a558377c5d7def3b1");
|
||||
BuildIdDebugInfo eu_id = new BuildIdDebugInfo(
|
||||
NumericUtilities.convertStringToBytes("421e1abd8faf1cb290df755a558377c5d7def3b1"));
|
||||
assertStreamHash("f5894783abae9084e531b8da76bbb2444a688d18",
|
||||
httpProvider.getStream(id, monitor));
|
||||
httpProvider.getStream(eu_id, monitor));
|
||||
}
|
||||
|
||||
private void assertStreamResult(String expectedResult, StreamInfo stream) throws IOException {
|
||||
|
||||
@@ -33,6 +33,8 @@ public class LocalDirDebugInfoDProviderTest extends AbstractGenericTest {
|
||||
private TaskMonitor monitor = TaskMonitor.DUMMY;
|
||||
private File tmpDir;
|
||||
|
||||
BuildIdDebugInfo id = new BuildIdDebugInfo(new byte[20] /* all 00's */);
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
tmpDir = createTempDirectory("debuginfod_provider_test");
|
||||
@@ -43,9 +45,7 @@ public class LocalDirDebugInfoDProviderTest extends AbstractGenericTest {
|
||||
LocalDirDebugInfoDProvider provider = new LocalDirDebugInfoDProvider(tmpDir);
|
||||
provider.purgeAll();
|
||||
|
||||
String buildId = "0000000000000000000000000000000000000000";
|
||||
|
||||
File f = new File(tmpDir, buildId + "/debuginfo");
|
||||
File f = new File(tmpDir, id.getBuildIdHexString() + "/debuginfo");
|
||||
|
||||
FileUtilities.checkedMkdirs(f.getParentFile());
|
||||
FileUtilities.writeStringToFile(f, "test1");
|
||||
@@ -70,13 +70,11 @@ public class LocalDirDebugInfoDProviderTest extends AbstractGenericTest {
|
||||
LocalDirDebugInfoDProvider provider = new LocalDirDebugInfoDProvider(tmpDir);
|
||||
provider.purgeAll();
|
||||
|
||||
String buildId = "0000000000000000000000000000000000000000";
|
||||
|
||||
File f = new File(tmpDir, buildId + "/debuginfo");
|
||||
File f = new File(tmpDir, id.getBuildIdHexString() + "/debuginfo");
|
||||
FileUtilities.checkedMkdirs(f.getParentFile());
|
||||
FileUtilities.writeStringToFile(f, "test1");
|
||||
|
||||
File result = provider.getFile(ExternalDebugInfo.forBuildId(buildId), monitor);
|
||||
File result = provider.getFile(id, monitor);
|
||||
|
||||
assertEquals("debuginfo", result.getName());
|
||||
assertEquals(5, result.length());
|
||||
@@ -87,10 +85,9 @@ public class LocalDirDebugInfoDProviderTest extends AbstractGenericTest {
|
||||
LocalDirDebugInfoDProvider provider = new LocalDirDebugInfoDProvider(tmpDir);
|
||||
provider.purgeAll();
|
||||
|
||||
String buildId = "0000000000000000000000000000000000000000";
|
||||
byte bytes[] = "test".getBytes();
|
||||
StreamInfo stream = new StreamInfo(new ByteArrayInputStream(bytes), bytes.length);
|
||||
File f = provider.putStream(ExternalDebugInfo.forBuildId(buildId), stream, monitor);
|
||||
File f = provider.putStream(id, stream, monitor);
|
||||
|
||||
assertEquals("debuginfo", f.getName());
|
||||
assertEquals(bytes.length, f.length());
|
||||
@@ -104,8 +101,8 @@ public class LocalDirDebugInfoDProviderTest extends AbstractGenericTest {
|
||||
byte bytes[] = "test".getBytes();
|
||||
StreamInfo stream = new StreamInfo(new ByteArrayInputStream(bytes), bytes.length);
|
||||
try {
|
||||
File f = provider.putStream(ExternalDebugInfo.forDebugLink("test.debug", 0x11223344),
|
||||
stream, monitor);
|
||||
File f = provider.putStream(new DebugLinkDebugInfo("test.debug", 0x11223344), stream,
|
||||
monitor);
|
||||
fail("Shouldn't get here: " + f);
|
||||
}
|
||||
catch (IOException e) {
|
||||
|
||||
@@ -48,7 +48,7 @@ public class LocalDirDebugLinkProviderTest extends AbstractGenericTest {
|
||||
|
||||
LocalDirDebugLinkProvider provider = new LocalDirDebugLinkProvider(tmpDir);
|
||||
File result =
|
||||
provider.getFile(ExternalDebugInfo.forDebugLink("debugfile.abc", crc), monitor);
|
||||
provider.getFile(new DebugLinkDebugInfo("debugfile.abc", crc), monitor);
|
||||
|
||||
assertEquals("test_debuglink", Files.readString(result.toPath()));
|
||||
}
|
||||
|
||||
@@ -103,9 +103,11 @@ public class FSRLTest {
|
||||
|
||||
@Test
|
||||
public void testStringFormat() throws MalformedURLException {
|
||||
FSRL fsrl = FSRL.fromString("fsrl://path/filename?MD5=1234|subfsrl://subpath/subfile");
|
||||
FSRL fsrl = FSRL.fromString(
|
||||
"fsrl://path/filename?MD5=00000000000000000000000000000000|subfsrl://subpath/subfile");
|
||||
|
||||
assertEquals("string format bad", "fsrl://path/filename?MD5=1234|subfsrl://subpath/subfile",
|
||||
assertEquals("string format bad",
|
||||
"fsrl://path/filename?MD5=00000000000000000000000000000000|subfsrl://subpath/subfile",
|
||||
fsrl.toString());
|
||||
assertEquals("pretty string format bad", "fsrl://path/filename|subfsrl://subpath/subfile",
|
||||
fsrl.toPrettyString());
|
||||
@@ -117,11 +119,12 @@ public class FSRLTest {
|
||||
|
||||
@Test
|
||||
public void testStringFormat2() throws MalformedURLException {
|
||||
FSRL fsrl =
|
||||
FSRL.fromString("fsrl://path/filename?MD5=1234|subfsrl://subpath/subfile|sub2://");
|
||||
FSRL fsrl = FSRL.fromString(
|
||||
"fsrl://path/filename?MD5=00000000000000000000000000000000|subfsrl://subpath/subfile|sub2://");
|
||||
|
||||
assertEquals("string format bad",
|
||||
"fsrl://path/filename?MD5=1234|subfsrl://subpath/subfile|sub2://", fsrl.toString());
|
||||
"fsrl://path/filename?MD5=00000000000000000000000000000000|subfsrl://subpath/subfile|sub2://",
|
||||
fsrl.toString());
|
||||
assertEquals("pretty string format bad",
|
||||
"fsrl://path/filename|subfsrl://subpath/subfile|sub2://", fsrl.toPrettyString());
|
||||
assertEquals("partial string format bad", "sub2://", fsrl.toStringPart());
|
||||
@@ -131,11 +134,12 @@ public class FSRLTest {
|
||||
|
||||
@Test
|
||||
public void testStringFormat3() throws MalformedURLException {
|
||||
FSRL fsrl =
|
||||
FSRL.fromString("fsrl:///path/filename?MD5=1234|subfsrl:///subpath/subfile|sub2://");
|
||||
FSRL fsrl = FSRL.fromString(
|
||||
"fsrl:///path/filename?MD5=00000000000000000000000000000000|subfsrl:///subpath/subfile|sub2://");
|
||||
|
||||
assertEquals("string format bad",
|
||||
"fsrl:///path/filename?MD5=1234|subfsrl:///subpath/subfile|sub2://", fsrl.toString());
|
||||
"fsrl:///path/filename?MD5=00000000000000000000000000000000|subfsrl:///subpath/subfile|sub2://",
|
||||
fsrl.toString());
|
||||
assertEquals("pretty string format bad",
|
||||
"fsrl:///path/filename|subfsrl:///subpath/subfile|sub2://", fsrl.toPrettyString());
|
||||
assertEquals("partial string format bad", "sub2://", fsrl.toStringPart());
|
||||
@@ -243,4 +247,12 @@ public class FSRLTest {
|
||||
|
||||
assertTrue(childFSRL.isDescendantOf(parentFSRL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadMD5s() throws MalformedURLException {
|
||||
assertNull(FSRL.fromString("fsrl://path/rootfile?MD5").getMD5());
|
||||
assertNull(FSRL.fromString("fsrl://path/rootfile?MD5=").getMD5());
|
||||
assertNull(FSRL.fromString("fsrl://path/rootfile?MD5=xyz").getMD5());
|
||||
assertNull(FSRL.fromString("fsrl://path/rootfile?MD5=AABB").getMD5());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -23,6 +23,7 @@ import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class SquashInodeTable {
|
||||
private static final int MAX_SANE_INODE_COUNT = 500_000;
|
||||
|
||||
// An array of inodes indexed by their inode number
|
||||
private final SquashInode[] inodes;
|
||||
@@ -43,6 +44,9 @@ public class SquashInodeTable {
|
||||
*/
|
||||
public SquashInodeTable(BinaryReader reader, SquashSuperBlock superBlock, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
if (superBlock.getInodeCount() > MAX_SANE_INODE_COUNT) {
|
||||
throw new IOException("Inode count large: " + superBlock.getInodeCount());
|
||||
}
|
||||
|
||||
// Read from the start of the inode table
|
||||
reader.setPointerIndex(superBlock.getInodeTableStart());
|
||||
@@ -52,7 +56,7 @@ public class SquashInodeTable {
|
||||
decompressInodeTable(reader, superBlock.getDirectoryTableStart(), superBlock, monitor);
|
||||
|
||||
// Create inode array. inode count is off by one
|
||||
inodes = new SquashInode[(int) superBlock.getInodeCount() + 1];
|
||||
inodes = new SquashInode[superBlock.getInodeCount() + 1];
|
||||
|
||||
// inodes begin indexing at 1, so 0th inode is null
|
||||
inodes[0] = null;
|
||||
|
||||
@@ -187,7 +187,7 @@ public class ZipFileSystem extends AbstractFileSystem<ZipArchiveEntry> {
|
||||
}, monitor);
|
||||
}
|
||||
|
||||
private void indexFiles(TaskMonitor monitor) throws CancelledException {
|
||||
private void indexFiles(TaskMonitor monitor) throws CancelledException, IOException {
|
||||
int zipIndex = 0;
|
||||
for (ZipArchiveEntry zipEntry : entries) {
|
||||
monitor.checkCancelled();
|
||||
|
||||
@@ -59,8 +59,9 @@ public class SkeletonFileSystem implements GFileSystem {
|
||||
* Mounts (opens) the file system.
|
||||
*
|
||||
* @param monitor A cancellable task monitor.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void mount(TaskMonitor monitor) {
|
||||
public void mount(TaskMonitor monitor) throws IOException {
|
||||
monitor.setMessage("Opening " + SkeletonFileSystem.class.getSimpleName() + "...");
|
||||
|
||||
// Customize how things in the file system are stored. The following should be
|
||||
|
||||
Reference in New Issue
Block a user