Skip to content

Commit

Permalink
Debugger: Improve symbol search performance
Browse files Browse the repository at this point in the history
Signed-off-by: iipeace <[email protected]>
  • Loading branch information
iipeace committed May 20, 2024
1 parent 85b5cef commit 0b90bf4
Showing 1 changed file with 166 additions and 36 deletions.
202 changes: 166 additions & 36 deletions guider/guider.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
__credits__ = "Peace Lee"
__license__ = "GPLv2"
__version__ = "3.9.8"
__revision__ = "240519"
__revision__ = "240520"
__maintainer__ = "Peace Lee"
__email__ = "[email protected]"
__repository__ = "https://github.com/iipeace/guider"
Expand Down Expand Up @@ -6654,7 +6654,7 @@ def isElfFile(path=None, fd=None):
ord("F"),
)
except IOError:
SysMgr.printOpenErr(path)
SysMgr.printOpenWarn(path)
return False
except SystemExit:
sys.exit(0)
Expand Down Expand Up @@ -62202,6 +62202,7 @@ def _doCommonJobs(pids, procList):
targetBpList.setdefault(pid, {})
targetBpFileList.setdefault(pid, {})
exceptBpFileList.setdefault(pid, {})
objectList.setdefault(pid, {})

# create object #
procObj = Debugger(pid=pid, mode="break")
Expand Down Expand Up @@ -62234,6 +62235,7 @@ def _doCommonJobs(pids, procList):
exceptBpFileList[pid] = UtilMgr.deepcopy(
procObj.exceptBpFileList
)
objectList[pid] = UtilMgr.deepcopy(procObj.objectList)

# create a lock for the multi-threaded process #
if SysMgr.getTids(pid, sibling=True):
Expand Down Expand Up @@ -62268,6 +62270,7 @@ def _doCommonJobs(pids, procList):
targetBpList = {}
targetBpFileList = {}
exceptBpFileList = {}
objectList = {}

# get argument #
if tid:
Expand Down Expand Up @@ -62563,6 +62566,8 @@ def _doCommonJobs(pids, procList):
targetBpFileList = targetBpFileList[ppid]
if ppid in exceptBpFileList:
exceptBpFileList = exceptBpFileList[ppid]
if ppid in objectList:
objectList = objectList[ppid]
if ppid in lockList:
lockObj = lockList[ppid]
else:
Expand All @@ -62584,6 +62589,7 @@ def _doCommonJobs(pids, procList):
targetBpList=targetBpList,
targetBpFileList=targetBpFileList,
exceptBpFileList=exceptBpFileList,
objectList=objectList,
)
elif mode == "hook":
Debugger.hookFunc(pid, SysMgr.customCmd)
Expand Down Expand Up @@ -62748,20 +62754,23 @@ def _printRes(procInfo, resInfo, maxSymLen):

# load android JIT caches #
try:
if not SysMgr.isAndroid:
raise Exception("not android")
if SysMgr.isAndroid:
# remove on-memory caches #
ElfAnalyzer.cachedFiles = {}

# remove on-memory caches #
ElfAnalyzer.cachedFiles = {}

# load android JIT symbols #
dobj = Debugger(pid=pid, attach=False)
dobj.updateProcMap(onlyExec=False)
dobj.loadAndroidJITSymbols()

if SysMgr.isAndroid:
# load android JIT symbols #
dobj.loadAndroidJITSymbols()
except SystemExit:
sys.exit(0)
except:
pass
SysMgr.printErr("failed to analyze %s" % procInfo, True)
continue

# TODO: remove useless map info to improve search performance #

for addr in addrList:
ret = dobj.getSymbolInfo(
Expand Down Expand Up @@ -63861,11 +63870,16 @@ def getProcAddrBySymbol(pid, symbolList, fileFilter=None, errExit=False):
if not FileAnalyzer.isValidFile(filePath, special=True):
continue

# check symbol strings #
origFilePath = filePath.rsplit(SysMgr.magicStr, 1)[0]
if UtilMgr.isElfFile(
origFilePath
) and not ElfAnalyzer.getObjectStr(origFilePath, symbolList):
continue

for sym in symbolList:
# create an ELF object #
try:
origFilePath = filePath.rsplit(SysMgr.magicStr, 1)[0]

res = ElfAnalyzer.getSymOffset(sym, origFilePath)
if not res:
continue
Expand Down Expand Up @@ -83889,6 +83903,7 @@ def __init__(self, pid=None, execCmd=None, attach=True, mode=None):
self.targetBpList = {}
self.targetBpFileList = {}
self.exceptBpFileList = {}
self.objectList = {}
self.symbolCacheList = {}
self.failedAddrList = {}
self.ldInjected = False
Expand Down Expand Up @@ -86902,8 +86917,17 @@ def loadAndroidJITSymbols(self):
# get JITDescriptor #
decChar = "Q" if wordSize == 8 else "I"
varAddr = list(addrInfo.values())[0][2]
desc = self.readMem(long(varAddr, 16), (8 + wordSize * 2))
version, action, rel, head = struct.unpack("II" + (decChar * 2), desc)
desc = self.readMem(long(varAddr, 16), (8 + wordSize * 2 + 32))
ret = struct.unpack(
"II" + (decChar * 2) + "8B" + ("I" * 4) + "Q", desc
)
version, action, rel, head = ret[:4]
magic = ret[4:12]
flags, szdesc, szentry, seqlock, ts = ret[12:]

# check timestamp #
last = self.objectList.get("__jit_debug_descriptor", 0)
self.objectList["__jit_debug_descriptor"] = ts

# enable DWARF flag #
origDwarfEnable = SysMgr.dwarfEnable
Expand All @@ -86912,7 +86936,7 @@ def loadAndroidJITSymbols(self):
# get code entry #
codeCnt = 0
codep = head
while 1:
while ts > last:
# get JITCodeEntry #
code = self.readMem(codep, (wordSize * 3) + 8)
next_, prev_, sfaddr, sfsize = struct.unpack(
Expand Down Expand Up @@ -86944,7 +86968,7 @@ def loadAndroidJITSymbols(self):

# merge symbols and DWARFs #
for n, v in elfObj.attr.items():
if n in ("symTable", "dymsymTable"):
if n in ("symTable", "dynsymTable"):
symInfo = origElfObj.attr.get(n, {})
for sym, attrs in sorted(
v.items(), key=lambda x: x[1]["value"]
Expand Down Expand Up @@ -86984,6 +87008,9 @@ def loadAndroidJITSymbols(self):
# recover DWARF flag #
SysMgr.dwarfEnable = origDwarfEnable

# skip dex parsing #
return

# -------------------- __dex_debug_descriptor -------------------- #
"""
// Public/stable binary interface.
Expand Down Expand Up @@ -87124,7 +87151,7 @@ def loadAndroidJITSymbols(self):

# merge symbols and DWARFs #
for n, v in elfObj.attr.items():
if n in ("symTable", "dymsymTable"):
if n in ("symTable", "dynsymTable"):
for sym, attrs in sorted(
v.items(), key=lambda x: x[1]["value"]
):
Expand Down Expand Up @@ -96537,9 +96564,25 @@ def _getOneItem():
return []

# search symbols from all memory-mapped files #
checkList = {}
realSym = symbol.strip("* ")
for mfile in list(self.pmap):
mfile = mfile.split(SysMgr.magicStr)[0]

if binary and not mfile in binary:
continue
elif mfile in checkList:
continue
elif (
realSym
and not mfile in ElfAnalyzer.cachedFiles
and UtilMgr.isElfFile(mfile)
and not ElfAnalyzer.getObjectStr(mfile, symbol)
):
continue

# save check list #
checkList[mfile] = None

# get ELF object #
try:
Expand Down Expand Up @@ -96867,6 +96910,7 @@ def restartTrace(self, initStatus=None):
dobj.targetBpList = self.targetBpList
dobj.targetBpFileList = self.targetBpFileList
dobj.exceptBpFileList = self.exceptBpFileList
dobj.objectList = self.objectList

# initialize variables #
if dobj.mode == "break":
Expand Down Expand Up @@ -97546,6 +97590,7 @@ def trace(
targetBpList={},
targetBpFileList={},
exceptBpFileList={},
objectList={},
initStatus=None,
):
# initialize variables #
Expand All @@ -97558,6 +97603,8 @@ def trace(
self.targetBpFileList = targetBpFileList
if not self.exceptBpFileList:
self.exceptBpFileList = exceptBpFileList
if not self.objectList:
self.objectList = objectList

# context variables #
self.cmd = None
Expand Down Expand Up @@ -102549,6 +102596,30 @@ def getFilterFlags(symbol):

return symbol, inc, start, end

@staticmethod
def getObjectStr(fpath, symbol):
candSymList = {}
try:
if type(symbol) != list:
symbol = [symbol]
condSym = [s.strip("*") for s in symbol]

# check real path #
p = fpath.split(SysMgr.magicStr)[0]

# get and check symbols #
eobj = ElfAnalyzer(p, onlyStr=True)
for s in eobj.strTable:
if UtilMgr.isValidStr(s, condSym, inc=True):
candSymList[s] = None
except SystemExit:
sys.exit(0)
except:
SysMgr.printWarn(
"failed to get string table of '%s'" % fpath, reason=True
)
return candSymList

@staticmethod
def getSymOffset(
symbol, binPath, objdumpPath=None, loadAddr=False, cache=False
Expand Down Expand Up @@ -103332,6 +103403,7 @@ def __init__(
path=None,
debug=False,
onlyHdr=False,
onlyStr=False,
fd=None,
size=sys.maxsize,
incArg=False,
Expand Down Expand Up @@ -103746,6 +103818,7 @@ def _printer(item):
self.attr = {}
self.is32Bit = True
self.saved = False
self.strTable = {}
self.sortedSymTable = []
self.sortedAddrTable = []
self.sortedLineTable = []
Expand Down Expand Up @@ -103961,18 +104034,28 @@ def _isValidSect(name):
% (debugPath, self.path)
)

dobj = ElfAnalyzer(debugPath, debug=debug, origPath=self.path)
dobj = ElfAnalyzer(
debugPath,
debug=debug,
onlyHdr=onlyHdr,
onlyStr=onlyStr,
origPath=self.path,
)
if dobj:
# merge tables #
dobj.mergeSymTable()
self.addrTable.update(dobj.addrTable)
dobj.addrTable.clear()
self.attr["symTable"] = UtilMgr.deepcopy(dobj.attr["symTable"])
self.attr["dynsymTable"] = UtilMgr.deepcopy(
dobj.attr["dynsymTable"]
)
if "dwarf" in dobj.attr:
self.attr["dwarf"] = UtilMgr.deepcopy(dobj.attr["dwarf"])
del dobj
self.strTable = UtilMgr.deepcopy(dobj.strTable)

# copy tables #
for tname in ("symTable", "dynsymTable", "dwarf"):
if not tname in dobj.attr:
continue
self.attr[tname] = UtilMgr.deepcopy(dobj.attr[tname])

# remove object #
del dobj

# check file #
if not os.path.exists(path):
Expand Down Expand Up @@ -104746,6 +104829,54 @@ def _printStrSect(sh_name, strtab):
if onlyHdr:
return None

# return only string table #
if onlyStr:
for x in (e_shstrndx, e_shdynstr, e_shdbgstr):
if x < 0:
continue

# section info #
(
sh_name,
sh_type,
sh_flags,
sh_addr,
sh_offset,
sh_size,
sh_link,
sh_info,
sh_addralign,
sh_entsize,
) = self.getSectionInfo(fd, e_shoff + e_shentsize * x)

# read str data #
fd.seek(sh_offset)
strtable = fd.read(sh_size)
try:
strtable_decoded = strtable.decode()
except SystemExit:
sys.exit(0)
except:
strtable_decoded = strtable

lastnull = 0
symTable = {}
for i, s in enumerate(strtable_decoded):
if s != "\0":
continue
try:
sd = strtable[lastnull:i].decode()
except SystemExit:
sys.exit(0)
except:
sd = strtable[lastnull:i]
lastnull = i + 1

sd = ElfAnalyzer.demangleSymbol(sd)
self.strTable[sd] = None

return None

# define versym info #
self.attr["versymList"] = []

Expand Down Expand Up @@ -104813,16 +104944,15 @@ def _printStrSect(sh_name, strtab):
lastnull = 0
dynsymTable = {}
for i, s in enumerate(dynstr_section_decoded):
if s == "\0":
try:
dynsymTable[lastnull] = dynstr_section[
lastnull:i
].decode()
except SystemExit:
sys.exit(0)
except:
dynsymTable[lastnull] = dynstr_section[lastnull:i]
lastnull = i + 1
if s != "\0":
continue
try:
dynsymTable[lastnull] = dynstr_section[lastnull:i].decode()
except SystemExit:
sys.exit(0)
except:
dynsymTable[lastnull] = dynstr_section[lastnull:i]
lastnull = i + 1

# parse .gnu.version_d table #
if e_shverdef >= 0:
Expand Down Expand Up @@ -105115,7 +105245,7 @@ def _printStrSect(sh_name, strtab):
and self.attr["sectionHeader"][".symtab"]["type"] != "NOBITS"
and self.attr["sectionHeader"][".strtab"]["type"] != "NOBITS"
):
# get .symtab section info #
# get .strtab section info #
(
sh_name,
sh_type,
Expand Down

0 comments on commit 0b90bf4

Please sign in to comment.