ROSE 0.11.145.134
InstructionCache.h
1#ifndef ROSE_BinaryAnalysis_InstructionCache_H
2#define ROSE_BinaryAnalysis_InstructionCache_H
3#include <featureTests.h>
4#ifdef ROSE_ENABLE_BINARY_ANALYSIS
5
6#include <Rose/BinaryAnalysis/Address.h>
7#include <Rose/BinaryAnalysis/BasicTypes.h>
8#include <Rose/BinaryAnalysis/Disassembler/BasicTypes.h>
9#include <Rose/Exception.h>
10
11#include <memory>
12#include <unordered_map>
13
15
16namespace Rose {
17namespace BinaryAnalysis {
18
19class InstructionCache;
20class InstructionPtr;
21class LockedInstruction;
22
24// ManagedInstruction
26
35private:
36 // Every ManagedInstruction is owned by a cache. The user might create additional shared pointers to this object, but as long
37 // as any ManagedInstruction object exists and can potentially be in the ABSENT state, we need to have a cache that can
38 // reconstruct the AST.
39 InstructionCache *cache; // not null, set by constructor and never changed
40
41 // Protects all following data members
42 mutable SAWYER_THREAD_TRAITS::Mutex mutex_;
43
44 // As is typical of cache-like objects, most of the data members are mutable because the some of the member functions that
45 // modify them are conceptually const. For example, the operator-> is simply a dereference from the caller's point of void
46 // and is thus const, but under the covers it needs to be able to convert this object from the ABSENT state to the PRESENT
47 // state.
48
49 // "time" of last dereference. This informs the cache eviction algorithm.
50 mutable size_t lastAccess;
51
52 // C++11 doesn't have discriminated unions like C++17, so we do it the hard way.
53 enum State {
54 ABSENT, // the pointer is non-null but the AST is not present
55 PRESENT // the AST is present or is a null pointer
56 };
57 mutable State state;
58 union U {
59 SgAsmInstruction *ast; // the AST or null when in the PRESENT state
60 rose_addr_t va; // the instruction starting address when in the ABSENT state.
61 };
62 mutable U u;
63
64private:
65 friend class InstructionCache;
66
67 ManagedInstruction() = delete;
69 ManagedInstruction& operator=(const ManagedInstruction&) = delete;
70
71 ManagedInstruction(InstructionCache *cache, rose_addr_t va)
72 : cache{cache}, state{ABSENT}, u{.va = va} {
73 ASSERT_not_null(cache);
74 }
75
77 : cache{cache}, state{PRESENT}, u{.ast = nullptr} {
78 ASSERT_not_null(cache);
79 }
80
81 // There is no safe way to do this with implicit locking. Any reference we would return could be held indefinitely by the
82 // caller and there's no way we can automatically lock it.
83 SgAsmInstruction& operator*() const = delete;
84
85public:
94 LockedInstruction operator->() const; // hot
95
96private:
97 friend class InstructionPtr;
98
99 // True if the underlying instructon is a null pointer.
100 bool isNull() const; // hot
101
102 // Create a locking pointer around the AST, and mark the AST as having been accessed.
103 LockedInstruction lock() const; // hot
104
105 // Make sure the AST is present and return a special pointer that causes it to be locked in the cache. The function is const
106 // because it's typically called from a const context (pointer dereference) and from the user's point of void is constant even
107 // though under the covers it's creating a new AST and swapping it into this object.
108 LockedInstruction makePresentNS() const; // hot
109
110 // Evicts the AST from memory, deleting it from this object and replacing it with only the instruction address. The
111 // instruction address, together with the information stored in the cache, is enough to recreate the AST if we ever need it
112 // again.
113 void evict();
114
115 // Update the last access time used by the cache eviction algorithm. The function is const because it's typically called
116 // in a const context (pointer dereferencing).
117 void updateTimerNS() const; // hot
118
119 // Take the AST and its ownership away from this object, returning the AST. Throws an exception if the AST is locked, since
120 // its not possible for the returned raw pointer and the cache to share ownership.
121 SgAsmInstruction* take();
122};
123
125// LockedInstruction
127
138private:
139 mutable SAWYER_THREAD_TRAITS::Mutex mutex_; // protects all following data members
140 SgAsmInstruction *insn;
141
142public:
145
149 explicit LockedInstruction(SgAsmInstruction *insn); // hot
150
155 explicit LockedInstruction(const InstructionPtr &insn);
156
161
168
173
180 void reset();
181
189
195 SgAsmInstruction* operator->() const; // hot
196
201
207 explicit operator bool() const;
208};
209
211// InstructionPtr
213
274 mutable SAWYER_THREAD_TRAITS::Mutex mutex_; // protects all following data members
275 std::shared_ptr<ManagedInstruction> mi_;
276
277public:
280
283 : mi_(other.mi_) {}
284
285
289 InstructionPtr& operator=(const InstructionPtr &other); // hot
290
296 void reset();
297
298 // Dereferences are inherently unsafe because we have no opportunity to lock the instruction in a way that we can then unlock
299 // it, and we have no control over the lifetime of the reference that we would return.
300 SgAsmInstruction& operator*() const = delete;
301
307 LockedInstruction operator->() const; // hot
308
314 explicit operator bool() const; // hot
315
321
330
336 bool operator==(const InstructionPtr &other) const;
337 bool operator!=(const InstructionPtr &other) const;
338 bool operator<=(const InstructionPtr &other) const;
339 bool operator>=(const InstructionPtr &other) const;
340 bool operator<(const InstructionPtr &other) const;
341 bool operator>(const InstructionPtr &other) const;
342 bool operator==(std::nullptr_t) const;
343 bool operator!=(std::nullptr_t) const; // hot
346private:
347 friend class InstructionCache;
348
349 // Construct pointer to a ManagedInstruction that exists in an instruction cache. */
350 static InstructionPtr instance(InstructionCache *cache, rose_addr_t va);
351 static InstructionPtr instance(InstructionCache *cache);
352};
353
355// InstructionCache
357
365public:
368 public:
369 Exception(const std::string &mesg)
370 : Rose::Exception(mesg) {}
371 ~Exception() throw() {}
372 };
373
374private:
375 MemoryMapPtr memory_; // not null, constant for life of object
376 Disassembler::BasePtr decoder_; // not null, constant for life of object
377
378 mutable SAWYER_THREAD_TRAITS::Mutex mutex_; // protects all following data members
379 std::unordered_map<rose_addr_t, InstructionPtr> insns_;
380
381 InstructionCache(const InstructionCache&) = delete;
382 InstructionCache& operator=(const InstructionCache&) = delete;
383
384public:
386
393
400
405
414 InstructionPtr get(rose_addr_t va);
415
419 LockedInstruction lock(rose_addr_t va);
420
424 void evict();
425
426private:
427 friend class ManagedInstruction;
428
429 // Decode a single instruction at the specified address. This function is thread safe.
430 SgAsmInstruction* decode(rose_addr_t);
431};
432
434// InstructionGuard
436
441 // The InstructionGuard was originally slightly more complicated, but the intruction of the automatic temporary locking
442 // made it a lot simpler! All we need to do is hold onto a locked instruction pointer. However, we keep this class around because
443 // it's better documentation for the programmer's intent than simply holding a locked pointer.
445
446public:
448 explicit InstructionGuard(const InstructionPtr &insn)
449 : lock(insn) {}
450};
451
453// Inline definitions for hot functions
455
456inline InstructionPtr&
458 SAWYER_THREAD_TRAITS::LockGuard2 lock(mutex_, other.mutex_);
459 mi_ = other.mi_;
460 return *this;
461}
462
465 SAWYER_THREAD_TRAITS::LockGuard lock(mutex_);
466 ASSERT_not_null(mi_);
467 ManagedInstruction &mi = *mi_.get();
468 return mi.lock();
469}
470
473 return lock();
474}
475
477ManagedInstruction::lock() const {
478 SAWYER_THREAD_TRAITS::LockGuard lock(mutex_);
479 updateTimerNS();
480 return makePresentNS();
481}
482
483inline void
484ManagedInstruction::updateTimerNS() const {
485 static size_t nextTimer = 0;
486 lastAccess = ++nextTimer;
487}
488
489inline LockedInstruction
490ManagedInstruction::makePresentNS() const {
491 if (ABSENT == state) { // unlikely
492 SgAsmInstruction *decoded = cache->decode(u.va);
493 ASSERT_not_null(decoded); // at worst, the decoder will return an unknown instruction
494 state = PRESENT; // no-throw
495 u.ast = decoded; // no-throw
496 }
497 return LockedInstruction{u.ast};
498}
499
500inline SgAsmInstruction*
502 SAWYER_THREAD_TRAITS::LockGuard lock(mutex_);
503 ASSERT_not_null(insn);
504 return insn;
505}
506
507inline
508InstructionPtr::operator bool() const {
509 SAWYER_THREAD_TRAITS::LockGuard lock(mutex_);
510 return mi_.get() ? !(*mi_).isNull() : false;
511}
512
513inline bool
514ManagedInstruction::isNull() const {
515 SAWYER_THREAD_TRAITS::LockGuard lock(mutex_);
516 // A null pointer can be in the absent state only if it was never yet in the present state. This is because all we know
517 // about an absent pointer is it's address, not whether we can create an instruction AST at that address. Therefore, we
518 // have to try to create the AST.
519 makePresentNS();
520 return u.ast == nullptr;
521}
522
523inline bool
524InstructionPtr::operator!=(const std::nullptr_t) const {
525 SAWYER_THREAD_TRAITS::LockGuard lock(mutex_);
526 return mi_ && !(*mi_).isNull()? true : false;
527}
528
529
530
531
532} // namespace
533} // namespacd
534#endif
535#endif
InstructionPtr get(rose_addr_t va)
Obtain the instruction at a specified address.
void evict()
Garbage collection.
LockedInstruction lock(rose_addr_t va)
Shortcut to obtain a lock.
MemoryMapPtr memoryMap() const
Property: memory map providing the opcodes for the instructions.
Disassembler::BasePtr decoder() const
Property: the decoder used to construct the instruction ASTs from data in memory.
InstructionCache(const MemoryMapPtr &, const Disassembler::BasePtr &decoder)
Construct a new instruction cache.
InstructionGuard(const InstructionPtr &insn)
Constuct the object and acquire locks.
bool operator==(const InstructionPtr &other) const
Comparison.
bool operator>=(const InstructionPtr &other) const
Comparison.
SgAsmInstruction * take()
Give ownership to caller.
InstructionPtr(const InstructionPtr &other)
Copy constructor.
bool operator>(const InstructionPtr &other) const
Comparison.
bool operator!=(const InstructionPtr &other) const
Comparison.
void reset()
Clear the pointer.
LockedInstruction lock() const
Return a locking pointer.
bool operator<=(const InstructionPtr &other) const
Comparison.
bool operator<(const InstructionPtr &other) const
Comparison.
bool operator==(std::nullptr_t) const
Comparison.
LockedInstruction operator->() const
Dereference.
InstructionPtr()
Construct a null pointer.
InstructionPtr & operator=(const InstructionPtr &other)
Assignment operator.
Smart pointer that locks an instruction AST.
SgAsmInstruction * get() const
Get the raw pointer.
LockedInstruction & operator=(const LockedInstruction &other)
Assignment.
SgAsmInstruction * operator->() const
Dereference.
SgAsmInstruction & operator*() const
Dereference.
LockedInstruction(SgAsmInstruction *insn)
Point to a specific instruction.
LockedInstruction(const LockedInstruction &other)
Copy constructor.
LockedInstruction(const InstructionPtr &insn)
Point to a specific instruction.
LockedInstruction()
Construct a null pointer.
Wrapper for AST that can be evicted.
LockedInstruction operator->() const
Access an instruction member.
Base class for all ROSE exceptions.
Base class for reference counted objects.
Base class for machine instructions.
The ROSE library.