Skip to content

Commit 4a4cb2a

Browse files
committed
GT-3260_emteere cache of address set of memory blocks that are marked
execute
1 parent 9b3de9f commit 4a4cb2a

File tree

4 files changed

+123
-34
lines changed

4 files changed

+123
-34
lines changed

Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ public void testBlockChanges() throws Exception {
296296

297297
block2.setSourceName("Test");
298298
assertEquals("Test", block2.getSourceName());
299-
300299
}
301300

302301
@Test
@@ -398,6 +397,90 @@ public void testGetBlockByName() throws Exception {
398397
transactionID = program.startTransaction("Test");
399398
}
400399

400+
@Test
401+
public void testMemoryMapExecuteSet() throws Exception {
402+
403+
AddressSetView executeSet = mem.getExecuteSet();
404+
assertTrue(executeSet.isEmpty());
405+
MemoryBlock block1 = createBlock("Test1", addr(100), 100);
406+
executeSet = mem.getExecuteSet();
407+
assertTrue(executeSet.isEmpty());
408+
MemoryBlock block2 = createBlock("Test2", addr(300), 100);
409+
executeSet = mem.getExecuteSet();
410+
assertTrue(executeSet.isEmpty());
411+
412+
MemoryBlock block = mem.getBlock("Test1");
413+
executeSet = mem.getExecuteSet();
414+
assertTrue(executeSet.isEmpty());
415+
416+
block.setExecute(false);
417+
executeSet = mem.getExecuteSet();
418+
assertTrue(executeSet.isEmpty());
419+
420+
block.setExecute(true);
421+
executeSet = mem.getExecuteSet();
422+
assertTrue(executeSet.isEmpty() != true);
423+
Address start = block.getStart();
424+
Address end = block.getEnd();
425+
assertTrue(executeSet.contains(start,end));
426+
427+
// non-existent block
428+
block = mem.getBlock("NoExist");
429+
assertNull(block);
430+
431+
program.endTransaction(transactionID, true);
432+
transactionID = program.startTransaction("Test");
433+
434+
// now exists
435+
mem.getBlock("Test1").setName("NoExist");
436+
// Test1 no longer exists
437+
block = mem.getBlock("NoExist");
438+
executeSet = mem.getExecuteSet();
439+
start = block.getStart();
440+
end = block.getEnd();
441+
// should be same block
442+
assertTrue(executeSet.contains(start,end));
443+
block.setExecute(false);
444+
executeSet = mem.getExecuteSet();
445+
assertTrue(executeSet.contains(start,end) == false);
446+
447+
block2.setExecute(true);
448+
Address start2 = block2.getStart();
449+
Address end2 = block2.getEnd();
450+
mem.removeBlock(block2, new TaskMonitorAdapter());
451+
452+
program.endTransaction(transactionID, true);
453+
454+
program.undo();
455+
456+
transactionID = program.startTransaction("Test");
457+
458+
// should be execute set on block2, deleted, then undone
459+
executeSet = mem.getExecuteSet();
460+
assertTrue(executeSet.contains(start2,end2) == false);
461+
462+
// undid set execute block should now be contained
463+
block = mem.getBlock("Test1");
464+
start = block.getStart();
465+
end = block.getEnd();
466+
executeSet = mem.getExecuteSet();
467+
assertTrue(executeSet.contains(start,end));
468+
469+
mem.split(block, addr(150));
470+
block = mem.getBlock("Test1");
471+
executeSet = mem.getExecuteSet();
472+
assertTrue(executeSet.isEmpty() != true);
473+
assertTrue(executeSet.contains(block.getStart(), block.getEnd()));
474+
475+
// remove block that was split, should still be executable memory
476+
start = block.getStart();
477+
end = block.getEnd();
478+
mem.removeBlock(block, new TaskMonitorAdapter());
479+
executeSet = mem.getExecuteSet();
480+
assertTrue(executeSet.isEmpty() != true);
481+
assertTrue(executeSet.contains(start, end) == false);
482+
}
483+
401484
@Test
402485
public void testSave() throws Exception {
403486
MemoryBlock block1 = createBlock("Test1", addr(0), 100);

Ghidra/Framework/Project/src/main/java/ghidra/util/Lock.java

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/* ###
22
* IP: GHIDRA
3-
* REVIEWED: YES
43
*
54
* Licensed under the Apache License, Version 2.0 (the "License");
65
* you may not use this file except in compliance with the License.
@@ -17,27 +16,29 @@
1716
package ghidra.util;
1817

1918
/**
20-
* Ghidra synchronization lock. This class allows creation of named locks for
19+
* Ghidra synchronization lock. This class allows creation of named locks for
2120
* modifying tables in the Ghidra data base. This class also creates an instance
22-
* of a global lock that must first be obtained when synchronizing using multiple
23-
* of the named locks.
21+
* of a global lock that must first be obtained when synchronizing using
22+
* multiple of the named locks.
2423
*/
2524
public class Lock {
2625
private Thread owner;
2726
private int cnt = 0;
27+
private int waiting = 0;
2828
private String name;
2929

3030
/**
3131
* Creates an instance of a lock for synchronization within Ghidra.
32+
*
3233
* @param name the name of this lock
3334
*/
3435
public Lock(String name) {
3536
this.name = name;
3637
}
3738

3839
/**
39-
* Acquire this synchronization lock.
40-
* (i.e. begin synchronizing on this named lock.)
40+
* Acquire this synchronization lock. (i.e. begin synchronizing on this named
41+
* lock.)
4142
*/
4243
public synchronized void acquire() {
4344
Thread currThread = Thread.currentThread();
@@ -47,15 +48,16 @@ public synchronized void acquire() {
4748
cnt = 1;
4849
owner = currThread;
4950
return;
50-
}
51-
else if (owner == currThread) {
51+
} else if (owner == currThread) {
5252
cnt++;
5353
return;
5454
}
5555
try {
56+
waiting++;
5657
wait();
57-
}
58-
catch (InterruptedException e) {
58+
} catch (InterruptedException e) {
59+
} finally {
60+
waiting--;
5961
}
6062
}
6163
}
@@ -70,20 +72,24 @@ public synchronized void release() {
7072
if (cnt > 0 && (owner == currThread)) {
7173
if (--cnt == 0) {
7274
owner = null;
73-
notify();
75+
// This is purely to help sample profiling. If notify() is called the
76+
// sampler can attribute time to the methods calling this erroneously. For some reason
77+
// the visualvm sampler gets a sample more often when notify() is called.
78+
if (waiting != 0) {
79+
notify();
80+
}
7481
}
75-
}
76-
else {
82+
} else {
7783
throw new IllegalStateException("Attempted to release an unowned lock: " + name);
7884
}
7985
}
8086

8187
/**
8288
* Gets the thread that currently owns the lock.
89+
*
8390
* @return the thread that owns the lock or null.
8491
*/
8592
public Thread getOwner() {
8693
return owner;
8794
}
88-
8995
}

Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoDisassembler.java

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,6 @@ public class PseudoDisassembler {
9595

9696
private boolean respectExecuteFlag = false;
9797

98-
private AddressSetView executeSet;
99-
10098
/**
10199
* Create a pseudo disassembler for the given program.
102100
*/
@@ -111,18 +109,6 @@ public PseudoDisassembler(Program program) {
111109

112110
this.programContext = program.getProgramContext();
113111
}
114-
115-
/**
116-
* @return cached addressSet of executable memory blocks
117-
*/
118-
private AddressSetView getExecuteSet() {
119-
if (executeSet != null) {
120-
return executeSet;
121-
}
122-
123-
executeSet = memory.getExecuteSet();
124-
return executeSet;
125-
}
126112

127113
/**
128114
* Set the maximum number of instructions to check
@@ -617,6 +603,7 @@ public boolean checkValidSubroutine(Address entryPoint, PseudoDisassemblerContex
617603
boolean allowExistingInstructions, boolean mustTerminate) {
618604
AddressSet body = new AddressSet();
619605
AddressSet instrStarts = new AddressSet();
606+
AddressSetView execSet = memory.getExecuteSet();
620607

621608
if (hasLowBitCodeModeInAddrValues(program)) {
622609
entryPoint = setTargeContextForDisassembly(procContext, entryPoint);
@@ -801,7 +788,6 @@ else if (flowType.isComputed()) {
801788
}
802789
}
803790
// if respecting execute flag on memory, test to make sure we did flow into non-execute memory
804-
AddressSetView execSet = getExecuteSet();
805791
if (respectExecuteFlag && !execSet.isEmpty() && !execSet.contains(flows[j])) {
806792
if (!flows[j].isExternalAddress()) {
807793
MemoryBlock block = memory.getBlock(flows[j]);
@@ -902,8 +888,8 @@ private boolean checkPseudoBody(Address entry, AddressSet body, AddressSet start
902888
}
903889

904890
// check that body does not wander into non-executable memory
905-
AddressSetView execSet = getExecuteSet();
906-
if (respectExecuteFlag && !execSet.isEmpty() && !body.subtract(execSet).isEmpty()) {
891+
AddressSetView execSet = memory.getExecuteSet();
892+
if (respectExecuteFlag && !execSet.isEmpty() && !execSet.contains(body)) {
907893
return false;
908894
}
909895

@@ -914,8 +900,9 @@ private boolean checkPseudoBody(Address entry, AddressSet body, AddressSet start
914900
return false;
915901
}
916902

903+
boolean canHaveOffcutEntry = hasLowBitCodeModeInAddrValues(program);
917904
AddressSet strictlyBody = body.subtract(starts);
918-
if (hasLowBitCodeModeInAddrValues(program)) {
905+
if (canHaveOffcutEntry) {
919906
strictlyBody.deleteRange(entry, entry.add(1));
920907
}
921908
AddressIterator addrIter =

Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
5757
private AddressSet addrSet = new AddressSet();
5858
private AddressSet initializedLoadedAddrSet = new AddressSet();
5959
private AddressSet allInitializedAddrSet = new AddressSet();
60+
private AddressSetView executeSet = new AddressSet();
61+
6062
private MemoryBlock lastBlock;// the last accessed block
6163
private LiveMemoryHandler liveMemory;
6264

@@ -187,6 +189,7 @@ private synchronized void initializeBlocks() {
187189
blocks = newBlocks;
188190
addrMap.memoryMapChanged(this);
189191
nameBlockMap = new HashMap<>();
192+
executeSet = null;
190193
}
191194

192195
public void setLanguage(Language newLanguage) {
@@ -396,6 +399,12 @@ void fireBlockChanged(MemoryBlock block) {
396399

397400
// name could have changed
398401
nameBlockMap = new HashMap<>();
402+
403+
// execute state could have changed. check if in set and shouldn't be or vice/versa
404+
if (executeSet != null && executeSet.contains(block.getStart(), block.getEnd()) != block.isExecute()) {
405+
// don't regenerate now, do lazily later if needed
406+
executeSet = null;
407+
}
399408
}
400409

401410
void fireBytesChanged(Address addr, int count) {
@@ -1972,13 +1981,17 @@ public int hashCode() {
19721981
*/
19731982
@Override
19741983
public AddressSetView getExecuteSet() {
1984+
if (executeSet != null) {
1985+
return executeSet;
1986+
}
19751987
AddressSet set = new AddressSet();
19761988
for (MemoryBlock block : blocks) {
19771989
if (block.isExecute()) {
19781990
set.addRange(block.getStart(), block.getEnd());
19791991
}
19801992
}
1981-
return set;
1993+
executeSet = new AddressSetViewAdapter(set);
1994+
return executeSet;
19821995
}
19831996

19841997
@Override

0 commit comments

Comments
 (0)