Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more docs about MRA data structures. #290

Merged
merged 1 commit into from
Dec 18, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 51 additions & 3 deletions src/main/scala/analysis/MemoryModelMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,56 @@ case class RangeKey(start: BigInt, end: BigInt) extends Ordered[RangeKey]:
}
override def toString: String = s"Range[$start, $end] (size: $size)"

// Custom data structure for storing range-to-object mappings
/** Custom data structure for storing range-to-object mappings.
*
* Provides a structure for how the program is accessing different memory regions.
*/
class MemoryModelMap(val globalOffsets: Map[BigInt, BigInt]) {
/** Contains the names of callers. Top element is name of the current calling function. */
private val contextStack = mutable.Stack.empty[String]
/** not used any more. Could be removed. */
private val sharedContextStack = mutable.Stack.empty[List[StackRegion]]
/** Maps function names to the set of objects in that stack frame. */
private val localStacks = mutable.Map[String, List[StackRegion]]().withDefaultValue(List.empty)
/** not used any more. Could be removed. */
private val sharedStacks = mutable.Map[String, List[StackRegion]]()

/** Maps an interval of stack addresses to the stack region, for the current function. */
private val stackMap: mutable.Map[RangeKey, StackRegion] = mutable.TreeMap()
/** Caches the results of `stackMap` for each function that has been visited. */
private val bufferedStackMap: mutable.Map[String, mutable.Map[RangeKey, StackRegion]] = mutable.Map()
/** not used any more. Could be removed. */
private val sharedStackMap: mutable.Map[Procedure, mutable.TreeMap[RangeKey, StackRegion]] = mutable.Map[Procedure, mutable.TreeMap[RangeKey, StackRegion]]()
/** not used any more. Could be removed. */
private val bufferedSharedStackMap: mutable.Map[String, mutable.Map[Procedure, mutable.TreeMap[RangeKey, StackRegion]]] = mutable.Map()

/** Maps an interval of heap offsets to the heap region.
*
* Note that heap start offsets are allocated globally across the whole program.
* For example, if main has malloc(8) and malloc(4), those regions will have start offsets of 0 and 8.
* Then if another function has malloc(16) and malloc(4), those regions will have start offset of 12 and 28.
*/
private val heapMap: mutable.Map[RangeKey, HeapRegion] = mutable.TreeMap()
/** Maps intervals of global absolute addresses to the corresponding data region. */
private val dataMap: mutable.Map[RangeKey, DataRegion] = mutable.TreeMap()
/** Maps load and store instructions to the global data regions that it might be accessing. */
private val cfgPositionToDataRegion: mutable.Map[CFGPosition, Set[DataRegion]] = mutable.Map()
/** Maps malloc call locations to the corresponding heap region. */
private val heapCalls: mutable.Map[DirectCall, HeapRegion] = mutable.Map()
/** Handles the pointers-to-pointers in the global relocation tables.
*
* So this maps an address of a relocation table entry to the address of the resolved object after linking.
* This saves having to look up the relation table every time a function (or global data) is called.
*/
private var relocatedAddressesMap: Map[BigInt, DataRegion] = Map()

/** Maps load and store instructions to the stack data regions that it might be accessing. */
private val stackAllocationSites: mutable.Map[CFGPosition, Set[StackRegion]] = mutable.Map()

/** The union-find data structure used to merge multiple regions together. */
private val uf = UnionFind()
/** Debugging data from relf files, to determine source code names of global functions and variables. */
val relfContent: mutable.Map[DataRegion, mutable.Set[String]] = mutable.Map()
/** Records the sizes of accesses to stack regions. */
val stackSubAccesses: mutable.Map[StackRegion, mutable.Set[BigInt]] = mutable.Map()

/** Add a range and object to the mapping
Expand Down Expand Up @@ -252,7 +281,7 @@ class MemoryModelMap(val globalOffsets: Map[BigInt, BigInt]) {
}
}

def findStackObject(value: BigInt): Option[StackRegion] =
def findStackObject(value: BigInt): Option[StackRegion] =
stackMap.find((range, _) => range.start <= value && value <= range.end).map((range, obj) => returnRegion(obj))

def findSharedStackObject(value: BigInt): Set[StackRegion] =
Expand Down Expand Up @@ -387,14 +416,33 @@ trait MemoryRegion {
val start: BigInt
}

/** Stack regions track a contiguous portion inside a stack frame.
*
* @param regionIdentifier a unique stack name within each function.
* @param start an real offset from the frame pointer to the start of this memory.
* @param parent the function associated with this stack frame.
*/
case class StackRegion(override val regionIdentifier: String, override val start: BigInt, parent: Procedure) extends MemoryRegion {
override def toString: String = s"Stack($regionIdentifier, $start, ${parent.name}"
}

/** Heap regions track a contiguous portion inside a block of allocated heap memory.
*
* @param regionIdentifier identifies which malloc allocated the heap.
* @param start a virtual offset for this memory, being the sum of sizes of previous mallocs in the same function.
* @param size the size of the allocated heap memory.
* @param parent the function that contained the malloc.
*/
case class HeapRegion(override val regionIdentifier: String, override val start: BigInt, size: BigInt, parent: Procedure) extends MemoryRegion {
override def toString: String = s"Heap($regionIdentifier, $size)"
}

/** Data regions track a contiguous portion of the global data section.
*
* @param regionIdentifier a real name from the global symbol table if available, else a unique generated id.
* @param start absolute address of the start of this region.
* @param size the size, either from the symbol table or estimated.
*/
case class DataRegion(override val regionIdentifier: String, override val start: BigInt, size: BigInt) extends MemoryRegion {
override def toString: String = s"Data($regionIdentifier, $start, $size)"
val end: BigInt = start + size - 1
Expand Down
Loading