|  | #!/usr/bin/env python3 | 
|  | # Copyright 2021 The Chromium Authors. All rights reserved. | 
|  | # Use of this source code is governed by a BSD-style license that can be | 
|  | # found in the LICENSE file. | 
|  | """Extract physical addresses (PFNs) from /proc/PID/pagemap files. | 
|  |  | 
|  | This can be useful, for example, to compare whether two addresses from | 
|  | potentially two different processes map to the same physical page. The virtual | 
|  | addresses required for this script can be obtained from log messages, while the | 
|  | pagemap files can be copied or fetched from an Android device using `adb pull`. | 
|  | """ | 
|  |  | 
|  | import argparse | 
|  | import logging | 
|  | import os | 
|  | import struct | 
|  | import sys | 
|  |  | 
|  | _BYTES_PER_PAGEMAP_VALUE = 8 | 
|  | _BYTES_PER_OS_PAGE = 4096 | 
|  | _VIRTUAL_TO_PAGEMAP_OFFSET = _BYTES_PER_OS_PAGE // _BYTES_PER_PAGEMAP_VALUE | 
|  | _MASK_PRESENT = 1 << 63 | 
|  | _MASK_PFN = (1 << 55) - 1 | 
|  |  | 
|  |  | 
|  | def PrintPfn(fd, vaddr): | 
|  | os.lseek(fd, vaddr // _VIRTUAL_TO_PAGEMAP_OFFSET, os.SEEK_SET) | 
|  | buf = os.read(fd, _BYTES_PER_PAGEMAP_VALUE) | 
|  | if len(buf) < _BYTES_PER_PAGEMAP_VALUE: | 
|  | logging.error('Could not retrieve the pagemap entry') | 
|  | return False | 
|  | pagemap_values = struct.unpack( | 
|  | '=%dQ' % (len(buf) // _BYTES_PER_PAGEMAP_VALUE), buf) | 
|  | for pagemap_value in pagemap_values: | 
|  | if pagemap_value & _MASK_PRESENT: | 
|  | print(hex(pagemap_value & _MASK_PFN)) | 
|  | else: | 
|  | logging.error('Page not present: %s', hex(vaddr)) | 
|  | return False | 
|  | return True | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | logging.getLogger().setLevel(logging.INFO) | 
|  | parser = argparse.ArgumentParser() | 
|  | parser.add_argument('--pagemap-file', | 
|  | required=True, | 
|  | help='Path to a saved /proc/pid/pagemap file.') | 
|  | parser.add_argument('--vaddr', | 
|  | required=True, | 
|  | help='Virtual address (in hex) to inspect.') | 
|  | args = parser.parse_args() | 
|  | fd = os.open(args.pagemap_file, os.O_RDONLY) | 
|  | if not PrintPfn(fd, int(args.vaddr, 16)): | 
|  | return 1 | 
|  | return 0 | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | sys.exit(main()) |