Skip to content

Commit be824ad

Browse files
committed
Remove drcove bb dups and speed it up
1 parent c363dab commit be824ad

File tree

1 file changed

+30
-2
lines changed
  • qiling/extensions/coverage/formats

1 file changed

+30
-2
lines changed

qiling/extensions/coverage/formats/drcov.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
from __future__ import annotations
77

88
from ctypes import Structure, c_uint32, c_uint16
9-
from typing import TYPE_CHECKING, BinaryIO
9+
from functools import lru_cache
10+
from typing import TYPE_CHECKING, BinaryIO, Dict, Tuple
1011

1112
from .base import QlBaseCoverage
1213

1314

1415
if TYPE_CHECKING:
1516
from qiling import Qiling
17+
from qiling.loader.loader import QlLoader
1618

1719

1820
# Adapted from https://www.ayrx.me/drcov-file-format
@@ -40,9 +42,35 @@ def __init__(self, ql: Qiling):
4042

4143
self.drcov_version = 2
4244
self.drcov_flavor = 'drcov'
43-
self.basic_blocks = []
45+
self.basic_blocks: Dict[int, bb_entry] = {}
4446
self.bb_callback = None
4547

48+
@lru_cache(maxsize=64)
49+
def _get_img_base(self, loader: QlLoader, address: int) -> Tuple[int, int]:
50+
"""Retrieve the containing image of a given address.
51+
52+
Addresses are expected to be aligned to page boundary, and cached for faster retrieval.
53+
"""
54+
55+
return next((i, img.base) for i, img in enumerate(loader.images) if img.base <= address < img.end)
56+
57+
def block_callback(self, ql: Qiling, address: int, size: int):
58+
if address not in self.basic_blocks:
59+
try:
60+
# we rely on the fact that images are allocated on page size boundary and
61+
# use it to speed up image retrieval. we align the basic block address to
62+
# page boundary, knowing basic blocks within the same page belong to the
63+
# same image. then we use the aligned address to retreive the containing
64+
# image. returned values are cached so subsequent retrievals for basic
65+
# blocks within the same page will return the cached value instead of
66+
# going through the retreival process again (up to maxsize cached pages)
67+
68+
i, img_base = self._get_img_base(ql.loader, address & ~(0x1000 - 1))
69+
except StopIteration:
70+
pass
71+
else:
72+
self.basic_blocks[address] = bb_entry(address - img_base, size, i)
73+
4674
def activate(self) -> None:
4775
self.bb_callback = self.ql.hook_block(self.block_callback)
4876

0 commit comments

Comments
 (0)