Merge remote-tracking branch 'origin/patch'

This commit is contained in:
Ryan Kurtz 2025-03-25 14:17:57 -04:00
commit 1bed582491
8 changed files with 51 additions and 14 deletions

View file

@ -109,8 +109,16 @@ public class ProgramEmulationUtils {
<interface name='Thread' /> <interface name='Thread' />
<interface name='Activatable' /> <interface name='Activatable' />
<interface name='Aggregate' /> <interface name='Aggregate' />
<attribute name='Stack' schema='Stack' />
<attribute name='Registers' schema='RegisterContainer' /> <attribute name='Registers' schema='RegisterContainer' />
</schema> </schema>
<schema name='Stack' canonical='yes'>
<interface name='Stack' />
<element schema='Frame' />
</schema>
<schema name='Frame'>
<interface name='StackFrame' />
</schema>
<schema name='RegisterContainer' canonical='yes' elementResync='NEVER' <schema name='RegisterContainer' canonical='yes' elementResync='NEVER'
attributeResync='NEVER'> attributeResync='NEVER'>
<interface name='RegisterContainer' /> <interface name='RegisterContainer' />

View file

@ -58,6 +58,11 @@ import ghidra.trace.model.breakpoint.TraceBreakpointKind;
import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemoryManager; import ghidra.trace.model.memory.TraceMemoryManager;
import ghidra.trace.model.memory.TraceMemorySpace; import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.stack.TraceObjectStack;
import ghidra.trace.model.stack.TraceObjectStackFrame;
import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.target.path.PathFilter;
import ghidra.trace.model.target.schema.TraceObjectSchema;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot; import ghidra.trace.model.time.TraceSnapshot;
import ghidra.trace.model.time.schedule.Scheduler; import ghidra.trace.model.time.schedule.Scheduler;
@ -976,4 +981,15 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
PcodeThread<byte[]> newEmuThread = emulator.newThread(newTraceThread.getPath()); PcodeThread<byte[]> newEmuThread = emulator.newThread(newTraceThread.getPath());
} }
} }
@Test
public void testEmuSchemaHasWorkingStackFrames() throws Exception {
TraceObjectSchema rootSchema = ProgramEmulationUtils.EMU_SESSION_SCHEMA;
TraceObjectSchema threadSchema = rootSchema.getSuccessorSchema(KeyPath.parse("Threads[1]"));
KeyPath found = threadSchema.searchForCanonicalContainer(TraceObjectStackFrame.class);
assertEquals(KeyPath.parse("Stack"), found);
PathFilter stackFilter = threadSchema.searchFor(TraceObjectStack.class, false);
assertNotNull("Non-unique Stack", stackFilter.getSingletonPath());
}
} }

View file

@ -721,7 +721,8 @@ public interface TraceObjectSchema {
for (Map.Entry<String, AttributeSchema> attrEnt : ent.schema.getAttributeSchemas() for (Map.Entry<String, AttributeSchema> attrEnt : ent.schema.getAttributeSchemas()
.entrySet()) { .entrySet()) {
TraceObjectSchema attrSchema = ctx.getSchema(attrEnt.getValue().getSchema()); TraceObjectSchema attrSchema = ctx.getSchema(attrEnt.getValue().getSchema());
if (TraceObjectInterface.class.isAssignableFrom(attrSchema.getType()) && if ((TraceObjectInterface.class.isAssignableFrom(attrSchema.getType()) ||
TraceObject.class.isAssignableFrom(attrSchema.getType())) &&
visited.add(attrSchema)) { visited.add(attrSchema)) {
nextLevel.add(new Private.CanonicalSearchEntry( nextLevel.add(new Private.CanonicalSearchEntry(
ent.path.key(attrEnt.getKey()), ent.path.key(attrEnt.getKey()),

View file

@ -748,7 +748,7 @@
</TABLE> </TABLE>
</DIV> </DIV>
<P>If the default Ghidra Server port (1111) is in use it need not be specified with URL. <P>If the default Ghidra Server base port (13100) is in use it need not be specified with URL.
The <EM>hostname</EM> may specify either a Fully Qualified Domain Name (FQDN, e.g., The <EM>hostname</EM> may specify either a Fully Qualified Domain Name (FQDN, e.g.,
<EM>host.abc.com</EM>) or IP v4 Address (e.g., <EM>1.2.3.4</EM>).</P> <EM>host.abc.com</EM>) or IP v4 Address (e.g., <EM>1.2.3.4</EM>).</P>
<STRONG>Local Ghidra Project</STRONG><BR> <STRONG>Local Ghidra Project</STRONG><BR>

View file

@ -105,7 +105,8 @@ def open_program(
analyze=True, analyze=True,
language: str = None, language: str = None,
compiler: str = None, compiler: str = None,
loader: Union[str, JClass] = None loader: Union[str, JClass] = None,
program_name: str = None
) -> ContextManager["FlatProgramAPI"]: # type: ignore ) -> ContextManager["FlatProgramAPI"]: # type: ignore
""" """
Opens given binary path in Ghidra and returns FlatProgramAPI object. Opens given binary path in Ghidra and returns FlatProgramAPI object.
@ -122,6 +123,8 @@ def open_program(
(Defaults to the Language's default compiler) (Defaults to the Language's default compiler)
:param loader: The `ghidra.app.util.opinion.Loader` class to use when importing the program. :param loader: The `ghidra.app.util.opinion.Loader` class to use when importing the program.
This may be either a Java class or its path. (Defaults to None) This may be either a Java class or its path. (Defaults to None)
:param program_name: The name to of the program to open in Ghidra.
(Defaults to None, which results in the name being derived from "binary_path")
:return: A Ghidra FlatProgramAPI object. :return: A Ghidra FlatProgramAPI object.
:raises ValueError: If the provided language, compiler or loader is invalid. :raises ValueError: If the provided language, compiler or loader is invalid.
:raises TypeError: If the provided loader does not implement `ghidra.app.util.opinion.Loader`. :raises TypeError: If the provided loader does not implement `ghidra.app.util.opinion.Loader`.
@ -306,7 +309,9 @@ import pdb # imports Python's pdb
import pdb_ # imports Ghidra's pdb import pdb_ # imports Ghidra's pdb
``` ```
## Change History ## Change History
__2.0.2:__ __2.1.0:__
* [`pyghidra.open_program()`](#pyghidraopen_program) now accepts a `program_name` parameter, which
can be used to override the program name derived from the `binary_path` parameter.
* [`pyghidra.open_program()`](#pyghidraopen_program) now properly throws an exception if the project * [`pyghidra.open_program()`](#pyghidraopen_program) now properly throws an exception if the project
exists and is locked. exists and is locked.

View file

@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
## ##
__version__ = "2.0.2" __version__ = "2.1.0"
# stub for documentation and typing # stub for documentation and typing
# this is mostly to hide the function parameter # this is mostly to hide the function parameter

View file

@ -81,13 +81,16 @@ def _setup_project(
project_name: str = None, project_name: str = None,
language: str = None, language: str = None,
compiler: str = None, compiler: str = None,
loader: Union[str, JClass] = None loader: Union[str, JClass] = None,
program_name: str = None
) -> Tuple["GhidraProject", "Program"]: ) -> Tuple["GhidraProject", "Program"]:
from ghidra.base.project import GhidraProject from ghidra.base.project import GhidraProject
from java.lang import ClassLoader # type:ignore @UnresolvedImport from java.lang import ClassLoader # type:ignore @UnresolvedImport
from ghidra.framework.model import ProjectLocator # type:ignore @UnresolvedImport from ghidra.framework.model import ProjectLocator # type:ignore @UnresolvedImport
if binary_path is not None: if binary_path is not None:
binary_path = Path(binary_path) binary_path = Path(binary_path)
if program_name is None and binary_path is not None:
program_name = binary_path.name
if project_location: if project_location:
project_location = Path(project_location) project_location = Path(project_location)
else: else:
@ -117,9 +120,9 @@ def _setup_project(
else: else:
project_location.mkdir(exist_ok=True, parents=True) project_location.mkdir(exist_ok=True, parents=True)
project = GhidraProject.createProject(project_location, project_name, False) project = GhidraProject.createProject(project_location, project_name, False)
if binary_path is not None: if program_name is not None:
if project.getRootFolder().getFile(binary_path.name): if project.getRootFolder().getFile(program_name):
program = project.openProgram("/", binary_path.name, False) program = project.openProgram("/", program_name, False)
# NOTE: GhidraProject.importProgram behaves differently when a loader is provided # NOTE: GhidraProject.importProgram behaves differently when a loader is provided
# loaderClass may not be null so we must use the correct method override # loaderClass may not be null so we must use the correct method override
@ -146,7 +149,7 @@ def _setup_project(
else: else:
message += f"The provided language ({language}) may be invalid." message += f"The provided language ({language}) may be invalid."
raise ValueError(message) raise ValueError(message)
project.saveAs(program, "/", program.getName(), True) project.saveAs(program, "/", program_name, True)
return project, program return project, program
@ -198,10 +201,11 @@ def open_program(
analyze=True, analyze=True,
language: str = None, language: str = None,
compiler: str = None, compiler: str = None,
loader: Union[str, JClass] = None loader: Union[str, JClass] = None,
program_name: str = None
) -> ContextManager["FlatProgramAPI"]: # type: ignore ) -> ContextManager["FlatProgramAPI"]: # type: ignore
""" """
Opens given binary path in Ghidra and returns FlatProgramAPI object. Opens given binary path (or optional program name) in Ghidra and returns FlatProgramAPI object.
:param binary_path: Path to binary file, may be None. :param binary_path: Path to binary file, may be None.
:param project_location: Location of Ghidra project to open/create. :param project_location: Location of Ghidra project to open/create.
@ -215,6 +219,8 @@ def open_program(
(Defaults to the Language's default compiler) (Defaults to the Language's default compiler)
:param loader: The `ghidra.app.util.opinion.Loader` class to use when importing the program. :param loader: The `ghidra.app.util.opinion.Loader` class to use when importing the program.
This may be either a Java class or its path. (Defaults to None) This may be either a Java class or its path. (Defaults to None)
:param program_name: The name of the program to open in Ghidra.
(Defaults to None, which results in the name being derived from "binary_path")
:return: A Ghidra FlatProgramAPI object. :return: A Ghidra FlatProgramAPI object.
:raises ValueError: If the provided language, compiler or loader is invalid. :raises ValueError: If the provided language, compiler or loader is invalid.
:raises TypeError: If the provided loader does not implement `ghidra.app.util.opinion.Loader`. :raises TypeError: If the provided loader does not implement `ghidra.app.util.opinion.Loader`.
@ -234,7 +240,8 @@ def open_program(
project_name, project_name,
language, language,
compiler, compiler,
loader loader,
program_name
) )
GhidraScriptUtil.acquireBundleHostReference() GhidraScriptUtil.acquireBundleHostReference()

View file

@ -1087,7 +1087,7 @@ public class FrontEndPlugin extends Plugin
Msg.showInfo(this, tool.getToolFrame(), "Cannot Find Tool", Msg.showInfo(this, tool.getToolFrame(), "Cannot Find Tool",
"<html>File type is unrecognized: <b>" + "<html>File type is unrecognized: <b>" +
HTMLUtilities.escapeHTML(domainFile.getName()) + HTMLUtilities.escapeHTML(domainFile.getName()) +
"</b>.<br><br>File may have been created with a neer version of Ghidra."); "</b>.<br><br>File may have been created with a newer version of Ghidra.");
return; return;
} }