Fix build id check for libraries.

The current build id check uses the file utility. Unfortunately, it
doesn't work on all systems. Replace with a call to llvm-readelf and
get the bitness and build id using that data.

Other small modifications:
- Only attempt to get the full path for a library once for each library.
- Do not add directories into the symbol libraries.

Test: Unit tests pass.
Test: Running using a libart.so that file doesn't understand and
Test: verifying the line numbers are correct.
Change-Id: I12c3d3c4599d201c3c01feeb48448fee6c633d71
diff --git a/scripts/stack_core.py b/scripts/stack_core.py
index 5eadef8..18a343f 100755
--- a/scripts/stack_core.py
+++ b/scripts/stack_core.py
@@ -60,6 +60,7 @@
   width = "{8}"
   spacing = ""
   apk_info = dict()
+  lib_to_path = dict()
 
   register_names = {
     "arm": "r0|r1|r2|r3|r4|r5|r6|r7|r8|r9|sl|fp|ip|sp|lr|pc|cpsr",
@@ -72,8 +73,9 @@
 
   # We use the "file" command line tool to extract BuildId from ELF files.
   ElfInfo = collections.namedtuple("ElfInfo", ["bitness", "build_id"])
-  file_tool_output = re.compile(r"ELF (?P<bitness>32|64)-bit .*"
-                                r"BuildID(\[.*\])?=(?P<build_id>[0-9a-f]+)")
+  readelf_output = re.compile(r"Class:\s*ELF(?P<bitness>32|64).*"
+                              r"Build ID:\s*(?P<build_id>[0-9a-f]+)",
+                              flags=re.DOTALL)
 
   def UpdateAbiRegexes(self):
     if symbol.ARCH == "arm64" or symbol.ARCH == "mips64" or symbol.ARCH == "x86_64":
@@ -317,14 +319,16 @@
   def GlobSymbolsDir(self, symbols_dir):
     files_by_basename = {}
     for path in sorted(pathlib.Path(symbols_dir).glob("**/*")):
+      if os.path.isdir(path):
+        next
       files_by_basename.setdefault(path.name, []).append(path)
     return files_by_basename
 
   # Use the "file" command line tool to find the bitness and build_id of given ELF file.
   @functools.lru_cache(maxsize=None)
   def GetLibraryInfo(self, lib):
-    stdout = subprocess.check_output(["file", lib], text=True)
-    match = self.file_tool_output.search(stdout)
+    stdout = subprocess.check_output([symbol.ToolPath("llvm-readelf"), "-h", "-n", lib], text=True)
+    match = self.readelf_output.search(stdout)
     if match:
       return self.ElfInfo(bitness=match.group("bitness"), build_id=match.group("build_id"))
     return None
@@ -339,6 +343,14 @@
     return None
 
   def GetLibPath(self, lib):
+    if lib in self.lib_to_path:
+      return self.lib_to_path[lib]
+
+    lib_path = self.FindLibPath(lib)
+    self.lib_to_path[lib] = lib_path
+    return lib_path
+
+  def FindLibPath(self, lib):
     symbol_dir = symbol.SYMBOLS_DIR
     if os.path.isfile(symbol_dir + lib):
       return lib
@@ -373,9 +385,9 @@
     # This is in vendor, look for the value in:
     #   /data/nativetest{64}/vendor/test_name/test_name
     if lib.startswith("/data/local/tests/vendor/"):
-       lib_path = os.path.join(test_dir + test_dir_bitness, "vendor", test_name, test_name)
-       if os.path.isfile(symbol_dir + lib_path):
-         return lib_path
+      lib_path = os.path.join(test_dir + test_dir_bitness, "vendor", test_name, test_name)
+      if os.path.isfile(symbol_dir + lib_path):
+        return lib_path
 
     # Look for the path in:
     #   /data/nativetest{64}/test_name/test_name