feat(ghidra): admit set_address_name and set_addresses_names (#1635)

* feat(ghidra): admit set_address_name and set_addresses_names

Ghidra sessions rejected the existing naming tools with "operation is not declared by this provider", so agents had to use annotate_native_function, which returns a complete function dossier per rename and cannot name data addresses.

The Java bridge now names a local function entry (namespace-aware, like annotate_native_function) or creates/renames the primary label at any other mapped address, verifying readback. set_addresses_names applies each address in its own transaction and reports per-address booleans. Both are mutating capabilities: excluded from read-only (Windows P0) handshakes and from snapshot replay.

* fix(ghidra): normalize qualified symbol names and reject empty leaves

Share namespace-aware leaf normalization across functions and primary labels. Invalid qualified leaves now return false for naming operations without marking the provider unavailable; function annotations retain typed invalid-input errors.

Extend real CLI/MCP namespace verification with namespaced globals, readback replay, failed-name rollback, mixed batches, and capability checks.

Validation: 75 focused tests and npm run check pass. Ghidra 12.1.4/JDK 21 namespace verification passes on macOS arm64 with unchanged source bytes and complete cleanup. The full host lane passes this workflow before the existing entry-alias fixture fails because the compiler places its interior label at the function entry.

---------

Co-authored-by: morluto <[email protected]>
This commit is contained in:
Giridhar
2026-10-11 04:35:38 +08:00
committed by GitHub
co-authored by morluto
parent d6f698d8af
commit 6e0d5d64d3
13 changed files with 351 additions and 24 deletions
@@ -152,7 +152,7 @@ context. PSP/stack/device state remains unmodeled. See [DOS guide](https://githu
### Function annotations in Ghidra
Use `annotate_native_function` for one function name and/or entry comments, with at least one explicit change. Review its annotation readback and refreshed dossier inline. Changes are atomic and session-scoped; empty comments clear them, omitted fields preserve them. Later MCP calls observe edits until close. CLI `annotate-native-function` returns the updated analysis before discarding the session. Original executable bytes are unchanged; edits invalidate immutable snapshots. Windows P0 does not admit database mutations.
Use `annotate_native_function` for one function name and/or entry comments, with at least one explicit change. Review its annotation readback and refreshed dossier inline. Changes are atomic and session-scoped; empty comments clear them, omitted fields preserve them. Later MCP calls observe edits until close. CLI `annotate-native-function` returns the updated analysis before discarding the session. Original executable bytes are unchanged; edits invalidate immutable snapshots. Windows P0 does not admit database mutations. For naming only, `set_address_name` / `set_addresses_names` also work with Ghidra: they name function entries or data addresses (primary labels) and return compact per-address success booleans instead of a dossier.
## Offline PE resource inventory
+81 -10
View File
@@ -120,6 +120,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
);
private static final String[] CAPABILITIES = {
"annotate_native_function",
"set_address_name",
"set_addresses_names",
"inspect_native_load_image",
"read_bytes",
"address_to_file_offset",
@@ -374,6 +376,12 @@ public final class ReaGhidraBridge extends HeadlessScript {
case "resolve_native_call_targets" -> resolveNativeCallTargets(request.params);
case "xrefs" -> xrefs(request.params);
case "analyze_function" -> analyzeFunction(request.params);
case "set_address_name", "set_addresses_names" -> {
if (!descriptor.transport.equals("unix-socket"))
throw new RequestFailure("method_unavailable", "Windows P0 does not admit database mutation");
yield request.method.equals("set_address_name")
? setAddressName(request.params) : setAddressesNames(request.params);
}
case "annotate_native_function" -> {
if (!descriptor.transport.equals("unix-socket"))
throw new RequestFailure("method_unavailable", "Windows P0 does not admit database mutation");
@@ -417,7 +425,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
result.addProperty("analysis_timed_out", timedOut);
result.add("capabilities", GSON.toJsonTree(
java.util.Arrays.stream(CAPABILITIES)
.filter(value -> !readOnly || !value.equals("annotate_native_function"))
.filter(value -> !readOnly || !(value.equals("annotate_native_function")
|| value.equals("set_address_name") || value.equals("set_addresses_names")))
.toArray(String[]::new)
));
result.add("target", target);
@@ -1027,11 +1036,11 @@ public final class ReaGhidraBridge extends HeadlessScript {
for (String field : List.of("name", "comment", "inline_comment")) {
if (params.has(field)) validateAnnotationText(requireText(params, field), field);
}
String leafName = params.has("name")
? annotationLeafName(function, requireString(params, "name")) : null;
int transaction = currentProgram.startTransaction("REA function annotations");
boolean commit = false;
try {
String leafName = params.has("name")
? symbolLeafName(function.getSymbol(), requireString(params, "name")) : null;
// Validate edits through Ghidra's own setters, including name rules.
// A later invalid name must roll back earlier comment writes.
for (String field : List.of("comment", "inline_comment")) {
@@ -1080,18 +1089,18 @@ public final class ReaGhidraBridge extends HeadlessScript {
}
}
private String annotationLeafName(Function function, String requested) {
private String symbolLeafName(Symbol symbol, String requested) throws ghidra.util.exception.InvalidInputException {
// Fully qualified readback must be usable as an idempotent rename input.
if (requested.equals(procedureName(function))) return function.getName();
String namespace = function.getParentNamespace().isGlobal()
? "" : function.getParentNamespace().getName(true);
if (requested.equals(symbol.getName(true))) return symbol.getName();
String namespace = symbol.getParentNamespace().isGlobal()
? "" : symbol.getParentNamespace().getName(true);
String prefix = namespace + "::";
if (!namespace.isEmpty() && requested.startsWith(prefix)) {
String leaf = requested.substring(prefix.length());
if (!leaf.isEmpty()) return leaf;
throw new RequestFailure("invalid_function_name",
"Function name has an empty leaf within namespace " + namespace + " at " +
canonicalAddress(function.getEntryPoint()) + ": " + requested);
throw new ghidra.util.exception.InvalidInputException(
"Symbol name has an empty leaf within namespace " + namespace + " at " +
canonicalAddress(symbol.getAddress()) + ": " + requested);
}
return requested;
}
@@ -1416,6 +1425,68 @@ public final class ReaGhidraBridge extends HeadlessScript {
return symbol == null ? JsonNull.INSTANCE : GSON.toJsonTree(symbol.getName(true));
}
// Analyst naming: a function entry renames the function, other addresses get a primary label.
private boolean applyAddressName(Address address, String name) throws Exception {
if (!currentProgram.getMemory().contains(address))
throw new RequestFailure("invalid_request", "Address is not mapped: " + canonicalAddress(address));
validateAnnotationText(name, "name");
int transaction = currentProgram.startTransaction("REA address name");
boolean commit = false;
try {
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
String expected = name;
if (function != null) {
expected = symbolLeafName(function.getSymbol(), name);
function.setName(expected, SourceType.USER_DEFINED);
} else {
var table = currentProgram.getSymbolTable();
Symbol primary = table.getPrimarySymbol(address);
if (primary != null && !primary.isDynamic()) {
expected = symbolLeafName(primary, name);
primary.setName(expected, SourceType.USER_DEFINED);
} else {
Symbol created = table.createLabel(address, name, SourceType.USER_DEFINED);
created.setPrimary();
}
}
invalidateAnalysisCaches();
Symbol readback = currentProgram.getSymbolTable().getPrimarySymbol(address);
commit = readback != null && expected.equals(readback.getName());
return commit;
}
catch (ghidra.util.exception.InvalidInputException | ghidra.util.exception.DuplicateNameException exception) {
return false;
}
finally {
currentProgram.endTransaction(transaction, commit);
}
}
private JsonElement setAddressName(JsonObject params) throws Exception {
requireKeys(params, Set.of("document", "address", "name"));
requireDocument(params);
return GSON.toJsonTree(applyAddressName(requireAddress(params, "address"), requireString(params, "name")));
}
private JsonElement setAddressesNames(JsonObject params) throws Exception {
requireKeys(params, Set.of("document", "names"));
requireDocument(params);
if (!params.has("names") || !params.get("names").isJsonObject())
throw new RequestFailure("invalid_request", "names must be an object of address -> name");
JsonObject result = new JsonObject();
for (Map.Entry<String, JsonElement> entry : params.getAsJsonObject("names").entrySet()) {
boolean ok;
try {
ok = applyAddressName(parseReaAddress(entry.getKey()), entry.getValue().getAsString());
}
catch (RequestFailure failure) {
ok = false;
}
result.addProperty(entry.getKey(), ok);
}
return result;
}
private JsonArray listDocuments(JsonObject params) {
requireKeys(params, Set.of());
JsonArray result = new JsonArray();
+4 -2
View File
@@ -610,8 +610,10 @@ dossiers, instructions, recovered data types, measured load mappings, loaded
memory bytes, and observed file offsets. Independent load-image attestation
supports DOS MZ and explicitly selected COM; PE returns its measurements with that limitation.
On Linux and macOS, `annotate_native_function` also edits a function name and/or
entry comments atomically and returns refreshed analysis. These session metadata
edits leave executable bytes unchanged and are discarded on close. GUI controls
entry comments atomically and returns refreshed analysis, and `set_address_name`
and `set_addresses_names` name a function entry or any other mapped address
(creating or renaming its primary label) with per-address success results. These
session metadata edits leave executable bytes unchanged and are discarded on close. GUI controls
require Hopper; Windows P0 remains read-only.
Windows P0 admits native x86 and x86-64 PE applications and DLLs on fixed local NTFS volumes.
+6
View File
@@ -397,6 +397,12 @@ unpaired Unicode surrogate; a rejection identifies the field and UTF-16 index
and leaves every annotation unchanged. CRLF, supplementary Unicode characters,
and combining characters are preserved. Windows P0 remains read-only. Ghidra has no
GUI authority, and REA never falls back automatically to Hopper.
`set_address_name` and `set_addresses_names` give Ghidra sessions the same
compact naming results as Hopper: a local function entry renames that function,
and any other mapped address, such as a global, receives a new or renamed primary
label. A batch applies each address separately and reports one success boolean
per address. Use them to name many globals and helpers cheaply; use
`annotate_native_function` when you also want comments and a refreshed dossier.
Ghidra name edits preserve the existing namespace. Supply either a leaf name
such as `renamed` or a fully qualified name in that namespace, such as
`alpha::renamed`. The returned qualified name can be reused as an idempotent
@@ -54,6 +54,14 @@ export async function verifyGhidraNamespaceAnnotations({
);
return { namespace, address: procedure.address };
});
const names = await call("list_names");
const globals = ["alpha", "outer::inner"].map((namespace) => {
const symbol = names.find(
(item) => item.value === `${namespace}::value` && item.symbol.primary,
);
assert.ok(symbol, `Missing demangled fixture global ${namespace}::value`);
return { namespace, address: symbol.address };
});
const ambiguous = await reject("procedure_address", { procedure: "same" });
assert.equal(ambiguous.code, "invalid_request");
for (const { address } of entries)
@@ -125,6 +133,16 @@ export async function verifyGhidraNamespaceAnnotations({
});
assert.deepEqual(unchanged.annotations, qualified.annotations);
}
await verifyAddressNames(call, [entries[0], entries[2], ...globals]);
for (const { namespace, address } of entries) {
assert.equal(
await call("set_address_name", {
address,
name: `${namespace}::qualified`,
}),
true,
);
}
const { stdout } = await exec(
process.execPath,
[
@@ -159,6 +177,96 @@ export async function verifyGhidraNamespaceAnnotations({
}
}
async function verifyAddressNames(call, symbols) {
for (const { namespace, address } of symbols) {
assert.equal(
await call("set_address_name", { address, name: "address_renamed" }),
true,
);
const qualified = `${namespace}::address_qualified`;
assert.equal(
await call("set_address_name", { address, name: qualified }),
true,
);
assert.equal(await call("address_name", { address }), qualified);
assert.equal(
await call("set_address_name", { address, name: "other::literal" }),
true,
);
const literal = await call("address_name", { address });
assert.equal(literal, `${namespace}::other::literal`);
assert.equal(
await call("set_address_name", { address, name: literal }),
true,
);
assert.equal(await call("address_name", { address }), literal);
assert.equal(
await call("set_address_name", { address, name: qualified }),
true,
);
for (let repeat = 0; repeat < 2; repeat++) {
const readback = await call("address_name", { address });
assert.deepEqual(
await call("set_addresses_names", { names: { [address]: readback } }),
{ [address]: true },
);
assert.equal(await call("address_name", { address }), qualified);
}
assert.equal(
await call("set_address_name", { address, name: `${namespace}::` }),
false,
);
const session = await call("binary_session");
for (const operation of [
"set_address_name",
"set_addresses_names",
"address_name",
"list_names",
])
assert.equal(
session.capabilities.find((item) => item.operation === operation)
?.available,
true,
`An invalid name must not mark ${operation} unavailable`,
);
assert.equal(await call("address_name", { address }), qualified);
}
const [valid, invalid] = symbols;
assert.deepEqual(
await call("set_addresses_names", {
names: {
[valid.address]: `${valid.namespace}::batch_renamed`,
[invalid.address]: `${invalid.namespace}::`,
},
}),
{ [valid.address]: true, [invalid.address]: false },
);
assert.equal(
await call("address_name", { address: valid.address }),
`${valid.namespace}::batch_renamed`,
);
assert.equal(
await call("address_name", { address: invalid.address }),
`${invalid.namespace}::address_qualified`,
);
const names = await call("list_names");
for (const { namespace, address } of symbols) {
const expected =
address === valid.address
? `${namespace}::batch_renamed`
: `${namespace}::address_qualified`;
assert.ok(
names.some(
(item) =>
item.address === address &&
item.value === expected &&
item.symbol.primary,
),
`Missing refreshed primary symbol ${expected}`,
);
}
}
async function requireNamespaceDemangler(env) {
const inspection = inspectGhidraInstallation({
environment: process.env,
+27 -1
View File
@@ -13,7 +13,10 @@ import { jsonValueSchema, type JsonValue } from "../domain/jsonValue.js";
import { err, ok, type Result } from "../domain/result.js";
import { analysisInputErrorFromIssues } from "../domain/inputIssueProjection.js";
/** Read-only direct inventory operations admitted by the Ghidra adapter. */
/**
* Direct inventory operations admitted by the Ghidra adapter. All are reads
* except the naming operations, which edit the ephemeral session database.
*/
export const GHIDRA_INVENTORY_OPERATIONS = [
"inspect_native_load_image",
"read_bytes",
@@ -28,8 +31,16 @@ export const GHIDRA_INVENTORY_OPERATIONS = [
"resolve_containing_procedure",
"search_procedures",
"search_strings",
"set_address_name",
"set_addresses_names",
] as const;
/** Inventory operations that write analyst names into the session database. */
export const GHIDRA_NAMING_OPERATIONS = [
"set_address_name",
"set_addresses_names",
] as const satisfies readonly GhidraInventoryOperation[];
/** One direct inventory operation implemented by the packaged Java bridge. */
export type GhidraInventoryOperation =
(typeof GHIDRA_INVENTORY_OPERATIONS)[number];
@@ -83,6 +94,19 @@ const inputSchemas = {
.strict(),
search_procedures: z.object(searchInput).strict(),
search_strings: z.object(searchInput).strict(),
set_address_name: z
.object({ document, address: explicitAddress, name: z.string().min(1) })
.strict(),
set_addresses_names: z
.object({
document,
names: z.record(explicitAddress, z.string().min(1)),
})
.strict()
.refine(
(value) => Object.keys(value.names).length > 0,
"Supply at least one address name",
),
} satisfies Readonly<Record<GhidraInventoryOperation, z.ZodType>>;
/** Validate and default one provider request before it crosses the socket. */
@@ -281,6 +305,8 @@ const resultSchemas = {
resolve_containing_procedure: containingProcedure,
search_procedures: z.array(searchItem),
search_strings: z.array(searchItem),
set_address_name: z.boolean(),
set_addresses_names: z.record(z.string(), z.boolean()),
} satisfies Readonly<Record<GhidraInventoryOperation, z.ZodType>>;
/** Validate exact Java-bridge output before creating Evidence. */
+11 -2
View File
@@ -8,6 +8,7 @@ import {
} from "../process/WindowsAuthority.js";
import { GHIDRA_FUNCTION_OPERATIONS } from "./GhidraFunctionValues.js";
import { GHIDRA_INVENTORY_OPERATIONS } from "./GhidraInventoryValues.js";
import { GHIDRA_MUTATING_OPERATIONS } from "./GhidraSessionValues.js";
/** Public identity committed by every Ghidra-backed observation. */
export const GHIDRA_PROVIDER_IDENTITY: ProviderIdentity = Object.freeze({
@@ -50,6 +51,14 @@ export const limitationsFor = (operation: string): readonly string[] => {
"Annotation text rejects NUL and unpaired Unicode surrogates before mutation, with the field and UTF-16 index in the error. Supported Unicode and line endings are preserved.",
"Metadata edits invalidate immutable analysis snapshots and are discarded on close. CLI returns the updated dossier before session cleanup; this is not a saved Ghidra project.",
];
case "set_address_name":
case "set_addresses_names":
return [
...common,
"Names use Ghidra USER_DEFINED source. A local function entry renames that function; any other mapped address receives a new or renamed primary label. Existing symbols retain their namespace and accept leaf names or qualified names within that namespace; qualified readback can be reused without adding namespace prefixes. Readback compares the primary symbol name.",
"set_addresses_names applies each address in its own transaction and reports per-address success; invalid names, unmapped addresses, and readback mismatches roll back only that address.",
"Metadata edits invalidate immutable analysis snapshots and are discarded on close; this is not a saved Ghidra project.",
];
case "inspect_native_load_image":
return [
...common,
@@ -162,12 +171,12 @@ export const CAPABILITIES: readonly CapabilityDescriptor[] = Object.freeze(
reason: null,
// Project writes are private import machinery. Analysis reads are bound
// to the artifact/profile; session annotations disable snapshot replay.
...(operation === "annotate_native_function" ||
...(GHIDRA_MUTATING_OPERATIONS.has(operation) ||
operation === "list_documents"
? {}
: { cachePolicy: "snapshot" as const }),
effects: Object.freeze({
mutatesArtifact: operation === "annotate_native_function",
mutatesArtifact: GHIDRA_MUTATING_OPERATIONS.has(operation),
launchesProcess: true,
mayShowUi: false,
mayAccessNetwork: false,
+22 -1
View File
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
import {
GHIDRA_SESSION_CAPABILITIES,
GHIDRA_MUTATING_OPERATIONS,
parseGhidraSessionInfo,
} from "./GhidraSessionValues.js";
@@ -81,7 +82,7 @@ describe("Ghidra mutation handshake", () => {
...value,
read_only: true,
capabilities: value.capabilities.filter(
(c) => c !== "annotate_native_function",
(c) => !GHIDRA_MUTATING_OPERATIONS.has(c),
),
};
expect(
@@ -92,4 +93,24 @@ describe("Ghidra mutation handshake", () => {
parseGhidraSessionInfo(value, { ...expected, expectedReadOnly: true }).ok,
).toBe(false);
});
it("treats address naming as database mutation", () => {
expect([...GHIDRA_MUTATING_OPERATIONS].sort()).toEqual([
"annotate_native_function",
"set_address_name",
"set_addresses_names",
]);
const value = session();
for (const naming of ["set_address_name", "set_addresses_names"]) {
expect(value.capabilities).toContain(naming);
expect(
parseGhidraSessionInfo(
{
...value,
capabilities: value.capabilities.filter((c) => c !== naming),
},
expected,
).ok,
).toBe(false);
}
});
});
+11 -2
View File
@@ -2,7 +2,10 @@ import { z } from "zod";
import type { JsonValue } from "../domain/jsonValue.js";
import { err, ok, type Result } from "../domain/result.js";
import { GHIDRA_INVENTORY_OPERATIONS } from "./GhidraInventoryValues.js";
import {
GHIDRA_INVENTORY_OPERATIONS,
GHIDRA_NAMING_OPERATIONS,
} from "./GhidraInventoryValues.js";
import { GHIDRA_FUNCTION_OPERATIONS } from "./GhidraFunctionValues.js";
import { ghidraExtensionResultSchema } from "./extensions/GhidraExtensions.js";
@@ -14,6 +17,12 @@ export const GHIDRA_SESSION_CAPABILITIES = [
...GHIDRA_FUNCTION_OPERATIONS,
] as const;
/** Methods that edit the session database; read-only bridges omit them. */
export const GHIDRA_MUTATING_OPERATIONS: ReadonlySet<string> = new Set([
"annotate_native_function",
...GHIDRA_NAMING_OPERATIONS,
]);
const capabilitySchema = z.enum(GHIDRA_SESSION_CAPABILITIES);
const sessionInfoSchema = z
@@ -64,7 +73,7 @@ export const parseGhidraSessionInfo = (
const parsed = sessionInfoSchema.safeParse(value);
const readOnly = expected.expectedReadOnly ?? false;
const expectedCapabilities = GHIDRA_SESSION_CAPABILITIES.filter(
(capability) => !readOnly || capability !== "annotate_native_function",
(capability) => !readOnly || !GHIDRA_MUTATING_OPERATIONS.has(capability),
);
if (
!parsed.success ||
@@ -62,6 +62,66 @@ it("rejects contradictory annotation readback across the provider and MCP bounda
}
});
it("routes address naming to Ghidra with compact per-address results", async () => {
const requests: { operation: string; input: unknown }[] = [];
let output: unknown = null;
const harness = await connectGhidraMcp(
"ghidra-address-naming",
(operation, input) => {
requests.push({ operation, input });
return Promise.resolve(ok(jsonValueSchema.parse(output)));
},
);
try {
output = true;
const single = await harness.mcp.callTool({
name: "set_address_name",
arguments: { address: "0x1000", name: "fixture_entry" },
});
expect(single.isError).not.toBe(true);
expect(single.structuredContent).toMatchObject({ normalized_result: true });
output = { "0x1000": true, "0x2000": false };
const batch = await harness.mcp.callTool({
name: "set_addresses_names",
arguments: { names: { "0x1000": "fixture_entry", "0x2000": "g_value" } },
});
expect(batch.isError).not.toBe(true);
expect(batch.structuredContent).toMatchObject({
normalized_result: { "0x1000": true, "0x2000": false },
});
expect(requests).toEqual([
{
operation: "set_address_name",
input: { document: null, address: "0x1000", name: "fixture_entry" },
},
{
operation: "set_addresses_names",
input: {
document: null,
names: { "0x1000": "fixture_entry", "0x2000": "g_value" },
},
},
]);
const empty = await harness.mcp.callTool({
name: "set_addresses_names",
arguments: { names: {} },
});
expect(empty.isError).toBe(true);
expect(requests).toHaveLength(2);
output = "fixture_entry";
const malformed = await harness.mcp.callTool({
name: "set_address_name",
arguments: { address: "0x1000", name: "fixture_entry" },
});
expect(malformed.isError).toBe(true);
expect(parseMcpToolError(malformed)).toMatchObject({
error: { code: "unreadable_output" },
});
} finally {
await harness.close();
}
});
describe("Ghidra MCP evidence parity", () => {
it("preserves provider evidence, composed parity, and capability routing", async () => {
const harness = await connectGhidraMcp("ghidra-parity");
@@ -19,7 +19,10 @@ import type {
GhidraLauncher,
} from "../../../../src/ghidra/GhidraLauncher.js";
import type { GhidraTransportKind } from "../../../../src/ghidra/GhidraTransport.js";
import { GHIDRA_SESSION_CAPABILITIES } from "../../../../src/ghidra/GhidraSessionValues.js";
import {
GHIDRA_MUTATING_OPERATIONS,
GHIDRA_SESSION_CAPABILITIES,
} from "../../../../src/ghidra/GhidraSessionValues.js";
import { waitForExit as waitForChildExit } from "../../../support/process/processFixture.js";
import { createTestTempDirectory } from "../../../fixtures/temporaryDirectory.js";
@@ -177,7 +180,7 @@ describe("GhidraClient", () => {
capabilities: GHIDRA_SESSION_CAPABILITIES.filter(
(value) =>
HOST_TRANSPORT === "unix-socket" ||
value !== "annotate_native_function",
!GHIDRA_MUTATING_OPERATIONS.has(value),
),
target: {
image_base: "0x1000",
+7 -2
View File
@@ -5,6 +5,11 @@
#define REA_CPP_SYMBOL(name) name
#endif
volatile int rea_namespace_alpha_value
__asm__(REA_CPP_SYMBOL("_ZN5alpha5valueE")) = 3;
volatile int rea_namespace_nested_value
__asm__(REA_CPP_SYMBOL("_ZN5outer5inner5valueE")) = 1;
__attribute__((noinline, used)) int rea_namespace_alpha(int value)
__asm__(REA_CPP_SYMBOL("_ZN5alpha4sameEi"));
__attribute__((noinline, used)) int rea_namespace_beta(int value)
@@ -12,9 +17,9 @@ __attribute__((noinline, used)) int rea_namespace_beta(int value)
__attribute__((noinline, used)) int rea_namespace_nested(int value)
__asm__(REA_CPP_SYMBOL("_ZN5outer5inner4sameEi"));
int rea_namespace_alpha(int value) { return value + 3; }
int rea_namespace_alpha(int value) { return value + rea_namespace_alpha_value; }
int rea_namespace_beta(int value) { return value * 2; }
int rea_namespace_nested(int value) { return value - 1; }
int rea_namespace_nested(int value) { return value - rea_namespace_nested_value; }
int main(void) {
return rea_namespace_alpha(1) + rea_namespace_beta(2) +
+8 -1
View File
@@ -231,6 +231,8 @@ const sessionInfo = ({
"resolve_containing_procedure",
"search_procedures",
"search_strings",
"set_address_name",
"set_addresses_names",
"annotate_native_function",
"inspect_native_data_type",
"inspect_native_instruction",
@@ -246,7 +248,12 @@ const sessionInfo = ({
"xrefs",
].filter(
(value) =>
transport === "unix-socket" || value !== "annotate_native_function",
transport === "unix-socket" ||
![
"annotate_native_function",
"set_address_name",
"set_addresses_names",
].includes(value),
),
target: {
name: "fixture",