mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GP-1286 DWARF external debug files
This commit is contained in:
parent
1a7b45e656
commit
f023b8ad3f
21 changed files with 1339 additions and 31 deletions
|
@ -0,0 +1,55 @@
|
||||||
|
/* ###
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
// Example analyzeHeadless prescript to set the DWARF analyzer's external debug files
|
||||||
|
// search location.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// export DWARF_EXTERNAL_DEBUG_FILES=/home/myuserid/debugfiles
|
||||||
|
// analyzeHeadless [...] -preScript DWARFSetExternalDebugFilesLocationPrescript.java
|
||||||
|
//@category DWARF
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
import ghidra.app.util.bin.format.dwarf4.external.*;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
|
public class DWARFSetExternalDebugFilesLocationPrescript extends GhidraScript {
|
||||||
|
@Override
|
||||||
|
protected void run() throws Exception {
|
||||||
|
String dwarfExtDebugFilesLocEnvVar = System.getenv("DWARF_EXTERNAL_DEBUG_FILES");
|
||||||
|
if (dwarfExtDebugFilesLocEnvVar == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File dir = new File(dwarfExtDebugFilesLocEnvVar);
|
||||||
|
if (!dir.isDirectory()) {
|
||||||
|
Msg.warn(this, "Invalid DWARF external debug files location specified: " + dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<SearchLocation> searchLocations = new ArrayList<>();
|
||||||
|
|
||||||
|
File buildIdDir = new File(dir, ".build-id");
|
||||||
|
if (buildIdDir.isDirectory()) {
|
||||||
|
searchLocations.add(new BuildIdSearchLocation(buildIdDir));
|
||||||
|
}
|
||||||
|
searchLocations.add(new LocalDirectorySearchLocation(dir));
|
||||||
|
ExternalDebugFilesService edfs = new ExternalDebugFilesService(searchLocations);
|
||||||
|
DWARFExternalDebugFilesPlugin.saveExternalDebugFilesService(edfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
/* ###
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
// Bulk extracts ELF external debug files from distro container files (rpm, ddeb, etc) so that
|
||||||
|
// the DWARF analyzer can find them using the "Edit | DWARF External Debug Config" location.
|
||||||
|
//
|
||||||
|
// When using this script, do not co-mingle different architectures or versions of debug files in
|
||||||
|
// the same directory as debug file names may conflict.
|
||||||
|
//
|
||||||
|
// Known issues: symlinks between different locations inside the debug package are not extracted,
|
||||||
|
// so some layout schemes where a single debug binary file is present in both the .build-id directory
|
||||||
|
// and the /usr/lib/, /usr/bin/, etc directory will not fully work.
|
||||||
|
//
|
||||||
|
// @category DWARF
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
|
import ghidra.formats.gfilesystem.*;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import utilities.util.FileUtilities;
|
||||||
|
|
||||||
|
public class ExtractELFDebugFilesScript extends GhidraScript {
|
||||||
|
private static final Set<String> containerExts =
|
||||||
|
Set.of("ddeb", "tar", "xz", "rpm", "tgz", "deb", "srpm", "cpio");
|
||||||
|
|
||||||
|
private FileSystemService fsService;
|
||||||
|
private int fileCount;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() throws Exception {
|
||||||
|
fsService = FileSystemService.getInstance();
|
||||||
|
|
||||||
|
File baseDir = askDirectory("Debug Packages Directory", "Select");
|
||||||
|
if (baseDir == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File destDir = askDirectory("Extract Destination Directory", "Select");
|
||||||
|
if (destDir == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Source directory: " + baseDir);
|
||||||
|
println("Destination directory: " + destDir);
|
||||||
|
try (FileSystemRef fsRef =
|
||||||
|
fsService.probeFileForFilesystem(fsService.getLocalFSRL(baseDir), monitor, null)) {
|
||||||
|
processDir(fsRef.getFilesystem().lookup(null), destDir);
|
||||||
|
}
|
||||||
|
println("Extracted: " + fileCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void processDir(GFile dir, File destDir) throws IOException, CancelledException {
|
||||||
|
List<GFile> listing = dir.getListing();
|
||||||
|
for (GFile file : listing) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String extension = FilenameUtils.getExtension(file.getName()).toLowerCase();
|
||||||
|
if (containerExts.contains(extension)) {
|
||||||
|
try (FileSystemRef fsRef =
|
||||||
|
fsService.probeFileForFilesystem(file.getFSRL(), monitor, null)) {
|
||||||
|
if (fsRef == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
processDir(fsRef.getFilesystem().lookup(null), destDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ("debug".equalsIgnoreCase(extension)) {
|
||||||
|
extractDebugFileToDestDir(file, destDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (GFile file : listing) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
processDir(file, destDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void extractDebugFileToDestDir(GFile file, File destDir)
|
||||||
|
throws CancelledException, IOException {
|
||||||
|
File destFile = new File(destDir, file.getPath()).getCanonicalFile();
|
||||||
|
if (!FileUtilities.isPathContainedWithin(destDir, destFile)) {
|
||||||
|
throw new IOException("Bad path / filename: " + file);
|
||||||
|
}
|
||||||
|
if (destFile.exists()) {
|
||||||
|
printerr("Duplicate debug file: " + file.getFSRL() + ", " + destFile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (ByteProvider bp = file.getFilesystem().getByteProvider(file, monitor)) {
|
||||||
|
FileUtilities.checkedMkdirs(destFile.getParentFile());
|
||||||
|
FSUtilities.copyByteProviderToFile(bp, destFile, monitor);
|
||||||
|
fileCount++;
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
printerr("Error extracting file: " + file + ", " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -123,9 +123,10 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWARFSectionProvider dsp = DWARFSectionProviderFactory.createSectionProviderFor(program);
|
DWARFSectionProvider dsp =
|
||||||
|
DWARFSectionProviderFactory.createSectionProviderFor(program, monitor); // closed by DWARFProgram
|
||||||
if (dsp == null) {
|
if (dsp == null) {
|
||||||
log.appendMsg("Unable to find DWARF information, skipping DWARF analysis");
|
Msg.info(this, "Unable to find DWARF information, skipping DWARF analysis");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +146,7 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
|
||||||
parseResults.logSummaryResults();
|
parseResults.logSummaryResults();
|
||||||
}
|
}
|
||||||
propList.setBoolean(DWARF_LOADED_OPTION_NAME, true);
|
propList.setBoolean(DWARF_LOADED_OPTION_NAME, true);
|
||||||
|
dsp.updateProgramInfo(program);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (CancelledException ce) {
|
catch (CancelledException ce) {
|
||||||
|
|
|
@ -26,6 +26,19 @@ import ghidra.program.model.mem.MemoryBlock;
|
||||||
*/
|
*/
|
||||||
public class MemoryByteProvider implements ByteProvider {
|
public class MemoryByteProvider implements ByteProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link ByteProvider} that is limited to the specified {@link MemoryBlock}.
|
||||||
|
*
|
||||||
|
* @param memory {@link Memory} of the program
|
||||||
|
* @param block {@link MemoryBlock} to read from
|
||||||
|
* @return new {@link ByteProvider} that contains the bytes of the specified MemoryBlock
|
||||||
|
*/
|
||||||
|
public static ByteProvider createMemoryBlockByteProvider(Memory memory, MemoryBlock block) {
|
||||||
|
long blockLen = block.getEnd().subtract(block.getStart()) + 1;
|
||||||
|
ByteProvider bp = new MemoryByteProvider(memory, block.getStart());
|
||||||
|
return new ByteProviderWrapper(bp, 0, blockLen);
|
||||||
|
}
|
||||||
|
|
||||||
protected Memory memory;
|
protected Memory memory;
|
||||||
protected Address baseAddress;
|
protected Address baseAddress;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
/* ###
|
||||||
|
* 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.dwarf4.external;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import ghidra.formats.gfilesystem.FSRL;
|
||||||
|
import ghidra.formats.gfilesystem.FileSystemService;
|
||||||
|
import ghidra.util.NumericUtilities;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link SearchLocation} that expects the external debug files to be named using the hexadecimal
|
||||||
|
* value of the hash of the file, and to be arranged in a bucketed directory hierarchy using the
|
||||||
|
* first 2 hexdigits of the hash.
|
||||||
|
* <p>
|
||||||
|
* For example, the debug file with hash {@code 6addc39dc19c1b45f9ba70baf7fd81ea6508ea7f} would
|
||||||
|
* be stored as "6a/ddc39dc19c1b45f9ba70baf7fd81ea6508ea7f.debug" (under some root directory).
|
||||||
|
*/
|
||||||
|
public class BuildIdSearchLocation implements SearchLocation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the specified location string specifies a BuildIdSearchLocation.
|
||||||
|
*
|
||||||
|
* @param locString string to test
|
||||||
|
* @return boolean true if locString specifies a BuildId location
|
||||||
|
*/
|
||||||
|
public static boolean isBuildIdSearchLocation(String locString) {
|
||||||
|
return locString.startsWith(BUILD_ID_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link BuildIdSearchLocation} instance using the specified location string.
|
||||||
|
*
|
||||||
|
* @param locString string, earlier returned from {@link #getName()}
|
||||||
|
* @param context {@link SearchLocationCreatorContext} to allow accessing information outside
|
||||||
|
* of the location string that might be needed to create a new instance
|
||||||
|
* @return new {@link BuildIdSearchLocation} instance
|
||||||
|
*/
|
||||||
|
public static BuildIdSearchLocation create(String locString,
|
||||||
|
SearchLocationCreatorContext context) {
|
||||||
|
locString = locString.substring(BUILD_ID_PREFIX.length());
|
||||||
|
|
||||||
|
return new BuildIdSearchLocation(new File(locString));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String BUILD_ID_PREFIX = "build-id://";
|
||||||
|
private final File rootDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link BuildIdSearchLocation} at the specified location.
|
||||||
|
*
|
||||||
|
* @param rootDir path to the root directory of the build-id directory (typically ends with
|
||||||
|
* "./build-id")
|
||||||
|
*/
|
||||||
|
public BuildIdSearchLocation(File rootDir) {
|
||||||
|
this.rootDir = rootDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return BUILD_ID_PREFIX + rootDir.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescriptiveName() {
|
||||||
|
return rootDir.getPath() + " (build-id)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FSRL findDebugFile(ExternalDebugInfo debugInfo, TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException {
|
||||||
|
String hash = NumericUtilities.convertBytesToString(debugInfo.getHash());
|
||||||
|
if (hash == null || hash.length() < 4 /* 2 bytes = 4 hex digits */ ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
File bucketDir = new File(rootDir, hash.substring(0, 2));
|
||||||
|
File file = new File(bucketDir, hash.substring(2) + ".debug");
|
||||||
|
return file.isFile() ? FileSystemService.getInstance().getLocalFSRL(file) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
/* ###
|
||||||
|
* 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.dwarf4.external;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import docking.action.builder.ActionBuilder;
|
||||||
|
import docking.tool.ToolConstants;
|
||||||
|
import docking.widgets.filechooser.GhidraFileChooser;
|
||||||
|
import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||||
|
import ghidra.app.CorePluginPackage;
|
||||||
|
import ghidra.app.plugin.PluginCategoryNames;
|
||||||
|
import ghidra.framework.plugintool.*;
|
||||||
|
import ghidra.framework.plugintool.util.PluginStatus;
|
||||||
|
import ghidra.framework.preferences.Preferences;
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
@PluginInfo(
|
||||||
|
status = PluginStatus.RELEASED,
|
||||||
|
packageName = CorePluginPackage.NAME,
|
||||||
|
category = PluginCategoryNames.COMMON,
|
||||||
|
shortDescription = "DWARF External Debug Files",
|
||||||
|
description = "Configure how the DWARF analyzer finds external debug files."
|
||||||
|
)
|
||||||
|
//@formatter:on
|
||||||
|
public class DWARFExternalDebugFilesPlugin extends Plugin {
|
||||||
|
|
||||||
|
private static final String EXT_DEBUG_FILES_OPTION = "ExternalDebugFiles";
|
||||||
|
private static final String SEARCH_LOCATIONS_LIST_OPTION =
|
||||||
|
EXT_DEBUG_FILES_OPTION + ".searchLocations";
|
||||||
|
|
||||||
|
public DWARFExternalDebugFilesPlugin(PluginTool tool) {
|
||||||
|
super(tool);
|
||||||
|
|
||||||
|
createActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createActions() {
|
||||||
|
new ActionBuilder("DWARF External Debug Config", this.getName())
|
||||||
|
.menuPath(ToolConstants.MENU_EDIT, "DWARF External Debug Config")
|
||||||
|
.menuGroup(ToolConstants.TOOL_OPTIONS_MENU_GROUP)
|
||||||
|
.onAction(ac -> showConfigDialog())
|
||||||
|
.buildAndInstall(tool);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showConfigDialog() {
|
||||||
|
// Let the user pick a single directory, and configure a ".build-id/" search location
|
||||||
|
// and a recursive dir search location at that directory, as well as a
|
||||||
|
// same-dir search location to search the program's import directory.
|
||||||
|
GhidraFileChooser chooser = new GhidraFileChooser(tool.getActiveWindow());
|
||||||
|
chooser.setMultiSelectionEnabled(false);
|
||||||
|
chooser.setApproveButtonText("Select");
|
||||||
|
chooser.setFileSelectionMode(GhidraFileChooserMode.DIRECTORIES_ONLY);
|
||||||
|
chooser.setTitle("Select External Debug Files Directory");
|
||||||
|
File selectedDir = chooser.getSelectedFile();
|
||||||
|
if (selectedDir == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildIdSearchLocation bisl = new BuildIdSearchLocation(new File(selectedDir, ".build-id"));
|
||||||
|
LocalDirectorySearchLocation ldsl = new LocalDirectorySearchLocation(selectedDir);
|
||||||
|
SameDirSearchLocation sdsl = new SameDirSearchLocation(new File("does not matter"));
|
||||||
|
|
||||||
|
ExternalDebugFilesService edfs = new ExternalDebugFilesService(List.of(bisl, ldsl, sdsl));
|
||||||
|
saveExternalDebugFilesService(edfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a new instance of {@link ExternalDebugFilesService} using the previously saved
|
||||||
|
* information (via {@link #saveExternalDebugFilesService(ExternalDebugFilesService)}).
|
||||||
|
*
|
||||||
|
* @param context created via {@link SearchLocationRegistry#newContext(ghidra.program.model.listing.Program)}
|
||||||
|
* @return new {@link ExternalDebugFilesService} instance
|
||||||
|
*/
|
||||||
|
public static ExternalDebugFilesService getExternalDebugFilesService(
|
||||||
|
SearchLocationCreatorContext context) {
|
||||||
|
SearchLocationRegistry searchLocRegistry = SearchLocationRegistry.getInstance();
|
||||||
|
String searchPathStr = Preferences.getProperty(SEARCH_LOCATIONS_LIST_OPTION, "", true);
|
||||||
|
String[] pathParts = searchPathStr.split(";");
|
||||||
|
List<SearchLocation> searchLocs = new ArrayList<>();
|
||||||
|
for (String part : pathParts) {
|
||||||
|
if (!part.isBlank()) {
|
||||||
|
SearchLocation searchLoc = searchLocRegistry.createSearchLocation(part, context);
|
||||||
|
if (searchLoc != null) {
|
||||||
|
searchLocs.add(searchLoc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (searchLocs.isEmpty()) {
|
||||||
|
// default to search the same directory as the program
|
||||||
|
searchLocs.add(SameDirSearchLocation.create(null, context));
|
||||||
|
}
|
||||||
|
return new ExternalDebugFilesService(searchLocs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes an {@link ExternalDebugFilesService} to a string and writes to the Ghidra
|
||||||
|
* global preferences.
|
||||||
|
*
|
||||||
|
* @param service the {@link ExternalDebugFilesService} to commit to preferences
|
||||||
|
*/
|
||||||
|
public static void saveExternalDebugFilesService(ExternalDebugFilesService service) {
|
||||||
|
if (service != null) {
|
||||||
|
String path = service.getSearchLocations()
|
||||||
|
.stream()
|
||||||
|
.map(SearchLocation::getName)
|
||||||
|
.collect(Collectors.joining(";"));
|
||||||
|
Preferences.setProperty(SEARCH_LOCATIONS_LIST_OPTION, path);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Preferences.setProperty(SEARCH_LOCATIONS_LIST_OPTION, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
/* ###
|
||||||
|
* 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.dwarf4.external;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ghidra.formats.gfilesystem.FSRL;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of {@link SearchLocation search locations} that can be queried to find a
|
||||||
|
* DWARF external debug file, which is a second ELF binary that contains the debug information
|
||||||
|
* that was stripped from the original ELF binary.
|
||||||
|
*/
|
||||||
|
public class ExternalDebugFilesService {
|
||||||
|
private List<SearchLocation> searchLocations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance using the list of search locations.
|
||||||
|
*
|
||||||
|
* @param searchLocations list of {@link SearchLocation search locations}
|
||||||
|
*/
|
||||||
|
public ExternalDebugFilesService(List<SearchLocation> searchLocations) {
|
||||||
|
this.searchLocations = searchLocations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the configured search locations.
|
||||||
|
*
|
||||||
|
* @return list of search locations
|
||||||
|
*/
|
||||||
|
public List<SearchLocation> getSearchLocations() {
|
||||||
|
return searchLocations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for the specified external debug file.
|
||||||
|
* <p>
|
||||||
|
* Returns the FSRL of a matching file, or null if not found.
|
||||||
|
*
|
||||||
|
* @param debugInfo information about the external debug file
|
||||||
|
* @param monitor {@link TaskMonitor}
|
||||||
|
* @return {@link FSRL} of found file, or {@code null} if not found
|
||||||
|
* @throws IOException if error
|
||||||
|
*/
|
||||||
|
public FSRL findDebugFile(ExternalDebugInfo debugInfo, TaskMonitor monitor)
|
||||||
|
throws IOException {
|
||||||
|
try {
|
||||||
|
for (SearchLocation searchLoc : searchLocations) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
FSRL result = searchLoc.findDebugFile(debugInfo, monitor);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (CancelledException ce) {
|
||||||
|
// fall thru, return null
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/* ###
|
||||||
|
* 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.dwarf4.external;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.format.elf.GnuBuildIdSection;
|
||||||
|
import ghidra.app.util.bin.format.elf.GnuBuildIdSection.GnuBuildIdValues;
|
||||||
|
import ghidra.app.util.bin.format.elf.GnuDebugLinkSection;
|
||||||
|
import ghidra.app.util.bin.format.elf.GnuDebugLinkSection.GnuDebugLinkSectionValues;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.util.NumericUtilities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information needed to find an ELF/DWARF external debug file, found in an ELF binary's
|
||||||
|
* ".gnu_debuglink" section and/or ".note.gnu.build-id" section.
|
||||||
|
* <p>
|
||||||
|
* 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 static ExternalDebugInfo fromProgram(Program program) {
|
||||||
|
GnuDebugLinkSectionValues debugLinkValues = GnuDebugLinkSection.fromProgram(program);
|
||||||
|
GnuBuildIdValues buildIdValues = GnuBuildIdSection.fromProgram(program);
|
||||||
|
if (buildIdValues != null && !buildIdValues.isValid()) {
|
||||||
|
buildIdValues = null;
|
||||||
|
}
|
||||||
|
if (debugLinkValues == null && buildIdValues == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ExternalDebugInfo(debugLinkValues.getFilename(), debugLinkValues.getCrc(),
|
||||||
|
buildIdValues != null ? buildIdValues.getDescription() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String filename;
|
||||||
|
private int crc;
|
||||||
|
private byte[] hash;
|
||||||
|
|
||||||
|
public ExternalDebugInfo(String filename, int crc, byte[] hash) {
|
||||||
|
this.filename = filename;
|
||||||
|
this.crc = crc;
|
||||||
|
this.hash = hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCrc() {
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getHash() {
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("ExternalDebugInfo [filename=%s, crc=%s, hash=%s]",
|
||||||
|
filename,
|
||||||
|
Integer.toHexString(crc),
|
||||||
|
NumericUtilities.convertBytesToString(hash));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
/* ###
|
||||||
|
* 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.dwarf4.external;
|
||||||
|
|
||||||
|
import java.util.zip.CRC32;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
import ghidra.formats.gfilesystem.FSRL;
|
||||||
|
import ghidra.formats.gfilesystem.FileSystemService;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link SearchLocation} that recursively searches for dwarf external debug files
|
||||||
|
* under a configured directory.
|
||||||
|
*/
|
||||||
|
public class LocalDirectorySearchLocation implements SearchLocation {
|
||||||
|
|
||||||
|
private static final String LOCAL_DIR_PREFIX = "dir://";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the specified location string specifies a LocalDirectorySearchLocation.
|
||||||
|
*
|
||||||
|
* @param locString string to test
|
||||||
|
* @return boolean true if locString specifies a local dir search location
|
||||||
|
*/
|
||||||
|
public static boolean isLocalDirSearchLoc(String locString) {
|
||||||
|
return locString.startsWith(LOCAL_DIR_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link LocalDirectorySearchLocation} instance using the specified location string.
|
||||||
|
*
|
||||||
|
* @param locString string, earlier returned from {@link #getName()}
|
||||||
|
* @param context {@link SearchLocationCreatorContext} to allow accessing information outside
|
||||||
|
* of the location string that might be needed to create a new instance
|
||||||
|
* @return new {@link LocalDirectorySearchLocation} instance
|
||||||
|
*/
|
||||||
|
public static LocalDirectorySearchLocation create(String locString,
|
||||||
|
SearchLocationCreatorContext context) {
|
||||||
|
locString = locString.substring(LOCAL_DIR_PREFIX.length());
|
||||||
|
return new LocalDirectorySearchLocation(new File(locString));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final File searchDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link LocalDirectorySearchLocation} at the specified location.
|
||||||
|
*
|
||||||
|
* @param searchDir path to the root directory of where to search
|
||||||
|
*/
|
||||||
|
public LocalDirectorySearchLocation(File searchDir) {
|
||||||
|
this.searchDir = searchDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return LOCAL_DIR_PREFIX + searchDir.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescriptiveName() {
|
||||||
|
return searchDir.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FSRL findDebugFile(ExternalDebugInfo debugInfo, TaskMonitor monitor)
|
||||||
|
throws CancelledException, IOException {
|
||||||
|
String filename = debugInfo.getFilename();
|
||||||
|
if (filename == null || filename.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ensureSafeFilename(filename);
|
||||||
|
return findFile(searchDir, debugInfo, monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureSafeFilename(String filename) throws IOException {
|
||||||
|
File testFile = new File(searchDir, filename);
|
||||||
|
if (!searchDir.equals(testFile.getParentFile())) {
|
||||||
|
throw new IOException("Unsupported path specified in debug file: " + filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FSRL findFile(File dir, ExternalDebugInfo debugInfo, TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException {
|
||||||
|
File file = new File(dir, debugInfo.getFilename());
|
||||||
|
if (file.isFile()) {
|
||||||
|
int fileCRC = calcCRC(file);
|
||||||
|
if (fileCRC == debugInfo.getCrc()) {
|
||||||
|
return FileSystemService.getInstance().getLocalFSRL(file);
|
||||||
|
}
|
||||||
|
Msg.info(this, "DWARF external debug file found with mismatching crc, ignored: " +
|
||||||
|
file + ", (" + Integer.toHexString(fileCRC) + ")");
|
||||||
|
}
|
||||||
|
File[] subDirs;
|
||||||
|
if ((subDirs = dir.listFiles(f -> f.isDirectory())) != null) {
|
||||||
|
for (File subDir : subDirs) {
|
||||||
|
FSRL result = findFile(subDir, debugInfo, monitor);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the crc32 for the specified file.
|
||||||
|
*
|
||||||
|
* @param f {@link File} to read
|
||||||
|
* @return int crc32
|
||||||
|
* @throws IOException if error reading file
|
||||||
|
*/
|
||||||
|
public static int calcCRC(File f) throws IOException {
|
||||||
|
byte[] bytes = new byte[64 * 1024];
|
||||||
|
|
||||||
|
CRC32 crc32 = new CRC32();
|
||||||
|
int bytesRead;
|
||||||
|
try (FileInputStream fis = new FileInputStream(f)) {
|
||||||
|
while ((bytesRead = fis.read(bytes)) > 0) {
|
||||||
|
crc32.update(bytes, 0, bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (int) crc32.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/* ###
|
||||||
|
* 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.dwarf4.external;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
|
||||||
|
import ghidra.formats.gfilesystem.FSRL;
|
||||||
|
import ghidra.formats.gfilesystem.FileSystemService;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link SearchLocation} that only looks in the program's original import directory.
|
||||||
|
*/
|
||||||
|
public class SameDirSearchLocation implements SearchLocation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the specified location string specifies a SameDirSearchLocation.
|
||||||
|
*
|
||||||
|
* @param locString string to test
|
||||||
|
* @return boolean true if locString specifies a BuildId location
|
||||||
|
*/
|
||||||
|
public static boolean isSameDirSearchLocation(String locString) {
|
||||||
|
return locString.equals(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link SameDirSearchLocation} instance using the current program's
|
||||||
|
* import location.
|
||||||
|
*
|
||||||
|
* @param locString unused
|
||||||
|
* @param context {@link SearchLocationCreatorContext}
|
||||||
|
* @return new {@link SameDirSearchLocation} instance
|
||||||
|
*/
|
||||||
|
public static SameDirSearchLocation create(String locString,
|
||||||
|
SearchLocationCreatorContext context) {
|
||||||
|
File exeLocation =
|
||||||
|
new File(FilenameUtils.getFullPath(context.getProgram().getExecutablePath()));
|
||||||
|
return new SameDirSearchLocation(exeLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final File progDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link SameDirSearchLocation} at the specified location.
|
||||||
|
*
|
||||||
|
* @param progDir path to the program's import directory
|
||||||
|
*/
|
||||||
|
public SameDirSearchLocation(File progDir) {
|
||||||
|
this.progDir = progDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescriptiveName() {
|
||||||
|
return progDir.getPath() + " (Program's Import Location)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FSRL findDebugFile(ExternalDebugInfo debugInfo, TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException {
|
||||||
|
File file = new File(progDir, debugInfo.getFilename());
|
||||||
|
if (!file.isFile()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int fileCRC = LocalDirectorySearchLocation.calcCRC(file);
|
||||||
|
if (fileCRC != debugInfo.getCrc()) {
|
||||||
|
Msg.info(this, "DWARF external debug file found with mismatching crc, ignored: " +
|
||||||
|
file + ", (" + Integer.toHexString(fileCRC) + ")");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return FileSystemService.getInstance().getLocalFSRL(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/* ###
|
||||||
|
* 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.dwarf4.external;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import ghidra.formats.gfilesystem.FSRL;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a collection of dwarf external debug files that can be searched.
|
||||||
|
*/
|
||||||
|
public interface SearchLocation {
|
||||||
|
/**
|
||||||
|
* Searchs for a debug file that fulfills the criteria specified in the {@link ExternalDebugInfo}.
|
||||||
|
*
|
||||||
|
* @param debugInfo search criteria
|
||||||
|
* @param monitor {@link TaskMonitor}
|
||||||
|
* @return {@link FSRL} of the matching file, or {@code null} if not found
|
||||||
|
* @throws IOException if error
|
||||||
|
* @throws CancelledException if cancelled
|
||||||
|
*/
|
||||||
|
FSRL findDebugFile(ExternalDebugInfo debugInfo, TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of this instance, which should be a serialized copy of this instance.
|
||||||
|
*
|
||||||
|
* @return String serialized data of this instance, typically in "something://serialized_data"
|
||||||
|
* form
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a human formatted string describing this location, used in UI prompts or lists.
|
||||||
|
*
|
||||||
|
* @return formatted string
|
||||||
|
*/
|
||||||
|
String getDescriptiveName();
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/* ###
|
||||||
|
* 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.dwarf4.external;
|
||||||
|
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information outside of a location string that might be needed to create a new {@link SearchLocation}
|
||||||
|
* instance.
|
||||||
|
*/
|
||||||
|
public class SearchLocationCreatorContext {
|
||||||
|
private final SearchLocationRegistry registry;
|
||||||
|
private final Program program;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new context object with references to the registry and the current program.
|
||||||
|
*
|
||||||
|
* @param registry {@link SearchLocationRegistry}
|
||||||
|
* @param program the current {@link Program}
|
||||||
|
*/
|
||||||
|
public SearchLocationCreatorContext(SearchLocationRegistry registry, Program program) {
|
||||||
|
this.registry = registry;
|
||||||
|
this.program = program;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the {@link SearchLocationRegistry} that is creating the {@link SearchLocation}
|
||||||
|
*/
|
||||||
|
public SearchLocationRegistry getRegistry() {
|
||||||
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the current {@link Program}
|
||||||
|
*/
|
||||||
|
public Program getProgram() {
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
/* ###
|
||||||
|
* 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.dwarf4.external;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of {@link SearchLocation} types that can be saved / restored from a configuration string.
|
||||||
|
*/
|
||||||
|
public class SearchLocationRegistry {
|
||||||
|
public static SearchLocationRegistry getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final SearchLocationRegistry instance = new SearchLocationRegistry(true);
|
||||||
|
|
||||||
|
private List<SearchLocationCreationInfo> searchLocCreators = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new registry, optionally registering the default SearchLocations.
|
||||||
|
*
|
||||||
|
* @param registerDefault boolean flag, if true register the built-in {@link SearchLocation}s
|
||||||
|
*/
|
||||||
|
public SearchLocationRegistry(boolean registerDefault) {
|
||||||
|
if (registerDefault) {
|
||||||
|
register(LocalDirectorySearchLocation::isLocalDirSearchLoc,
|
||||||
|
LocalDirectorySearchLocation::create);
|
||||||
|
register(BuildIdSearchLocation::isBuildIdSearchLocation, BuildIdSearchLocation::create);
|
||||||
|
register(SameDirSearchLocation::isSameDirSearchLocation, SameDirSearchLocation::create);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a {@link SearchLocation} to this registry.
|
||||||
|
*
|
||||||
|
* @param testFunc a {@link Predicate} that tests a location string, returning true if the
|
||||||
|
* string specifies the SearchLocation in question
|
||||||
|
* @param createFunc a {@link SearchLocationCreator} that will create a new {@link SearchLocation}
|
||||||
|
* instance given a location string and a {@link SearchLocationCreatorContext context}
|
||||||
|
*/
|
||||||
|
public void register(Predicate<String> testFunc, SearchLocationCreator createFunc) {
|
||||||
|
searchLocCreators.add(new SearchLocationCreationInfo(testFunc, createFunc));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link SearchLocationCreatorContext context}.
|
||||||
|
*
|
||||||
|
* @param program {@link Program}
|
||||||
|
* @return new {@link SearchLocationCreatorContext}
|
||||||
|
*/
|
||||||
|
public SearchLocationCreatorContext newContext(Program program) {
|
||||||
|
return new SearchLocationCreatorContext(this, program);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link SearchLocation} using the provided location string.
|
||||||
|
*
|
||||||
|
* @param locString location string (previously returned by {@link SearchLocation#getName()}
|
||||||
|
* @param context a {@link SearchLocationCreatorContext context}
|
||||||
|
* @return new {@link SearchLocation} instance, or null if there are no registered matching
|
||||||
|
* SearchLocations
|
||||||
|
*/
|
||||||
|
public SearchLocation createSearchLocation(String locString,
|
||||||
|
SearchLocationCreatorContext context) {
|
||||||
|
for (SearchLocationCreationInfo slci : searchLocCreators) {
|
||||||
|
if (slci.testFunc.test(locString)) {
|
||||||
|
return slci.createFunc.create(locString, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface SearchLocationCreator {
|
||||||
|
/**
|
||||||
|
* Creates a new {@link SearchLocation} instance using the provided location string.
|
||||||
|
*
|
||||||
|
* @param locString location string, previously returned by {@link SearchLocation#getName()}
|
||||||
|
* @param context {@link SearchLocationCreatorContext context}
|
||||||
|
* @return new {@link SearchLocation}
|
||||||
|
*/
|
||||||
|
SearchLocation create(String locString, SearchLocationCreatorContext context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SearchLocationCreationInfo {
|
||||||
|
Predicate<String> testFunc;
|
||||||
|
SearchLocationCreator createFunc;
|
||||||
|
|
||||||
|
SearchLocationCreationInfo(Predicate<String> testFunc,
|
||||||
|
SearchLocationCreator createFunc) {
|
||||||
|
this.testFunc = testFunc;
|
||||||
|
this.createFunc = createFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,10 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.dwarf4.next;
|
package ghidra.app.util.bin.format.dwarf4.next;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
import org.apache.commons.collections4.ListValuedMap;
|
import org.apache.commons.collections4.ListValuedMap;
|
||||||
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
|
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
|
||||||
|
@ -28,6 +27,7 @@ import ghidra.app.util.bin.format.dwarf4.*;
|
||||||
import ghidra.app.util.bin.format.dwarf4.attribs.DWARFAttributeFactory;
|
import ghidra.app.util.bin.format.dwarf4.attribs.DWARFAttributeFactory;
|
||||||
import ghidra.app.util.bin.format.dwarf4.encoding.*;
|
import ghidra.app.util.bin.format.dwarf4.encoding.*;
|
||||||
import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException;
|
import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException;
|
||||||
|
import ghidra.app.util.bin.format.dwarf4.external.ExternalDebugInfo;
|
||||||
import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.*;
|
import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.*;
|
||||||
import ghidra.app.util.opinion.*;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
|
@ -72,10 +72,8 @@ public class DWARFProgram implements Closeable {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case ElfLoader.ELF_NAME:
|
case ElfLoader.ELF_NAME:
|
||||||
case PeLoader.PE_NAME:
|
case PeLoader.PE_NAME:
|
||||||
try (DWARFSectionProvider dsp =
|
return BaseSectionProvider.hasDWARFSections(program) ||
|
||||||
DWARFSectionProviderFactory.createSectionProviderFor(program)) {
|
ExternalDebugInfo.fromProgram(program) != null;
|
||||||
return dsp != null;
|
|
||||||
}
|
|
||||||
case MachoLoader.MACH_O_NAME:
|
case MachoLoader.MACH_O_NAME:
|
||||||
return DSymSectionProvider.getDSYMForProgram(program) != null;
|
return DSymSectionProvider.getDSYMForProgram(program) != null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,18 +15,24 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.dwarf4.next.sectionprovider;
|
package ghidra.app.util.bin.format.dwarf4.next.sectionprovider;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.bin.MemoryByteProvider;
|
import ghidra.app.util.bin.MemoryByteProvider;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.mem.MemoryBlock;
|
import ghidra.program.model.mem.MemoryBlock;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches DWARF sections from a normal program using simple Ghidra memory blocks.
|
* Fetches DWARF sections from a normal program using simple Ghidra memory blocks.
|
||||||
*/
|
*/
|
||||||
public class BaseSectionProvider implements DWARFSectionProvider {
|
public class BaseSectionProvider implements DWARFSectionProvider {
|
||||||
private Program program;
|
protected Program program;
|
||||||
|
|
||||||
|
public static boolean hasDWARFSections(Program program) {
|
||||||
|
try (BaseSectionProvider tmp = new BaseSectionProvider(program)) {
|
||||||
|
return tmp.hasSection(DWARFSectionNames.MINIMAL_DWARF_SECTIONS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static BaseSectionProvider createSectionProviderFor(Program program) {
|
public static BaseSectionProvider createSectionProviderFor(Program program) {
|
||||||
return new BaseSectionProvider(program);
|
return new BaseSectionProvider(program);
|
||||||
|
@ -43,7 +49,7 @@ public class BaseSectionProvider implements DWARFSectionProvider {
|
||||||
if (block == null) {
|
if (block == null) {
|
||||||
block = program.getMemory().getBlock("." + sectionName);
|
block = program.getMemory().getBlock("." + sectionName);
|
||||||
}
|
}
|
||||||
if (block != null) {
|
if (block != null && block.isInitialized()) {
|
||||||
// TODO: limit the returned ByteProvider to block.getSize() bytes
|
// TODO: limit the returned ByteProvider to block.getSize() bytes
|
||||||
return new MemoryByteProvider(program.getMemory(), block.getStart());
|
return new MemoryByteProvider(program.getMemory(), block.getStart());
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,17 +15,16 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.dwarf4.next.sectionprovider;
|
package ghidra.app.util.bin.format.dwarf4.next.sectionprovider;
|
||||||
|
|
||||||
import ghidra.app.util.bin.*;
|
|
||||||
import ghidra.app.util.bin.format.macho.*;
|
|
||||||
import ghidra.app.util.opinion.MachoLoader;
|
|
||||||
import ghidra.program.model.listing.Program;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import generic.continues.RethrowContinuesFactory;
|
import generic.continues.RethrowContinuesFactory;
|
||||||
|
import ghidra.app.util.bin.*;
|
||||||
|
import ghidra.app.util.bin.format.macho.*;
|
||||||
|
import ghidra.app.util.opinion.MachoLoader;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches DWARF section data for a MachO program with co-located .dSYM folder. (ie. Mac OSX
|
* Fetches DWARF section data for a MachO program with co-located .dSYM folder. (ie. Mac OSX
|
||||||
|
|
|
@ -15,11 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.dwarf4.next.sectionprovider;
|
package ghidra.app.util.bin.format.dwarf4.next.sectionprovider;
|
||||||
|
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A DWARFSectionProvider is responsible for allowing access to DWARF section data of
|
* A DWARFSectionProvider is responsible for allowing access to DWARF section data of
|
||||||
* a Ghidra program.
|
* a Ghidra program.
|
||||||
|
@ -31,7 +32,15 @@ import java.io.IOException;
|
||||||
* <p>
|
* <p>
|
||||||
*/
|
*/
|
||||||
public interface DWARFSectionProvider extends Closeable {
|
public interface DWARFSectionProvider extends Closeable {
|
||||||
public boolean hasSection(String... sectionNames);
|
boolean hasSection(String... sectionNames);
|
||||||
public ByteProvider getSectionAsByteProvider(String sectionName) throws IOException;
|
|
||||||
public void close();
|
ByteProvider getSectionAsByteProvider(String sectionName) throws IOException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void close();
|
||||||
|
|
||||||
|
default void updateProgramInfo(Program program) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,12 @@
|
||||||
package ghidra.app.util.bin.format.dwarf4.next.sectionprovider;
|
package ghidra.app.util.bin.format.dwarf4.next.sectionprovider;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Auto-detects which {@link DWARFSectionProvider} matches a Ghidra program.
|
* Auto-detects which {@link DWARFSectionProvider} matches a Ghidra program.
|
||||||
|
@ -39,12 +39,9 @@ public class DWARFSectionProviderFactory {
|
||||||
* The method should not throw anything, instead just return a NULL.
|
* The method should not throw anything, instead just return a NULL.
|
||||||
*/
|
*/
|
||||||
private static final List<Function<Program, DWARFSectionProvider>> sectionProviderFactoryFuncs =
|
private static final List<Function<Program, DWARFSectionProvider>> sectionProviderFactoryFuncs =
|
||||||
new ArrayList<>();
|
List.of(
|
||||||
|
BaseSectionProvider::createSectionProviderFor,
|
||||||
static {
|
DSymSectionProvider::createSectionProviderFor);
|
||||||
sectionProviderFactoryFuncs.add(BaseSectionProvider::createSectionProviderFor);
|
|
||||||
sectionProviderFactoryFuncs.add(DSymSectionProvider::createSectionProviderFor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates through the statically registered {@link #sectionProviderFactoryFuncs factory funcs},
|
* Iterates through the statically registered {@link #sectionProviderFactoryFuncs factory funcs},
|
||||||
|
@ -78,4 +75,28 @@ public class DWARFSectionProviderFactory {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through the statically registered {@link #sectionProviderFactoryFuncs factory funcs},
|
||||||
|
* trying each factory method until one returns a {@link DWARFSectionProvider}
|
||||||
|
* that can successfully retrieve the {@link DWARFSectionNames#MINIMAL_DWARF_SECTIONS minimal}
|
||||||
|
* sections we need to do a DWARF import.
|
||||||
|
* <p>
|
||||||
|
* The resulting {@link DWARFSectionProvider} is {@link Closeable} and it is the caller's
|
||||||
|
* responsibility to ensure that the object is closed when done.
|
||||||
|
*
|
||||||
|
* @param program Ghidra {@link Program}
|
||||||
|
* @param monitor {@link TaskMonitor}
|
||||||
|
* @return {@link DWARFSectionProvider} that should be closed by the caller or NULL if no
|
||||||
|
* section provider types match the specified program.
|
||||||
|
*/
|
||||||
|
public static DWARFSectionProvider createSectionProviderFor(Program program,
|
||||||
|
TaskMonitor monitor) {
|
||||||
|
DWARFSectionProvider result = createSectionProviderFor(program);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result = ExternalDebugFileSectionProvider.createSectionProviderFor(program, monitor);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
/* ###
|
||||||
|
* 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.dwarf4.next.sectionprovider;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
|
import ghidra.app.util.bin.format.dwarf4.external.*;
|
||||||
|
import ghidra.app.util.importer.MessageLog;
|
||||||
|
import ghidra.app.util.opinion.ElfLoader;
|
||||||
|
import ghidra.formats.gfilesystem.*;
|
||||||
|
import ghidra.framework.options.Options;
|
||||||
|
import ghidra.program.database.ProgramDB;
|
||||||
|
import ghidra.program.model.lang.CompilerSpec;
|
||||||
|
import ghidra.program.model.lang.Language;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link DWARFSectionProvider} that reads .debug_info (and friends) sections from an external
|
||||||
|
* ELF file that is referenced in the original ELF file's build-id or debuglink sections.
|
||||||
|
* <p>
|
||||||
|
* Creates a pinning reference from the temporary external ELF debug file to this SectionProvider
|
||||||
|
* instance using the program's {@link Program#addConsumer(Object)}, and then releases the
|
||||||
|
* consumer when this instance is closed, allowing the temporary Program to be destroyed.
|
||||||
|
*/
|
||||||
|
public class ExternalDebugFileSectionProvider extends BaseSectionProvider {
|
||||||
|
public static final String PROGRAM_INFO_DWARF_EXTERNAL_DEBUG_FILE = "DWARF External Debug File";
|
||||||
|
|
||||||
|
public static DWARFSectionProvider createSectionProviderFor(Program program,
|
||||||
|
TaskMonitor monitor) {
|
||||||
|
try {
|
||||||
|
ExternalDebugInfo extDebugInfo = ExternalDebugInfo.fromProgram(program);
|
||||||
|
if (extDebugInfo == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Msg.info(ExternalDebugFilesService.class,
|
||||||
|
"DWARF external debug information found: " + extDebugInfo);
|
||||||
|
ExternalDebugFilesService edfs =
|
||||||
|
DWARFExternalDebugFilesPlugin.getExternalDebugFilesService(
|
||||||
|
SearchLocationRegistry.getInstance().newContext(program));
|
||||||
|
FSRL extDebugFile = edfs.findDebugFile(extDebugInfo, monitor);
|
||||||
|
if (extDebugFile == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Msg.info(ExternalDebugFilesService.class,
|
||||||
|
"DWARF External Debug File: found: " + extDebugFile);
|
||||||
|
FileSystemService fsService = FileSystemService.getInstance();
|
||||||
|
try (
|
||||||
|
RefdFile refdDebugFile = fsService.getRefdFile(extDebugFile, monitor);
|
||||||
|
ByteProvider debugFileByteProvider =
|
||||||
|
fsService.getByteProvider(refdDebugFile.file.getFSRL(), false, monitor);) {
|
||||||
|
Object consumer = new Object();
|
||||||
|
Language lang = program.getLanguage();
|
||||||
|
CompilerSpec compSpec =
|
||||||
|
lang.getCompilerSpecByID(program.getCompilerSpec().getCompilerSpecID());
|
||||||
|
Program debugProgram =
|
||||||
|
new ProgramDB("temp external debug info for " + program.getName(), lang,
|
||||||
|
compSpec, consumer);
|
||||||
|
ElfLoader elfLoader = new ElfLoader();
|
||||||
|
elfLoader.load(debugFileByteProvider, null, List.of(), debugProgram, monitor,
|
||||||
|
new MessageLog());
|
||||||
|
ExternalDebugFileSectionProvider result =
|
||||||
|
new ExternalDebugFileSectionProvider(debugProgram,
|
||||||
|
debugFileByteProvider.getFSRL());
|
||||||
|
debugProgram.release(consumer);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException | CancelledException e) {
|
||||||
|
// fall thru
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final FSRL externalDebugFileLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link ExternalDebugFileSectionProvider}
|
||||||
|
*
|
||||||
|
* @param program the external ELF {@link Program}
|
||||||
|
* @param externalDebugFileLocation the location where the external ELF debug file is located
|
||||||
|
*/
|
||||||
|
ExternalDebugFileSectionProvider(Program program, FSRL externalDebugFileLocation) {
|
||||||
|
super(program);
|
||||||
|
this.externalDebugFileLocation = externalDebugFileLocation;
|
||||||
|
program.addConsumer(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (program != null) {
|
||||||
|
program.release(this);
|
||||||
|
}
|
||||||
|
super.close();
|
||||||
|
|
||||||
|
program = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateProgramInfo(Program program) {
|
||||||
|
Options options = program.getOptions(Program.PROGRAM_INFO);
|
||||||
|
options.setString(PROGRAM_INFO_DWARF_EXTERNAL_DEBUG_FILE,
|
||||||
|
externalDebugFileLocation.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the previouly saved value of the external debug file location from the program's
|
||||||
|
* metadata.
|
||||||
|
*
|
||||||
|
* @param program DWARF that previously was analyzed
|
||||||
|
* @return FSRL of external debug file, or null if missing or corrupted value
|
||||||
|
*/
|
||||||
|
public static FSRL getExternalDebugFileLocation(Program program) {
|
||||||
|
Options options = program.getOptions(Program.PROGRAM_INFO);
|
||||||
|
String fsrlStr = options.getString(PROGRAM_INFO_DWARF_EXTERNAL_DEBUG_FILE, null);
|
||||||
|
try {
|
||||||
|
return fsrlStr != null ? FSRL.fromString(fsrlStr) : null;
|
||||||
|
}
|
||||||
|
catch (MalformedURLException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,9 +17,12 @@ package ghidra.app.util.bin.format.elf;
|
||||||
|
|
||||||
import static ghidra.app.util.bin.StructConverter.*;
|
import static ghidra.app.util.bin.StructConverter.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.mem.MemBuffer;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.mem.MemoryAccessException;
|
import ghidra.program.model.mem.*;
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,7 +30,63 @@ import ghidra.util.exception.DuplicateNameException;
|
||||||
* ELF .note.gnu.build-id section
|
* ELF .note.gnu.build-id section
|
||||||
*/
|
*/
|
||||||
public class GnuBuildIdSection extends FactoryStructureDataType {
|
public class GnuBuildIdSection extends FactoryStructureDataType {
|
||||||
|
public static final String BUILD_ID_SECTION_NAME = ".note.gnu.build-id";
|
||||||
private static final int MAX_SANE_STR_LENS = 1024;
|
private static final int MAX_SANE_STR_LENS = 1024;
|
||||||
|
|
||||||
|
public static GnuBuildIdValues fromProgram(Program program) {
|
||||||
|
MemoryBlock buildIdSection = program.getMemory().getBlock(BUILD_ID_SECTION_NAME);
|
||||||
|
if (buildIdSection == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try (ByteProvider bp = MemoryByteProvider.createMemoryBlockByteProvider(program.getMemory(),
|
||||||
|
buildIdSection)) {
|
||||||
|
BinaryReader br = new BinaryReader(bp, !program.getMemory().isBigEndian());
|
||||||
|
long nameLen = br.readNextUnsignedInt();
|
||||||
|
long descLen = br.readNextUnsignedInt();
|
||||||
|
int vendorType = br.readNextInt();
|
||||||
|
if (nameLen > MAX_SANE_STR_LENS || descLen > MAX_SANE_STR_LENS) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String name = br.readNextAsciiString((int) nameLen);
|
||||||
|
byte[] desc = br.readNextByteArray((int) descLen);
|
||||||
|
return new GnuBuildIdValues(name, desc, vendorType);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
// fall thru and return null
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GnuBuildIdValues {
|
||||||
|
private static final int SHA1_DESC_LEN = 20; // 160bit SHA1 == 20 bytes
|
||||||
|
|
||||||
|
private String name; // ie. "gnu"
|
||||||
|
private byte[] description; // the hash
|
||||||
|
private int vendorType;
|
||||||
|
|
||||||
|
private GnuBuildIdValues(String name, byte[] description, int vendorType) {
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
this.vendorType = vendorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVendorType() {
|
||||||
|
return vendorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValid() {
|
||||||
|
return "GNU".equals(name) && description.length == SHA1_DESC_LEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private long sectionSize;
|
private long sectionSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,9 +17,14 @@ package ghidra.app.util.bin.format.elf;
|
||||||
|
|
||||||
import static ghidra.app.util.bin.StructConverter.*;
|
import static ghidra.app.util.bin.StructConverter.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.*;
|
||||||
import ghidra.docking.settings.SettingsImpl;
|
import ghidra.docking.settings.SettingsImpl;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.mem.MemBuffer;
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
|
import ghidra.program.model.mem.MemoryBlock;
|
||||||
import ghidra.util.NumericUtilities;
|
import ghidra.util.NumericUtilities;
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
|
|
||||||
|
@ -27,6 +32,45 @@ import ghidra.util.exception.DuplicateNameException;
|
||||||
* Factory data type that marks up a ELF .gnu_debuglink section.
|
* Factory data type that marks up a ELF .gnu_debuglink section.
|
||||||
*/
|
*/
|
||||||
public class GnuDebugLinkSection extends FactoryStructureDataType {
|
public class GnuDebugLinkSection extends FactoryStructureDataType {
|
||||||
|
public static final String DEBUG_LINK_SECTION_NAME = ".gnu_debuglink";
|
||||||
|
|
||||||
|
public static GnuDebugLinkSectionValues fromProgram(Program program) {
|
||||||
|
MemoryBlock debugLinkSection = program.getMemory().getBlock(DEBUG_LINK_SECTION_NAME);
|
||||||
|
if (debugLinkSection == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try (ByteProvider bp = MemoryByteProvider.createMemoryBlockByteProvider(program.getMemory(),
|
||||||
|
debugLinkSection)) {
|
||||||
|
BinaryReader br = new BinaryReader(bp, !program.getMemory().isBigEndian());
|
||||||
|
String filename = br.readNextAsciiString();
|
||||||
|
br.setPointerIndex(NumericUtilities.getUnsignedAlignedValue(br.getPointerIndex(), 4));
|
||||||
|
int crc = br.readNextInt();
|
||||||
|
return new GnuDebugLinkSectionValues(filename, crc);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
// fall thru and return null
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GnuDebugLinkSectionValues {
|
||||||
|
private String filename;
|
||||||
|
private int crc;
|
||||||
|
|
||||||
|
public GnuDebugLinkSectionValues(String filename, int crc) {
|
||||||
|
this.filename = filename;
|
||||||
|
this.crc = crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCrc() {
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private long sectionSize;
|
private long sectionSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue