├── .gitignore ├── samples ├── arm-app.so └── arm64-app.so ├── assets ├── r2-metadata-1.png └── r2-metadata-2.png ├── darter ├── asm │ ├── _x64.py │ ├── _ia32.py │ ├── _arm.py │ ├── _arm64.py │ └── base.py ├── data │ ├── stub_code_list.json │ ├── runtime_offsets_parse.py │ ├── class_ids.json │ ├── base_objects.py │ └── type_data.py ├── read.py ├── other.py ├── constants.py ├── file.py └── clusters.py ├── utils ├── generate_imports.py ├── files │ ├── ca.crt │ ├── timemachine.crt │ └── timemachine.key ├── generate_metadata.py ├── timemachine_pub.js └── list_versions.ipynb ├── info ├── serialization-notes.md ├── info.md └── versions.md ├── README.md ├── 1-introduction.ipynb └── 2-playground.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | .ipynb_checkpoints 4 | 5 | /darter/settings.py 6 | -------------------------------------------------------------------------------- /samples/arm-app.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdw09/darter/HEAD/samples/arm-app.so -------------------------------------------------------------------------------- /samples/arm64-app.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdw09/darter/HEAD/samples/arm64-app.so -------------------------------------------------------------------------------- /assets/r2-metadata-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdw09/darter/HEAD/assets/r2-metadata-1.png -------------------------------------------------------------------------------- /assets/r2-metadata-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdw09/darter/HEAD/assets/r2-metadata-2.png -------------------------------------------------------------------------------- /darter/asm/_x64.py: -------------------------------------------------------------------------------- 1 | import capstone as cs 2 | from capstone.arm64 import * 3 | 4 | supports = lambda _, arch: arch == 'x64' 5 | make_engine = lambda _: cs.Cs(cs.CS_ARCH_X86, cs.CS_MODE_64) 6 | 7 | # TODO 8 | -------------------------------------------------------------------------------- /darter/asm/_ia32.py: -------------------------------------------------------------------------------- 1 | import capstone as cs 2 | from capstone.arm64 import * 3 | 4 | supports = lambda _, arch: arch == 'ia32' 5 | make_engine = lambda _: cs.Cs(cs.CS_ARCH_X86, cs.CS_MODE_32) 6 | 7 | # TODO 8 | -------------------------------------------------------------------------------- /utils/generate_imports.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # Generates a .dart main file that imports every possible 3 | # file from all packages in `.packages`, obeying some blacklists. 4 | 5 | import re 6 | import os 7 | 8 | # place your own 9 | BLACKLISTS = { 10 | 'sky_engine', 11 | ('intl', 'intl_browser.dart'), 12 | ('intl', 'date_symbol_data_http_request.dart'), 13 | ('matcher', 'mirror_matchers.dart'), ('quiver', 'mirrors.dart'), 14 | } 15 | SKIP_DIRS = { 'src' } 16 | 17 | with open('.packages') as f: 18 | ls = f.read().splitlines() 19 | 20 | ls = [ re.fullmatch(r'(.*?)(\#.*)?', x).group(1).strip() for x in ls ] 21 | ls = [ x for x in ls if x ] 22 | for x in ls: 23 | package, target = re.fullmatch(r'(\w+):(?:file://)?(.+)', x).groups() 24 | if target == 'lib/': continue # skip our own module 25 | if package in BLACKLISTS: continue 26 | 27 | for folder, dirs, files in os.walk(target): 28 | for d in SKIP_DIRS: 29 | if d in dirs: 30 | dirs.remove(d) 31 | for f in files: 32 | if not f.endswith('.dart'): continue 33 | f = os.path.relpath(os.path.join(folder, f), target) 34 | if (package, f) in BLACKLISTS: continue 35 | print(f"import 'package:{package}/{f}';") 36 | 37 | print('void main() {}') 38 | -------------------------------------------------------------------------------- /utils/files/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDSzCCAjOgAwIBAgIIOxYV4AuQqsYwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE 3 | AxMVbWluaWNhIHJvb3QgY2EgM2IxNjE1MCAXDTIwMTAxMTEyNDE1MVoYDzIxMjAx 4 | MDExMTI0MTUxWjAgMR4wHAYDVQQDExVtaW5pY2Egcm9vdCBjYSAzYjE2MTUwggEi 5 | MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVO2vsknZcj/z/vgCaK2tYjnQs 6 | HjiJDXWwBpCAkgWScPBbioSgzWk607KrVkMFRUGwKvnFk9o338DuHlz0l6Uq5bpb 7 | GexplMcn0QrqCyFLGb8W021gLftM1BRVR668vIvsPfUi29+rLEan2GX0YBjWQST4 8 | dxNYd9y/HCn2sQiY+JMbwP9JtNVy0l7H+3fI/iCkWKy3p49PEkGBkasmkW2Atrn8 9 | m8B56xGWwE0inygMnEM7EVt2mJbRVqFp2A2laiaOkGkGHm24bauC5nYzahdzWhfG 10 | CC7CHjHoDyvfT5qAefPcl5h3GTw84gdZNMWLULMomoLFP/oKlqoaCsiXwTDFAgMB 11 | AAGjgYYwgYMwDgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr 12 | BgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBQwIQHhXogQz6Sm 13 | 4YPHc7j0QMHM5jAfBgNVHSMEGDAWgBQwIQHhXogQz6Sm4YPHc7j0QMHM5jANBgkq 14 | hkiG9w0BAQsFAAOCAQEAQX+HRF/YGZ726CTaqJn/COBm/XlEdjdvvNEf18afsJ1Z 15 | 0niL0Phag9+KJwqGAn4orqo5vhZBxZjXyxs8PtyfJxX52nj1KHFhXBC+8H5/WVl4 16 | tdguJUh/ztyoQJSuNVdG4EVB+wcAFNCVugmfGzUFDqRXC1mEZsHX0ZxH5pdX+9NF 17 | zzXT49asQ03PjAxSwU0D5pDAV81obLCDE525zLzzC+ZNumORYeYlFX7cNiHRYVaY 18 | ERSHA5PIssdHiDtrs612+SgftcsvM1FDjy/mONuDITogLpgK0rzRYn6EcznkWHpL 19 | ZcQSSQG+XBZJKz0gfXwtR6HS3m5o9sipgymtoP94TA== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /utils/files/timemachine.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDRTCCAi2gAwIBAgIIKV5n2fU/ZScwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE 3 | AxMVbWluaWNhIHJvb3QgY2EgM2IxNjE1MB4XDTIwMTAxMTEyNDE1MVoXDTIyMTEx 4 | MDEzNDE1MVowGzEZMBcGA1UEAxMQcHViLmRhcnRsYW5nLm9yZzCCASIwDQYJKoZI 5 | hvcNAQEBBQADggEPADCCAQoCggEBALrei1hSZ7fEwvKdlmJ5g8vJJ2LAZuX7CUfh 6 | 5lnaNgts0vjqLnAtwcKBrh1We9L2pQHkifcr7T8ymyZplRxFhoCKXDyKKhPFmNXm 7 | Vk08HbtNb/OIfENmsSs+XXLk7Y7lnpVjHnoEt1VZcFl7Gm+OgmtRLStkuNOMNoE+ 8 | wNvK93NYVEhp5QHCiPvYVQHFXjig+Zto95I2qoxR5+/rp9QHw4y54AparNIwfXOe 9 | tCDhpeqT32T6WQU/S5gn6f75stwVPs2t1aW8MqNwQMZWsTw3j7L23U+/91HYlrZi 10 | 2OIyUXNvItfpHB1aCCAOwIqDQaxDpEGWd0n+0vyV27Nq3+FF0lMCAwEAAaOBhzCB 11 | hDAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC 12 | MAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUMCEB4V6IEM+kpuGDx3O49EDBzOYw 13 | JAYDVR0RBB0wG4IQcHViLmRhcnRsYW5nLm9yZ4IHcHViLmRldjANBgkqhkiG9w0B 14 | AQsFAAOCAQEANkkB5uhqzKoL/i7Nklv1hKNp+P6fvUize//tzwRBBN/YEDRUCZtA 15 | 60zgsgIO9fJ4Xr+oOpxq4OlLcqN56VY/4Yfaw4O2NaKxYK5PYyX5CNEY8tE8Am0k 16 | JP/xxnI0mtqfoXuki1cra/4dqVJRMtansKZ+63bAqJuhOFZjPrF7Nj019kRPgRj1 17 | 8FAZXiuzc7LA042oSpImPQEXpS7zdEBt/L1/NdktC+J1w3JpFB14S3dVD+NuTQo9 18 | 3tXC1TlVqXGkcAN1MTL6e9/HPyXEliF782bdMGaw1RzhMbcrQT0Rl9bqpR8NnyWO 19 | uyLNt43LAroGaVk9Ami9hOgLXvt7V2Gu5A== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /darter/data/stub_code_list.json: -------------------------------------------------------------------------------- 1 | ["GetCStackPointer","JumpToFrame","RunExceptionHandler","DeoptForRewind","WriteBarrier","WriteBarrierWrappers","ArrayWriteBarrier","PrintStopMessage","AllocateArray","AllocateContext","CallToRuntime","LazyCompile","InterpretCall","CallBootstrapNative","CallNoScopeNative","CallAutoScopeNative","FixCallersTarget","CallStaticFunction","OptimizeFunction","InvokeDartCode","InvokeDartCodeFromBytecode","DebugStepCheck","UnlinkedCall","MonomorphicMiss","SingleTargetCall","ICCallThroughCode","MegamorphicCall","FixAllocationStubTarget","Deoptimize","DeoptimizeLazyFromReturn","DeoptimizeLazyFromThrow","UnoptimizedIdenticalWithNumberCheck","OptimizedIdenticalWithNumberCheck","ICCallBreakpoint","UnoptStaticCallBreakpoint","RuntimeCallBreakpoint","OneArgCheckInlineCache","TwoArgsCheckInlineCache","SmiAddInlineCache","SmiLessInlineCache","SmiEqualInlineCache","OneArgOptimizedCheckInlineCache","TwoArgsOptimizedCheckInlineCache","ZeroArgsUnoptimizedStaticCall","OneArgUnoptimizedStaticCall","TwoArgsUnoptimizedStaticCall","Subtype1TestCache","Subtype2TestCache","Subtype4TestCache","Subtype6TestCache","DefaultTypeTest","TopTypeTypeTest","TypeRefTypeTest","UnreachableTypeTest","SlowTypeTest","LazySpecializeTypeTest","CallClosureNoSuchMethod","FrameAwaitingMaterialization","AsynchronousGapMarker","NullErrorSharedWithFPURegs","NullErrorSharedWithoutFPURegs","StackOverflowSharedWithFPURegs","StackOverflowSharedWithoutFPURegs","OneArgCheckInlineCacheWithExactnessCheck","OneArgOptimizedCheckInlineCacheWithExactnessCheck","EnterSafepoint","ExitSafepoint","VerifyCallback","CallNativeThroughSafepoint"] -------------------------------------------------------------------------------- /darter/read.py: -------------------------------------------------------------------------------- 1 | # READING PRIMITIVES 2 | 3 | from struct import unpack, pack 4 | 5 | 6 | def readcstr(f): 7 | buf = bytes() 8 | while True: 9 | b = f.read(1) 10 | if b == None: raise Exception('Unexpected EOF') 11 | if b[0] == 0: 12 | return bytes(buf) 13 | buf += b 14 | 15 | def readuint(f, bits=64, signed=False): 16 | if bits == 8: return unpack('b' if signed else 'B', f.read(1))[0] 17 | x = 0; s = 0 18 | while True: 19 | b = f.read(1)[0] 20 | if b & 0x80: break 21 | x |= (b & 0x7F) << s 22 | s += 7 23 | x |= (b - (0xc0 if signed else 0x80)) << s 24 | assert s < bits 25 | #assert x.bit_length() <= bits # stronger assertion (not actually made in dart) 26 | if x.bit_length() > bits: 27 | print('--> Int {} longer than {} bits'.format(x, bits)) 28 | return x 29 | 30 | def readint(f, bits=64): 31 | return readuint(f, bits, signed=True) 32 | 33 | readcid = lambda f: readint(f, 32) 34 | read1 = lambda f: { 0: False, 1: True}[f.read(1)[0]] 35 | readtokenposition = lambda f: readint(f, 32) 36 | 37 | # FIXME: verify these functions work correctly, then change 70 to 64 38 | readfloat = lambda f: unpack(' 4 | 5 | import sys 6 | from os.path import dirname 7 | sys.path.append(dirname(dirname(__file__))) 8 | from darter.file import parse_elf_snapshot 9 | from darter.asm.base import populate_native_references 10 | from collections import defaultdict 11 | from base64 import b64encode 12 | 13 | snapshot_file = sys.argv[1] 14 | metadata_out = snapshot_file + '.meta.r2' 15 | 16 | print('[Loading snapshot]') 17 | s = parse_elf_snapshot(snapshot_file) 18 | 19 | print('[Analyzing code]') 20 | populate_native_references(s) 21 | 22 | print('[Generating metadata]') 23 | 24 | do_b64 = lambda x: 'base64:' + b64encode(x.encode('utf-8')).decode('ascii') 25 | 26 | def produce_metadata(f, snapshot): 27 | comments = defaultdict(lambda: []) 28 | print('fs functions', file=f) 29 | for code in snapshot.getrefs('Code'): 30 | instr = code.x['instructions'] 31 | name = 'c_{}'.format(code.ref) 32 | comment = ' '.join(map(str, code.locate())) 33 | print('f {name} {len} {addr} {c}'.format( name=name, len=len(instr['data']), addr=instr['data_addr'], c=do_b64(comment) ), file=f) 34 | for target, pc, kind, *args in code.x.get('nrefs', []): 35 | if kind == 'load': 36 | comments[pc].append( 'load: {reg} = {tg}'.format(tg=target.describe(), reg=args[0]) ) 37 | for addr, lines in comments.items(): 38 | print('CCu {} @ {}'.format( do_b64("\n".join(lines)), addr ), file=f) 39 | 40 | with open(metadata_out, 'w') as f: produce_metadata(f, s) 41 | 42 | -------------------------------------------------------------------------------- /utils/files/timemachine.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAut6LWFJnt8TC8p2WYnmDy8knYsBm5fsJR+HmWdo2C2zS+Oou 3 | cC3BwoGuHVZ70valAeSJ9yvtPzKbJmmVHEWGgIpcPIoqE8WY1eZWTTwdu01v84h8 4 | Q2axKz5dcuTtjuWelWMeegS3VVlwWXsab46Ca1EtK2S404w2gT7A28r3c1hUSGnl 5 | AcKI+9hVAcVeOKD5m2j3kjaqjFHn7+un1AfDjLngClqs0jB9c560IOGl6pPfZPpZ 6 | BT9LmCfp/vmy3BU+za3Vpbwyo3BAxlaxPDePsvbdT7/3UdiWtmLY4jJRc28i1+kc 7 | HVoIIA7AioNBrEOkQZZ3Sf7S/JXbs2rf4UXSUwIDAQABAoIBAQCqiiMdbcceHUHU 8 | 7SCrqj4votqA8xp1VI75vR3ebpjYAG7hn8bVtcyY2ISwXZcPxux8N7f5jWstO+AA 9 | RGdCr+i6MotO7MDN3o1G2Ll2LyDrKRa8aJkPYg7aakYabvambMQ3jYfoz5kJGWDB 10 | KOnNWX+ItULF0VwkJFgxx7V2YVtHNYu86QGsPCpGT4gqMpCOOHM3CmsfX2VR30+M 11 | 092v5XKY/AEZydl4DumPWFugJLSMR7kRwOi5Dz/lfvtooLSPyEnGq0Btc+m8GM3e 12 | ojlwAuwbxl3SSXA3ZDDDOqn042ljmrN8qpjqm6XKRv6OB112lOFpCMT77S9nTlLQ 13 | 1HU2dbrRAoGBAOU38NyPmZ64Rih79HS0lSZ3MBEQ3twNOOcQiJrzH1WMdIgz3eBX 14 | DyX1nuHnl6LPFauCGJt95R0e1/nreMQrJo39sSZBZxRrzAl36sQ83MWNvdBQNa83 15 | /GorXDcBAjQQ3PwnHfL0rSemC/mbYyeANlUKzEOX/JvuZjbaqvlasgsfAoGBANCz 16 | 6gLeJoZI1iRMG2Sye3bCZ1cRQBdbhr3/csGq4v3f4ldb/fhuQPLUL6lNf7emxSvp 17 | AKrMcZca3qUhlcfQ60tIEYrPZX9lO2kjmhMmfc9UfdgeGHW5gIIU3Crf0ivbPQ5A 18 | 1MsF6uDhF6KkWaxieCBsSrYRYTryxKevDggNkkZNAoGAQvCCS7c4Eq0X+AyzCD4Q 19 | bKpUBDfcDYFjA7/FVRL5LJ+XDIuxGSnzh49idfCPTQGS/4I/s3Ehrs5qHldQ7mD3 20 | onHbASQKeWE9teoqSmLHyn2pWCOEegYslcjR5lJKFXBzSMQXr/MaekC4sI1beJat 21 | x0oe+wJwyAzoFs7nLWj6I9sCgYEAiAkduazP86u6WYJ6QJLOQ0TIU0pdOlxgYawG 22 | QxJ+TvkF8ed2jd5f22hPgM2ROEYuM0b3Nl14lQPgqEZR+SLGnBVnydER2Y5EwW2B 23 | WoY2b1Qlix9i1imKCEBMmm218vw+ufGsQoGi7d4Kp+r0wJaWspujvzVo6SGg6aQD 24 | Q2CFPekCgYBXuh0u3nI7Ptk/8/Xj2wcnhz1ntfJ0Eh5QkRSlMIV1bfli7Gx8oB+n 25 | RHQNggtjb6d7YePRAO6wOxIjAja0NinVh28rmsatmT4lkqaKNnuJpvdAXyG4am6I 26 | vtwp6qXewTar0pHkWz7eTre3qeJf0F1vcoAR+y/CWOezk8kEapsV+A== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /darter/data/runtime_offsets_parse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # This script parses the offset at runtime/vm/compiler/runtime_offsets_extracted.h 3 | # file and produces runtime_offsets.json 4 | 5 | from collections import OrderedDict 6 | from sys import stdin, stdout 7 | import json 8 | import re 9 | 10 | mappings = { 11 | 'defined(target_arch_arm)': 'arm', 12 | 'defined(target_arch_x64)': 'x64', 13 | 'defined(target_arch_ia32)': 'ia32', 14 | 'defined(target_arch_arm64)': 'arm64', 15 | 'defined(target_arch_dbc) && defined(target_arch_is_64_bit)': 'dbc-64', 16 | 'defined(target_arch_dbc) && defined(target_arch_is_32_bit)': 'dbc-32', 17 | } 18 | 19 | content = stdin.read() 20 | archs = OrderedDict() 21 | values = None 22 | while True: 23 | if m := re.match(r'\s*\#if ([^\n]+)\n', content): 24 | content = content[m.end():] 25 | cond = m.group(1).lower() 26 | assert cond not in archs 27 | assert values is None 28 | archs[mappings[cond]] = values = OrderedDict() 29 | elif m := re.match(r'\s*\#endif // ([^\n]+)\n', content): 30 | content = content[m.end():] 31 | assert values != None 32 | values = None 33 | elif m := re.match(r'\s*static constexpr dart::compiler::target::word\s+(?P\w+)\s*=\s*(?P-?\d+)\s*;', content): 34 | content = content[m.end():] 35 | values[m.group('name')] = int(m.group('value'), 0) 36 | elif m := re.match(r'\s*static constexpr dart::compiler::target::word\s+(?P\w+)\[\]\s*=\s*\{\s*(?P(-?\d+\s*\,\s*)+-?\d+)\s*\}\s*;', content): 37 | content = content[m.end():] 38 | values[m.group('name')] = [ int(x, 0) for x in m.group('value').split(',') ] 39 | else: 40 | break 41 | 42 | if content.strip(): 43 | raise Exception('Couldn\'t parse: {}'.format(repr(content))) 44 | 45 | json.dump(archs, stdout, indent=4) 46 | print() 47 | -------------------------------------------------------------------------------- /darter/other.py: -------------------------------------------------------------------------------- 1 | # Parsing for other substructures (PcDescriptors, CodeSourceMap, etc.) 2 | 3 | import io 4 | 5 | from .read import readint, read_uleb128 6 | 7 | 8 | def parse_pc_descriptors(data): 9 | f = io.BytesIO(data) 10 | def elem(): 11 | x = {} 12 | merged_kind = readuleb128(f) # FIXME: should be signed 13 | x['kind'] = kPcDescriptorKindBits[merged_kind & 0b111][0] 14 | x['try_index'] = merged_kind >> 3 15 | x['pc_offset'] = readuleb128(f) 16 | if kind == kkKind['kFullAOT']: # FIXME: check meaning of FLAG_precompiled_mode 17 | x['deopt_id'] = readuleb128(f) 18 | x['token_pos'] = readuleb128(f) 19 | return x 20 | els = [] 21 | while f.tell() < len(data): els.append(elem()) 22 | return els 23 | 24 | # runtime/vm/dwarf.cc and runtime/vm/code_descriptors.cc 25 | kCodeSourceMapOpCodes = ['kChangePosition', 'kAdvancePC', 'kPushFunction', 'kPopFunction', 'kNullCheck'] 26 | kkCodeSourceMapOpCodes = {k: v for v, k in enumerate(kCodeSourceMapOpCodes)} 27 | 28 | def parse_code_source_map(data): 29 | f = io.BytesIO(data) 30 | ops = [] 31 | while f.tell() < len(data): 32 | opcode = readint(f, 9) # <- FIXME: should be uint and 8... 33 | op = kCodeSourceMapOpCodes[opcode] if opcode < len(kCodeSourceMapOpCodes) else None 34 | if opcode == kkCodeSourceMapOpCodes['kChangePosition']: 35 | ops.append((op, readint(f, 32))) 36 | elif opcode == kkCodeSourceMapOpCodes['kAdvancePC']: 37 | ops.append((op, readint(f, 32))) 38 | elif opcode == kkCodeSourceMapOpCodes['kPushFunction']: 39 | ops.append((op, readint(f, 32))) 40 | elif opcode == kkCodeSourceMapOpCodes['kPopFunction']: 41 | ops.append((op, )) 42 | elif opcode == kkCodeSourceMapOpCodes['kNullCheck']: 43 | ops.append((op, readint(f,32))) 44 | else: raise Exception('Unknown opcode {}'.format(opcode)) 45 | return ops 46 | -------------------------------------------------------------------------------- /darter/asm/_arm64.py: -------------------------------------------------------------------------------- 1 | import re 2 | import capstone as cs 3 | from capstone.arm64 import * 4 | 5 | supports = lambda _, arch: arch == 'arm64' 6 | make_engine = lambda _: cs.Cs(cs.CS_ARCH_ARM64, cs.CS_MODE_ARM) 7 | 8 | find_register = lambda op, reg: re.search(r'(\W|^)' + reg + r'(\W|$)', op[3], flags=re.ASCII) 9 | int_opt = lambda x: 0 if x is None else int(x, 0) 10 | 11 | def match_nref(ops, i): 12 | if find_register(ops[i], 'x27'): 13 | res = match_loadobj(ops, i) 14 | if res is None: 15 | # print('Couldn\'t extract 0x{:x}: {} {}'.format(ops[i][0], ops[i][2], ops[i][3])) # FIXME: proper logging 16 | return 17 | i, offset, reg = res 18 | div, mod = divmod(offset, 8) 19 | assert mod == 0 20 | return i, 'load', div - 2, reg 21 | if ops[i][2] == 'bl': 22 | return i+1, 'call', int(ops[i][3][1:], 0) 23 | 24 | def match_loadobj(ops, i): 25 | m = None 26 | def match(name, pattern, func=lambda: True, mov=1): 27 | nonlocal m, i 28 | if not (0 <= i < len(ops) and ops[i][2] == name): return 29 | r = re.fullmatch(pattern, ops[i][3], flags=re.ASCII) 30 | if r is None: return 31 | m = r.groups() 32 | if not func(): return 33 | i += mov 34 | return True 35 | 36 | if match('add', r'(\w+), x27, \1'): 37 | src = m[0] 38 | if not match('ldr', r'(\w+), \[(\w+)\]', lambda: m[1] == src): return 39 | target = m[0] 40 | orig_i, i = i, i-3 41 | if match('orr', r'(\w+), xzr, #(\w+)', lambda: m[0] == src, -1): 42 | return orig_i, int(m[1], 0), target 43 | offset = 0 44 | if match('movk', r'(\w+), #(\w+), lsl #16', lambda: m[0] == src, -1): 45 | offset = int(m[1], 0) << 16 46 | if not match('movz', r'(\w+), #(\w+)', lambda: m[0] == src, -1): return 47 | offset |= int(m[1], 0) 48 | return orig_i, offset, target 49 | 50 | src, offset = 'x27', 0 51 | while match('add', r'(\w+), (\w+), #(\w+)(, lsl #(\d+))?', lambda: m[1] == src): 52 | offset += int(m[2], 0) << int_opt(m[4]) 53 | src = m[0] 54 | if match('ldr', r'(\w+), \[(\w+)(, #(\w+))?\]', lambda: m[1] == src): 55 | offset += int_opt(m[3]) 56 | return i, offset, m[0] 57 | if match('ldp', r'x5, x30, \[(\w+)(, #(\w+))?\]', lambda: m[0] == src): 58 | offset += int_opt(m[2]) 59 | if not match('blr', 'x30'): return 60 | return i, offset, 'call' 61 | -------------------------------------------------------------------------------- /darter/data/class_ids.json: -------------------------------------------------------------------------------- 1 | ["Illegal", "Stack", "kFreeListElement", "kForwardingCorpse", "Object", "Class", "PatchClass", "Function", "ClosureData", "SignatureData", "RedirectionData", "FfiTrampolineData", "Field", "Script", "Library", "Namespace", "KernelProgramInfo", "Code", "Bytecode", "Instructions", "ObjectPool", "PcDescriptors", "CodeSourceMap", "StackMap", "LocalVarDescriptors", "ExceptionHandlers", "Context", "ContextScope", "ParameterTypeCheck", "SingleTargetCache", "UnlinkedCall", "ICData", "MegamorphicCache", "SubtypeTestCache", "Error", "ApiError", "LanguageError", "UnhandledException", "UnwindError", "Instance", "LibraryPrefix", "TypeArguments", "AbstractType", "Type", "TypeRef", "TypeParameter", "Closure", "Number", "Integer", "Smi", "Mint", "Double", "Bool", "GrowableObjectArray", "Float32x4", "Int32x4", "Float64x2", "TypedDataBase", "TypedData", "ExternalTypedData", "TypedDataView", "Pointer", "DynamicLibrary", "Capability", "ReceivePort", "SendPort", "StackTrace", "RegExp", "WeakProperty", "MirrorReference", "LinkedHashMap", "UserTag", "TransferableTypedData", "Array", "ImmutableArray", "String", "OneByteString", "TwoByteString", "ExternalOneByteString", "ExternalTwoByteString", "FfiPointer", "FfiNativeFunction", "FfiInt8", "FfiInt16", "FfiInt32", "FfiInt64", "FfiUint8", "FfiUint16", "FfiUint32", "FfiUint64", "FfiIntPtr", "FfiFloat", "FfiDouble", "FfiVoid", "FfiNativeType", "FfiDynamicLibrary", "FfiStruct", "TypedDataInt8Array", "TypedDataInt8ArrayView", "ExternalTypedDataInt8Array", "TypedDataUint8Array", "TypedDataUint8ArrayView", "ExternalTypedDataUint8Array", "TypedDataUint8ClampedArray", "TypedDataUint8ClampedArrayView", "ExternalTypedDataUint8ClampedArray", "TypedDataInt16Array", "TypedDataInt16ArrayView", "ExternalTypedDataInt16Array", "TypedDataUint16Array", "TypedDataUint16ArrayView", "ExternalTypedDataUint16Array", "TypedDataInt32Array", "TypedDataInt32ArrayView", "ExternalTypedDataInt32Array", "TypedDataUint32Array", "TypedDataUint32ArrayView", "ExternalTypedDataUint32Array", "TypedDataInt64Array", "TypedDataInt64ArrayView", "ExternalTypedDataInt64Array", "TypedDataUint64Array", "TypedDataUint64ArrayView", "ExternalTypedDataUint64Array", "TypedDataFloat32Array", "TypedDataFloat32ArrayView", "ExternalTypedDataFloat32Array", "TypedDataFloat64Array", "TypedDataFloat64ArrayView", "ExternalTypedDataFloat64Array", "TypedDataFloat32x4Array", "TypedDataFloat32x4ArrayView", "ExternalTypedDataFloat32x4Array", "TypedDataInt32x4Array", "TypedDataInt32x4ArrayView", "ExternalTypedDataInt32x4Array", "TypedDataFloat64x2Array", "TypedDataFloat64x2ArrayView", "ExternalTypedDataFloat64x2Array", "ByteDataView", "ByteBuffer", "Null", "Dynamic", "Void"] -------------------------------------------------------------------------------- /info/serialization-notes.md: -------------------------------------------------------------------------------- 1 | ### Internals & deserialization 2 | 3 | runtime/vm/class_id.h specifies a list of class IDs -> preprocess and extract the enum 4 | 5 | Header { 6 | int32 magicValue = 0xdcdcf5f5 7 | int64 length -> length of snapshot data in bytes, not including magic value 8 | int64 kind 9 | } 10 | 11 | IncludesCode: FullJIT, FullAOT 12 | IncludesBytecode: Full, FullJIT 13 | the code (if any) starts after the snapshot data (rounded up to kMaxObjectAlignment) 14 | 15 | after header comes: SnapshotHeader { 16 | version string (32 bytes) 17 | features string (nul terminated) 18 | } 19 | 20 | CIDs are int32 21 | tags is a uint8 22 | 23 | except Header, the rest of the ints/uints longer than 8 bits are encoded using SLEB128, 24 | but inverted (high bit set to 1 to terminate int) 25 | 26 | objects are clustered by class -> type information written once per class 27 | 28 | 2 major sections: 29 | - how to allocate objects 30 | - how to initialize them 31 | 32 | allocation section is read for each cluster 33 | then initialization is read 34 | then finally a 'roots' section which initializes the ObjectStore 35 | 36 | 37 | ### Instructions layout 38 | 39 | #### Registers 40 | 41 | r5 holds the Data() of the global_object_pool minus one byte. 42 | so, to load object 9188 of the pool into r4, we do: 43 | `Data() + 4 * 9188` -> `Data() + 0x8f90` -> `r5 + 0x8f8f` -> instructions: 44 | 45 | ~~~ 46 | 024985e2 add r4, r5, 0x8000 47 | 8f4f94e5 ldr r4, [r4, 0xf8f] 48 | ~~~ 49 | 50 | #### Entry point 51 | 52 | A RawInstructions object has 4 entry points (depending on checked/unchecked, polymorphic/monomorphic). 53 | 54 | - The entry point is `data_pos + (appropriate constant from below)` 55 | For ARM + AOT: 0 for monomorphic, 20 for polymorphic 56 | - If it's unchecked, the `unchecked_entrypoint_pc_offset` field should be added too 57 | 58 | 59 | ~~~ 60 | #if defined(TARGET_ARCH_IA32) 61 | static const intptr_t kMonomorphicEntryOffsetJIT = 6; 62 | static const intptr_t kPolymorphicEntryOffsetJIT = 34; 63 | static const intptr_t kMonomorphicEntryOffsetAOT = 0; 64 | static const intptr_t kPolymorphicEntryOffsetAOT = 0; 65 | #elif defined(TARGET_ARCH_X64) 66 | static const intptr_t kMonomorphicEntryOffsetJIT = 8; 67 | static const intptr_t kPolymorphicEntryOffsetJIT = 40; 68 | static const intptr_t kMonomorphicEntryOffsetAOT = 8; 69 | static const intptr_t kPolymorphicEntryOffsetAOT = 32; 70 | #elif defined(TARGET_ARCH_ARM) 71 | static const intptr_t kMonomorphicEntryOffsetJIT = 0; 72 | static const intptr_t kPolymorphicEntryOffsetJIT = 40; 73 | static const intptr_t kMonomorphicEntryOffsetAOT = 0; 74 | static const intptr_t kPolymorphicEntryOffsetAOT = 20; 75 | #elif defined(TARGET_ARCH_ARM64) 76 | static const intptr_t kMonomorphicEntryOffsetJIT = 8; 77 | static const intptr_t kPolymorphicEntryOffsetJIT = 48; 78 | static const intptr_t kMonomorphicEntryOffsetAOT = 8; 79 | static const intptr_t kPolymorphicEntryOffsetAOT = 28; 80 | #elif defined(TARGET_ARCH_DBC) 81 | static const intptr_t kMonomorphicEntryOffsetJIT = 0; 82 | static const intptr_t kPolymorphicEntryOffsetJIT = 0; 83 | static const intptr_t kMonomorphicEntryOffsetAOT = 0; 84 | static const intptr_t kPolymorphicEntryOffsetAOT = 0; 85 | #else 86 | ~~~ 87 | -------------------------------------------------------------------------------- /darter/data/base_objects.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from ..constants import kClassId, kkClassId, kStubCodeList, kCachedICDataArrayCount, kCachedDescriptorCount 4 | 5 | class_cids = [ n for n in range(kkClassId['Class'], kkClassId['Instance']) if n != kkClassId['Error'] ] 6 | class_cids += [ kkClassId['Dynamic'], kkClassId['Void'] ] 7 | 8 | # tuples are (original_code, type, value[, additional_fields]) 9 | make_base_entries = lambda includes_code: [ 10 | ("Object::null()", "Null", "null"), 11 | ("Object::sentinel().raw()", "Null", "sentinel"), 12 | ("Object::transition_sentinel().raw()", "Null", "transition_sentinel"), 13 | ("Object::empty_array().raw()", "Array", ""), 14 | ("Object::zero_array().raw()", "Array", ""), 15 | ("Object::dynamic_type().raw()", "Type", ""), 16 | ("Object::void_type().raw()", "Type", ""), 17 | ("Object::empty_type_arguments().raw()", "TypeArguments", "[]"), 18 | ("Bool::True().raw()", "bool", "true"), 19 | ("Bool::False().raw()", "bool", "false"), 20 | ("Object::extractor_parameter_types().raw()", "Array", ""), 21 | ("Object::extractor_parameter_names().raw()", "Array", ""), 22 | ("Object::empty_context_scope().raw()", "ContextScope", ""), 23 | ("Object::empty_descriptors().raw()", "PcDescriptors", ""), 24 | ("Object::empty_var_descriptors().raw()", "LocalVarDescriptors", ""), 25 | ("Object::empty_exception_handlers().raw()", "ExceptionHandlers", ""), 26 | ("Object::implicit_getter_bytecode().raw()", "Bytecode", ""), 27 | ("Object::implicit_setter_bytecode().raw()", "Bytecode", ""), 28 | ("Object::implicit_static_getter_bytecode().raw()", "Bytecode", ""), 29 | ("Object::method_extractor_bytecode().raw()", "Bytecode", ""), 30 | ("Object::invoke_closure_bytecode().raw()", "Bytecode", ""), 31 | ("Object::invoke_field_bytecode().raw()", "Bytecode", ""), 32 | ("Object::nsm_dispatcher_bytecode().raw()", "Bytecode", ""), 33 | ("Object::dynamic_invocation_forwarder_bytecode().raw()", "Bytecode", ""), 34 | *( ("ArgumentsDescriptor::cached_args_descriptors_[i]", "ArgumentsDescriptor", "".format(i)) for i in range(kCachedDescriptorCount) ), 35 | *( ("ICData::cached_icdata_arrays_[i]", "Array", "".format(i)) for i in range(kCachedICDataArrayCount) ), 36 | *( ("class_table()->At(cid)", "Class", kClassId[cid], { 'cid': cid }) for cid in class_cids ), # Adapted 37 | *( ( ("StubCode::EntryAt(i).raw()", "Code", "".format(i)) for i in kStubCodeList ) if not includes_code else [] ), 38 | ] 39 | 40 | def init_base_objects(Ref, snapshot, includes_code): 41 | tmp_cluster = { 'handler': 'BaseObject', 'cid': 'BaseObject' } 42 | entries = make_base_entries(includes_code) 43 | get_data = lambda e: { 'type': e[1], 'value': e[2], **(e[3] if len(e) > 3 else {}) } 44 | # ref 0 is illegal 45 | snapshot.refs = { i+1: Ref(snapshot, i+1, tmp_cluster, get_data(entry)) 46 | for i, entry in enumerate(entries) } 47 | snapshot.refs['next'] = len(entries) + 1 48 | snapshot.base_clusters = [] 49 | -------------------------------------------------------------------------------- /darter/constants.py: -------------------------------------------------------------------------------- 1 | # CONSTANTS 2 | 3 | import json 4 | import os.path 5 | 6 | 7 | EXPECTED_VERSION = 'c8562f0ee0ebc38ba217c7955956d1cb' 8 | 9 | MAGIC_VALUE = 0xdcdcf5f5 10 | 11 | kSectionMarker = 0xABAB 12 | 13 | kMaxPreferredCodeAlignment = 32 14 | 15 | # as an exception, kClassId names are stripped of k- and -Cid (except items 2 and 3: kFreeListElement, kForwardingCorpse) 16 | with open(os.path.join(os.path.dirname(__file__), 'data', 'class_ids.json')) as f: 17 | kClassId = json.load(f) 18 | kkClassId = { k: v for (v, k) in enumerate(kClassId) } 19 | assert len(kClassId) == len(kkClassId) 20 | # kNumPredefinedCids is not included in kClassIds 21 | kNumPredefinedCids = len(kClassId) 22 | kTypedDataInt8ArrayCid = kkClassId['TypedDataInt8Array'] 23 | kByteDataViewCid = kkClassId['ByteDataView'] 24 | 25 | kTypedDataCidRemainderInternal = 0 26 | kTypedDataCidRemainderView = 1 27 | kTypedDataCidRemainderExternal = 2 28 | 29 | kDataSerializationAlignment = 8 30 | 31 | kEntryType = [ 'kTaggedObject', 'kImmediate', 'kNativeFunction', 'kNativeFunctionWrapper', 'kNativeEntryData' ] 32 | kkEntryType = { k: v for (v, k) in enumerate(kEntryType) } 33 | decode_object_entry_type_bits = lambda x: { "patchable": not (x >> 7), "type": x & 0x7F } 34 | 35 | __isBase = lambda x, r: \ 36 | (kTypedDataInt8ArrayCid <= x < kByteDataViewCid) and (x - kTypedDataInt8ArrayCid) % 3 == r 37 | isTypedData = lambda x: __isBase(x, kTypedDataCidRemainderInternal) 38 | isTypedDataView = lambda x: __isBase(x, kTypedDataCidRemainderView) or x == kByteDataViewCid 39 | isExternalTypedData = lambda x: __isBase(x, kTypedDataCidRemainderExternal) 40 | 41 | kKind = [ 42 | ('kFull', "Full snapshot of core libraries or an application"), 43 | ('kFullJIT', "Full + JIT code"), 44 | ('kFullAOT', "Full + AOT code"), 45 | ('kMessage', "A partial snapshot used only for isolate messaging"), 46 | ('kNone', "gen_snapshot"), 47 | ('kInvalid', None), 48 | ] 49 | kkKind = { k[0]: v for (v, k) in enumerate(kKind) } 50 | 51 | kPcDescriptorKindBits = [ 52 | ('deopt', 'Deoptimization continuation point.'), 53 | ('icCall', 'IC call.'), 54 | ('unoptStaticCall', 'Call to a known target via stub.'), 55 | ('runtimeCall', 'Runtime call.'), 56 | ('osrEntry', 'OSR entry point in unopt. code.'), 57 | ('rewind', 'Call rewind target address.'), 58 | ('other', None), 59 | ] 60 | kkPcDescriptorKindBits = { k[0]: v for (v, k) in enumerate(kPcDescriptorKindBits) } 61 | 62 | with open(os.path.join(os.path.dirname(__file__), 'data', 'stub_code_list.json')) as f: 63 | kStubCodeList = json.load(f) 64 | 65 | with open(os.path.join(os.path.dirname(__file__), 'data', 'runtime_offsets.json')) as f: 66 | kRuntimeOffsets = json.load(f) 67 | 68 | # runtime/vm/dart_entry.h 69 | kCachedDescriptorCount = 32 70 | # runtime/vm/object.h 71 | kCachedICDataArrayCount = 4 72 | 73 | 74 | ### Entry points 75 | 76 | # tuples are (kMonomorphicEntryOffset, kPolymorphicEntryOffset) 77 | kEntryOffsets = { 78 | 'ia32': ( 79 | (6, 34), # JIT 80 | (0, 0), # AOT 81 | ), 82 | 'x64': ( 83 | (8, 40), # JIT 84 | (8, 32), # AOT 85 | ), 86 | 'arm': ( 87 | (0, 40), # JIT 88 | (0, 20), # AOT 89 | ), 90 | 'arm64': ( 91 | (8, 48), # JIT 92 | (8, 28), # AOT 93 | ), 94 | 'dbc': ( 95 | (0, 0), # JIT 96 | (0, 0), # AOT 97 | ), 98 | } 99 | 100 | ### AppJIT blob wrapping 101 | 102 | kAppJITMagic = 0xf6f6dcdc 103 | kAppSnapshotPageSize = 4 * 1024 104 | 105 | ### AppAOT blob wrapping 106 | 107 | kAppAOTSymbols = [ 108 | '_kDartVmSnapshotData', 109 | '_kDartVmSnapshotInstructions', 110 | '_kDartIsolateSnapshotData', 111 | '_kDartIsolateSnapshotInstructions' 112 | ] 113 | -------------------------------------------------------------------------------- /darter/file.py: -------------------------------------------------------------------------------- 1 | # FILE: Stores top level logic to unwrap blobs from a snapshot file and parse them 2 | 3 | from struct import unpack 4 | 5 | from .constants import kAppAOTSymbols, kAppJITMagic, kAppSnapshotPageSize 6 | from .core import Snapshot 7 | 8 | has_elftools = False 9 | try: 10 | from elftools.elf.elffile import ELFFile 11 | from elftools.elf.sections import SymbolTableSection 12 | has_elftools = True 13 | except ImportError as e: 14 | pass 15 | 16 | 17 | def parse_elf_snapshot(fname, **kwargs): 18 | ''' Open and parse an ELF (executable) AppAOT snapshot. Note that the reported 19 | offsets are virtual addresses, not physical ones. Returns isolate snapshot. 20 | NOTE: This method requires pyelftools ''' 21 | log = lambda n, x: print(x) if kwargs.get('print_level', 3) >= n else None 22 | if not has_elftools: 23 | raise Exception('pyelftools not found, install it to use this method') 24 | 25 | # Open file, obtain symbols 26 | f = ELFFile(open(fname, 'rb')) 27 | sections = list(f.iter_sections()) 28 | tables = [ s for s in sections if isinstance(s, SymbolTableSection) ] 29 | symbols = { sym.name: sym.entry for table in tables for sym in table.iter_symbols() } 30 | 31 | # Extract blobs 32 | blobs, offsets = [], [] 33 | for s in kAppAOTSymbols: 34 | s = symbols[s] 35 | section = next(S for S in sections if 0 <= s.st_value - S['sh_addr'] < S.data_size) 36 | blob = section.data()[(s.st_value - section['sh_addr']):][:s.st_size] 37 | assert len(blob) == s.st_size 38 | blobs.append(blob), offsets.append(s.st_value) 39 | 40 | # Parse VM snapshot, then isolate snapshot 41 | log(3, '------- PARSING VM SNAPSHOT --------\n') 42 | base = Snapshot(data=blobs[0], data_offset=offsets[0], 43 | instructions=blobs[1], instructions_offset=offsets[1], 44 | vm=True, **kwargs).parse() 45 | log(3, '\n------- PARSING ISOLATE SNAPSHOT --------\n') 46 | res = Snapshot(data=blobs[2], data_offset=offsets[2], 47 | instructions=blobs[3], instructions_offset=offsets[3], 48 | base=base, **kwargs).parse() 49 | 50 | archs = { 'EM_386': 'ia32', 'EM_X86_64': 'x64', 'EM_ARM': 'arm', 'EM_AARCH64': 'arm64' } 51 | if archs.get(f['e_machine']) != res.arch.split('-')[0] or (f.elfclass == 64) != res.is_64: 52 | log(1, 'WARN: ELF arch ({}) and/or class ({}) not matching snapshot'.format(f['e_machine'], f.elfclass)) 53 | return res 54 | 55 | def parse_appjit_snapshot(fname, base=None, **kwargs): 56 | ''' Open and parse an AppJIT snapshot file. Returns isolate snapshot. ''' 57 | log = lambda n, x: print(x) if kwargs.get('print_level', 3) >= n else None 58 | 59 | # Read header, check magic 60 | f = open(fname, 'rb') 61 | magic = unpack(' returns 4 binary blobs -> **this is what flutter used at some point, the 4 blobs were packaged at assets dir in APK** 20 | - Dart_CreateAppAOTSnapshotAsAssembly 21 | - outputs assembly file that defines 4 symbols (the blobs) and can be linked however we want 22 | - Dart_CreateVMAOTSnapshotAsAssembly 23 | - Like Dart_CreateAppAOTSnapshotAsAssembly, but only includes 24 | kDartVmSnapshotData and kDartVmSnapshotInstructions. 25 | - Dart_CreateAppAOTSnapshotAsElf -> (newer one, see below) 26 | - Like Dart_CreateAppAOTSnapshotAsAssembly, but outputs a .so instead of assembly file 27 | - This is what is used now, and in tsunami, produced ELF gets placed in lib/.../libapp.so 28 | 29 | - Dart SDK can now compile AOT as ELF binaries directly 30 | [[vm] Direct generation of ELF shared libraries.](https://dart-review.googlesource.com/c/sdk/+/81323) 31 | Merged on 2019-05-28 32 | Introduces Dart_CreateAppAOTSnapshotAsElf and kAppAOTElf type 33 | 34 | AOT [snapshot types](https://dart.googlesource.com/sdk/+/af93ebcf4cb55ae5f0f39a183ad2d42ca13ae51f/runtime/bin/gen_snapshot.cc#79): 35 | 36 | kCore, 37 | kCoreJIT, 38 | kApp, 39 | kAppJIT, 40 | kAppAOTBlobs, 41 | kAppAOTAssembly, 42 | kAppAOTElf, 43 | kVMAOTAssembly, 44 | 45 | 46 | ### Flutter 47 | 48 | Flutter engine operation in AOT mode (beware of outdated content): 49 | https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode 50 | 51 | #32743: Support AOT .so (--build-shared-library) compilation for Android arm64 52 | https://github.com/flutter/flutter/issues/32743 53 | 54 | https://github.com/flutter/flutter/pull/32787 55 | 56 | 57 | Running `flutter build aot` on last Flutter, demo app, executes: 58 | 59 | # Build kernel file 60 | flutter/bin/cache/dart-sdk/bin/dart --sdk-root ... --strong --target=flutter 61 | --aot --tfa -Ddart.vm.product=true 62 | --packages .packages 63 | --output-dill build/aot/app.dill 64 | --depfile build/aot/kernel_compile.d 65 | package:myapp/main.dart 66 | 67 | # Compile kernel file into AOT snapshot, as ELF (strip, deterministic, casual_async_stack) 68 | flutter/bin/cache/artifacts/engine/android-arm-release/linux-x64/gen_snapshot 69 | --causal_async_stacks 70 | --deterministic 71 | --snapshot_kind=app-aot-elf 72 | --elf=build/aot/app.so 73 | --strip 74 | --no-sim-use-hardfp 75 | --no-use-integer-division 76 | build/aot/app.dill 77 | 78 | Code NOT OBFUSCATED by default, but can be made so with --obfuscate (https://github.com/flutter/flutter/wiki/Obfuscating-Dart-Code) 79 | 80 | https://github.com/flutter/engine/pull/8979 81 | 82 | Part of Flutter where AOT snapshot is loaded: 83 | https://github.com/flutter/engine/blob/master/shell/platform/android/io/flutter/view/FlutterNativeView.java#L105 84 | 85 | https://stackoverflow.com/questions/54388974/how-does-dart-flutter-get-compiled-to-android 86 | 87 | 88 | ### Dart building 89 | 90 | https://github.com/dart-lang/sdk/wiki/Building 91 | 92 | https://github.com/dart-lang/sdk/wiki/Building-Dart-SDK-for-ARM-processors 93 | 94 | ./tools/build.py -m release -a arm --os=android create_sdk 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # darter: Dart snapshot parser 2 | 3 | `darter` is a Python module that can fully parse the data inside a Dart snapshot 4 | (i.e. the `libapp.so` file in a release Flutter app). 5 | 6 | Features: 7 | 8 | - Parses 100% of the snapshot data, including memory structures. 9 | - Supports many architectures and the three snapshot types (old, AppJIT and AppAOT). 10 | - Usually zero-config: autodetects flags & settings from the snapshot. 11 | - Extracts the blobs from `app.so` or `.snapshot` files automatically. 12 | - Stores back-references, so you can navigate the graph easily. 13 | - Debugging output & strict mode controls. 14 | - Disassembles and analyzes the compiled code to find references to VM objects. 15 | 16 | Examples of what you can do with the parsed info: 17 | 18 | - Extract string table of the application 19 | - Find usages of a certain object 20 | - Export metadata for Radare2 21 | - Deobfuscate a snapshot by matching it with a reference one 22 | - Generate call graph, library dependency graph, etc. 23 | 24 | **Note:** 25 | Keep in mind that this is for parsing binary (i.e. architecture-dependent) snapshots. 26 | `.dill` files and some `.snapshot` files contain [Kernel AST](https://github.com/dart-lang/sdk/tree/master/pkg/kernel), which 27 | is a completely different format and currently not supported by `darter`. 28 | [[Learn more]](https://github.com/dart-lang/sdk/wiki/Snapshots#kernel-snapshots) 29 | 30 | 31 | ## How to use 32 | 33 | Most of the code is zero-dependency, except for: 34 | 35 | - `parse_elf_snapshot(...)` requires [pyelftools](https://github.com/eliben/pyelftools) 36 | 37 | - the `darter.asm` module (for analyzing the assembled code) requires 38 | [Capstone](https://www.capstone-engine.org/documentation.html) 39 | (and its python binding) 40 | 41 | `darter` in itself is just a module, it has no stand-alone program or CLI. 42 | The recommended way to use it is by including it in a notebook and 43 | playing with the parsed data. 44 | 45 | [Install Jupyter](https://jupyter.org/install) and open the `1-introduction` 46 | notebook for a basic walkthrough of the parsed data; then head to `2-playground` 47 | which contains more interesting examples of use. 48 | 49 | It's *highly recommended* that you first play with a known snapshot (i.e. 50 | that you have built yourself or have the code), before analyzing the 51 | snapshot you are after. 52 | 53 | 54 | ## Status 55 | 56 | The parser is still at an early stage and will not work in every case. 57 | 58 | - It has been heavily tested on AppAOT Product snapshots on ARM and ARM64. 59 | - It has been lightly tested on AppJIT Release snapshots on x64. 60 | - The disassembly analysis is architecture-dependent, and currently supports ARM and ARM64. 61 | - The rest of the code is mostly architecture-independent, but it may not work on other architectures without some modifications. 62 | 63 | This parser was written based on dart-sdk at `1ef83b86ae`. 64 | The snapshot format is internal to the VM. It dumps some of the objects as they appear 65 | in memory; you need to know how the VM (arch, compile flags) was compiled in order 66 | to parse it. It [can change frequently between versions](./info/versions.md), as 67 | there's not a standard spec (AFAIK) for the format. 68 | 69 | Any help or donations are welcome. 70 | 71 | 72 | ## Technical details 73 | 74 | Given an *data section* and an *instructions section* (and optionally a *base*): 75 | 76 | - Parse the clusters allocation section, building the reference table. 77 | - Parse the clusters fill section. 78 | - Parse the root object. 79 | - Link the references between objects. 80 | - Parse the native structures (`OneByteString`, `CodeSourceMap`, `Instructions`, etc.). 81 | - The resulting VM objects (and cluster descriptions) are returned. 82 | 83 | The information is returned as parsed as much as possible, so that it is easy to 84 | manipulate. Back-references are tracked too, so that it's easy to know where a certain 85 | object is referenced from. 86 | 87 | `darter` can parse both 'VM' snapshots and 'isolate' ones (the ones we care about). 88 | 89 | The `darter.asm` module disassembles the compiled code and analyzes it. 90 | This is crucial for AOT snapshots, because we get no high-level bytecode. 91 | 92 | 93 | ## See also 94 | 95 | If you are new to Dart / Flutter reverse-engineering, it's a good idea to read 96 | this introduction first: https://mrale.ph/dartvm/ 97 | 98 | The relevant code on snapshot serialization is at [`runtime/vm/clustered_snapshot.cc`](https://github.com/dart-lang/sdk/blob/1ef83b86ae637ffe7359173804cbc6d3fa25e6db/runtime/vm/clustered_snapshot.cc) 99 | and [`runtime/vm/raw_object.h`](https://github.com/dart-lang/sdk/blob/1ef83b86ae637ffe7359173804cbc6d3fa25e6db/runtime/vm/raw_object.h). 100 | 101 | There's also additional info in the `info` directory. 102 | -------------------------------------------------------------------------------- /utils/timemachine_pub.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Node.js script that runs whatever command was given (usually pub) 4 | // making traffic go through a MITM proxy that will intercept HTTPS 5 | // requests to pub.dartlang.org and pub.dev. When a package is queried, 6 | // the proxy will only return versions that existed at the given time. 7 | // 8 | // This is useful to simulate a `pub get` in the past. Remove 9 | // `pubspec.lock` first, then run i.e.: 10 | // 11 | // path/to/timemachine_pub.js 2019-10-14 flutter pub get 12 | // 13 | // Before using this, you'll need to add the fake CA (files/ca.crt) 14 | // to the system's (OpenSSL) trust store. 15 | 16 | const { promisify } = require('util') 17 | const { once } = require('events') 18 | const stream = require('stream') 19 | const pipeline = promisify(stream.pipeline) 20 | 21 | const { join } = require('path') 22 | const { readFileSync } = require('fs') 23 | const net = require('net') 24 | const http = require('http') 25 | const https = require('https') 26 | const { spawn } = require('child_process') 27 | 28 | const args = process.argv.slice(2) 29 | if (args.length < 2) { 30 | console.error(`Usage: ${process.argv[1]} command...`) 31 | process.exit(1) 32 | } 33 | const DATE = new Date(args[0]) 34 | const COMMAND = args.slice(1) 35 | 36 | 37 | const agent = new https.Agent({ keepAlive: true }) 38 | const upstreamOptions = { agent } 39 | 40 | const fakeServer = https.createServer({ 41 | key: readFileSync(join(__dirname, 'files', 'timemachine.key')), 42 | cert: readFileSync(join(__dirname, 'files', 'timemachine.crt')), 43 | }, async (req, res) => { 44 | let { url: path, headers, method } = req 45 | headers = { ...headers } 46 | delete headers.connection // connection-level headers must be removed 47 | delete headers.host // let Node.js populate it for us 48 | delete headers['accept-encoding'] // I'm too lazy to handle this correctly 49 | 50 | const oreq = https.request({ ...upstreamOptions, hostname: 'pub.dev', path, headers, method }) 51 | await pipeline(req, oreq) 52 | 53 | let ores 54 | try { 55 | ; [ores] = await once(oreq, 'response') 56 | } catch (e) { 57 | console.error(`[timemachine] Upstream request error: ${e}`) 58 | oreq.destroy() 59 | req.destroy() 60 | return 61 | } 62 | 63 | res.statusCode = ores.statusCode 64 | Object.keys(ores.headers).forEach(k => res.setHeader(k, ores.headers[k])) 65 | res.removeHeader('content-length') // let Node.js populate it for us 66 | 67 | if (method === 'GET' && path.startsWith('/api/packages/') && res.statusCode === 200) { 68 | const body = await collectBody(ores) 69 | let data = JSON.parse(body.toString()) 70 | data.versions = data.versions.filter(v => new Date(v.published) < DATE) 71 | data.latest = data.versions[data.versions.length - 1] 72 | return res.end(Buffer.from(JSON.stringify(data))) 73 | } 74 | 75 | res.setHeader('content-length', ores.headers['content-length']) 76 | await pipeline(ores, res) 77 | }) 78 | 79 | const httpsProxy = http.createServer((req, res) => { 80 | res.statusCode = 405 81 | return res.end('This is an HTTPS proxy, only CONNECT allowed\n') 82 | }).on('connect', (req, socket, head) => { 83 | socket.unshift(head) 84 | const { port, hostname } = new URL(`http://${req.url.toLowerCase()}`) 85 | 86 | if (['pub.dartlang.org', 'pub.dev'].includes(hostname) && port === '443') { 87 | socket.write('HTTP/1.1 200 Connection Established\r\n\r\n') 88 | return fakeServer.emit('connection', socket) 89 | } 90 | 91 | // console.log(`[timemachine] Connection request for ${req.url}; proxying through`) 92 | const upstream = net.connect(Number(port) || 80, hostname) 93 | once(upstream, 'connect').then(() => { 94 | socket.write('HTTP/1.1 200 Connection Established\r\n\r\n') 95 | pipeline(upstream, socket).catch(() => {}) 96 | pipeline(socket, upstream).catch(() => {}) 97 | }, error => { 98 | socket.end(`HTTP/1.1 502 Proxy Connection Failed\r\n\r\n${error}\n`) 99 | }) 100 | }) 101 | 102 | httpsProxy.unref() 103 | httpsProxy.listen(() => { 104 | console.log(`[timemachine] Ready, simulating ${DATE}`) 105 | const proxyUrl = `http://localhost:${httpsProxy.address().port}` 106 | const newEnv = { ...process.env, https_proxy: proxyUrl } 107 | const child = spawn(COMMAND[0], COMMAND.slice(1), { stdio: 'inherit', env: newEnv }) 108 | child.on('error', error => { 109 | console.log(`[timemachine] exec failed: ${error.message}`) 110 | process.exit(255) 111 | }) 112 | child.on('exit', (code, signal) => { 113 | if (signal) { 114 | console.log(`[timemachine] child died from signal ${signal}`) 115 | process.exit(255) 116 | } 117 | process.exit(code) 118 | }) 119 | }) 120 | 121 | 122 | // utils 123 | 124 | function collectBody(stream) { 125 | const chunks = [] 126 | stream.on('data', chunk => chunks.push(chunk)) 127 | return once(stream, 'end').then(() => Buffer.concat(chunks)) 128 | } 129 | -------------------------------------------------------------------------------- /darter/asm/base.py: -------------------------------------------------------------------------------- 1 | # ASM/BASE: Common API to disassemble compiled instructions and analyze them 2 | 3 | import time 4 | 5 | from ..constants import kEntryType 6 | from . import _arm, _arm64, _ia32, _x64 7 | 8 | 9 | ARCH_MODULES = _arm, _arm64, _ia32, _x64 10 | 11 | def _find_arch_module(snapshot): 12 | ''' 13 | Create and return the appropriate arch-specific module, 14 | according to the architecture and other settings of a given snapshot. 15 | Raises if the architecture / settings are not supported. 16 | ''' 17 | arch = snapshot.arch.split('-')[0] 18 | for m in ARCH_MODULES: 19 | if m.supports(snapshot, arch): return m 20 | raise Exception('Unknown / unsupported arch') 21 | 22 | def make_engine(snapshot): 23 | ''' 24 | Create and return an instance of the Capstone engine 25 | for the architecture/settings of the passed snapshot. 26 | ''' 27 | return _find_arch_module(snapshot).make_engine(snapshot) 28 | 29 | def disasm_code(md, code, lite=False, detail=False): 30 | ''' 31 | Convenience method to disassemble the `instructions` of a Code object. 32 | It is preferable to use this function when possible (i.e. for thumb mode). 33 | ''' 34 | instr = code.x['instructions'] 35 | data, addr = instr['data'], instr['data_addr'] 36 | md.detail = detail 37 | ops = list((md.disasm_lite if lite else md.disasm)(data, addr)) 38 | get_end = lambda x: (x[0]+x[1] if lite else x.address+x.size) 39 | if (get_end(ops[-1])-addr if ops else 0) != len(data): 40 | raise Exception('Not all instructions were disassembled') 41 | return ops 42 | 43 | def analyze_native_references(snapshot): 44 | ''' 45 | Analyzes all Code objects of a snapshot that have native instructions: 46 | the instructions are disassembled and searched for references to VM 47 | objects. Currently only ARM and ARM64 support this. 48 | 49 | This is a low-level function, most people should use 50 | `populate_native_references` instead. 51 | 52 | The returned object is a dictionary that associates Code objects with 53 | their results. The results are a list of (address, ) tuples; 54 | the fields depend on the kind of native reference: 55 | 56 | - "load", n, reg: object from global object pool entry `n` was loaded 57 | into register named `reg` (special value `call` means that next 58 | entry was also loaded and called). 59 | - "call", address: function call to `address` 60 | ''' 61 | arch = _find_arch_module(snapshot) 62 | if not hasattr(arch, 'match_nref'): 63 | raise Exception('Native reference analysis is not yet implemented for this architecture') 64 | md = arch.make_engine(snapshot) 65 | result = {} 66 | for code in snapshot.getrefs('Code'): 67 | if 'instructions' not in code.x: continue 68 | ops = disasm_code(md, code, lite=True) 69 | result[code] = nrefs = [] 70 | i = 0 71 | while i < len(ops): 72 | res = arch.match_nref(ops, i) 73 | if res: 74 | ii, *nref = res 75 | nrefs.append((ops[i][0], *nref)) 76 | assert ii > i 77 | i = ii 78 | else: 79 | i += 1 80 | return result 81 | 82 | def populate_native_references(snapshot): 83 | ''' 84 | High-level method that uses analyze_native_references, then associates 85 | the results to objects, and saves the results into the snapshot data, 86 | storing back-references on the pointed objects too: 87 | 88 | 1. Each analyzed Code object gets an `nrefs` entry on its data 89 | dictionary, which is a list of `(target, address, )` items, 90 | where `target` is the object (Ref) that was loaded, called, etc. 91 | 92 | 2. This also creates back-references on the pointed objects; every 93 | snapshot object (the object itself, not the data dictionary) 94 | will have an `nsrc` field containing a list of `(code, address, )` 95 | items, where `code` is the Code object the native reference was found at. 96 | 97 | `address` is the address of the instruction(s) which referenced the object, 98 | and the rest of the fields depend on the kind of native reference: 99 | 100 | - `"load", reg`: the object was loaded (through the global pool) into register 101 | named `reg` (special value `call` means that next entry was also loaded and 102 | called). 103 | - `"call", offset`: function call to the object, at offset `offset`. 104 | ''' 105 | print('Starting analysis...') 106 | start = time.time() 107 | results = analyze_native_references(snapshot) 108 | print('Done in {:.2f}s, processing results'.format(time.time() - start)) 109 | 110 | entries = snapshot.refs['root'].x['global_object_pool'].x['entries'] 111 | # initialize nsrc to an empty list on every object 112 | for i in range(1, snapshot.refs['next']): 113 | snapshot.refs[i].nsrc = [] 114 | 115 | for code, nrefs in results.items(): 116 | out_nrefs = code.x['nrefs'] = [] 117 | for address, kind, x, *rest in nrefs: 118 | if kind == 'call': 119 | match = snapshot.search_address(x) 120 | if match is None: 121 | print('Call address not found: 0x{:x}'.format(x)) 122 | continue 123 | target, offset = match 124 | rest = (offset, *rest) 125 | elif kind == 'load': 126 | if not (0 <= x < len(entries)): 127 | print('Entry index outside range: {}'.format(x)) 128 | continue 129 | if 'raw_obj' not in entries[x]: 130 | # print('Entry {} not an object: type={}'.format(x, kEntryType[entries[x]['type']])) # FIXME: proper logging 131 | continue 132 | # FIXME: take 'patchable' into account 133 | target = entries[x]['raw_obj'] 134 | else: 135 | continue 136 | out_nrefs.append(( target, address, kind, *rest )) 137 | target.nsrc.append(( code, address, kind, *rest )) 138 | -------------------------------------------------------------------------------- /darter/clusters.py: -------------------------------------------------------------------------------- 1 | # CLUSTERS: Stores the deserialization logic for every kind of cluster (used by CORE) 2 | 3 | from struct import unpack 4 | import re 5 | 6 | from .read import * 7 | from .constants import * 8 | from .other import parse_code_source_map 9 | 10 | def make_cluster_handlers(s): 11 | # Unpack any properties from Snapshot here, to make the dependencies clear 12 | 13 | parse_rodata = s.parse_rodata 14 | parse_csm = s.parse_csm 15 | rodata = s.rodata 16 | rodata_offset = s.rodata_offset 17 | 18 | kind = s.kind 19 | includes_code = s.includes_code 20 | includes_bytecode = s.includes_bytecode 21 | 22 | is_precompiled = s.is_precompiled 23 | is_product = s.is_product 24 | is_64 = s.is_64 25 | 26 | kObjectAlignmentLog2 = s.kObjectAlignmentLog2 27 | raw_instance_size_in_words = s.raw_instance_size_in_words 28 | 29 | allocref = s.allocref 30 | readref = s.readref 31 | storeref = s.storeref 32 | 33 | warning = s.warning 34 | 35 | # Base handlers 36 | 37 | class Handler: 38 | do_read_from = True 39 | def __init__(self, cid): 40 | pass 41 | 42 | class SimpleHandler(Handler): 43 | def alloc(self, f, cluster): 44 | for _ in range(readuint(f)): allocref(cluster, {}) 45 | 46 | class LengthHandler(Handler): 47 | def alloc(self, f, cluster): 48 | for _ in range(readuint(f)): allocref(cluster, { 'length': readuint(f) }) 49 | 50 | class RODataHandler(Handler): 51 | do_read_from = False 52 | def alloc(self, f, cluster): 53 | for _ in range(readuint(f)): 54 | allocref(cluster, { 'offset': readuint(f), 'shared': True }) # FIXME implement 55 | running_offset = 0 56 | for _ in range(readuint(f)): 57 | running_offset += readuint(f) << kObjectAlignmentLog2 58 | allocref(cluster, self.try_parse_object(running_offset)) 59 | def try_parse_object(self, offset): 60 | if not parse_rodata: return { 'offset': rodata_offset + offset } 61 | rodata.seek(offset) 62 | return self.parse_object(rodata) 63 | def fill(self, f, x, ref): pass 64 | 65 | # Handlers 66 | 67 | class HandlerStore: 68 | 69 | class TypedData(Handler): 70 | do_read_from = False 71 | type_associations = { 72 | 'Int8': (1, 'b'), 73 | 'Uint8': (1, 'B'), 74 | 'Int16': (2, 'h'), 75 | 'Uint16': (2, 'H'), 76 | 'Int32': (4, 'i'), 77 | 'Uint32': (4, 'I'), 78 | 'Int64': (8, 'q'), 79 | 'Uint64': (8, 'Q'), 80 | } 81 | def __init__(self, cid): 82 | m = re.fullmatch('(External)?TypedData(.+)Array', kClassId[cid]) 83 | self.external = bool(m.group(1)) 84 | element_size, parse_char = self.type_associations[m.group(2)] 85 | elem = lambda f: unpack('<' + parse_char, f.read(element_size))[0] 86 | self.parse_func = lambda f, count: [ elem(f) for _ in range(count) ] 87 | # Optimization: if Uint8 array, we can just read bytes 88 | if parse_char == 'B': self.parse_func = lambda f, count: f.read(count) 89 | def alloc(self, f, cluster): 90 | return (SimpleHandler if self.external else LengthHandler).alloc(self, f, cluster) 91 | def fill(self, f, x, ref): 92 | count = readuint(f) 93 | if self.external: 94 | while f.tell() % kDataSerializationAlignment != 0: f.read(1) 95 | else: 96 | x['canonical'] = read1(f) 97 | x['value'] = self.parse_func(f, count) 98 | 99 | class Class(Handler): 100 | def alloc(self, f, cluster): 101 | for _ in range(readuint(f)): 102 | allocref(cluster, { 'cid': readcid(f), 'predefined': True }) 103 | for _ in range(readuint(f)): 104 | allocref(cluster, { 'predefined': False }) 105 | 106 | def fill(self, f, x, ref): 107 | cid = readcid(f) 108 | if x['predefined'] and cid != x['cid']: 109 | warning('Predefined class has different CID (alloc={}, fill={})'.format(x['cid'], cid)) 110 | if not x['predefined'] and cid < kNumPredefinedCids: 111 | warning('CID is predefined') 112 | x['cid'] = cid 113 | 114 | if (not is_precompiled) and (kind != kkKind['kFullAOT']): 115 | x['binary_declaration'] = readuint(f, 32) 116 | 117 | # these two should be discarded if (predefined and IsInternalVMdefinedClassId) 118 | x['instance_size_in_words'] = readint(f, 32) 119 | x['next_field_offset_in_words'] = readint(f, 32) 120 | 121 | x['type_arguments_field_offset_in_words'] = readint(f, 32) 122 | x['num_type_arguments'] = readint(f, 16) 123 | x['num_native_fields'] = readuint(f, 16) 124 | x['token_pos'] = readtokenposition(f) 125 | x['end_token_pos'] = readtokenposition(f) 126 | x['state_bits'] = readuint(f, 32) 127 | 128 | class Instance(Handler): 129 | do_read_from = False 130 | def alloc(self, f, cluster): 131 | count = readuint(f) 132 | cluster['next_field_offset_in_words'] = readint(f, 32) 133 | cluster['instance_size_in_words'] = readint(f, 32) 134 | for _ in range(count): allocref(cluster, {}) 135 | def fill(self, f, x, ref): 136 | x['canonical'] = read1(f) 137 | count = ref.cluster['next_field_offset_in_words'] - raw_instance_size_in_words 138 | x['fields'] = [ readref(f, (ref, 'fields', n)) for n in range(count) ] 139 | 140 | class Type(Handler): 141 | def alloc(self, f, cluster): 142 | canonical_items = readuint(f) 143 | for i in range(canonical_items + readuint(f)): 144 | allocref(cluster, { 'canonical': i < canonical_items }) 145 | def fill(self, f, x, ref): 146 | x['token_pos'] = readtokenposition(f) 147 | x['type_state'] = readint(f, 8) 148 | 149 | class Mint(Handler): 150 | do_read_from = False 151 | def alloc(self, f, cluster): 152 | for _ in range(readuint(f)): 153 | allocref(cluster, { 'canonical': read1(f), 'value': readint(f, 64) }) 154 | def fill(self, f, x, ref): pass 155 | 156 | class PatchClass(SimpleHandler): 157 | def fill(self, f, x, ref): 158 | if (not is_precompiled) and (kind != kkKind['kFullAOT']): 159 | x['library_kernel_offset'] = readint(f, 32) 160 | 161 | class Function(SimpleHandler): 162 | def fill(self, f, x, ref): 163 | if not is_precompiled: 164 | if kind == kkKind['kFullJIT']: 165 | storeref(f, x, 'unoptimized_code', ref) 166 | if includes_bytecode: 167 | storeref(f, x, 'bytecode', ref) 168 | if includes_code: 169 | storeref(f, x, 'code', ref) 170 | if kind == kkKind['kFullJIT']: 171 | storeref(f, x, 'ic_data_array', ref) 172 | 173 | if (not is_precompiled) and (kind != kkKind['kFullAOT']): 174 | x['token_pos'] = readtokenposition(f) 175 | x['end_token_pos'] = readtokenposition(f) 176 | x['binary_declaration'] = readuint(f, 32) 177 | x['packed_fields'] = readuint(f, 32) 178 | x['kind_tag'] = readuint(f, 64) # FIXME it should be 32 179 | 180 | class ClosureData(SimpleHandler): 181 | def fill(self, f, x, ref): pass 182 | 183 | class SignatureData(SimpleHandler): 184 | def fill(self, f, x, ref): pass 185 | 186 | class Field(SimpleHandler): 187 | def fill(self, f, x, ref): 188 | if kind != kkKind['kFullAOT']: 189 | x['token_pos'] = readtokenposition(f) 190 | x['end_token_pos'] = readtokenposition(f) 191 | x['guarded_cid'] = readcid(f) 192 | x['is_nullable'] = readcid(f) 193 | x['static_type_exactness_state'] = readint(f,8) 194 | if not is_precompiled: 195 | x['binary_declaration'] = readuint(f,32) 196 | x['kind_bits'] = readuint(f,16) 197 | 198 | class Script(SimpleHandler): 199 | def fill(self, f, x, ref): 200 | x['line_offset'] = readint(f,32) 201 | x['col_offset'] = readint(f,32) 202 | x['kind'] = readint(f,8) 203 | x['kernel_script_index'] = readint(f,32) 204 | 205 | class Library(SimpleHandler): 206 | def fill(self, f, x, ref): 207 | x['index'] = readint(f,32) 208 | x['num_imports'] = readuint(f,16) 209 | x['load_state'] = readint(f,8) 210 | x['is_dart_scheme'] = read1(f) 211 | x['debuggable'] = read1(f) 212 | if not is_precompiled: 213 | x['binary_declaration'] = readuint(f,32) 214 | 215 | class Code(SimpleHandler): 216 | def fill(self, f, x, ref): 217 | x['state_bits'] = readint(f, 32) 218 | 219 | class ObjectPool(LengthHandler): 220 | do_read_from = False 221 | def fill(self, f, x, ref): 222 | def read_entry(n): 223 | e = decode_object_entry_type_bits(readuint(f,8)) 224 | if e['type'] in {kkEntryType['kNativeEntryData'], kkEntryType['kTaggedObject']}: 225 | e['raw_obj'] = readref(f, (ref, 'entries', n, 'raw_obj')) 226 | elif e['type'] in {kkEntryType['kImmediate']}: 227 | e['raw_value'] = readint(f) 228 | elif e['type'] in {kkEntryType['kNativeFunction'], kkEntryType['kNativeFunctionWrapper']}: 229 | pass 230 | else: 231 | warning('Unknown entry type {}...'.format(e['type'])) 232 | return e 233 | x['entries'] = [read_entry(n) for n in range(readuint(f))] 234 | 235 | class ExceptionHandlers(LengthHandler): 236 | do_read_from = False 237 | def fill(self, f, x, ref): 238 | count = readuint(f) 239 | storeref(f, x, 'handled_types_data', ref) 240 | def read_info(): 241 | i = {} 242 | i['handler_pc_offset'] = readuint(f,32) 243 | i['outer_try_index'] = readint(f,16) 244 | i['needs_stacktrace'] = readint(f,8) 245 | i['has_catch_all'] = readint(f,8) 246 | i['is_generated'] = readint(f,8) 247 | return i 248 | x['entries'] = [read_info() for _ in range(count)] 249 | 250 | class UnlinkedCall(SimpleHandler): 251 | def fill(self, f, x, ref): pass 252 | 253 | class MegamorphicCache(SimpleHandler): 254 | def fill(self, f, x, ref): 255 | x['filled_entry_count'] = readint(f, 32) 256 | 257 | class SubtypeTestCache(SimpleHandler): 258 | def fill(self, f, x, ref): pass 259 | 260 | class UnhandledException(SimpleHandler): 261 | def fill(self, f, x, ref): pass 262 | 263 | class TypeArguments(LengthHandler): 264 | do_read_from = False 265 | def fill(self, f, x, ref): 266 | count = readuint(f) 267 | x['canonical'] = read1(f) 268 | x['hash'] = readint(f, 32) 269 | storeref(f, x, 'instantiations', ref) 270 | x['types'] = [ readref(f, (ref, 'types', n)) for n in range(count) ] 271 | 272 | class TypeRef(SimpleHandler): 273 | def fill(self, f, x, ref): pass 274 | 275 | class TypeParameter(SimpleHandler): 276 | def fill(self, f, x, ref): 277 | x['parameterized_class_id'] = readint(f, 32) 278 | x['token_pos'] = readtokenposition(f) 279 | x['index'] = readint(f, 16) 280 | x['flags'] = readuint(f, 8) 281 | 282 | class Closure(SimpleHandler): 283 | def fill(self, f, x, ref): pass 284 | 285 | class Double(SimpleHandler): 286 | do_read_from = False 287 | def fill(self, f, x, ref): 288 | x['canonical'] = read1(f) 289 | x['value'] = readdouble(f) 290 | 291 | class GrowableObjectArray(SimpleHandler): 292 | def fill(self, f, x, ref): pass 293 | 294 | class StackTrace(SimpleHandler): 295 | def fill(self, f, x, ref): pass 296 | 297 | class Array(LengthHandler): 298 | do_read_from = False 299 | def fill(self, f, x, ref): 300 | count = readuint(f) 301 | x['canonical'] = read1(f) 302 | storeref(f, x, 'type_arguments', ref) 303 | x['value'] = [ readref(f, (ref, 'value', n)) for n in range(count) ] 304 | 305 | class Namespace(SimpleHandler): 306 | def fill(self, f, x, ref): pass 307 | 308 | class KernelProgramInfo(SimpleHandler): 309 | def fill(self, f, x, ref): 310 | x['kernel_binary_version'] = readuint(f, 32) 311 | 312 | class ContextScope(LengthHandler): 313 | do_read_from = False 314 | def fill(self, f, x, ref): 315 | length = readuint(f) 316 | x['implicit'] = read1(f) 317 | def read_variable_desc(src): 318 | x = {} 319 | x['declaration_token_pos'] = readuint(f) 320 | x['token_pos'] = readuint(f) 321 | storeref(f, x, 'name', src) 322 | storeref(f, x, 'is_final', src) 323 | storeref(f, x, 'is_const', src) 324 | storeref(f, x, 'value_or_type', src) 325 | x['context_index'] = readuint(f) 326 | x['context_level'] = readuint(f) 327 | return x 328 | x['variables'] = [ read_variable_desc((ref, 'variables', i)) for i in range(length) ] 329 | 330 | class ICData(SimpleHandler): 331 | def fill(self, f, x, ref): 332 | if not is_precompiled: 333 | x['deopt_id'] = readint(f, 32) 334 | x['state_bits'] = readint(f, 32) 335 | 336 | class LibraryPrefix(SimpleHandler): 337 | def fill(self, f, x, ref): 338 | x['num_imports'] = readuint(f, 16) 339 | x['deferred_load'] = read1(f) 340 | 341 | class RegExp(SimpleHandler): 342 | def fill(self, f, x, ref): 343 | x['num_one_byte_registers'] = readint(f, 32) 344 | x['num_two_byte_registers'] = readint(f, 32) 345 | x['type_flags'] = readint(f, 8) 346 | 347 | class WeakProperty(SimpleHandler): 348 | def fill(self, f, x, ref): pass 349 | 350 | if includes_code: 351 | class OneByteString(RODataHandler): 352 | def parse_object(self, f): 353 | if is_64: 354 | tags, hash_, length = unpack(' 0: 410 | c = f.read(1)[0] 411 | for i in range(8): 412 | if length == 0: break 413 | bits.append(bool((c >> i) & 1)) 414 | length -= 1 415 | return { 'tags': tags, 'pc_offset': pc_offset, 'bits': bits, 'slow_path_bit_count': slow_path_bit_count } 416 | 417 | # Doesn't really exist, but used for parsing roots 418 | class ObjectStore(SimpleHandler): 419 | def fill(self, f, x, ref): pass 420 | 421 | return HandlerStore 422 | -------------------------------------------------------------------------------- /info/versions.md: -------------------------------------------------------------------------------- 1 | # Snapshot hash 2 | 3 | Snapshots are prepended with a header specifying the **version hash**. This hash depends on all source code files that can potentially affect the snapshot format. Therefore, if the version hash has been verified to work with Darter, it'll probably parse without major problems. Currently, the only snapshot version supported is `c8562f0ee0ebc38ba217c7955956d1cb`. 4 | The table below shows snapshot hash values for every Flutter release. 5 | 6 | ## How to get snapshot hash for a Dart SDK version 7 | 8 | Version-related stuff in the Dart runtime is injected into the `runtime/vm/version_in.cc` template, you can see the fields there: 9 | 10 | - `SNAPSHOT_HASH`: the hash that appears as the 'version' field in the snapshot header 11 | - `VERSION_STR` and `COMMIT_TIME` 12 | - `ABI_VERSION` and `OLDEST_SUPPORTED_ABI_VERSION` 13 | 14 | According to `runtime/BUILD.gn`, this file is generated by `tools/make_version.py`. The hash is generated by concatenating the contents of each file in `VM_SNAPSHOT_FILES`, and taking the MD5 of the result. 15 | Quick way to generate it: 16 | 17 | echo '{{SNAPSHOT_HASH}}' > /tmp/mv-template && python2 tools/make_version.py --input /tmp/mv-template --output /tmp/v-result 18 | 19 | 20 | # Flutter versions 21 | 22 | Info about the various release channels: 23 | 24 | Listed in , which loads: 25 | 26 | 27 | 28 | 29 | 30 | Containing releases by channel, with: release date, commit, tag (and also filename and sha256 hash?) 31 | 32 | ## How to get Dart SDK version used by Flutter 33 | 34 | - For a certain Flutter version, look at the used `engine` version by looking at the **`bin/internal/engine.version`** file. 35 | 36 | - Then go to that commit of flutter/engine, and look at `DEPS` file. `dart_revision` indicates commit of dart/sdk. 37 | 38 | 39 | # Flutter version table 40 | 41 | | Release date | Channel | Version | Commit | Engine commit | Dart SDK commit | Snapshot version | 42 | | ------------ | ------- | ------- | ------ | ------------- | --------------- | ---------------- | 43 | | 2020-10-08 | stable | 1.22.1 | f30b7f4db93ee747cd727df747941a28ead25ff5 | 75bef9f6c8ac2ed4e1e04cdfcd88b177d9f1850d | efd753621946a89008b76b76d85d54d1aa57fce8 | 8ee4ef7a67df9845fba331734198a953 | 44 | | 2020-10-08 | beta | 1.22.0-12.4.pre | f30b7f4db93ee747cd727df747941a28ead25ff5 | 75bef9f6c8ac2ed4e1e04cdfcd88b177d9f1850d | efd753621946a89008b76b76d85d54d1aa57fce8 | 8ee4ef7a67df9845fba331734198a953 | 45 | | 2020-10-01 | stable | 1.22.0 | d408d302e22179d598f467e11da5dd968dbdc9ec | 5babba6c4d25fa237bbf755ab85c9a0c50b3c6ec | 41eab9b49ccce8960f71c657861dc629f96295af | 8ee4ef7a67df9845fba331734198a953 | 46 | | 2020-09-29 | beta | 1.22.0-12.3.pre | d408d302e22179d598f467e11da5dd968dbdc9ec | 5babba6c4d25fa237bbf755ab85c9a0c50b3c6ec | 41eab9b49ccce8960f71c657861dc629f96295af | 8ee4ef7a67df9845fba331734198a953 | 47 | | 2020-09-28 | beta | 1.22.0-12.2.pre | 2bafdc822636426fa09afb43236400a60ea432b2 | f763b5b9b936872bc6c84b4395286ce684e3b431 | 4215dca724fb80de592f51a6cdba51e7638d1723 | 8ee4ef7a67df9845fba331734198a953 | 48 | | 2020-09-16 | beta | 1.22.0-12.1.pre | 8b3760638a189741cd9ca881aa2dd237c1df1be5 | 4654fc6cf6416daae78eac2c211ad84c46e21625 | 52130c19ca593b185ea9cf72b26b1d02455551ef | 8ee4ef7a67df9845fba331734198a953 | 49 | | 2020-09-15 | stable | 1.20.4 | fba99f6cf9a14512e461e3122c8ddfaa25394e89 | d1bc06f032f9d6c148ea6b96b48261d6f545004f | b07da893600eadc4efafc5a54b8f9533e43c0034 | 04645b6182fad3d68350d84669869ce5 | 50 | | 2020-09-02 | stable | 1.20.3 | 216dee60c0cc9449f0b29bcf922974d612263e24 | d1bc06f032f9d6c148ea6b96b48261d6f545004f | b07da893600eadc4efafc5a54b8f9533e43c0034 | 04645b6182fad3d68350d84669869ce5 | 51 | | 2020-08-28 | beta | 1.21.0-9.2.pre | 81a45ec2e5f80fa71d5135f1702ce540558b416d | 20a953183580250aac2e15d36007664118bda5ab | 2ea332979fbc9e8c42f9efe4a33dec83108c897b | 5f40b0a9f04b5018fa08a9b67fd316cd | 52 | | 2020-08-19 | beta | 1.21.0-9.1.pre | be9bc8cb3942bda5d8ef4e44b44616c470625e18 | 267070c17a6956de1a03dbe09cda56f0c485f41b | 7cb472077b56a99aef7233bb616ba200b4de8682 | 5f40b0a9f04b5018fa08a9b67fd316cd | 53 | | 2020-08-14 | beta | 1.20.2 | bbfbf1770cca2da7c82e887e4e4af910034800b6 | 9d5b21729ff53dbf8eadd8bc97e0e30d77abec95 | e940ff7819053ed8a4c04a4dfcda7df12e969331 | 04645b6182fad3d68350d84669869ce5 | 54 | | 2020-08-13 | stable | 1.20.2 | bbfbf1770cca2da7c82e887e4e4af910034800b6 | 9d5b21729ff53dbf8eadd8bc97e0e30d77abec95 | e940ff7819053ed8a4c04a4dfcda7df12e969331 | 04645b6182fad3d68350d84669869ce5 | 55 | | 2020-08-06 | stable | 1.20.1 | 2ae34518b87dd891355ed6c6ea8cb68c4d52bb9d | c8e3b9485386425213e2973126d6f57e7ed83c54 | 6eb17654b6501e2617c67854ed113ab550d2b3c7 | 04645b6182fad3d68350d84669869ce5 | 56 | | 2020-08-05 | stable | 1.20.0 | 840c9205b344a59e48a5926ee2d791cc5640924c | c8e3b9485386425213e2973126d6f57e7ed83c54 | 6eb17654b6501e2617c67854ed113ab550d2b3c7 | 04645b6182fad3d68350d84669869ce5 | 57 | | 2020-08-03 | beta | 1.20.0-7.4.pre | 916c3ac648aa0498a70f32b5fc4f6c51447628e3 | d6ee1499c27a156a797d9f1539ffb7892855c1d0 | e2ea2e82e8785e18df30b7a06ef7cbc73fd9a81a | 04645b6182fad3d68350d84669869ce5 | 58 | | 2020-07-29 | beta | 1.20.0-7.3.pre | e606910f28be51c8151f6169072afe3b3a8b3c5e | ac95267aef5175b3f6c3387d502070c68f588ad5 | e2ea2e82e8785e18df30b7a06ef7cbc73fd9a81a | 04645b6182fad3d68350d84669869ce5 | 59 | | 2020-07-21 | beta | 1.20.0-7.2.pre | a2bde82fbd52e09057a4146f46889f4e10342d32 | 60b269d898cbe0be27e9b9ba9d21eae97b887ab6 | 57f76512bee70f7e96abf9306797a5e256e02453 | 8b2ca977d1d2920b9839d1b60eade6a7 | 60 | | 2020-07-01 | beta | 1.19.0-4.3.pre | 8fe7655ed20ffd1395f68e30539a847a01a30351 | 9a28c3bcf40ce64fee61e807ee3e1395fd6bd954 | 5815449a2935fd4a0a9505fa0f7ca562a53f2465 | 59da07d9da5a83be4ce75b7913b63dbd | 61 | | 2020-07-01 | beta | 1.19.0-4.2.pre | 9b9b543d9265484132c798adaab6caca52055b08 | 9a28c3bcf40ce64fee61e807ee3e1395fd6bd954 | 5815449a2935fd4a0a9505fa0f7ca562a53f2465 | 59da07d9da5a83be4ce75b7913b63dbd | 62 | | 2020-07-01 | stable | 1.17.5 | 8af6b2f038c1172e61d418869363a28dffec3cb4 | ee76268252c22f5c11e82a7b87423ca3982e51a7 | caebd6700d5ece73b5566b33ff1daecb91dac500 | be7d304ff826e2dfac63538e227c3cc5 | 63 | | 2020-06-18 | stable | 1.17.4 | 1ad9baa8b99a2897c20f9e6e54d3b9b359ade314 | ee76268252c22f5c11e82a7b87423ca3982e51a7 | caebd6700d5ece73b5566b33ff1daecb91dac500 | be7d304ff826e2dfac63538e227c3cc5 | 64 | | 2020-06-10 | beta | 1.19.0-4.1.pre | f994b769743368b36b9c03fb359f62230b60ab92 | 9a28c3bcf40ce64fee61e807ee3e1395fd6bd954 | 5815449a2935fd4a0a9505fa0f7ca562a53f2465 | 59da07d9da5a83be4ce75b7913b63dbd | 65 | | 2020-06-04 | stable | 1.17.3 | b041144f833e05cf463b8887fa12efdec9493488 | ee76268252c22f5c11e82a7b87423ca3982e51a7 | caebd6700d5ece73b5566b33ff1daecb91dac500 | be7d304ff826e2dfac63538e227c3cc5 | 66 | | 2020-05-28 | stable | 1.17.2 | 5f21edf8b66e31a39133177319414395cc5b5f48 | b851c718295a896918dc93cb1ff14f2f895a1b90 | 9a618e5661665b8d687a28e6b1ec25e9177ec2d7 | be7d304ff826e2dfac63538e227c3cc5 | 67 | | 2020-05-14 | beta | 1.18.0-11.1.pre | 2738a1148ba6c9a6114df62358109407c3ef2553 | ef9215ceb2884ddf520d321bcd822d1461330876 | 14c6a93013bfcd311dfeac1c0ad0a5f9f4afb5ef | b58ead73b2c5dfec69565df469bba387 | 68 | | 2020-05-13 | stable | 1.17.1 | f7a6a7906be96d2288f5d63a5a54c515a6e987fe | 6bc433c6b6b5b98dcf4cc11aff31cdee90849f32 | ae8b2249c8fb43ff894eb6f858c41520cf9ce5cc | 74edb834fac3fcea79d7ac2d1d6f1fb2 | 69 | | 2020-05-06 | stable | 1.17.0 | e6b34c2b5c96bb95325269a29a84e83ed8909b5f | 540786dd51f112885a89792d678296b95e6622e5 | f12284ca12b9076dcc86f1524fefd57a7318ee52 | 74edb834fac3fcea79d7ac2d1d6f1fb2 | 70 | | 2020-05-02 | beta | 1.17.0-3.4.pre | e6b34c2b5c96bb95325269a29a84e83ed8909b5f | 540786dd51f112885a89792d678296b95e6622e5 | f12284ca12b9076dcc86f1524fefd57a7318ee52 | 74edb834fac3fcea79d7ac2d1d6f1fb2 | 71 | | 2020-04-29 | beta | 1.17.0-3.3.pre | 0da1ab09224f6c6d69fcff1195a3662fe7ad7534 | 376ad6a64b08aa26005e3f82aed26de2e290b572 | 7611c1abb42779a7ef1117527a7b86677b130c03 | 74edb834fac3fcea79d7ac2d1d6f1fb2 | 72 | | 2020-04-22 | beta | 1.17.0-3.2.pre | 2a7bc389f28d83c581f7ddd4601588a22e12512e | 4c8c31f591882b3c668992d2e9da761118899f38 | 91f39d8eb15aad876332637eea090e196ad295ff | 74edb834fac3fcea79d7ac2d1d6f1fb2 | 73 | | 2020-04-17 | stable | v1.12.13+hotfix.9 | f139b11009aeb8ed2a3a3aa8b0066e482709dde3 | af51afceb8886cc11e25047523c4e0c7e1f5d408 | 1c9356d8990a2a8c90c66097e20cb2f22e5cc267 | 20e5c4f7dc44368ac5a17643b93665f6 | 74 | | 2020-04-06 | beta | 1.17.0-dev.3.1 | d3ed9ec945f8869f0e136c357d0c2a6be2b60c98 | c9506cb8e93e5e8879152ff5c948b175abb5b997 | eea97179386a9ced7d68452cea158345f4019baa | 9e7cb7c9394c24c2398410b902673e13 | 75 | | 2020-03-17 | beta | v1.15.17 | 2294d75bfa8d067ba90230c0fc2268f3636d7584 | 5aff3119480996ca014ec0f8d26d74db617b5852 | 9983424a3c50c623730fd43b4ce263df660eb455 | ee91a9191a5286c31d91a89754ba36af | 76 | | 2020-02-11 | stable | v1.12.13+hotfix.8 | 0b8abb4724aa590dd0f429683339b1e045a1594d | e1e6ced81d029258d449bdec2ba3cddca9c2ca0c | 4cc36055d6803b899667feaedc1216a96e9d1c72 | 20e5c4f7dc44368ac5a17643b93665f6 | 77 | | 2020-02-05 | beta | v1.14.6 | fabeb2a16f1d008ab8230f450c49141d35669798 | c4229bfbbae455ad69c967be19aee3fadd6486e1 | fc3af737c75931908521e9c36358a151408d6084 | e739779cc1d28f0f697a92f2daf5f10f | 78 | | 2020-01-27 | stable | v1.12.13+hotfix.7 | 9f5ff2306bb3e30b2b98eee79cd231b1336f41f4 | a67792536ca236a971d0efbcfd7af4efb8f6c119 | a4911c63f4f72ba571d6db94de56007b09f4af99 | 20e5c4f7dc44368ac5a17643b93665f6 | 79 | | 2020-01-15 | beta | v1.13.6 | 659dc8129d4edb9166e9a0d600439d135740933f | bdc9708d235e582483d299642ad8682826ebb90d | c547f5d933e5a10e18b1b26b54a6249e88fa0f1c | 81662522448cdd4d02eb060669e5d48b | 80 | | 2019-12-11 | beta | v1.12.13+hotfix.6 | 18cd7a3601bcffb36fdf2f679f763b5e827c2e8e | 2994f7e1e682039464cb25e31a78b86a3c59b695 | a4911c63f4f72ba571d6db94de56007b09f4af99 | 20e5c4f7dc44368ac5a17643b93665f6 | 81 | | 2019-12-11 | stable | v1.12.13+hotfix.5 | 27321ebbad34b0a3fafe99fac037102196d655ff | 2994f7e1e682039464cb25e31a78b86a3c59b695 | a4911c63f4f72ba571d6db94de56007b09f4af99 | 20e5c4f7dc44368ac5a17643b93665f6 | 82 | | 2019-12-11 | beta | v1.12.13+hotfix.5 | 27321ebbad34b0a3fafe99fac037102196d655ff | 2994f7e1e682039464cb25e31a78b86a3c59b695 | a4911c63f4f72ba571d6db94de56007b09f4af99 | 20e5c4f7dc44368ac5a17643b93665f6 | 83 | | 2019-12-10 | beta | v1.12.13+hotfix.4 | fb60324e6fa791bedeade8be4773a42037e11f62 | ac9391978e7c0693b75a82c219e059b6ffee35c4 | a4911c63f4f72ba571d6db94de56007b09f4af99 | 20e5c4f7dc44368ac5a17643b93665f6 | 84 | | 2019-12-06 | beta | v1.12.13+hotfix.3 | 57f2df76d75cff290cbe2765b07db1ad3e67b50d | ac9391978e7c0693b75a82c219e059b6ffee35c4 | a4911c63f4f72ba571d6db94de56007b09f4af99 | 20e5c4f7dc44368ac5a17643b93665f6 | 85 | | 2019-12-04 | beta | v1.12.13+hotfix.2 | 4f54e46f56c2ffc92eb440dbdab1a7f8e722e593 | 6955b06cedb2425f4363f10642c9b0e63e496af0 | a4911c63f4f72ba571d6db94de56007b09f4af99 | 20e5c4f7dc44368ac5a17643b93665f6 | 86 | | 2019-12-03 | beta | v1.12.13+hotfix.1 | 5b07015393539822da275ab9a348b9e9ce92a29e | c1e322b685a81c11c16bddd22282925b7d0272e8 | a4911c63f4f72ba571d6db94de56007b09f4af99 | 20e5c4f7dc44368ac5a17643b93665f6 | 87 | | 2019-11-22 | beta | v1.11.0 | 856a90e67c9284124d44d2be6c785bacd3a1c772 | af04338413c3ed73316350f64248a152433073b6 | fa4379946109467c8a48f20f19d83d7c72968a3e | 2fb364d659ea53f7892be9ba5e036047 | 88 | | 2019-10-23 | stable | v1.9.1+hotfix.6 | 68587a0916366e9512a78df22c44163d041dd5f3 | b863200c37df4ed378042de11c4e9ff34e4e58c9 | 1ef83b86ae637ffe7359173804cbc6d3fa25e6db | c8562f0ee0ebc38ba217c7955956d1cb | 89 | | 2019-10-17 | stable | v1.9.1+hotfix.5 | 1aedbb1835bd6eb44550293d57d4d124f19901f0 | b863200c37df4ed378042de11c4e9ff34e4e58c9 | 1ef83b86ae637ffe7359173804cbc6d3fa25e6db | c8562f0ee0ebc38ba217c7955956d1cb | 90 | | 2019-10-10 | beta | v1.10.7 | e70236e36ce1d32067dc68eb55519ec3e14b6b01 | 9e6314d348f9b5521e3c66856324d7a9c4a928c9 | 1103600280676ea169a30d7a503e836671cdc553 | c3bbfe8f226120ad0569d7b78ed2d9ef | 91 | | 2019-10-01 | stable | v1.9.1+hotfix.4 | cc949a8e8b9cf394b9290a8e80f87af3e207dce5 | b863200c37df4ed378042de11c4e9ff34e4e58c9 | 1ef83b86ae637ffe7359173804cbc6d3fa25e6db | c8562f0ee0ebc38ba217c7955956d1cb | 92 | | 2019-09-27 | beta | v1.9.1+hotfix.4 | cc949a8e8b9cf394b9290a8e80f87af3e207dce5 | b863200c37df4ed378042de11c4e9ff34e4e58c9 | 1ef83b86ae637ffe7359173804cbc6d3fa25e6db | c8562f0ee0ebc38ba217c7955956d1cb | 93 | | 2019-09-26 | beta | v1.9.1+hotfix.3 | a72edc27064c2cbfbbae17ea1695333e1b3d9595 | b863200c37df4ed378042de11c4e9ff34e4e58c9 | 1ef83b86ae637ffe7359173804cbc6d3fa25e6db | c8562f0ee0ebc38ba217c7955956d1cb | 94 | | 2019-09-10 | stable | v1.9.1+hotfix.2 | 2d2a1ffec95cc70a3218872a2cd3f8de4933c42f | b863200c37df4ed378042de11c4e9ff34e4e58c9 | 1ef83b86ae637ffe7359173804cbc6d3fa25e6db | c8562f0ee0ebc38ba217c7955956d1cb | 95 | | 2019-09-08 | beta | v1.9.1+hotfix.2 | 2d2a1ffec95cc70a3218872a2cd3f8de4933c42f | b863200c37df4ed378042de11c4e9ff34e4e58c9 | 1ef83b86ae637ffe7359173804cbc6d3fa25e6db | c8562f0ee0ebc38ba217c7955956d1cb | 96 | | 2019-09-04 | beta | v1.9.1+hotfix.1 | a1fb3fabec40144f57344635c37c28eed4fb122b | cc88fa45dbf4c55bc23cecea17fb90f43bccf588 | 74e4033d316abb6cc2290051ec85534caf4dca54 | c8562f0ee0ebc38ba217c7955956d1cb | 97 | | 2019-08-08 | beta | v1.8.3 | e4ebcdf6f4facee5779c38a04d91d08dc58ea7a4 | 38ac5f30a7026e870619c2e8e8c99c070d74036f | 0ca1582afdb057e8a701908c1c527e0a56a7b5b2 | 34948253b59d5a56b2ec161e17975a4e | 98 | | 2019-07-24 | stable | v1.7.8+hotfix.4 | 20e59316b8b8474554b38493b8ca888794b0234a | fee001c93f25a1e7258e762781a7361f122d29f5 | 7340a569caac6431d8698dc3788579b57ffcf0c6 | 1d7acad1540192ac459cf60344efb7c1 | 99 | | 2019-07-19 | beta | v1.7.8+hotfix.4 | 20e59316b8b8474554b38493b8ca888794b0234a | fee001c93f25a1e7258e762781a7361f122d29f5 | 7340a569caac6431d8698dc3788579b57ffcf0c6 | 1d7acad1540192ac459cf60344efb7c1 | 100 | | 2019-07-09 | stable | v1.7.8+hotfix.3 | b712a172f9694745f50505c93340883493b505e5 | 54ad777fd29b031b87c7a68a6637fb48c0932862 | 7340a569caac6431d8698dc3788579b57ffcf0c6 | 1d7acad1540192ac459cf60344efb7c1 | 101 | | 2019-07-09 | beta | v1.7.8+hotfix.3 | b712a172f9694745f50505c93340883493b505e5 | 54ad777fd29b031b87c7a68a6637fb48c0932862 | 7340a569caac6431d8698dc3788579b57ffcf0c6 | 1d7acad1540192ac459cf60344efb7c1 | 102 | | 2019-07-08 | stable | v1.7.8+hotfix.2 | 2e540931f73593e35627592ca4f9a4ca3035ed31 | b1cb0d9e9b44393efeb735f664672a74732cdc8b | 7340a569caac6431d8698dc3788579b57ffcf0c6 | 1d7acad1540192ac459cf60344efb7c1 | 103 | | 2019-07-02 | beta | v1.7.8+hotfix.2 | 2e540931f73593e35627592ca4f9a4ca3035ed31 | b1cb0d9e9b44393efeb735f664672a74732cdc8b | 7340a569caac6431d8698dc3788579b57ffcf0c6 | 1d7acad1540192ac459cf60344efb7c1 | 104 | | 2019-05-30 | beta | v1.6.3 | bc7bc940836f1f834699625426795fd6f07c18ec | 8dc3a4cde2075a4f5458fd0eb199627f5124508d | e3edfd36b2aa7ff4e98fe541ef5666ef2e70d17e | c89592e3e4956c33956c8ba0f691dbd0 | 105 | | 2019-05-07 | stable | v1.5.4-hotfix.2 | 7a4c33425ddd78c54aba07d86f3f9a4a0051769b | 52c7a1e849a170be4b2b2fe34142ca2c0a6fea1f | a1668566e563aef64025d0af88a099cbbe847b7e | eed485c757fba5d731e4054412c99f2e | 106 | | 2019-05-02 | beta | v1.5.4-hotfix.2 | 7a4c33425ddd78c54aba07d86f3f9a4a0051769b | 52c7a1e849a170be4b2b2fe34142ca2c0a6fea1f | a1668566e563aef64025d0af88a099cbbe847b7e | eed485c757fba5d731e4054412c99f2e | 107 | | 2019-04-30 | beta | v1.5.4-hotfix.1 | 09cbc34a0b19cef287a82aa4b9966d525369eecc | 52c7a1e849a170be4b2b2fe34142ca2c0a6fea1f | a1668566e563aef64025d0af88a099cbbe847b7e | eed485c757fba5d731e4054412c99f2e | 108 | | 2019-04-27 | beta | v1.5.4 | b593f5167bce84fb3cad5c258477bf3abc1b14eb | ca31a7c57bada458fa7f5c0d3f36bc1af4ccbc79 | cf4444b803a376bb1ad6442e1f09434a4d58167b | eed485c757fba5d731e4054412c99f2e | 109 | | 2019-04-12 | beta | v1.4.9-hotfix.1 | 88fa7ea4031f5c86225573e58e5558dc4ea1c251 | 4737fc5cd89b8f0136e927b00f2e159444b95a73 | f6768b6fb3e58bb704aca1e22a7ffd11e7ff07cb | f630ecdf457e27dd24d3b9e0a6bc1c13 | 110 | | 2019-03-15 | beta | v1.3.8 | e5b1ed7a7f7b85c1877e09a9495681f719be5578 | f4951df193a7966f9ed4da43d555eee0913d84d1 | 571ea80e1101e706980ea8aefa7fc18a0c8ba2ec | 9a66dcb2da955dffdbdb0eafa0288784 | 111 | | 2019-02-26 | stable | v1.2.1 | 8661d8aecd626f7f57ccbcb735553edc05a2e713 | 3757390fa4b00d2d261bfdf5182d2e87c9113ff9 | 0a7dcf17eb5f2450480527d6ad1e201fb47f1e36 | 0c73eb70aa4d30f450273cb424be8c62 | 112 | | 2019-02-26 | beta | v1.2.1 | 8661d8aecd626f7f57ccbcb735553edc05a2e713 | 3757390fa4b00d2d261bfdf5182d2e87c9113ff9 | 0a7dcf17eb5f2450480527d6ad1e201fb47f1e36 | 0c73eb70aa4d30f450273cb424be8c62 | 113 | | 2019-01-29 | beta | v1.1.8 | 985ccb6d14c6ce5ce74823a4d366df2438eac44f | 7112b72cc229e05d36716c3d7739885d3ffa72e6 | ec86471ccc47a62df8b4009e1fb37c66ff9dc91b | 317d4c7e607b1fd7d682c0010aadf1d0 | 114 | | 2018-12-04 | stable | v1.0.0 | 5391447fae6209bb21a89e6a5a6583cac1af9b4b | 7375a0f414bde4bc941e623482221db2fc8c4ab5 | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c | 115 | | 2018-12-04 | beta | v1.0.0 | 5391447fae6209bb21a89e6a5a6583cac1af9b4b | 7375a0f414bde4bc941e623482221db2fc8c4ab5 | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c | 116 | | 2018-11-30 | beta | v0.11.13 | 58c8489fcdb4e4ef6c010117584c9b23d15221aa | 7375a0f414bde4bc941e623482221db2fc8c4ab5 | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c | 117 | | 2018-11-29 | beta | v0.11.12 | 06ec8d3b41beb469d845626e36a246ee09300fa7 | 72c7a7567228cdaf8b7aa4a9e3d212ef9d4cc0ed | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c | 118 | | 2018-11-28 | beta | v0.11.11 | e7680128afbbde443d69f89bb264015276a8475a | be973ea196127383f356d39be466e2f3bd163083 | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c | 119 | | 2018-11-27 | beta | v0.11.10 | c27c4a265e9ad295e5d434cddabbc639b2e3542d | eebc6a58958587203f624528ff46b1d4b2b0f2fa | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c | 120 | | 2018-11-21 | beta | v0.11.9 | d48e6e433cc5ca67b24b19f70aaa197e84ba63c1 | 5c8147450db52b81232c138b9f9a65a8f9c61862 | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c | 121 | | 2018-11-20 | beta | v0.11.8 | f5b02e3c05ed1ab31e890add84fb56e35de2d392 | 1baf081343530dbaa8bec378fe1ce26b4897c23f | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c | 122 | | 2018-11-19 | beta | v0.11.7 | 7a005e1dcda665ace7241a24e79fae1a71f17b18 | 2e06da3df9cb370795f49747fdfd295b8168c133 | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c | 123 | | 2018-11-13 | beta | v0.11.3 | 72bf075e8d6961d2ca6df462b2228954f8d0e73a | 5646e86a6f442dc6f4158ae7010ab13d72a0b356 | 9c07fb64c48adb3d6fde50bab6b8b641c5b67683 | d124ce50a30741a188e41c52c424c127 | 124 | | 2018-11-06 | beta | v0.10.2 | d8cbb80206db06d151206f8b599b7dde5a386a2d | 6c2ade9fa2b555899530a31cc8cbd1dff3bf5eea | bf26f760b1bb3d5fea6bda110f6a17b590364120 | 46b2bfb57b5647c5f7527ff9aa56c69b | 125 | | 2018-10-09 | beta | v0.9.4 | f37c235c32fc15babe6dc7b7bc2ee4387e5ecf92 | 74625aed323d04f2add0410e84038d250f51b616 | a2eb050044eec93f0844667b8b6132e858467e4e | a135b1a4c6790a272609c9405379bc63 | 126 | | 2018-09-18 | beta | v0.8.2 | 5ab9e70727d858def3a586db7fb98ee580352957 | 58a1894a1cd798de2f9a206f157a90d45944028b | 760a9690c22ec3f3d163173737f9949f97e6e02a | 1b444eb4796616ea2f955f3f1e440801 | 127 | | 2018-09-05 | beta | v0.7.3 | 3b309bda072a6b326e8aa4591a5836af600923ce | af42b6dc95bd9f719e43c4e9f29a00640f0f0bba | ccb16f72824374163562364bf19dd18e8a882fab | d0cf500478165d79bdefccb0847ffb33 | 128 | | 2018-08-28 | beta | v0.6.0 | 9299c02cf708497d6f72edda8efae0bb8340660e | e3687f70c7ece72000b32ee1b3c02755ba5361ac | be6309690fd60284a87f3258a740c7c30efb1092 | 35224090f45cbae1402bafd97a112a40 | 129 | | 2018-06-19 | beta | v0.5.1 | c7ea3ca377e909469c68f2ab878a5bc53d3cf66b | 1ed25ca7b7e3e3e8047df050bba4174074c9b336 | f981f097602ca434ce0a36b1f704723cad105fb6 | 04cb98b58e7d69109004454c20b492f7 | 130 | | 2018-05-22 | beta | v0.4.4 | f9bb4289e9fd861d70ae78bcc3a042ef1b35cc9d | 06afdfe54ebef9168a90ca00a6721c2d36e6aafa | 46ab040e589adc5200370dec7952ce5150850822 | 1b155eedbb3a2640a88d2e54d2f2d204 | 131 | | 2018-05-07 | beta | v0.3.2 | 44b7e7d3f42f050a79712daab253af06e9daf530 | 09d05a38912a3c1a906e95099cac9a7e14fae85f | fe606f890b0a311da802c78b0af414a3c2087a79 | 39646f79e9336fb65ac68c8568544c92 | 132 | | 2018-04-24 | beta | v0.3.1 | 12bbaba9ae044d0ea77da4dd5e4db15eed403f09 | 09d05a38912a3c1a906e95099cac9a7e14fae85f | fe606f890b0a311da802c78b0af414a3c2087a79 | 39646f79e9336fb65ac68c8568544c92 | 133 | | 2018-04-09 | beta | v0.2.8 | b397406561f5e7a9c94e28f58d9e49fca0dd58b7 | c903c217a1a8206cdebdab1703b52ec6180edf37 | 52afcba357ad398e8c24f3e3363ac6ff5293df63 | d72bf5003e5924b61a7943f58e7b6814 | 134 | | 2018-04-02 | beta | v0.2.3 | 5a58b36e36b8d7aace89d3950e6deb307956a6a0 | e61bb9ac3a3fd789754e2e54220bcfc27076a857 | 290c576264faa096a0b3206c71b2435309d9f904 | 0d015018f02a6de0c92ac1ac59191b55 | 135 | | 2018-03-15 | beta | v0.1.5 | 3ea4d06340a97a1e9d7cae97567c64e0569dcaa2 | ead227f118077d1f2b57842a32abaf105b573b8a | 0b4f01f7593c8c42a77dc27d1fc234c95eacc88e | 9bc066b6e8ef5a9f7224c2926c6ad2f4 | 136 | 137 | `dev` channel not included for brevity. See `list_versions.ipynb` for generation of this table. 138 | -------------------------------------------------------------------------------- /darter/data/type_data.py: -------------------------------------------------------------------------------- 1 | def make_type_data(is_precompiled, is_product): 2 | not_in_precompiled = lambda x: [] if is_precompiled else [x] 3 | not_in_product = lambda x: [] if is_product else [x] 4 | 5 | FIELDS = { 6 | # The root type (not raw object -- defined in separate header) 7 | 'ObjectStore': [ 8 | ['Class', 'object_class', None], 9 | ['Type', 'object_type', None], 10 | ['Class', 'null_class', None], 11 | ['Type', 'null_type', None], 12 | ['Type', 'function_type', None], 13 | ['Type', 'type_type', None], 14 | ['Class', 'closure_class', None], 15 | ['Type', 'number_type', None], 16 | ['Type', 'int_type', None], 17 | ['Class', 'integer_implementation_class', None], 18 | ['Type', 'int64_type', None], 19 | ['Class', 'smi_class', None], 20 | ['Type', 'smi_type', None], 21 | ['Class', 'mint_class', None], 22 | ['Type', 'mint_type', None], 23 | ['Class', 'double_class', None], 24 | ['Type', 'double_type', None], 25 | ['Type', 'float32x4_type', None], 26 | ['Type', 'int32x4_type', None], 27 | ['Type', 'float64x2_type', None], 28 | ['Type', 'string_type', None], 29 | ['TypeArguments', 'type_argument_int', None], 30 | ['TypeArguments', 'type_argument_double', None], 31 | ['TypeArguments', 'type_argument_string', None], 32 | ['TypeArguments', 'type_argument_string_dynamic', None], 33 | ['TypeArguments', 'type_argument_string_string', None], 34 | ['Class', 'compiletime_error_class', None], 35 | ['Class', 'pragma_class', None], 36 | ['Field', 'pragma_name', None], 37 | ['Field', 'pragma_options', None], 38 | ['Class', 'future_class', None], 39 | ['Class', 'completer_class', None], 40 | ['Class', 'symbol_class', None], 41 | ['Class', 'one_byte_string_class', None], 42 | ['Class', 'two_byte_string_class', None], 43 | ['Class', 'external_one_byte_string_class', None], 44 | ['Class', 'external_two_byte_string_class', None], 45 | ['Type', 'bool_type', None], 46 | ['Class', 'bool_class', None], 47 | ['Class', 'array_class', None], 48 | ['Type', 'array_type', None], 49 | ['Class', 'immutable_array_class', None], 50 | ['Class', 'growable_object_array_class', None], 51 | ['Class', 'linked_hash_map_class', None], 52 | ['Class', 'linked_hash_set_class', None], 53 | ['Class', 'float32x4_class', None], 54 | ['Class', 'int32x4_class', None], 55 | ['Class', 'float64x2_class', None], 56 | ['Class', 'error_class', None], 57 | ['Class', 'weak_property_class', None], 58 | ['Array', 'symbol_table', None], 59 | ['Array', 'canonical_types', None], 60 | ['Array', 'canonical_type_arguments', None], 61 | ['Library', 'async_library', None], 62 | ['Library', 'builtin_library', None], 63 | ['Library', 'core_library', None], 64 | ['Library', 'collection_library', None], 65 | ['Library', 'convert_library', None], 66 | ['Library', 'developer_library', None], 67 | ['Library', 'ffi_library', None], 68 | ['Library', '_internal_library', None], 69 | ['Library', 'isolate_library', None], 70 | ['Library', 'math_library', None], 71 | ['Library', 'mirrors_library', None], 72 | ['Library', 'native_wrappers_library', None], 73 | ['Library', 'profiler_library', None], 74 | ['Library', 'root_library', None], 75 | ['Library', 'typed_data_library', None], 76 | ['Library', '_vmservice_library', None], 77 | ['GrowableObjectArray', 'libraries', None], 78 | ['Array', 'libraries_map', None], 79 | ['GrowableObjectArray', 'closure_functions', None], 80 | ['GrowableObjectArray', 'pending_classes', None], 81 | ['GrowableObjectArray', 'pending_unevaluated_const_fields', None], 82 | ['GrowableObjectArray', 'pending_deferred_loads', None], 83 | ['GrowableObjectArray', 'resume_capabilities', None], 84 | ['GrowableObjectArray', 'exit_listeners', None], 85 | ['GrowableObjectArray', 'error_listeners', None], 86 | ['Instance', 'stack_overflow', None], 87 | ['Instance', 'out_of_memory', None], 88 | ['UnhandledException', 'preallocated_unhandled_exception', None], 89 | ['StackTrace', 'preallocated_stack_trace', None], 90 | ['Function', 'lookup_port_handler', None], 91 | ['Function', 'handle_message_function', None], 92 | ['Function', 'growable_list_factory', None], 93 | ['Function', 'simple_instance_of_function', None], 94 | ['Function', 'simple_instance_of_true_function', None], 95 | ['Function', 'simple_instance_of_false_function', None], 96 | ['Function', 'async_clear_thread_stack_trace', None], 97 | ['Function', 'async_set_thread_stack_trace', None], 98 | ['Function', 'async_star_move_next_helper', None], 99 | ['Function', 'complete_on_async_return', None], 100 | ['Class', 'async_star_stream_controller', None], 101 | ['ObjectPool', 'global_object_pool', None], 102 | ['Array', 'library_load_error_table', None], 103 | ['Array', 'unique_dynamic_targets', None], 104 | ['GrowableObjectArray', 'megamorphic_cache_table', None], 105 | ['Code', 'build_method_extractor_code', None], 106 | ['Code', 'null_error_stub_with_fpu_regs_stub', None], 107 | ['Code', 'null_error_stub_without_fpu_regs_stub', None], 108 | ['Code', 'stack_overflow_stub_with_fpu_regs_stub', None], 109 | ['Code', 'stack_overflow_stub_without_fpu_regs_stub', None], 110 | ['Code', 'write_barrier_wrappers_stub', None], 111 | ['Code', 'array_write_barrier_stub', None], 112 | ['Code', 'megamorphic_miss_code', None], 113 | ['Function', 'megamorphic_miss_function', None], 114 | ['Array', 'code_order_table', None], 115 | ['Array', 'obfuscation_map', None], 116 | ['GrowableObjectArray', 'changed_in_last_reload', None], 117 | ['Class', 'ffi_pointer_class', None], 118 | ['Class', 'ffi_native_type_class', None], 119 | ['Class', 'ffi_struct_class', None], 120 | ['Object', 'ffi_as_function_internal', None], 121 | ], 122 | 123 | # Manually parsed types: 124 | 'Function': [ 125 | ['String', 'name', None], 126 | ['Object', 'owner', 'Class or patch class or mixin class\n where this function is defined.'], 127 | ['AbstractType', 'result_type', None], 128 | ['Array', 'parameter_types', None], 129 | ['Array', 'parameter_names', None], 130 | ['TypeArguments', 'type_parameters', 'Array of TypeParameter.'], 131 | ['Object', 'data', 'Additional data specific to the function kind. See\n Function::set_data() for details.'], 132 | ], 133 | 'Field': [ 134 | ['String', 'name', None], 135 | ['Object', 'owner', 'Class or patch class or mixin class\n where this field is defined or original field.'], 136 | ['AbstractType', 'type', None], 137 | ['(RawInstance* static_value) Value for static fields | (RawSmi* offset) Offset in words for instance fields.', 'value', None], 138 | ['Function', 'initializer_function', 'Static initializer function.'], 139 | *not_in_precompiled(['Instance', 'saved_initial_value', 'Saved initial value - static fields.']), 140 | ['Smi', 'guarded_list_length', None], 141 | ['Array', 'dependent_code', None], 142 | ], 143 | 'Code': [ 144 | ['ObjectPool', 'object_pool', 'Accessed from generated code.'], 145 | # parsed differently: ['Instructions', 'instructions', 'Accessed from generated code.'], 146 | ['Object', 'owner', 'Function, Null, or a Class.'], 147 | ['ExceptionHandlers', 'exception_handlers', None], 148 | ['PcDescriptors', 'pc_descriptors', None], 149 | ['(RawTypedData* catch_entry_moves_maps) | (RawSmi* variables)', 'catch_entry', None], 150 | ['Array', 'stackmaps', None], 151 | ['Array', 'inlined_id_to_function', None], 152 | ['CodeSourceMap', 'code_source_map', None], 153 | # parsed differently: *not_in_precompiled(['Instructions', 'active_instructions', None]), 154 | *not_in_precompiled(['Array', 'deopt_info_array', None]), 155 | *not_in_precompiled(['Array', 'static_calls_target_table', '\n (code-offset, function, code) triples.']), 156 | *not_in_product(['Object', 'return_address_metadata', '\n If return_address_metadata_ is a Smi, it is the offset to the prologue.\n Else, return_address_metadata_ is null.']), 157 | # not in snapshot: *not_in_product(['LocalVarDescriptors', 'var_descriptors', None]), 158 | # not in snapshot: *not_in_product(['Array', 'comments', None]), 159 | ], 160 | 'Bytecode': [ 161 | ['ObjectPool', 'object_pool', None], 162 | ['Function', 'function', None], 163 | ['Array', 'closures', None], 164 | ['ExceptionHandlers', 'exception_handlers', None], 165 | ['PcDescriptors', 'pc_descriptors', None], 166 | *not_in_product(['LocalVarDescriptors', 'var_descriptors', None]), 167 | ], 168 | 'ICData': [ 169 | ['Array', 'entries', 'Contains class-ids, target and count.'], 170 | ['String', 'target_name', 'Name of target function.'], 171 | ['Array', 'args_descriptor', 'Arguments descriptor.'], 172 | *not_in_precompiled(['AbstractType', 'receivers_static_type', 'Static type of the receiver, if instance call and available.']), 173 | ['Object', 'owner', 'Parent/calling function or original IC of cloned IC.'], 174 | ], 175 | 'TypedDataView': [ 176 | ['Smi', 'length', None], 177 | ['TypedDataBase', 'typed_data', None], 178 | ['Smi', 'offset_in_bytes', None], 179 | ], 180 | 'ExternalTypedData': [ 181 | ['Smi', 'length', None], 182 | ], 183 | 'RegExp': [ 184 | ['Smi', 'num_bracket_expressions', None], 185 | ['Array', 'capture_name_map', None], 186 | ['String', 'pattern', 'Pattern to be used for matching.'], 187 | ['(RawFunction* function) | (RawTypedData* bytecode)', 'one_byte', None], 188 | ['(RawFunction* function) | (RawTypedData* bytecode)', 'two_byte', None], 189 | ['Function', 'external_one_byte_function', None], 190 | ['Function', 'external_two_byte_function', None], 191 | ['(RawFunction* function) | (RawTypedData* bytecode)', 'one_byte_sticky', None], 192 | ['(RawFunction* function) | (RawTypedData* bytecode)', 'two_byte_sticky', None], 193 | ['Function', 'external_one_byte_sticky_function', None], 194 | ['Function', 'external_two_byte_sticky_function', None], 195 | ], 196 | 'Type': [ 197 | ['Code', 'type_test_stub', None], 198 | ['Smi', 'type_class_id', None], 199 | ['TypeArguments', 'arguments', None], 200 | ['Smi', 'hash', None], 201 | ['Function', 'signature', 'If not null, this type is a function type.'], 202 | ], 203 | 204 | # Variable-length types that were not defined here: 205 | # LocalVarDescriptors 206 | # Context 207 | # TypeArguments 208 | # TypedData 209 | # Array 210 | 211 | # Automatically parsed types: 212 | 'Class': [ 213 | ['String', 'name', None], 214 | ['String', 'user_name', None], 215 | ['Array', 'functions', None], 216 | ['Array', 'functions_hash_table', None], 217 | ['Array', 'fields', None], 218 | ['Array', 'offset_in_words_to_field', None], 219 | ['Array', 'interfaces', 'Array of AbstractType.'], 220 | ['Script', 'script', None], 221 | ['Library', 'library', None], 222 | ['TypeArguments', 'type_parameters', 'Array of TypeParameter.'], 223 | ['AbstractType', 'super_type', None], 224 | ['Function', 'signature_function', 'Associated function for typedef class.'], 225 | ['Array', 'constants', 'Canonicalized const instances of this class.'], 226 | ['Type', 'declaration_type', 'Declaration type for this class.'], 227 | ['Array', 'invocation_dispatcher_cache', 'Cache for dispatcher functions.'], 228 | ['Code', 'allocation_stub', 'Stub code for allocation of instances.'], 229 | ['GrowableObjectArray', 'direct_implementors', 'Array of Class.'], 230 | ['GrowableObjectArray', 'direct_subclasses', 'Array of Class.'], 231 | ['Array', 'dependent_code', 'CHA optimized codes.'], 232 | ], 233 | 'PatchClass': [ 234 | ['Class', 'patched_class', None], 235 | ['Class', 'origin_class', None], 236 | ['Script', 'script', None], 237 | ['ExternalTypedData', 'library_kernel_data', None], 238 | ], 239 | 'ClosureData': [ 240 | ['ContextScope', 'context_scope', None], 241 | ['Function', 'parent_function', 'Enclosing function of this local function.'], 242 | ['Type', 'signature_type', None], 243 | ['Instance', 'closure', 'Closure object for static implicit closures.'], 244 | ], 245 | 'SignatureData': [ 246 | ['Function', 'parent_function', 'Enclosing function of this sig. function.'], 247 | ['Type', 'signature_type', None], 248 | ], 249 | 'RedirectionData': [ 250 | ['Type', 'type', None], 251 | ['String', 'identifier', None], 252 | ['Function', 'target', None], 253 | ], 254 | 'FfiTrampolineData': [ 255 | ['Type', 'signature_type', None], 256 | ['Function', 'c_signature', None], 257 | ['Function', 'callback_target', None], 258 | ['Instance', 'callback_exceptional_return', None], 259 | ], 260 | 'Script': [ 261 | ['String', 'url', None], 262 | ['String', 'resolved_url', None], 263 | ['Array', 'compile_time_constants', None], 264 | ['TypedData', 'line_starts', None], 265 | ['Array', 'debug_positions', None], 266 | ['Array', 'yield_positions', None], 267 | ['KernelProgramInfo', 'kernel_program_info', None], 268 | ['String', 'source', None], 269 | ], 270 | 'Library': [ 271 | ['String', 'name', None], 272 | ['String', 'url', None], 273 | ['String', 'private_key', None], 274 | ['Array', 'dictionary', 'Top-level names in this library.'], 275 | ['GrowableObjectArray', 'metadata', 'Metadata on classes, methods etc.'], 276 | ['Class', 'toplevel_class', 'Class containing top-level elements.'], 277 | ['GrowableObjectArray', 'owned_scripts', None], 278 | ['Array', 'imports', 'List of Namespaces imported without prefix.'], 279 | ['Array', 'exports', 'List of re-exported Namespaces.'], 280 | ['Instance', 'load_error', 'Error iff load_state_ == kLoadError.'], 281 | ['ExternalTypedData', 'kernel_data', None], 282 | ['Array', 'resolved_names', 'Cache of resolved names in library scope.'], 283 | ['Array', 'exported_names', 'Cache of exported names by library.'], 284 | ['Array', 'loaded_scripts', 'Array of scripts loaded in this library.'], 285 | ], 286 | 'Namespace': [ 287 | ['Library', 'library', 'library with name dictionary.'], 288 | ['Array', 'show_names', 'list of names that are exported.'], 289 | ['Array', 'hide_names', 'blacklist of names that are not exported.'], 290 | ['Field', 'metadata_field', 'remembers the token pos of metadata if any,\n and the metadata values if computed.\n'], 291 | ], 292 | 'KernelProgramInfo': [ 293 | ['TypedData', 'string_offsets', None], 294 | ['ExternalTypedData', 'string_data', None], 295 | ['TypedData', 'canonical_names', None], 296 | ['ExternalTypedData', 'metadata_payloads', None], 297 | ['ExternalTypedData', 'metadata_mappings', None], 298 | ['Array', 'scripts', None], 299 | ['Array', 'constants', None], 300 | ['Array', 'bytecode_component', None], 301 | ['GrowableObjectArray', 'potential_natives', None], 302 | ['GrowableObjectArray', 'potential_pragma_functions', None], 303 | ['ExternalTypedData', 'constants_table', None], 304 | ], 305 | 'ExceptionHandlers': [ 306 | ['Array', 'handled_types_data', None], 307 | ], 308 | 'ParameterTypeCheck': [ 309 | ['AbstractType', 'param', None], 310 | ['AbstractType', 'type_or_bound', None], 311 | ['String', 'name', None], 312 | ['SubtypeTestCache', 'cache', None], 313 | ], 314 | 'SingleTargetCache': [ 315 | ['Code', 'target', None], 316 | ], 317 | 'UnlinkedCall': [ 318 | ['String', 'target_name', None], 319 | ['Array', 'args_descriptor', None], 320 | ], 321 | 'MegamorphicCache': [ 322 | ['Array', 'buckets', None], 323 | ['Smi', 'mask', None], 324 | ['String', 'target_name', 'Name of target function.'], 325 | ['Array', 'args_descriptor', 'Arguments descriptor.'], 326 | ], 327 | 'SubtypeTestCache': [ 328 | ['Array', 'cache', None], 329 | ], 330 | 'ApiError': [ 331 | ['String', 'message', None], 332 | ], 333 | 'LanguageError': [ 334 | ['Error', 'previous_error', 'May be null.'], 335 | ['Script', 'script', None], 336 | ['String', 'message', None], 337 | ['String', 'formatted_message', "Incl. previous error's formatted message."], 338 | ], 339 | 'UnhandledException': [ 340 | ['Instance', 'exception', None], 341 | ['Instance', 'stacktrace', None], 342 | ], 343 | 'UnwindError': [ 344 | ['String', 'message', None], 345 | ], 346 | 'LibraryPrefix': [ 347 | ['String', 'name', 'Library prefix name.'], 348 | ['Library', 'importer', 'Library which declares this prefix.'], 349 | ['Array', 'imports', 'Libraries imported with this prefix.'], 350 | ['Array', 'dependent_code', 'Code that refers to deferred, unloaded\n library prefix.\n'], 351 | ], 352 | 'TypeRef': [ 353 | ['Code', 'type_test_stub', None], 354 | ['AbstractType', 'type', 'The referenced type.'], 355 | ], 356 | 'TypeParameter': [ 357 | ['Code', 'type_test_stub', None], 358 | ['String', 'name', None], 359 | ['Smi', 'hash', None], 360 | ['AbstractType', 'bound', 'ObjectType if no explicit bound specified.'], 361 | ['Function', 'parameterized_function', None], 362 | ], 363 | 'Closure': [ 364 | ['TypeArguments', 'instantiator_type_arguments', None], 365 | ['TypeArguments', 'function_type_arguments', None], 366 | ['TypeArguments', 'delayed_type_arguments', None], 367 | ['Function', 'function', None], 368 | ['Context', 'context', None], 369 | ['Smi', 'hash', None], 370 | ], 371 | 'GrowableObjectArray': [ 372 | ['TypeArguments', 'type_arguments', None], 373 | ['Smi', 'length', None], 374 | ['Array', 'data', None], 375 | ], 376 | 'LinkedHashMap': [ 377 | ['TypeArguments', 'type_arguments', None], 378 | ['TypedData', 'index', None], 379 | ['Smi', 'hash_mask', None], 380 | ['Array', 'data', None], 381 | ['Smi', 'used_data', None], 382 | ['Smi', 'deleted_keys', None], 383 | ], 384 | 'Pointer': [ 385 | ['TypeArguments', 'type_arguments', None], 386 | ['Integer', 'c_memory_address', None], 387 | ], 388 | 'ReceivePort': [ 389 | ['SendPort', 'send_port', None], 390 | ['Instance', 'handler', None], 391 | ], 392 | 'StackTrace': [ 393 | ['StackTrace', 'async_link', 'Link to parent async stack trace.'], 394 | ['Array', 'code_array', 'Code object for each frame in the stack trace.'], 395 | ['Array', 'pc_offset_array', 'Offset of PC for each frame.'], 396 | ], 397 | 'WeakProperty': [ 398 | ['Object', 'key', None], 399 | ['Object', 'value', None], 400 | ], 401 | 'MirrorReference': [ 402 | ['Object', 'referent', None], 403 | ], 404 | 'UserTag': [ 405 | ['String', 'label', None], 406 | ], 407 | } 408 | 409 | # Meanings: 410 | # -> to_snapshot() defined, always returns last field 411 | # True: to_snapshot() defined, returns last field for the three valid values 412 | # False: to_snapshot() not defined, at least on that class 413 | # (a,b,c): fields returned by to_snapshot() for kFull, kFullJIT, kFullAOT 414 | MAPPINGS = { 415 | 'ObjectStore': ('library_load_error_table', 'megamorphic_miss_function', 'megamorphic_miss_function'), 416 | 417 | 'Function': True, 418 | 'Field': ('guarded_list_length', 'dependent_code', 'initializer_function'), 419 | 'Code': False, 420 | 'ICData': ('owner', 'owner', 'args_descriptor'), 421 | 'TypeArguments': False, 422 | 'String': False, 423 | 'RawExternalTypedData': False, 424 | 425 | # Automatically parsed types: 426 | 'Class': ('direct_subclasses', 'dependent_code', 'allocation_stub'), 427 | 'PatchClass': ('library_kernel_data', 'library_kernel_data', 'script'), 428 | 'ClosureData': False, 429 | 'Script': ('kernel_program_info', 'kernel_program_info', 'url'), 430 | 'Library': ('kernel_data', 'kernel_data', 'load_error'), 431 | 'ExceptionHandlers': False, 432 | 'SingleTargetCache': False, 433 | 'SubtypeTestCache': False, 434 | 'ApiError': False, 435 | 'UnwindError': False, 436 | 'LibraryPrefix': ('imports', 'imports', 'importer'), 437 | 'LinkedHashMap': False, 438 | 'Pointer': False, 439 | 'ReceivePort': False, 440 | 'MirrorReference': False, 441 | 'UserTag': False, 442 | } 443 | 444 | return FIELDS, MAPPINGS 445 | -------------------------------------------------------------------------------- /utils/list_versions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 31, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import json\n", 10 | "import re\n", 11 | "import requests\n", 12 | "from subprocess import run\n", 13 | "from collections import defaultdict\n", 14 | "import runpy\n", 15 | "import tempfile" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": 4, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "data = json.loads(requests.get('https://storage.googleapis.com/flutter_infra/releases/releases_linux.json').text)\n", 25 | "flutter_versions = data['releases']" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 53, 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "# replace these\n", 35 | "flutter_repo_path = '../co/sdk/flutter'\n", 36 | "engine_repo_path = '../co/sdk/engine'\n", 37 | "dart_repo_path = '../co/sdk/dart-sdk/full-clone'\n", 38 | "\n", 39 | "def make_cache(fn):\n", 40 | " d = {}\n", 41 | " def caching_fn(x):\n", 42 | " if x not in d:\n", 43 | " d[x] = fn(x)\n", 44 | " return d[x]\n", 45 | " return caching_fn\n", 46 | "\n", 47 | "def get_engine_version(flutter_commit):\n", 48 | " p = run(['git', 'show', flutter_commit + ':bin/internal/engine.version'], check=True, capture_output=True, cwd=flutter_repo_path)\n", 49 | " return p.stdout.decode().strip()\n", 50 | "\n", 51 | "def get_dart_version(engine_commit):\n", 52 | " contents = run(['git', 'show', engine_commit + ':DEPS'], check=True, capture_output=True, cwd=engine_repo_path).stdout\n", 53 | " m = re.search(r\"'dart_revision': '([a-fA-F0-9]+)'\", contents.decode())\n", 54 | " return m.group(1)\n", 55 | "\n", 56 | "sv_template_file = tempfile.NamedTemporaryFile()\n", 57 | "sv_template_file.write('{{SNAPSHOT_HASH}}'.encode())\n", 58 | "sv_template_file.flush()\n", 59 | "sv_out_file = tempfile.NamedTemporaryFile()\n", 60 | "\n", 61 | "def get_snapshot_hash(dart_commit):\n", 62 | " run(['git', 'checkout', dart_commit], check=True, cwd=dart_repo_path)\n", 63 | " sv_out_file.seek(0)\n", 64 | " run(['python2', 'tools/make_version.py', '--input=' + sv_template_file.name, '--output=' + sv_out_file.name], check=True, cwd=dart_repo_path)\n", 65 | " return sv_out_file.read().decode()\n", 66 | "\n", 67 | "get_engine_version = make_cache(get_engine_version)\n", 68 | "get_dart_version = make_cache(get_dart_version)\n", 69 | "get_snapshot_hash = make_cache(get_snapshot_hash)\n", 70 | "\n", 71 | "#get_engine_version('master')\n", 72 | "#get_dart_version('11d756a62ed0ddf87a9ce20b219b55300ec6b67d')\n", 73 | "#get_snapshot_version('06536d68ca0f27528b0bf729f4b8d673ed14beda')" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": 60, 79 | "metadata": {}, 80 | "outputs": [ 81 | { 82 | "name": "stdout", 83 | "output_type": "stream", 84 | "text": [ 85 | "| Release date | Channel | Version | Commit | Engine commit | Dart SDK commit | Snapshot version |\n", 86 | "| ------------ | ------- | ------- | ------ | ------------- | --------------- | ---------------- |\n", 87 | "| 2020-10-08 | stable | 1.22.1 | f30b7f4db93ee747cd727df747941a28ead25ff5 | 75bef9f6c8ac2ed4e1e04cdfcd88b177d9f1850d | efd753621946a89008b76b76d85d54d1aa57fce8 | 8ee4ef7a67df9845fba331734198a953 |\n", 88 | "| 2020-10-08 | beta | 1.22.0-12.4.pre | f30b7f4db93ee747cd727df747941a28ead25ff5 | 75bef9f6c8ac2ed4e1e04cdfcd88b177d9f1850d | efd753621946a89008b76b76d85d54d1aa57fce8 | 8ee4ef7a67df9845fba331734198a953 |\n", 89 | "| 2020-10-01 | stable | 1.22.0 | d408d302e22179d598f467e11da5dd968dbdc9ec | 5babba6c4d25fa237bbf755ab85c9a0c50b3c6ec | 41eab9b49ccce8960f71c657861dc629f96295af | 8ee4ef7a67df9845fba331734198a953 |\n", 90 | "| 2020-09-29 | beta | 1.22.0-12.3.pre | d408d302e22179d598f467e11da5dd968dbdc9ec | 5babba6c4d25fa237bbf755ab85c9a0c50b3c6ec | 41eab9b49ccce8960f71c657861dc629f96295af | 8ee4ef7a67df9845fba331734198a953 |\n", 91 | "| 2020-09-28 | beta | 1.22.0-12.2.pre | 2bafdc822636426fa09afb43236400a60ea432b2 | f763b5b9b936872bc6c84b4395286ce684e3b431 | 4215dca724fb80de592f51a6cdba51e7638d1723 | 8ee4ef7a67df9845fba331734198a953 |\n", 92 | "| 2020-09-16 | beta | 1.22.0-12.1.pre | 8b3760638a189741cd9ca881aa2dd237c1df1be5 | 4654fc6cf6416daae78eac2c211ad84c46e21625 | 52130c19ca593b185ea9cf72b26b1d02455551ef | 8ee4ef7a67df9845fba331734198a953 |\n", 93 | "| 2020-09-15 | stable | 1.20.4 | fba99f6cf9a14512e461e3122c8ddfaa25394e89 | d1bc06f032f9d6c148ea6b96b48261d6f545004f | b07da893600eadc4efafc5a54b8f9533e43c0034 | 04645b6182fad3d68350d84669869ce5 |\n", 94 | "| 2020-09-02 | stable | 1.20.3 | 216dee60c0cc9449f0b29bcf922974d612263e24 | d1bc06f032f9d6c148ea6b96b48261d6f545004f | b07da893600eadc4efafc5a54b8f9533e43c0034 | 04645b6182fad3d68350d84669869ce5 |\n", 95 | "| 2020-08-28 | beta | 1.21.0-9.2.pre | 81a45ec2e5f80fa71d5135f1702ce540558b416d | 20a953183580250aac2e15d36007664118bda5ab | 2ea332979fbc9e8c42f9efe4a33dec83108c897b | 5f40b0a9f04b5018fa08a9b67fd316cd |\n", 96 | "| 2020-08-19 | beta | 1.21.0-9.1.pre | be9bc8cb3942bda5d8ef4e44b44616c470625e18 | 267070c17a6956de1a03dbe09cda56f0c485f41b | 7cb472077b56a99aef7233bb616ba200b4de8682 | 5f40b0a9f04b5018fa08a9b67fd316cd |\n", 97 | "| 2020-08-14 | beta | 1.20.2 | bbfbf1770cca2da7c82e887e4e4af910034800b6 | 9d5b21729ff53dbf8eadd8bc97e0e30d77abec95 | e940ff7819053ed8a4c04a4dfcda7df12e969331 | 04645b6182fad3d68350d84669869ce5 |\n", 98 | "| 2020-08-13 | stable | 1.20.2 | bbfbf1770cca2da7c82e887e4e4af910034800b6 | 9d5b21729ff53dbf8eadd8bc97e0e30d77abec95 | e940ff7819053ed8a4c04a4dfcda7df12e969331 | 04645b6182fad3d68350d84669869ce5 |\n", 99 | "| 2020-08-06 | stable | 1.20.1 | 2ae34518b87dd891355ed6c6ea8cb68c4d52bb9d | c8e3b9485386425213e2973126d6f57e7ed83c54 | 6eb17654b6501e2617c67854ed113ab550d2b3c7 | 04645b6182fad3d68350d84669869ce5 |\n", 100 | "| 2020-08-05 | stable | 1.20.0 | 840c9205b344a59e48a5926ee2d791cc5640924c | c8e3b9485386425213e2973126d6f57e7ed83c54 | 6eb17654b6501e2617c67854ed113ab550d2b3c7 | 04645b6182fad3d68350d84669869ce5 |\n", 101 | "| 2020-08-03 | beta | 1.20.0-7.4.pre | 916c3ac648aa0498a70f32b5fc4f6c51447628e3 | d6ee1499c27a156a797d9f1539ffb7892855c1d0 | e2ea2e82e8785e18df30b7a06ef7cbc73fd9a81a | 04645b6182fad3d68350d84669869ce5 |\n", 102 | "| 2020-07-29 | beta | 1.20.0-7.3.pre | e606910f28be51c8151f6169072afe3b3a8b3c5e | ac95267aef5175b3f6c3387d502070c68f588ad5 | e2ea2e82e8785e18df30b7a06ef7cbc73fd9a81a | 04645b6182fad3d68350d84669869ce5 |\n", 103 | "| 2020-07-21 | beta | 1.20.0-7.2.pre | a2bde82fbd52e09057a4146f46889f4e10342d32 | 60b269d898cbe0be27e9b9ba9d21eae97b887ab6 | 57f76512bee70f7e96abf9306797a5e256e02453 | 8b2ca977d1d2920b9839d1b60eade6a7 |\n", 104 | "| 2020-07-01 | beta | 1.19.0-4.3.pre | 8fe7655ed20ffd1395f68e30539a847a01a30351 | 9a28c3bcf40ce64fee61e807ee3e1395fd6bd954 | 5815449a2935fd4a0a9505fa0f7ca562a53f2465 | 59da07d9da5a83be4ce75b7913b63dbd |\n", 105 | "| 2020-07-01 | beta | 1.19.0-4.2.pre | 9b9b543d9265484132c798adaab6caca52055b08 | 9a28c3bcf40ce64fee61e807ee3e1395fd6bd954 | 5815449a2935fd4a0a9505fa0f7ca562a53f2465 | 59da07d9da5a83be4ce75b7913b63dbd |\n", 106 | "| 2020-07-01 | stable | 1.17.5 | 8af6b2f038c1172e61d418869363a28dffec3cb4 | ee76268252c22f5c11e82a7b87423ca3982e51a7 | caebd6700d5ece73b5566b33ff1daecb91dac500 | be7d304ff826e2dfac63538e227c3cc5 |\n", 107 | "| 2020-06-18 | stable | 1.17.4 | 1ad9baa8b99a2897c20f9e6e54d3b9b359ade314 | ee76268252c22f5c11e82a7b87423ca3982e51a7 | caebd6700d5ece73b5566b33ff1daecb91dac500 | be7d304ff826e2dfac63538e227c3cc5 |\n", 108 | "| 2020-06-10 | beta | 1.19.0-4.1.pre | f994b769743368b36b9c03fb359f62230b60ab92 | 9a28c3bcf40ce64fee61e807ee3e1395fd6bd954 | 5815449a2935fd4a0a9505fa0f7ca562a53f2465 | 59da07d9da5a83be4ce75b7913b63dbd |\n", 109 | "| 2020-06-04 | stable | 1.17.3 | b041144f833e05cf463b8887fa12efdec9493488 | ee76268252c22f5c11e82a7b87423ca3982e51a7 | caebd6700d5ece73b5566b33ff1daecb91dac500 | be7d304ff826e2dfac63538e227c3cc5 |\n", 110 | "| 2020-05-28 | stable | 1.17.2 | 5f21edf8b66e31a39133177319414395cc5b5f48 | b851c718295a896918dc93cb1ff14f2f895a1b90 | 9a618e5661665b8d687a28e6b1ec25e9177ec2d7 | be7d304ff826e2dfac63538e227c3cc5 |\n", 111 | "| 2020-05-14 | beta | 1.18.0-11.1.pre | 2738a1148ba6c9a6114df62358109407c3ef2553 | ef9215ceb2884ddf520d321bcd822d1461330876 | 14c6a93013bfcd311dfeac1c0ad0a5f9f4afb5ef | b58ead73b2c5dfec69565df469bba387 |\n", 112 | "| 2020-05-13 | stable | 1.17.1 | f7a6a7906be96d2288f5d63a5a54c515a6e987fe | 6bc433c6b6b5b98dcf4cc11aff31cdee90849f32 | ae8b2249c8fb43ff894eb6f858c41520cf9ce5cc | 74edb834fac3fcea79d7ac2d1d6f1fb2 |\n", 113 | "| 2020-05-06 | stable | 1.17.0 | e6b34c2b5c96bb95325269a29a84e83ed8909b5f | 540786dd51f112885a89792d678296b95e6622e5 | f12284ca12b9076dcc86f1524fefd57a7318ee52 | 74edb834fac3fcea79d7ac2d1d6f1fb2 |\n", 114 | "| 2020-05-02 | beta | 1.17.0-3.4.pre | e6b34c2b5c96bb95325269a29a84e83ed8909b5f | 540786dd51f112885a89792d678296b95e6622e5 | f12284ca12b9076dcc86f1524fefd57a7318ee52 | 74edb834fac3fcea79d7ac2d1d6f1fb2 |\n", 115 | "| 2020-04-29 | beta | 1.17.0-3.3.pre | 0da1ab09224f6c6d69fcff1195a3662fe7ad7534 | 376ad6a64b08aa26005e3f82aed26de2e290b572 | 7611c1abb42779a7ef1117527a7b86677b130c03 | 74edb834fac3fcea79d7ac2d1d6f1fb2 |\n", 116 | "| 2020-04-22 | beta | 1.17.0-3.2.pre | 2a7bc389f28d83c581f7ddd4601588a22e12512e | 4c8c31f591882b3c668992d2e9da761118899f38 | 91f39d8eb15aad876332637eea090e196ad295ff | 74edb834fac3fcea79d7ac2d1d6f1fb2 |\n", 117 | "| 2020-04-17 | stable | v1.12.13+hotfix.9 | f139b11009aeb8ed2a3a3aa8b0066e482709dde3 | af51afceb8886cc11e25047523c4e0c7e1f5d408 | 1c9356d8990a2a8c90c66097e20cb2f22e5cc267 | 20e5c4f7dc44368ac5a17643b93665f6 |\n", 118 | "| 2020-04-06 | beta | 1.17.0-dev.3.1 | d3ed9ec945f8869f0e136c357d0c2a6be2b60c98 | c9506cb8e93e5e8879152ff5c948b175abb5b997 | eea97179386a9ced7d68452cea158345f4019baa | 9e7cb7c9394c24c2398410b902673e13 |\n", 119 | "| 2020-03-17 | beta | v1.15.17 | 2294d75bfa8d067ba90230c0fc2268f3636d7584 | 5aff3119480996ca014ec0f8d26d74db617b5852 | 9983424a3c50c623730fd43b4ce263df660eb455 | ee91a9191a5286c31d91a89754ba36af |\n", 120 | "| 2020-02-11 | stable | v1.12.13+hotfix.8 | 0b8abb4724aa590dd0f429683339b1e045a1594d | e1e6ced81d029258d449bdec2ba3cddca9c2ca0c | 4cc36055d6803b899667feaedc1216a96e9d1c72 | 20e5c4f7dc44368ac5a17643b93665f6 |\n", 121 | "| 2020-02-05 | beta | v1.14.6 | fabeb2a16f1d008ab8230f450c49141d35669798 | c4229bfbbae455ad69c967be19aee3fadd6486e1 | fc3af737c75931908521e9c36358a151408d6084 | e739779cc1d28f0f697a92f2daf5f10f |\n", 122 | "| 2020-01-27 | stable | v1.12.13+hotfix.7 | 9f5ff2306bb3e30b2b98eee79cd231b1336f41f4 | a67792536ca236a971d0efbcfd7af4efb8f6c119 | a4911c63f4f72ba571d6db94de56007b09f4af99 | 20e5c4f7dc44368ac5a17643b93665f6 |\n", 123 | "| 2020-01-15 | beta | v1.13.6 | 659dc8129d4edb9166e9a0d600439d135740933f | bdc9708d235e582483d299642ad8682826ebb90d | c547f5d933e5a10e18b1b26b54a6249e88fa0f1c | 81662522448cdd4d02eb060669e5d48b |\n", 124 | "| 2019-12-11 | beta | v1.12.13+hotfix.6 | 18cd7a3601bcffb36fdf2f679f763b5e827c2e8e | 2994f7e1e682039464cb25e31a78b86a3c59b695 | a4911c63f4f72ba571d6db94de56007b09f4af99 | 20e5c4f7dc44368ac5a17643b93665f6 |\n", 125 | "| 2019-12-11 | stable | v1.12.13+hotfix.5 | 27321ebbad34b0a3fafe99fac037102196d655ff | 2994f7e1e682039464cb25e31a78b86a3c59b695 | a4911c63f4f72ba571d6db94de56007b09f4af99 | 20e5c4f7dc44368ac5a17643b93665f6 |\n", 126 | "| 2019-12-11 | beta | v1.12.13+hotfix.5 | 27321ebbad34b0a3fafe99fac037102196d655ff | 2994f7e1e682039464cb25e31a78b86a3c59b695 | a4911c63f4f72ba571d6db94de56007b09f4af99 | 20e5c4f7dc44368ac5a17643b93665f6 |\n", 127 | "| 2019-12-10 | beta | v1.12.13+hotfix.4 | fb60324e6fa791bedeade8be4773a42037e11f62 | ac9391978e7c0693b75a82c219e059b6ffee35c4 | a4911c63f4f72ba571d6db94de56007b09f4af99 | 20e5c4f7dc44368ac5a17643b93665f6 |\n", 128 | "| 2019-12-06 | beta | v1.12.13+hotfix.3 | 57f2df76d75cff290cbe2765b07db1ad3e67b50d | ac9391978e7c0693b75a82c219e059b6ffee35c4 | a4911c63f4f72ba571d6db94de56007b09f4af99 | 20e5c4f7dc44368ac5a17643b93665f6 |\n", 129 | "| 2019-12-04 | beta | v1.12.13+hotfix.2 | 4f54e46f56c2ffc92eb440dbdab1a7f8e722e593 | 6955b06cedb2425f4363f10642c9b0e63e496af0 | a4911c63f4f72ba571d6db94de56007b09f4af99 | 20e5c4f7dc44368ac5a17643b93665f6 |\n", 130 | "| 2019-12-03 | beta | v1.12.13+hotfix.1 | 5b07015393539822da275ab9a348b9e9ce92a29e | c1e322b685a81c11c16bddd22282925b7d0272e8 | a4911c63f4f72ba571d6db94de56007b09f4af99 | 20e5c4f7dc44368ac5a17643b93665f6 |\n", 131 | "| 2019-11-22 | beta | v1.11.0 | 856a90e67c9284124d44d2be6c785bacd3a1c772 | af04338413c3ed73316350f64248a152433073b6 | fa4379946109467c8a48f20f19d83d7c72968a3e | 2fb364d659ea53f7892be9ba5e036047 |\n", 132 | "| 2019-10-23 | stable | v1.9.1+hotfix.6 | 68587a0916366e9512a78df22c44163d041dd5f3 | b863200c37df4ed378042de11c4e9ff34e4e58c9 | 1ef83b86ae637ffe7359173804cbc6d3fa25e6db | c8562f0ee0ebc38ba217c7955956d1cb |\n", 133 | "| 2019-10-17 | stable | v1.9.1+hotfix.5 | 1aedbb1835bd6eb44550293d57d4d124f19901f0 | b863200c37df4ed378042de11c4e9ff34e4e58c9 | 1ef83b86ae637ffe7359173804cbc6d3fa25e6db | c8562f0ee0ebc38ba217c7955956d1cb |\n", 134 | "| 2019-10-10 | beta | v1.10.7 | e70236e36ce1d32067dc68eb55519ec3e14b6b01 | 9e6314d348f9b5521e3c66856324d7a9c4a928c9 | 1103600280676ea169a30d7a503e836671cdc553 | c3bbfe8f226120ad0569d7b78ed2d9ef |\n", 135 | "| 2019-10-01 | stable | v1.9.1+hotfix.4 | cc949a8e8b9cf394b9290a8e80f87af3e207dce5 | b863200c37df4ed378042de11c4e9ff34e4e58c9 | 1ef83b86ae637ffe7359173804cbc6d3fa25e6db | c8562f0ee0ebc38ba217c7955956d1cb |\n", 136 | "| 2019-09-27 | beta | v1.9.1+hotfix.4 | cc949a8e8b9cf394b9290a8e80f87af3e207dce5 | b863200c37df4ed378042de11c4e9ff34e4e58c9 | 1ef83b86ae637ffe7359173804cbc6d3fa25e6db | c8562f0ee0ebc38ba217c7955956d1cb |\n", 137 | "| 2019-09-26 | beta | v1.9.1+hotfix.3 | a72edc27064c2cbfbbae17ea1695333e1b3d9595 | b863200c37df4ed378042de11c4e9ff34e4e58c9 | 1ef83b86ae637ffe7359173804cbc6d3fa25e6db | c8562f0ee0ebc38ba217c7955956d1cb |\n", 138 | "| 2019-09-10 | stable | v1.9.1+hotfix.2 | 2d2a1ffec95cc70a3218872a2cd3f8de4933c42f | b863200c37df4ed378042de11c4e9ff34e4e58c9 | 1ef83b86ae637ffe7359173804cbc6d3fa25e6db | c8562f0ee0ebc38ba217c7955956d1cb |\n", 139 | "| 2019-09-08 | beta | v1.9.1+hotfix.2 | 2d2a1ffec95cc70a3218872a2cd3f8de4933c42f | b863200c37df4ed378042de11c4e9ff34e4e58c9 | 1ef83b86ae637ffe7359173804cbc6d3fa25e6db | c8562f0ee0ebc38ba217c7955956d1cb |\n", 140 | "| 2019-09-04 | beta | v1.9.1+hotfix.1 | a1fb3fabec40144f57344635c37c28eed4fb122b | cc88fa45dbf4c55bc23cecea17fb90f43bccf588 | 74e4033d316abb6cc2290051ec85534caf4dca54 | c8562f0ee0ebc38ba217c7955956d1cb |\n", 141 | "| 2019-08-08 | beta | v1.8.3 | e4ebcdf6f4facee5779c38a04d91d08dc58ea7a4 | 38ac5f30a7026e870619c2e8e8c99c070d74036f | 0ca1582afdb057e8a701908c1c527e0a56a7b5b2 | 34948253b59d5a56b2ec161e17975a4e |\n", 142 | "| 2019-07-24 | stable | v1.7.8+hotfix.4 | 20e59316b8b8474554b38493b8ca888794b0234a | fee001c93f25a1e7258e762781a7361f122d29f5 | 7340a569caac6431d8698dc3788579b57ffcf0c6 | 1d7acad1540192ac459cf60344efb7c1 |\n", 143 | "| 2019-07-19 | beta | v1.7.8+hotfix.4 | 20e59316b8b8474554b38493b8ca888794b0234a | fee001c93f25a1e7258e762781a7361f122d29f5 | 7340a569caac6431d8698dc3788579b57ffcf0c6 | 1d7acad1540192ac459cf60344efb7c1 |\n", 144 | "| 2019-07-09 | stable | v1.7.8+hotfix.3 | b712a172f9694745f50505c93340883493b505e5 | 54ad777fd29b031b87c7a68a6637fb48c0932862 | 7340a569caac6431d8698dc3788579b57ffcf0c6 | 1d7acad1540192ac459cf60344efb7c1 |\n", 145 | "| 2019-07-09 | beta | v1.7.8+hotfix.3 | b712a172f9694745f50505c93340883493b505e5 | 54ad777fd29b031b87c7a68a6637fb48c0932862 | 7340a569caac6431d8698dc3788579b57ffcf0c6 | 1d7acad1540192ac459cf60344efb7c1 |\n", 146 | "| 2019-07-08 | stable | v1.7.8+hotfix.2 | 2e540931f73593e35627592ca4f9a4ca3035ed31 | b1cb0d9e9b44393efeb735f664672a74732cdc8b | 7340a569caac6431d8698dc3788579b57ffcf0c6 | 1d7acad1540192ac459cf60344efb7c1 |\n", 147 | "| 2019-07-02 | beta | v1.7.8+hotfix.2 | 2e540931f73593e35627592ca4f9a4ca3035ed31 | b1cb0d9e9b44393efeb735f664672a74732cdc8b | 7340a569caac6431d8698dc3788579b57ffcf0c6 | 1d7acad1540192ac459cf60344efb7c1 |\n", 148 | "| 2019-05-30 | beta | v1.6.3 | bc7bc940836f1f834699625426795fd6f07c18ec | 8dc3a4cde2075a4f5458fd0eb199627f5124508d | e3edfd36b2aa7ff4e98fe541ef5666ef2e70d17e | c89592e3e4956c33956c8ba0f691dbd0 |\n", 149 | "| 2019-05-07 | stable | v1.5.4-hotfix.2 | 7a4c33425ddd78c54aba07d86f3f9a4a0051769b | 52c7a1e849a170be4b2b2fe34142ca2c0a6fea1f | a1668566e563aef64025d0af88a099cbbe847b7e | eed485c757fba5d731e4054412c99f2e |\n", 150 | "| 2019-05-02 | beta | v1.5.4-hotfix.2 | 7a4c33425ddd78c54aba07d86f3f9a4a0051769b | 52c7a1e849a170be4b2b2fe34142ca2c0a6fea1f | a1668566e563aef64025d0af88a099cbbe847b7e | eed485c757fba5d731e4054412c99f2e |\n", 151 | "| 2019-04-30 | beta | v1.5.4-hotfix.1 | 09cbc34a0b19cef287a82aa4b9966d525369eecc | 52c7a1e849a170be4b2b2fe34142ca2c0a6fea1f | a1668566e563aef64025d0af88a099cbbe847b7e | eed485c757fba5d731e4054412c99f2e |\n", 152 | "| 2019-04-27 | beta | v1.5.4 | b593f5167bce84fb3cad5c258477bf3abc1b14eb | ca31a7c57bada458fa7f5c0d3f36bc1af4ccbc79 | cf4444b803a376bb1ad6442e1f09434a4d58167b | eed485c757fba5d731e4054412c99f2e |\n", 153 | "| 2019-04-12 | beta | v1.4.9-hotfix.1 | 88fa7ea4031f5c86225573e58e5558dc4ea1c251 | 4737fc5cd89b8f0136e927b00f2e159444b95a73 | f6768b6fb3e58bb704aca1e22a7ffd11e7ff07cb | f630ecdf457e27dd24d3b9e0a6bc1c13 |\n", 154 | "| 2019-03-15 | beta | v1.3.8 | e5b1ed7a7f7b85c1877e09a9495681f719be5578 | f4951df193a7966f9ed4da43d555eee0913d84d1 | 571ea80e1101e706980ea8aefa7fc18a0c8ba2ec | 9a66dcb2da955dffdbdb0eafa0288784 |\n", 155 | "| 2019-02-26 | stable | v1.2.1 | 8661d8aecd626f7f57ccbcb735553edc05a2e713 | 3757390fa4b00d2d261bfdf5182d2e87c9113ff9 | 0a7dcf17eb5f2450480527d6ad1e201fb47f1e36 | 0c73eb70aa4d30f450273cb424be8c62 |\n", 156 | "| 2019-02-26 | beta | v1.2.1 | 8661d8aecd626f7f57ccbcb735553edc05a2e713 | 3757390fa4b00d2d261bfdf5182d2e87c9113ff9 | 0a7dcf17eb5f2450480527d6ad1e201fb47f1e36 | 0c73eb70aa4d30f450273cb424be8c62 |\n", 157 | "| 2019-01-29 | beta | v1.1.8 | 985ccb6d14c6ce5ce74823a4d366df2438eac44f | 7112b72cc229e05d36716c3d7739885d3ffa72e6 | ec86471ccc47a62df8b4009e1fb37c66ff9dc91b | 317d4c7e607b1fd7d682c0010aadf1d0 |\n", 158 | "| 2018-12-04 | stable | v1.0.0 | 5391447fae6209bb21a89e6a5a6583cac1af9b4b | 7375a0f414bde4bc941e623482221db2fc8c4ab5 | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c |\n", 159 | "| 2018-12-04 | beta | v1.0.0 | 5391447fae6209bb21a89e6a5a6583cac1af9b4b | 7375a0f414bde4bc941e623482221db2fc8c4ab5 | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c |\n", 160 | "| 2018-11-30 | beta | v0.11.13 | 58c8489fcdb4e4ef6c010117584c9b23d15221aa | 7375a0f414bde4bc941e623482221db2fc8c4ab5 | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c |\n", 161 | "| 2018-11-29 | beta | v0.11.12 | 06ec8d3b41beb469d845626e36a246ee09300fa7 | 72c7a7567228cdaf8b7aa4a9e3d212ef9d4cc0ed | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c |\n", 162 | "| 2018-11-28 | beta | v0.11.11 | e7680128afbbde443d69f89bb264015276a8475a | be973ea196127383f356d39be466e2f3bd163083 | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c |\n", 163 | "| 2018-11-27 | beta | v0.11.10 | c27c4a265e9ad295e5d434cddabbc639b2e3542d | eebc6a58958587203f624528ff46b1d4b2b0f2fa | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c |\n", 164 | "| 2018-11-21 | beta | v0.11.9 | d48e6e433cc5ca67b24b19f70aaa197e84ba63c1 | 5c8147450db52b81232c138b9f9a65a8f9c61862 | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c |\n", 165 | "| 2018-11-20 | beta | v0.11.8 | f5b02e3c05ed1ab31e890add84fb56e35de2d392 | 1baf081343530dbaa8bec378fe1ce26b4897c23f | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c |\n", 166 | "| 2018-11-19 | beta | v0.11.7 | 7a005e1dcda665ace7241a24e79fae1a71f17b18 | 2e06da3df9cb370795f49747fdfd295b8168c133 | f9ebf2129732fd2b606286fdf58e500384b8a0bc | 8343f188ada07642f47c56e518f1307c |\n", 167 | "| 2018-11-13 | beta | v0.11.3 | 72bf075e8d6961d2ca6df462b2228954f8d0e73a | 5646e86a6f442dc6f4158ae7010ab13d72a0b356 | 9c07fb64c48adb3d6fde50bab6b8b641c5b67683 | d124ce50a30741a188e41c52c424c127 |\n", 168 | "| 2018-11-06 | beta | v0.10.2 | d8cbb80206db06d151206f8b599b7dde5a386a2d | 6c2ade9fa2b555899530a31cc8cbd1dff3bf5eea | bf26f760b1bb3d5fea6bda110f6a17b590364120 | 46b2bfb57b5647c5f7527ff9aa56c69b |\n", 169 | "| 2018-10-09 | beta | v0.9.4 | f37c235c32fc15babe6dc7b7bc2ee4387e5ecf92 | 74625aed323d04f2add0410e84038d250f51b616 | a2eb050044eec93f0844667b8b6132e858467e4e | a135b1a4c6790a272609c9405379bc63 |\n", 170 | "| 2018-09-18 | beta | v0.8.2 | 5ab9e70727d858def3a586db7fb98ee580352957 | 58a1894a1cd798de2f9a206f157a90d45944028b | 760a9690c22ec3f3d163173737f9949f97e6e02a | 1b444eb4796616ea2f955f3f1e440801 |\n", 171 | "| 2018-09-05 | beta | v0.7.3 | 3b309bda072a6b326e8aa4591a5836af600923ce | af42b6dc95bd9f719e43c4e9f29a00640f0f0bba | ccb16f72824374163562364bf19dd18e8a882fab | d0cf500478165d79bdefccb0847ffb33 |\n", 172 | "| 2018-08-28 | beta | v0.6.0 | 9299c02cf708497d6f72edda8efae0bb8340660e | e3687f70c7ece72000b32ee1b3c02755ba5361ac | be6309690fd60284a87f3258a740c7c30efb1092 | 35224090f45cbae1402bafd97a112a40 |\n", 173 | "| 2018-06-19 | beta | v0.5.1 | c7ea3ca377e909469c68f2ab878a5bc53d3cf66b | 1ed25ca7b7e3e3e8047df050bba4174074c9b336 | f981f097602ca434ce0a36b1f704723cad105fb6 | 04cb98b58e7d69109004454c20b492f7 |\n", 174 | "| 2018-05-22 | beta | v0.4.4 | f9bb4289e9fd861d70ae78bcc3a042ef1b35cc9d | 06afdfe54ebef9168a90ca00a6721c2d36e6aafa | 46ab040e589adc5200370dec7952ce5150850822 | 1b155eedbb3a2640a88d2e54d2f2d204 |\n", 175 | "| 2018-05-07 | beta | v0.3.2 | 44b7e7d3f42f050a79712daab253af06e9daf530 | 09d05a38912a3c1a906e95099cac9a7e14fae85f | fe606f890b0a311da802c78b0af414a3c2087a79 | 39646f79e9336fb65ac68c8568544c92 |\n", 176 | "| 2018-04-24 | beta | v0.3.1 | 12bbaba9ae044d0ea77da4dd5e4db15eed403f09 | 09d05a38912a3c1a906e95099cac9a7e14fae85f | fe606f890b0a311da802c78b0af414a3c2087a79 | 39646f79e9336fb65ac68c8568544c92 |\n", 177 | "| 2018-04-09 | beta | v0.2.8 | b397406561f5e7a9c94e28f58d9e49fca0dd58b7 | c903c217a1a8206cdebdab1703b52ec6180edf37 | 52afcba357ad398e8c24f3e3363ac6ff5293df63 | d72bf5003e5924b61a7943f58e7b6814 |\n", 178 | "| 2018-04-02 | beta | v0.2.3 | 5a58b36e36b8d7aace89d3950e6deb307956a6a0 | e61bb9ac3a3fd789754e2e54220bcfc27076a857 | 290c576264faa096a0b3206c71b2435309d9f904 | 0d015018f02a6de0c92ac1ac59191b55 |\n", 179 | "| 2018-03-15 | beta | v0.1.5 | 3ea4d06340a97a1e9d7cae97567c64e0569dcaa2 | ead227f118077d1f2b57842a32abaf105b573b8a | 0b4f01f7593c8c42a77dc27d1fc234c95eacc88e | 9bc066b6e8ef5a9f7224c2926c6ad2f4 |\n" 180 | ] 181 | } 182 | ], 183 | "source": [ 184 | "print('| Release date | Channel | Version | Commit | Engine commit | Dart SDK commit | Snapshot version |')\n", 185 | "print('| ------------ | ------- | ------- | ------ | ------------- | --------------- | ---------------- |')\n", 186 | "for v in sorted(flutter_versions, key=lambda x: x['release_date'], reverse=True):\n", 187 | " if v['channel'] not in { 'stable', 'beta' }: continue\n", 188 | " engine_commit = get_engine_version(v['hash'])\n", 189 | " dart_commit = get_dart_version(engine_commit)\n", 190 | " snapshot_hash = get_snapshot_hash(dart_commit)\n", 191 | " print(f\"| {v['release_date'][:10]} | {v['channel']} | {v['version']} | {v['hash']} | {engine_commit} | {dart_commit} | {snapshot_hash} |\")" 192 | ] 193 | } 194 | ], 195 | "metadata": { 196 | "kernelspec": { 197 | "display_name": "Python 3", 198 | "language": "python", 199 | "name": "python3" 200 | }, 201 | "language_info": { 202 | "codemirror_mode": { 203 | "name": "ipython", 204 | "version": 3 205 | }, 206 | "file_extension": ".py", 207 | "mimetype": "text/x-python", 208 | "name": "python", 209 | "nbconvert_exporter": "python", 210 | "pygments_lexer": "ipython3", 211 | "version": "3.8.6" 212 | } 213 | }, 214 | "nbformat": 4, 215 | "nbformat_minor": 4 216 | } 217 | -------------------------------------------------------------------------------- /1-introduction.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from darter.file import parse_elf_snapshot, parse_appjit_snapshot" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "# Loading and parsing the snapshot file\n", 17 | "\n", 18 | "Here we open the file to inspect. It actually contains *two* snapshots, one is the common **base** and the other contains the actual **user code**. \n", 19 | "`parse_elf_snapshot` extracts the 2 blobs for each of the two snapshots, and parses them.\n", 20 | "It only returns the second snapshot, which is the interesting one (and contains the base, too).\n", 21 | "\n", 22 | "By default we are inspecting an included sample file which results from building the default Flutter app:" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 2, 28 | "metadata": { 29 | "collapsed": true, 30 | "jupyter": { 31 | "outputs_hidden": true 32 | } 33 | }, 34 | "outputs": [ 35 | { 36 | "name": "stdout", 37 | "output_type": "stream", 38 | "text": [ 39 | "------- PARSING VM SNAPSHOT --------\n", 40 | "\n", 41 | "[Header]\n", 42 | " length = 4733\n", 43 | " kind = 2 ('kFullAOT', 'Full + AOT code')\n", 44 | "\n", 45 | "[Snapshot header]\n", 46 | " version = 'c8562f0ee0ebc38ba217c7955956d1cb'\n", 47 | " features = {'product': True, 'use_bare_instructions': True, 'asserts\"': False, 'causal_async_stacks': True, 'bytecode': False, 'arm-eabi': True, 'softfp': True}\n", 48 | "\n", 49 | " base objects: 95\n", 50 | " objects: 935\n", 51 | " clusters: 5\n", 52 | " code order length = 69\n", 53 | "\n", 54 | "[002c1094]: INFO: Reading allocation clusters...\n", 55 | "[002c13a9]: INFO: Reading fill clusters...\n", 56 | "[002c2215]: INFO: Reading roots...\n", 57 | "[002c2281]: INFO: Snasphot parsed.\n", 58 | "\n", 59 | "------- PARSING ISOLATE SNAPSHOT --------\n", 60 | "\n", 61 | "[Header]\n", 62 | " length = 836159\n", 63 | " kind = 2 ('kFullAOT', 'Full + AOT code')\n", 64 | "\n", 65 | "[Snapshot header]\n", 66 | " version = 'c8562f0ee0ebc38ba217c7955956d1cb'\n", 67 | " features = {'product': True, 'use_bare_instructions': True, 'asserts\"': False, 'causal_async_stacks': True, 'bytecode': False, 'arm-eabi': True, 'softfp': True}\n", 68 | "\n", 69 | " base objects: 935\n", 70 | " objects: 74247\n", 71 | " clusters: 222\n", 72 | " code order length = 7228\n", 73 | "\n", 74 | "[002c7098]: INFO: Reading allocation clusters...\n", 75 | "[002d8228]: INFO: Reading fill clusters...\n", 76 | "[00393161]: INFO: Reading roots...\n", 77 | "[00393243]: INFO: Snasphot parsed.\n" 78 | ] 79 | } 80 | ], 81 | "source": [ 82 | "s = parse_elf_snapshot('samples/arm-app.so')" 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "metadata": {}, 88 | "source": [ 89 | "If your snapshot is AppJIT instead of AppAOT, you can use `parse_appjit_snapshot`:" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": 3, 95 | "metadata": { 96 | "collapsed": true, 97 | "jupyter": { 98 | "outputs_hidden": true 99 | } 100 | }, 101 | "outputs": [ 102 | { 103 | "name": "stdout", 104 | "output_type": "stream", 105 | "text": [ 106 | "Blob lengths: (0, 0, 17599488, 6822944)\n", 107 | "No base snapshot, skipping base snasphot parsing...\n", 108 | "\n", 109 | "------- PARSING ISOLATE SNAPSHOT --------\n", 110 | "\n", 111 | "[Header]\n", 112 | " length = 12708502\n", 113 | " kind = 1 ('kFullJIT', 'Full + JIT code')\n", 114 | "\n", 115 | "[Snapshot header]\n", 116 | " version = 'c8562f0ee0ebc38ba217c7955956d1cb'\n", 117 | " features = {'release': True, 'use_bare_instructions': True, 'asserts\"': False, 'use_field_guards\"': True, 'use_osr\"': True, 'causal_async_stacks': True, 'bytecode': False, 'x64-sysv': True}\n", 118 | "\n", 119 | " base objects: 934\n", 120 | " objects: 297885\n", 121 | " clusters: 160\n", 122 | " code order length = 0\n", 123 | "\n", 124 | "[000010ad]: NOTICE: Snapshot expected 934 base objects, but the provided base has 95\n", 125 | "[000010ad]: INFO: Reading allocation clusters...\n", 126 | "[0003c3f2]: INFO: Reading fill clusters...\n", 127 | "[00c1f990]: INFO: Reading roots...\n", 128 | "[00c1fa9a]: INFO: Snasphot parsed.\n" 129 | ] 130 | } 131 | ], 132 | "source": [ 133 | "s = parse_appjit_snapshot('samples/appjit.snapshot')" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": {}, 139 | "source": [ 140 | "If the parsing was successful, then you are good to go!" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "# Navigating the snapshot data\n", 148 | "\n", 149 | "A Dart snapshot is a collection of objects. Each object is assigned a unique **ref number** starting with 1 (ref 0 is invalid). \n", 150 | "To access an object by its ref number, use `refs`:" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": 5, 156 | "metadata": {}, 157 | "outputs": [ 158 | { 159 | "data": { 160 | "text/plain": [ 161 | "Class('package:myapp/main.dart', 'MyApp')->2436" 162 | ] 163 | }, 164 | "execution_count": 5, 165 | "metadata": {}, 166 | "output_type": "execute_result" 167 | } 168 | ], 169 | "source": [ 170 | "s.refs[2436]" 171 | ] 172 | }, 173 | { 174 | "cell_type": "markdown", 175 | "metadata": {}, 176 | "source": [ 177 | "We can see that object 2436 is a **Class**. Its representation prints some of its fields (library and name), but we can access them all through its *data dictionary*:" 178 | ] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "execution_count": 6, 183 | "metadata": { 184 | "scrolled": true 185 | }, 186 | "outputs": [ 187 | { 188 | "data": { 189 | "text/plain": [ 190 | "{'predefined': False,\n", 191 | " 'name': 'MyApp'->71516,\n", 192 | " 'user_name': null,\n", 193 | " 'functions': Array(1)->63064,\n", 194 | " 'functions_hash_table': null,\n", 195 | " 'fields': ,\n", 196 | " 'offset_in_words_to_field': null,\n", 197 | " 'interfaces': Array(0)->63063,\n", 198 | " 'script': Script('package:myapp/main.dart')->11481,\n", 199 | " 'library': Library('package:myapp/main.dart')->11847,\n", 200 | " 'type_parameters': null,\n", 201 | " 'super_type': Type(Class('package:flutter/src/widgets/framework.dart', 'StatelessWidget')->1249)->48108,\n", 202 | " 'signature_function': null,\n", 203 | " 'constants': ,\n", 204 | " 'declaration_type': Type(Class('package:myapp/main.dart', 'MyApp')->2436)->48663,\n", 205 | " 'invocation_dispatcher_cache': ,\n", 206 | " 'allocation_stub': Code->18051,\n", 207 | " 'cid': 981,\n", 208 | " 'instance_size_in_words': 2,\n", 209 | " 'next_field_offset_in_words': 2,\n", 210 | " 'type_arguments_field_offset_in_words': -1,\n", 211 | " 'num_type_arguments': 0,\n", 212 | " 'num_native_fields': 64,\n", 213 | " 'token_pos': 74,\n", 214 | " 'end_token_pos': 954,\n", 215 | " 'state_bits': 1097768}" 216 | ] 217 | }, 218 | "execution_count": 6, 219 | "metadata": {}, 220 | "output_type": "execute_result" 221 | } 222 | ], 223 | "source": [ 224 | "s.refs[2436].x" 225 | ] 226 | }, 227 | { 228 | "cell_type": "markdown", 229 | "metadata": {}, 230 | "source": [ 231 | "As you can see some of the fields point to other objects, so we can go on and inspect them too:" 232 | ] 233 | }, 234 | { 235 | "cell_type": "code", 236 | "execution_count": 7, 237 | "metadata": {}, 238 | "outputs": [ 239 | { 240 | "data": { 241 | "text/plain": [ 242 | "Type(Class('package:flutter/src/widgets/framework.dart', 'StatelessWidget')->1249)->48108" 243 | ] 244 | }, 245 | "execution_count": 7, 246 | "metadata": {}, 247 | "output_type": "execute_result" 248 | } 249 | ], 250 | "source": [ 251 | "s.refs[2436].x['super_type']" 252 | ] 253 | }, 254 | { 255 | "cell_type": "markdown", 256 | "metadata": {}, 257 | "source": [ 258 | "`snapshot.getrefs(...)` lists all objects of a certain type:\n", 259 | "\n", 260 | "~~~ python\n", 261 | "for obj in s.getrefs('Function'):\n", 262 | " # TODO\n", 263 | "~~~\n", 264 | "\n", 265 | "To iterate over all objects of a snapshot (including its base):\n", 266 | "\n", 267 | "~~~ python\n", 268 | "for ref in range(1, s.refs['next']):\n", 269 | " obj = s.refs[ref]\n", 270 | " # TODO\n", 271 | "~~~" 272 | ] 273 | }, 274 | { 275 | "cell_type": "markdown", 276 | "metadata": {}, 277 | "source": [ 278 | "### Arrays\n", 279 | "\n", 280 | "**Array** or **ImmutableArray** objects have one or more object items, which can be accessed through the `value` field in its data dictionary. \n", 281 | "However, it's better to just call `obj.values()`, which also handles the special case of an empty array:" 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": 11, 287 | "metadata": {}, 288 | "outputs": [ 289 | { 290 | "name": "stdout", 291 | "output_type": "stream", 292 | "text": [ 293 | "Array(7)->63969\n", 294 | "[null, null, Class('package:myapp/main.dart', '_MyHomePageState@465264790')->2061, Class('package:myapp/main.dart', 'MyApp')->2436, Function('main', 0)->9741, Class('package:myapp/main.dart', 'MyHomePage')->2099, Mint(4)->49841]\n" 295 | ] 296 | } 297 | ], 298 | "source": [ 299 | "obj = s.refs[2436].x['library'].x['dictionary']\n", 300 | "print(obj)\n", 301 | "print(obj.values()) # or obj.x['value']" 302 | ] 303 | }, 304 | { 305 | "cell_type": "markdown", 306 | "metadata": {}, 307 | "source": [ 308 | "**GrowableObjectArray** objects have a *backing array* and a length:" 309 | ] 310 | }, 311 | { 312 | "cell_type": "code", 313 | "execution_count": 9, 314 | "metadata": {}, 315 | "outputs": [ 316 | { 317 | "data": { 318 | "text/plain": [ 319 | "GrowableObjectArray(1, Array(3)->63967)->54880" 320 | ] 321 | }, 322 | "execution_count": 9, 323 | "metadata": {}, 324 | "output_type": "execute_result" 325 | } 326 | ], 327 | "source": [ 328 | "s.refs[2436].x['library'].x['owned_scripts']" 329 | ] 330 | }, 331 | { 332 | "cell_type": "markdown", 333 | "metadata": {}, 334 | "source": [ 335 | "`values()` works on these objects too:" 336 | ] 337 | }, 338 | { 339 | "cell_type": "code", 340 | "execution_count": 10, 341 | "metadata": {}, 342 | "outputs": [ 343 | { 344 | "data": { 345 | "text/plain": [ 346 | "[Script('package:myapp/main.dart')->11481]" 347 | ] 348 | }, 349 | "execution_count": 10, 350 | "metadata": {}, 351 | "output_type": "execute_result" 352 | } 353 | ], 354 | "source": [ 355 | "s.refs[2436].x['library'].x['owned_scripts'].values()" 356 | ] 357 | }, 358 | { 359 | "cell_type": "markdown", 360 | "metadata": {}, 361 | "source": [ 362 | "### Strings\n", 363 | "\n", 364 | "**OneByteString** and **TwoByteString** objects hold a string, which is printed in its representation:" 365 | ] 366 | }, 367 | { 368 | "cell_type": "code", 369 | "execution_count": 13, 370 | "metadata": {}, 371 | "outputs": [ 372 | { 373 | "data": { 374 | "text/plain": [ 375 | "'Flutter Demo Home Page'->69306" 376 | ] 377 | }, 378 | "execution_count": 13, 379 | "metadata": {}, 380 | "output_type": "execute_result" 381 | } 382 | ], 383 | "source": [ 384 | "s.refs[69306]" 385 | ] 386 | }, 387 | { 388 | "cell_type": "markdown", 389 | "metadata": {}, 390 | "source": [ 391 | "You can access the string through the `value` field in its data dictionary:" 392 | ] 393 | }, 394 | { 395 | "cell_type": "code", 396 | "execution_count": 15, 397 | "metadata": {}, 398 | "outputs": [ 399 | { 400 | "data": { 401 | "text/plain": [ 402 | "'Flutter Demo Home Page'" 403 | ] 404 | }, 405 | "execution_count": 15, 406 | "metadata": {}, 407 | "output_type": "execute_result" 408 | } 409 | ], 410 | "source": [ 411 | "s.refs[69306].x['value']" 412 | ] 413 | }, 414 | { 415 | "cell_type": "markdown", 416 | "metadata": {}, 417 | "source": [ 418 | "Snapshots have a `strings` dictionary, which allows you to access the object for a certain string:" 419 | ] 420 | }, 421 | { 422 | "cell_type": "code", 423 | "execution_count": 16, 424 | "metadata": {}, 425 | "outputs": [ 426 | { 427 | "data": { 428 | "text/plain": [ 429 | "'Flutter Demo Home Page'->69306" 430 | ] 431 | }, 432 | "execution_count": 16, 433 | "metadata": {}, 434 | "output_type": "execute_result" 435 | } 436 | ], 437 | "source": [ 438 | "s.strings['Flutter Demo Home Page']" 439 | ] 440 | }, 441 | { 442 | "cell_type": "markdown", 443 | "metadata": {}, 444 | "source": [ 445 | "### Backreferences\n", 446 | "\n", 447 | "So far, we've seen how an object references other objects through its data dictionary:" 448 | ] 449 | }, 450 | { 451 | "cell_type": "code", 452 | "execution_count": 24, 453 | "metadata": {}, 454 | "outputs": [ 455 | { 456 | "name": "stdout", 457 | "output_type": "stream", 458 | "text": [ 459 | "Class('package:myapp/main.dart', 'MyApp')->2436\n", 460 | "Script('package:myapp/main.dart')->11481\n" 461 | ] 462 | } 463 | ], 464 | "source": [ 465 | "print(s.refs[2436])\n", 466 | "print(s.refs[2436].x['script'])" 467 | ] 468 | }, 469 | { 470 | "cell_type": "markdown", 471 | "metadata": {}, 472 | "source": [ 473 | "But we can look up *where is an object referenced from*, through its `src` attribute:" 474 | ] 475 | }, 476 | { 477 | "cell_type": "code", 478 | "execution_count": 26, 479 | "metadata": {}, 480 | "outputs": [ 481 | { 482 | "name": "stdout", 483 | "output_type": "stream", 484 | "text": [ 485 | "Referenced from: (Class('package:myapp/main.dart', '_MyHomePageState@465264790')->2061, 'script')\n", 486 | "Referenced from: (Class('package:myapp/main.dart', 'MyHomePage')->2099, 'script')\n", 487 | "Referenced from: (Class('package:myapp/main.dart', 'MyApp')->2436, 'script')\n", 488 | "Referenced from: (Class('package:myapp/main.dart', '::')->2565, 'script')\n", 489 | "Referenced from: (Array(3)->63967, 'value', 0)\n" 490 | ] 491 | } 492 | ], 493 | "source": [ 494 | "for backref in s.refs[2436].x['script'].src:\n", 495 | " print('Referenced from:', backref)" 496 | ] 497 | }, 498 | { 499 | "cell_type": "markdown", 500 | "metadata": {}, 501 | "source": [ 502 | "Each item of `src` is a tuple; its first item is the referencing object, and the next item(s) describe the field where it's referenced. \n", 503 | "We can see that 4 classes reference the object with its `script` field, and an array contains the object in its first item.\n", 504 | "\n", 505 | "This is pretty useful when combined with `snapshot.strings`. To find the `MyApp` class, we can get that string and look at its `src`:" 506 | ] 507 | }, 508 | { 509 | "cell_type": "code", 510 | "execution_count": 33, 511 | "metadata": {}, 512 | "outputs": [ 513 | { 514 | "data": { 515 | "text/plain": [ 516 | "[(Class('package:myapp/main.dart', 'MyApp')->2436, 'name'),\n", 517 | " (Array(16386)->64169, 'value', 14917)]" 518 | ] 519 | }, 520 | "execution_count": 33, 521 | "metadata": {}, 522 | "output_type": "execute_result" 523 | } 524 | ], 525 | "source": [ 526 | "s.strings['MyApp'].src" 527 | ] 528 | }, 529 | { 530 | "cell_type": "markdown", 531 | "metadata": {}, 532 | "source": [ 533 | "And there it is, in the first tuple." 534 | ] 535 | }, 536 | { 537 | "cell_type": "markdown", 538 | "metadata": {}, 539 | "source": [ 540 | "### Other operations\n", 541 | "\n", 542 | "Use `obj.is_cid(...)` to check that an object is of certain kind, and `obj.ref` to get its reference number:\n", 543 | "\n", 544 | "~~~ python\n", 545 | "s.refs[2436].is_cid('Class') # True\n", 546 | "s.refs[2436].ref # 2436\n", 547 | "~~~" 548 | ] 549 | }, 550 | { 551 | "cell_type": "markdown", 552 | "metadata": {}, 553 | "source": [ 554 | "Calling `obj.describe()` returns its representation, plus (for code-related objects) some context describing its location in the code:" 555 | ] 556 | }, 557 | { 558 | "cell_type": "code", 559 | "execution_count": 7, 560 | "metadata": {}, 561 | "outputs": [ 562 | { 563 | "name": "stdout", 564 | "output_type": "stream", 565 | "text": [ 566 | "Code->18052\n", 567 | "Code->18052{ Function('build', 2)->9029 Class('package:myapp/main.dart', 'MyApp')->2436 }\n" 568 | ] 569 | } 570 | ], 571 | "source": [ 572 | "print(s.refs[18052])\n", 573 | "print(s.refs[18052].describe())" 574 | ] 575 | }, 576 | { 577 | "cell_type": "markdown", 578 | "metadata": {}, 579 | "source": [ 580 | "### The root object\n", 581 | "\n", 582 | "There's one more object, the root object, which doesn't have a ref number and can be accessed through `snapshot.refs['root']`. It has things like the global object pool, the symbol table, core classes, and some stubs (Code objects) for low-level tasks:" 583 | ] 584 | }, 585 | { 586 | "cell_type": "code", 587 | "execution_count": 15, 588 | "metadata": { 589 | "scrolled": true 590 | }, 591 | "outputs": [ 592 | { 593 | "data": { 594 | "text/plain": [ 595 | "{'object_class': Class('Object')->1028,\n", 596 | " 'object_type': Type(Class('Object')->1028)->48997,\n", 597 | " 'null_class': Class('Null')->1027,\n", 598 | " 'null_type': Type(Class('Null')->1027)->48996,\n", 599 | " 'function_type': Type(Class('Function')->1029)->48995,\n", 600 | " 'type_type': Type(Class('Type')->2591)->48994,\n", 601 | " 'closure_class': Class('_Closure@0150898')->1026,\n", 602 | " 'number_type': Type(Class('num')->1007)->48993,\n", 603 | " 'int_type': Type(Class('int')->1832)->48992,\n", 604 | " 'integer_implementation_class': Class('_IntegerImplementation@0150898')->1025,\n", 605 | " 'int64_type': null,\n", 606 | " 'smi_class': Class('_Smi@0150898')->1024,\n", 607 | " 'smi_type': Type(Class('_Smi@0150898')->1024)->48991,\n", 608 | " 'mint_class': Class('_Mint@0150898')->1023,\n", 609 | " 'mint_type': Type(Class('_Mint@0150898')->1023)->48990,\n", 610 | " 'double_class': Class('_Double@0150898')->1022,\n", 611 | " 'double_type': Type(Class('double')->2588)->48989,\n", 612 | " 'float32x4_type': Type(Class('dart:typed_data', 'Float32x4')->2553)->48988,\n", 613 | " 'int32x4_type': Type(Class('dart:typed_data', 'Int32x4')->2555)->48987,\n", 614 | " 'float64x2_type': Type(Class('dart:typed_data', 'Float64x2')->2549)->48986,\n", 615 | " 'string_type': Type(Class('String')->2482)->48985,\n", 616 | " 'type_argument_int': TypeArguments->46641,\n", 617 | " 'type_argument_double': TypeArguments->46640,\n", 618 | " 'type_argument_string': TypeArguments->46639,\n", 619 | " 'type_argument_string_dynamic': TypeArguments->46638,\n", 620 | " 'type_argument_string_string': TypeArguments->46637,\n", 621 | " 'compiletime_error_class': null,\n", 622 | " 'pragma_class': null,\n", 623 | " 'pragma_name': null,\n", 624 | " 'pragma_options': null,\n", 625 | " 'future_class': null,\n", 626 | " 'completer_class': null,\n", 627 | " 'symbol_class': null,\n", 628 | " 'one_byte_string_class': Class('_OneByteString@0150898')->1021,\n", 629 | " 'two_byte_string_class': Class('_TwoByteString@0150898')->1020,\n", 630 | " 'external_one_byte_string_class': Class('_ExternalOneByteString@0150898')->1019,\n", 631 | " 'external_two_byte_string_class': Class('_ExternalTwoByteString@0150898')->1018,\n", 632 | " 'bool_type': Type(Class('bool')->1017)->48984,\n", 633 | " 'bool_class': Class('bool')->1017,\n", 634 | " 'array_class': Class('_List@0150898')->1016,\n", 635 | " 'array_type': Type(Class('_List@0150898')->1016)->48983,\n", 636 | " 'immutable_array_class': Class('_ImmutableList@0150898')->1015,\n", 637 | " 'growable_object_array_class': Class('_GrowableList@0150898')->1014,\n", 638 | " 'linked_hash_map_class': Class('dart:collection', '_InternalLinkedHashMap@3220832')->1013,\n", 639 | " 'linked_hash_set_class': Class('dart:collection', '_CompactLinkedHashSet@3220832')->2602,\n", 640 | " 'float32x4_class': Class('dart:typed_data', '_Float32x4@6027147')->1012,\n", 641 | " 'int32x4_class': Class('dart:typed_data', '_Int32x4@6027147')->1011,\n", 642 | " 'float64x2_class': Class('dart:typed_data', '_Float64x2@6027147')->1010,\n", 643 | " 'error_class': null,\n", 644 | " 'weak_property_class': Class('_WeakProperty@0150898')->1009,\n", 645 | " 'symbol_table': Array(16386)->64169,\n", 646 | " 'canonical_types': Array(4098)->64148,\n", 647 | " 'canonical_type_arguments': Array(1026)->64147,\n", 648 | " 'async_library': Library('dart:async')->11859,\n", 649 | " 'builtin_library': null,\n", 650 | " 'core_library': Library('dart:core')->11858,\n", 651 | " 'collection_library': Library('dart:collection')->11857,\n", 652 | " 'convert_library': Library('dart:convert')->11856,\n", 653 | " 'developer_library': Library('dart:developer')->11855,\n", 654 | " 'ffi_library': Library('dart:ffi')->11854,\n", 655 | " '_internal_library': Library('dart:_internal')->11853,\n", 656 | " 'isolate_library': Library('dart:isolate')->11852,\n", 657 | " 'math_library': Library('dart:math')->11851,\n", 658 | " 'mirrors_library': Library('dart:mirrors')->11850,\n", 659 | " 'native_wrappers_library': Library('dart:nativewrappers')->11849,\n", 660 | " 'profiler_library': Library('dart:profiler')->11848,\n", 661 | " 'root_library': Library('package:myapp/main.dart')->11847,\n", 662 | " 'typed_data_library': Library('dart:typed_data')->11846,\n", 663 | " '_vmservice_library': Library('dart:_vmservice')->11845,\n", 664 | " 'libraries': GrowableObjectArray(235, Array(255)->63758)->54877,\n", 665 | " 'libraries_map': Array(1026)->63683,\n", 666 | " 'closure_functions': GrowableObjectArray(388, Array(511)->63682)->54862,\n", 667 | " 'pending_classes': GrowableObjectArray(0, )->54861,\n", 668 | " 'pending_unevaluated_const_fields': null,\n", 669 | " 'pending_deferred_loads': GrowableObjectArray(0, )->54860,\n", 670 | " 'resume_capabilities': GrowableObjectArray(0, )->54859,\n", 671 | " 'exit_listeners': GrowableObjectArray(0, )->54858,\n", 672 | " 'error_listeners': GrowableObjectArray(0, )->54857,\n", 673 | " 'stack_overflow': Instance(Class('StackOverflowError')->2517)->74213,\n", 674 | " 'out_of_memory': Instance(Class('OutOfMemoryError')->2516)->74214,\n", 675 | " 'preallocated_unhandled_exception': UnhandledException->45521,\n", 676 | " 'preallocated_stack_trace': StackTrace->54893,\n", 677 | " 'lookup_port_handler': null,\n", 678 | " 'handle_message_function': null,\n", 679 | " 'growable_list_factory': null,\n", 680 | " 'simple_instance_of_function': null,\n", 681 | " 'simple_instance_of_true_function': null,\n", 682 | " 'simple_instance_of_false_function': null,\n", 683 | " 'async_clear_thread_stack_trace': Function('_clearAsyncThreadStackTrace@8048458', 0)->9573,\n", 684 | " 'async_set_thread_stack_trace': null,\n", 685 | " 'async_star_move_next_helper': null,\n", 686 | " 'complete_on_async_return': null,\n", 687 | " 'async_star_stream_controller': null,\n", 688 | " 'global_object_pool': ObjectPool->19088,\n", 689 | " 'library_load_error_table': null,\n", 690 | " 'unique_dynamic_targets': null,\n", 691 | " 'megamorphic_cache_table': GrowableObjectArray(8, Array(15)->54895)->54650,\n", 692 | " 'build_method_extractor_code': Code->11867,\n", 693 | " 'null_error_stub_with_fpu_regs_stub': Code->11866,\n", 694 | " 'null_error_stub_without_fpu_regs_stub': Code->11865,\n", 695 | " 'stack_overflow_stub_with_fpu_regs_stub': Code->11864,\n", 696 | " 'stack_overflow_stub_without_fpu_regs_stub': Code->11863,\n", 697 | " 'write_barrier_wrappers_stub': Code->11862,\n", 698 | " 'array_write_barrier_stub': Code->11861,\n", 699 | " 'megamorphic_miss_code': Code->11860,\n", 700 | " 'megamorphic_miss_function': Function('megamorphic_miss', 0)->2861}" 701 | ] 702 | }, 703 | "execution_count": 15, 704 | "metadata": {}, 705 | "output_type": "execute_result" 706 | } 707 | ], 708 | "source": [ 709 | "s.refs['root'].x" 710 | ] 711 | }, 712 | { 713 | "cell_type": "markdown", 714 | "metadata": {}, 715 | "source": [ 716 | "# Native analysis\n", 717 | "\n", 718 | "If we look at the `Flutter Demo Home Page` string, the only objects that reference it are the `global_object_pool` and the `symbol_table`:" 719 | ] 720 | }, 721 | { 722 | "cell_type": "code", 723 | "execution_count": 37, 724 | "metadata": {}, 725 | "outputs": [ 726 | { 727 | "name": "stdout", 728 | "output_type": "stream", 729 | "text": [ 730 | "[(ObjectPool->19088, 'entries', 9188, 'raw_obj'), (Array(16386)->64169, 'value', 3064)]\n" 731 | ] 732 | } 733 | ], 734 | "source": [ 735 | "src = s.strings['Flutter Demo Home Page'].src\n", 736 | "print(src)\n", 737 | "assert src[0][0] == s.refs['root'].x['global_object_pool']\n", 738 | "assert src[1][0] == s.refs['root'].x['symbol_table']" 739 | ] 740 | }, 741 | { 742 | "cell_type": "markdown", 743 | "metadata": {}, 744 | "source": [ 745 | "It's used in the `MyApp.build` function, but since this reference is only in the assembled instructions, we don't know about it. \n", 746 | "That's what the `darter.asm` module is for. We use the `populate_native_references` method to analyze the instructions:" 747 | ] 748 | }, 749 | { 750 | "cell_type": "code", 751 | "execution_count": 39, 752 | "metadata": {}, 753 | "outputs": [ 754 | { 755 | "name": "stdout", 756 | "output_type": "stream", 757 | "text": [ 758 | "Starting analysis...\n", 759 | "Done in 2.83s, processing results\n" 760 | ] 761 | } 762 | ], 763 | "source": [ 764 | "from darter.asm.base import populate_native_references\n", 765 | "populate_native_references(s)" 766 | ] 767 | }, 768 | { 769 | "cell_type": "markdown", 770 | "metadata": {}, 771 | "source": [ 772 | "And this populates an `nsrc` field on every object. This field tells us where it's used in native code:" 773 | ] 774 | }, 775 | { 776 | "cell_type": "code", 777 | "execution_count": 40, 778 | "metadata": {}, 779 | "outputs": [ 780 | { 781 | "data": { 782 | "text/plain": [ 783 | "[(Code->18052, 2440608, 'load', 'ip')]" 784 | ] 785 | }, 786 | "execution_count": 40, 787 | "metadata": {}, 788 | "output_type": "execute_result" 789 | } 790 | ], 791 | "source": [ 792 | "s.strings['Flutter Demo Home Page'].nsrc" 793 | ] 794 | }, 795 | { 796 | "cell_type": "markdown", 797 | "metadata": {}, 798 | "source": [ 799 | "And if we look more closely at that `Code` object..." 800 | ] 801 | }, 802 | { 803 | "cell_type": "code", 804 | "execution_count": 41, 805 | "metadata": {}, 806 | "outputs": [ 807 | { 808 | "data": { 809 | "text/plain": [ 810 | "\"Code->18052{ Function('build', 2)->9029 Class('package:myapp/main.dart', 'MyApp')->2436 }\"" 811 | ] 812 | }, 813 | "execution_count": 41, 814 | "metadata": {}, 815 | "output_type": "execute_result" 816 | } 817 | ], 818 | "source": [ 819 | "s.strings['Flutter Demo Home Page'].nsrc[0][0].describe()" 820 | ] 821 | }, 822 | { 823 | "cell_type": "markdown", 824 | "metadata": {}, 825 | "source": [ 826 | "We see it's the `build` function that we were expecting. The format of the tuples in `nsrc` is `(code object, instruction address, kind of reference, ...)`." 827 | ] 828 | } 829 | ], 830 | "metadata": { 831 | "kernelspec": { 832 | "display_name": "Python 3", 833 | "language": "python", 834 | "name": "python3" 835 | }, 836 | "language_info": { 837 | "codemirror_mode": { 838 | "name": "ipython", 839 | "version": 3 840 | }, 841 | "file_extension": ".py", 842 | "mimetype": "text/x-python", 843 | "name": "python", 844 | "nbconvert_exporter": "python", 845 | "pygments_lexer": "ipython3", 846 | "version": "3.8.0" 847 | } 848 | }, 849 | "nbformat": 4, 850 | "nbformat_minor": 4 851 | } 852 | -------------------------------------------------------------------------------- /2-playground.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Let's start by opening the file and analyzing native references:" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": { 14 | "collapsed": true, 15 | "jupyter": { 16 | "outputs_hidden": true 17 | } 18 | }, 19 | "outputs": [ 20 | { 21 | "name": "stdout", 22 | "output_type": "stream", 23 | "text": [ 24 | "------- PARSING VM SNAPSHOT --------\n", 25 | "\n", 26 | "[Header]\n", 27 | " length = 4733\n", 28 | " kind = 2 ('kFullAOT', 'Full + AOT code')\n", 29 | "\n", 30 | "[Snapshot header]\n", 31 | " version = 'c8562f0ee0ebc38ba217c7955956d1cb'\n", 32 | " features = {'product': True, 'use_bare_instructions': True, 'asserts\"': False, 'causal_async_stacks': True, 'bytecode': False, 'arm-eabi': True, 'softfp': True}\n", 33 | "\n", 34 | " base objects: 95\n", 35 | " objects: 935\n", 36 | " clusters: 5\n", 37 | " code order length = 69\n", 38 | "\n", 39 | "[002c1094]: INFO: Reading allocation clusters...\n", 40 | "[002c13a9]: INFO: Reading fill clusters...\n", 41 | "[002c2215]: INFO: Reading roots...\n", 42 | "[002c2281]: INFO: Snasphot parsed.\n", 43 | "\n", 44 | "------- PARSING ISOLATE SNAPSHOT --------\n", 45 | "\n", 46 | "[Header]\n", 47 | " length = 836159\n", 48 | " kind = 2 ('kFullAOT', 'Full + AOT code')\n", 49 | "\n", 50 | "[Snapshot header]\n", 51 | " version = 'c8562f0ee0ebc38ba217c7955956d1cb'\n", 52 | " features = {'product': True, 'use_bare_instructions': True, 'asserts\"': False, 'causal_async_stacks': True, 'bytecode': False, 'arm-eabi': True, 'softfp': True}\n", 53 | "\n", 54 | " base objects: 935\n", 55 | " objects: 74247\n", 56 | " clusters: 222\n", 57 | " code order length = 7228\n", 58 | "\n", 59 | "[002c7098]: INFO: Reading allocation clusters...\n", 60 | "[002d8228]: INFO: Reading fill clusters...\n", 61 | "[00393161]: INFO: Reading roots...\n", 62 | "[00393243]: INFO: Snasphot parsed.\n", 63 | "Starting analysis...\n", 64 | "Done in 3.12s, processing results\n" 65 | ] 66 | } 67 | ], 68 | "source": [ 69 | "from darter.file import parse_elf_snapshot, parse_appjit_snapshot\n", 70 | "from darter.asm.base import populate_native_references\n", 71 | "\n", 72 | "s = parse_elf_snapshot('samples/arm-app.so')\n", 73 | "populate_native_references(s)" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "metadata": {}, 79 | "source": [ 80 | "We will import the constants and define some basic functions to help us analyze the data:" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 5, 86 | "metadata": { 87 | "scrolled": true 88 | }, 89 | "outputs": [], 90 | "source": [ 91 | "import re\n", 92 | "from darter.constants import *\n", 93 | "from collections import defaultdict\n", 94 | "\n", 95 | "def is_irrelevant(src):\n", 96 | " if src[0] == src[0].s.refs['root'].x['global_object_pool']: return True\n", 97 | " if src[0] == src[0].s.refs['root'].x['symbol_table']: return True\n", 98 | "\n", 99 | "def show_rev_tree(ref, depth=4, max_srcs=5, i_step=4, hide_irrelevant=True, hide_location=True):\n", 100 | " ''' Shows a tree of back-references to an object; that is, things pointing to it. '''\n", 101 | " def show_src(src, depth, roots=set(), indent=0):\n", 102 | " x, *rest = src\n", 103 | " location = x.locate()\n", 104 | " location_str = ' {{ {} }}'.format(' '.join(map(str, location))) if location else ''\n", 105 | " print(' '*indent + '{} ({})'.format(x, ' '.join(map(str, rest))) + location_str)\n", 106 | " if depth > 0:\n", 107 | " roots, indent = roots | {x}, indent + i_step\n", 108 | " if hide_location and location: roots |= set(location)\n", 109 | " srcs = x.src + getattr(x, 'nsrc', [])\n", 110 | " filter_out = lambda src: (src[0] in roots) or (hide_irrelevant and is_irrelevant(src))\n", 111 | " srcs = [ src for src in srcs if not filter_out(src) ]\n", 112 | " for csrc in srcs[:max_srcs]: show_src(csrc, depth-1, roots, indent)\n", 113 | " if len(srcs) > max_srcs: print(\" \"*(indent) + '... {} more'.format(len(srcs)-max_srcs))\n", 114 | " show_src((ref,), depth)" 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "metadata": {}, 120 | "source": [ 121 | "# Export R2 metadata\n", 122 | "\n", 123 | "This exports part of the parsed data into metadata for loading into [Radare2](https://www.radare.org/r/), which can be useful for manual close analysis of the assembled code, or for Darter development.\n", 124 | "\n", 125 | "Flags are created for every code section (describing the code object), so you can seek / disassemble them more easily. \n", 126 | "Comments are placed at every native reference, describing the loaded object." 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": 16, 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "from collections import defaultdict\n", 136 | "from base64 import b64encode\n", 137 | "do_b64 = lambda x: 'base64:' + b64encode(x.encode('utf-8')).decode('ascii')\n", 138 | "\n", 139 | "def produce_metadata(snapshot):\n", 140 | " out = []\n", 141 | " comments = defaultdict(lambda: [])\n", 142 | " out.append('fs functions')\n", 143 | " for code in snapshot.getrefs('Code'):\n", 144 | " instr = code.x['instructions']\n", 145 | " name = 'c_{}'.format(code.ref)\n", 146 | " comment = ' '.join(map(str, code.locate()))\n", 147 | " out.append('f {name} {len} {addr} {c}'.format( name=name, len=len(instr['data']), addr=instr['data_addr'], c=do_b64(comment) ))\n", 148 | " for target, pc, kind, *args in code.x.get('nrefs', []):\n", 149 | " if kind == 'load':\n", 150 | " comments[pc].append( 'load: {reg} = {tg}'.format(tg=target.describe(), reg=args[0]) )\n", 151 | " for addr, lines in comments.items():\n", 152 | " out.append('CCu {} @ {}'.format( do_b64(\"\\n\".join(lines)), addr ))\n", 153 | " return ''.join(x + '\\n' for x in out)\n", 154 | "\n", 155 | "with open('metadata.r2', 'w') as f: f.write(produce_metadata(s))" 156 | ] 157 | }, 158 | { 159 | "cell_type": "markdown", 160 | "metadata": {}, 161 | "source": [ 162 | "To load the metadata, open the file in Radare2 and do: `. metadata.r2` \n", 163 | "Then, seek to a certain code object (`s c_18052`) and dissassemble it (`pD $(fl)`). \n", 164 | "The color of the annotations can be changed through something like: `ec comment rgb:f0f000`" 165 | ] 166 | }, 167 | { 168 | "cell_type": "markdown", 169 | "metadata": {}, 170 | "source": [ 171 | "# Print string table\n", 172 | "\n", 173 | "In order to do an initial reconnaissance, we can dump all strings in the application. \n", 174 | "The following code does that, excluding strings that look like obfuscated identifiers:" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": 23, 180 | "metadata": {}, 181 | "outputs": [], 182 | "source": [ 183 | "ID_PATTERN = r'(([sg]et|init|dyn)\\:)?_?[a-zA-Z]{1,3}(\\@\\d+)?.?' # pattern to ignore\n", 184 | "REF_ORDER = True # False sorts strings alphabetically\n", 185 | "\n", 186 | "with open('app-strings.txt', 'w') as f:\n", 187 | " key = (lambda x: x.ref) if REF_ORDER else (lambda x: x.x['value'])\n", 188 | " for x in sorted(s.strings_refs, key=key):\n", 189 | " if re.fullmatch(ID_PATTERN, x.x['value']): continue\n", 190 | " print('{} {}'.format(x.ref, repr(x.x['value'])), file=f)" 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": {}, 196 | "source": [ 197 | "# Find relevant libraries\n", 198 | "\n", 199 | "Most of the application code, objects, strings, etc. will often be open-source dependencies rather than useful code. \n", 200 | "Dart code is separated in *libraries*, so making a list of libraries that contain useful code is a big help." 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": 26, 206 | "metadata": { 207 | "scrolled": true 208 | }, 209 | "outputs": [ 210 | { 211 | "name": "stdout", 212 | "output_type": "stream", 213 | "text": [ 214 | "Library('dart:_http')->11838\n", 215 | "Library('dart:_internal')->11853\n", 216 | "Library('dart:_vmservice')->11845\n", 217 | "Library('dart:async')->11859\n", 218 | "Library('dart:collection')->11857\n", 219 | "Library('dart:convert')->11856\n", 220 | "Library('dart:core')->11858\n", 221 | "Library('dart:developer')->11855\n", 222 | "Library('dart:ffi')->11854\n", 223 | "Library('dart:io')->11708\n", 224 | "Library('dart:isolate')->11852\n", 225 | "Library('dart:math')->11851\n", 226 | "Library('dart:mirrors')->11850\n", 227 | "Library('dart:nativewrappers')->11849\n", 228 | "Library('dart:profiler')->11848\n", 229 | "Library('dart:typed_data')->11846\n", 230 | "Library('dart:ui')->11626\n", 231 | "Library('dart:vmservice_io')->11843\n", 232 | "Library('package:collection/src/priority_queue.dart')->11830\n", 233 | "Library('package:flutter/src/animation/animation.dart')->11638\n", 234 | "Library('package:flutter/src/animation/animation_controller.dart')->11744\n", 235 | "Library('package:flutter/src/animation/animations.dart')->11642\n", 236 | "Library('package:flutter/src/animation/curves.dart')->11625\n", 237 | "Library('package:flutter/src/animation/listener_helpers.dart')->11643\n", 238 | "Library('package:flutter/src/animation/tween.dart')->11641\n", 239 | "Library('package:flutter/src/cupertino/action_sheet.dart')->11710\n", 240 | "Library('package:flutter/src/cupertino/localizations.dart')->11689\n", 241 | "Library('package:flutter/src/cupertino/route.dart')->11640\n", 242 | "Library('package:flutter/src/cupertino/text_theme.dart')->11751\n", 243 | "Library('package:flutter/src/cupertino/theme.dart')->11711\n", 244 | "Library('package:flutter/src/foundation/_isolates_io.dart')->11823\n", 245 | "Library('package:flutter/src/foundation/_platform_io.dart')->11836\n", 246 | "Library('package:flutter/src/foundation/assertions.dart')->11820\n", 247 | "Library('package:flutter/src/foundation/binding.dart')->11661\n", 248 | "Library('package:flutter/src/foundation/change_notifier.dart')->11637\n", 249 | "Library('package:flutter/src/foundation/collections.dart')->11834\n", 250 | "Library('package:flutter/src/foundation/debug.dart')->11815\n", 251 | "Library('package:flutter/src/foundation/diagnostics.dart')->11627\n", 252 | "Library('package:flutter/src/foundation/isolates.dart')->11822\n", 253 | "Library('package:flutter/src/foundation/key.dart')->11646\n", 254 | "Library('package:flutter/src/foundation/licenses.dart')->11824\n", 255 | "Library('package:flutter/src/foundation/node.dart')->11659\n", 256 | "Library('package:flutter/src/foundation/observer_list.dart')->11827\n", 257 | "Library('package:flutter/src/foundation/platform.dart')->11765\n", 258 | "Library('package:flutter/src/foundation/print.dart')->11821\n", 259 | "Library('package:flutter/src/foundation/serialization.dart')->11811\n", 260 | "Library('package:flutter/src/foundation/synchronous_future.dart')->11738\n", 261 | "Library('package:flutter/src/gestures/arena.dart')->11712\n", 262 | "Library('package:flutter/src/gestures/binding.dart')->11662\n", 263 | "Library('package:flutter/src/gestures/converter.dart')->11818\n", 264 | "Library('package:flutter/src/gestures/drag_details.dart')->11657\n", 265 | "Library('package:flutter/src/gestures/events.dart')->11814\n", 266 | "Library('package:flutter/src/gestures/force_press.dart')->11833\n", 267 | "Library('package:flutter/src/gestures/hit_test.dart')->11632\n", 268 | "Library('package:flutter/src/gestures/long_press.dart')->11715\n", 269 | "Library('package:flutter/src/gestures/lsq_solver.dart')->11714\n", 270 | "Library('package:flutter/src/gestures/monodrag.dart')->11652\n", 271 | "Library('package:flutter/src/gestures/mouse_tracking.dart')->11739\n", 272 | "Library('package:flutter/src/gestures/multitap.dart')->11653\n", 273 | "Library('package:flutter/src/gestures/pointer_router.dart')->11828\n", 274 | "Library('package:flutter/src/gestures/pointer_signal_resolver.dart')->11829\n", 275 | "Library('package:flutter/src/gestures/recognizer.dart')->11628\n", 276 | "Library('package:flutter/src/gestures/scale.dart')->11839\n", 277 | "Library('package:flutter/src/gestures/tap.dart')->11716\n", 278 | "Library('package:flutter/src/gestures/velocity_tracker.dart')->11713\n", 279 | "Library('package:flutter/src/material/app.dart')->11794\n", 280 | "Library('package:flutter/src/material/app_bar.dart')->11762\n", 281 | "Library('package:flutter/src/material/app_bar_theme.dart')->11725\n", 282 | "Library('package:flutter/src/material/arc.dart')->11771\n", 283 | "Library('package:flutter/src/material/back_button.dart')->11745\n", 284 | "Library('package:flutter/src/material/banner_theme.dart')->11732\n", 285 | "Library('package:flutter/src/material/bottom_app_bar_theme.dart')->11726\n", 286 | "Library('package:flutter/src/material/bottom_sheet.dart')->11841\n", 287 | "Library('package:flutter/src/material/bottom_sheet_theme.dart')->11730\n", 288 | "Library('package:flutter/src/material/button.dart')->11718\n", 289 | "Library('package:flutter/src/material/button_theme.dart')->11800\n", 290 | "Library('package:flutter/src/material/card_theme.dart')->11724\n", 291 | "Library('package:flutter/src/material/chip_theme.dart')->11806\n", 292 | "Library('package:flutter/src/material/color_scheme.dart')->11803\n", 293 | "Library('package:flutter/src/material/colors.dart')->11807\n", 294 | "Library('package:flutter/src/material/dialog_theme.dart')->11727\n", 295 | "Library('package:flutter/src/material/divider_theme.dart')->11733\n", 296 | "Library('package:flutter/src/material/drawer.dart')->11788\n", 297 | "Library('package:flutter/src/material/feedback.dart')->11694\n", 298 | "Library('package:flutter/src/material/flexible_space_bar.dart')->11772\n", 299 | "Library('package:flutter/src/material/floating_action_button.dart')->11796\n", 300 | "Library('package:flutter/src/material/floating_action_button_location.dart')->11719\n", 301 | "Library('package:flutter/src/material/floating_action_button_theme.dart')->11728\n", 302 | "Library('package:flutter/src/material/icon_button.dart')->11770\n", 303 | "Library('package:flutter/src/material/ink_highlight.dart')->11691\n", 304 | "Library('package:flutter/src/material/ink_splash.dart')->11670\n", 305 | "Library('package:flutter/src/material/ink_well.dart')->11671\n", 306 | "Library('package:flutter/src/material/input_decorator.dart')->11801\n", 307 | "Library('package:flutter/src/material/list_tile.dart')->11837\n", 308 | "Library('package:flutter/src/material/material.dart')->11672\n", 309 | "Library('package:flutter/src/material/material_localizations.dart')->11695\n", 310 | "Library('package:flutter/src/material/material_state.dart')->11746\n", 311 | "Library('package:flutter/src/material/page.dart')->11792\n", 312 | "Library('package:flutter/src/material/page_transitions_theme.dart')->11674\n", 313 | "Library('package:flutter/src/material/popup_menu_theme.dart')->11731\n", 314 | "Library('package:flutter/src/material/scaffold.dart')->11749\n", 315 | "Library('package:flutter/src/material/slider_theme.dart')->11721\n", 316 | "Library('package:flutter/src/material/snack_bar.dart')->11787\n", 317 | "Library('package:flutter/src/material/snack_bar_theme.dart')->11729\n", 318 | "Library('package:flutter/src/material/tab_bar_theme.dart')->11722\n", 319 | "Library('package:flutter/src/material/tabs.dart')->11777\n", 320 | "Library('package:flutter/src/material/text_theme.dart')->11804\n", 321 | "Library('package:flutter/src/material/theme.dart')->11750\n", 322 | "Library('package:flutter/src/material/theme_data.dart')->11747\n", 323 | "Library('package:flutter/src/material/toggle_buttons_theme.dart')->11720\n", 324 | "Library('package:flutter/src/material/tooltip.dart')->11735\n", 325 | "Library('package:flutter/src/material/tooltip_theme.dart')->11723\n", 326 | "Library('package:flutter/src/material/typography.dart')->11795\n", 327 | "Library('package:flutter/src/painting/alignment.dart')->11754\n", 328 | "Library('package:flutter/src/painting/basic_types.dart')->11774\n", 329 | "Library('package:flutter/src/painting/binding.dart')->11665\n", 330 | "Library('package:flutter/src/painting/border_radius.dart')->11799\n", 331 | "Library('package:flutter/src/painting/borders.dart')->11699\n", 332 | "Library('package:flutter/src/painting/box_border.dart')->11677\n", 333 | "Library('package:flutter/src/painting/box_decoration.dart')->11676\n", 334 | "Library('package:flutter/src/painting/box_shadow.dart')->11696\n", 335 | "Library('package:flutter/src/painting/circle_border.dart')->11697\n", 336 | "Library('package:flutter/src/painting/clip.dart')->11680\n", 337 | "Library('package:flutter/src/painting/colors.dart')->11753\n", 338 | "Library('package:flutter/src/painting/decoration.dart')->11639\n", 339 | "Library('package:flutter/src/painting/decoration_image.dart')->11835\n", 340 | "Library('package:flutter/src/painting/edge_insets.dart')->11802\n", 341 | "Library('package:flutter/src/painting/geometry.dart')->11840\n", 342 | "Library('package:flutter/src/painting/gradient.dart')->11656\n", 343 | "Library('package:flutter/src/painting/image_cache.dart')->11825\n", 344 | "Library('package:flutter/src/painting/image_provider.dart')->11741\n", 345 | "Library('package:flutter/src/painting/inline_span.dart')->11758\n", 346 | "Library('package:flutter/src/painting/matrix_utils.dart')->11808\n", 347 | "Library('package:flutter/src/painting/placeholder_span.dart')->11775\n", 348 | "Library('package:flutter/src/painting/rounded_rectangle_border.dart')->11698\n", 349 | "Library('package:flutter/src/painting/shader_warm_up.dart')->11666\n", 350 | "Library('package:flutter/src/painting/stadium_border.dart')->11700\n", 351 | "Library('package:flutter/src/painting/strut_style.dart')->11842\n", 352 | "Library('package:flutter/src/painting/text_painter.dart')->11759\n", 353 | "Library('package:flutter/src/painting/text_span.dart')->11791\n", 354 | "Library('package:flutter/src/painting/text_style.dart')->11805\n", 355 | "Library('package:flutter/src/physics/simulation.dart')->11743\n", 356 | "Library('package:flutter/src/physics/spring_simulation.dart')->11701\n", 357 | "Library('package:flutter/src/physics/tolerance.dart')->11742\n", 358 | "Library('package:flutter/src/physics/utils.dart')->11832\n", 359 | "Library('package:flutter/src/rendering/binding.dart')->11668\n", 360 | "Library('package:flutter/src/rendering/box.dart')->11630\n", 361 | "Library('package:flutter/src/rendering/custom_layout.dart')->11736\n", 362 | "Library('package:flutter/src/rendering/custom_paint.dart')->11678\n", 363 | "Library('package:flutter/src/rendering/debug_overflow_indicator.dart')->11756\n", 364 | "Library('package:flutter/src/rendering/error.dart')->11817\n", 365 | "Library('package:flutter/src/rendering/flex.dart')->11755\n", 366 | "Library('package:flutter/src/rendering/layer.dart')->11636\n", 367 | "Library('package:flutter/src/rendering/object.dart')->11631\n", 368 | "Library('package:flutter/src/rendering/paragraph.dart')->11757\n", 369 | "Library('package:flutter/src/rendering/performance_overlay.dart')->11763\n", 370 | "Library('package:flutter/src/rendering/proxy_box.dart')->11633\n", 371 | "Library('package:flutter/src/rendering/shifted_box.dart')->11629\n", 372 | "Library('package:flutter/src/rendering/stack.dart')->11737\n", 373 | "Library('package:flutter/src/rendering/view.dart')->11826\n", 374 | "Library('package:flutter/src/rendering/viewport_offset.dart')->11783\n", 375 | "Library('package:flutter/src/scheduler/binding.dart')->11664\n", 376 | "Library('package:flutter/src/scheduler/ticker.dart')->11648\n", 377 | "Library('package:flutter/src/semantics/binding.dart')->11667\n", 378 | "Library('package:flutter/src/semantics/semantics.dart')->11760\n", 379 | "Library('package:flutter/src/semantics/semantics_event.dart')->11693\n", 380 | "Library('package:flutter/src/semantics/semantics_service.dart')->11752\n", 381 | "Library('package:flutter/src/services/asset_bundle.dart')->11635\n", 382 | "Library('package:flutter/src/services/binary_messenger.dart')->11816\n", 383 | "Library('package:flutter/src/services/binding.dart')->11663\n", 384 | "Library('package:flutter/src/services/haptic_feedback.dart')->11734\n", 385 | "Library('package:flutter/src/services/keyboard_key.dart')->11654\n", 386 | "Library('package:flutter/src/services/message_codec.dart')->11810\n", 387 | "Library('package:flutter/src/services/message_codecs.dart')->11809\n", 388 | "Library('package:flutter/src/services/platform_channel.dart')->11707\n", 389 | "Library('package:flutter/src/services/raw_keyboard.dart')->11655\n", 390 | "Library('package:flutter/src/services/raw_keyboard_android.dart')->11685\n", 391 | "Library('package:flutter/src/services/raw_keyboard_fuchsia.dart')->11684\n", 392 | "Library('package:flutter/src/services/raw_keyboard_linux.dart')->11682\n", 393 | "Library('package:flutter/src/services/raw_keyboard_macos.dart')->11683\n", 394 | "Library('package:flutter/src/services/system_chrome.dart')->11764\n", 395 | "Library('package:flutter/src/services/system_navigator.dart')->11819\n", 396 | "Library('package:flutter/src/services/system_sound.dart')->11692\n", 397 | "Library('package:flutter/src/services/text_editing.dart')->11761\n", 398 | "Library('package:flutter/src/widgets/annotated_region.dart')->11768\n", 399 | "Library('package:flutter/src/widgets/app.dart')->11789\n", 400 | "Library('package:flutter/src/widgets/automatic_keep_alive.dart')->11673\n", 401 | "Library('package:flutter/src/widgets/banner.dart')->11702\n", 402 | "Library('package:flutter/src/widgets/basic.dart')->11634\n", 403 | "Library('package:flutter/src/widgets/binding.dart')->11669\n", 404 | "Library('package:flutter/src/widgets/container.dart')->11748\n", 405 | "Library('package:flutter/src/widgets/focus_manager.dart')->11660\n", 406 | "Library('package:flutter/src/widgets/focus_scope.dart')->11706\n", 407 | "Library('package:flutter/src/widgets/focus_traversal.dart')->11776\n", 408 | "Library('package:flutter/src/widgets/framework.dart')->11645\n", 409 | "Library('package:flutter/src/widgets/gesture_detector.dart')->11703\n", 410 | "Library('package:flutter/src/widgets/grid_paper.dart')->11679\n", 411 | "Library('package:flutter/src/widgets/heroes.dart')->11778\n", 412 | "Library('package:flutter/src/widgets/icon.dart')->11797\n", 413 | "Library('package:flutter/src/widgets/icon_data.dart')->11717\n", 414 | "Library('package:flutter/src/widgets/icon_theme.dart')->11767\n", 415 | "Library('package:flutter/src/widgets/icon_theme_data.dart')->11790\n", 416 | "Library('package:flutter/src/widgets/image.dart')->11831\n", 417 | "Library('package:flutter/src/widgets/implicit_animations.dart')->11675\n", 418 | "Library('package:flutter/src/widgets/inherited_notifier.dart')->11687\n", 419 | "Library('package:flutter/src/widgets/layout_builder.dart')->11705\n", 420 | "Library('package:flutter/src/widgets/localizations.dart')->11690\n", 421 | "Library('package:flutter/src/widgets/media_query.dart')->11779\n", 422 | "Library('package:flutter/src/widgets/modal_barrier.dart')->11688\n", 423 | "Library('package:flutter/src/widgets/navigation_toolbar.dart')->11766\n", 424 | "Library('package:flutter/src/widgets/navigator.dart')->11644\n", 425 | "Library('package:flutter/src/widgets/notification_listener.dart')->11686\n", 426 | "Library('package:flutter/src/widgets/overlay.dart')->11658\n", 427 | "Library('package:flutter/src/widgets/page_storage.dart')->11709\n", 428 | "Library('package:flutter/src/widgets/pages.dart')->11650\n", 429 | "Library('package:flutter/src/widgets/performance_overlay.dart')->11781\n", 430 | "Library('package:flutter/src/widgets/preferred_size.dart')->11798\n", 431 | "Library('package:flutter/src/widgets/primary_scroll_controller.dart')->11773\n", 432 | "Library('package:flutter/src/widgets/routes.dart')->11649\n", 433 | "Library('package:flutter/src/widgets/safe_area.dart')->11769\n", 434 | "Library('package:flutter/src/widgets/scroll_configuration.dart')->11793\n", 435 | "Library('package:flutter/src/widgets/scroll_controller.dart')->11786\n", 436 | "Library('package:flutter/src/widgets/scroll_metrics.dart')->11784\n", 437 | "Library('package:flutter/src/widgets/scroll_position.dart')->11785\n", 438 | "Library('package:flutter/src/widgets/semantics_debugger.dart')->11740\n", 439 | "Library('package:flutter/src/widgets/text.dart')->11782\n", 440 | "Library('package:flutter/src/widgets/ticker_provider.dart')->11647\n", 441 | "Library('package:flutter/src/widgets/title.dart')->11780\n", 442 | "Library('package:flutter/src/widgets/transitions.dart')->11651\n", 443 | "Library('package:flutter/src/widgets/widget_inspector.dart')->11681\n", 444 | "Library('package:flutter/src/widgets/widget_span.dart')->11844\n", 445 | "Library('package:myapp/main.dart')->11847\n", 446 | "Library('package:typed_data/typed_buffers.dart')->11704\n", 447 | "Library('package:vector_math/hash.dart')->11812\n", 448 | "Library('package:vector_math/vector_math_64.dart')->11813\n" 449 | ] 450 | } 451 | ], 452 | "source": [ 453 | "for lib in sorted(s.getrefs('Library'), key=lambda x: x.x['url'].x['value']):\n", 454 | " print(lib)" 455 | ] 456 | }, 457 | { 458 | "cell_type": "markdown", 459 | "metadata": {}, 460 | "source": [ 461 | "However if the app is obfuscated, the names will be gibberish. In this case, we need..." 462 | ] 463 | }, 464 | { 465 | "cell_type": "markdown", 466 | "metadata": {}, 467 | "source": [ 468 | "# Deobfuscation\n", 469 | "\n", 470 | "The Dart snapshotter has support for obfuscation. It's easy to enable, and it renames identifiers (i.e. library URLs, class names, function names, field names) to random three-letter strings. This obfuscation is per string, so two functions named the same will also have the same obfuscated identifier. Also, some identifiers that are special to the VM (such as the top-level class name, `::`) are left untouched. This obfuscation is applied to everything, including dependencies and internal (i.e. Dart or Flutter) code.\n", 471 | "\n", 472 | "This brings up a need for deobfuscation. The process can be summarized as:\n", 473 | "\n", 474 | " 1. Find open-source dependencies used in the app.\n", 475 | " 2. Build a *reference snapshot* that contains these open-source dependencies.\n", 476 | " 3. Attempt to match functions in both snapshots, so we can unmask their real names.\n", 477 | "\n", 478 | "This deobfuscates most of the identifiers, helps us separate useful code from dependencies, and helps us see what the useful code is doing. \n", 479 | "In my experience, the fastest way to do this is:\n", 480 | "\n", 481 | " 1. Build an empty app. This will be the reference snapshot.\n", 482 | " 2. Print a string table, as we did before, but excluding strings that are present in the reference snapshot.\n", 483 | " 3. Look at the strings, google them, find the package they're defined in.\n", 484 | " 4. When you have found a package, import it in the app and rebuild it.\n", 485 | " 5. Go to step (2); this time there'll be less strings. \n", 486 | " 6. When only useful strings are left, the reference snapshot is ready. Use it to deobfuscate the names.\n", 487 | "\n", 488 | "However, Dart has a powerful **tree shaker** which will remove any dependencies we are not using. So, importing these dependencies in our empty app is not enough, we would have to call every function in their public API in order for them to be included in the reference snapshot. Because this is long and tedious, it's better to patch Dart to disable this tree shaker.\n", 489 | "\n", 490 | "TODO: continue explanation; publish nref-based deobfuscation code; add obfuscated sample" 491 | ] 492 | }, 493 | { 494 | "cell_type": "markdown", 495 | "metadata": {}, 496 | "source": [ 497 | "# Print library structure" 498 | ] 499 | }, 500 | { 501 | "cell_type": "markdown", 502 | "metadata": {}, 503 | "source": [ 504 | "Given a certain library, we can print all classes, functions, fields and constants in it." 505 | ] 506 | }, 507 | { 508 | "cell_type": "code", 509 | "execution_count": 27, 510 | "metadata": {}, 511 | "outputs": [ 512 | { 513 | "name": "stdout", 514 | "output_type": "stream", 515 | "text": [ 516 | "Library('package:myapp/main.dart')->11847\n" 517 | ] 518 | } 519 | ], 520 | "source": [ 521 | "lib = s.strings['package:myapp/main.dart'].src[1][0]\n", 522 | "print(lib)" 523 | ] 524 | } 525 | ], 526 | "metadata": { 527 | "kernelspec": { 528 | "display_name": "Python 3", 529 | "language": "python", 530 | "name": "python3" 531 | }, 532 | "language_info": { 533 | "codemirror_mode": { 534 | "name": "ipython", 535 | "version": 3 536 | }, 537 | "file_extension": ".py", 538 | "mimetype": "text/x-python", 539 | "name": "python", 540 | "nbconvert_exporter": "python", 541 | "pygments_lexer": "ipython3", 542 | "version": "3.8.0" 543 | } 544 | }, 545 | "nbformat": 4, 546 | "nbformat_minor": 4 547 | } 548 | --------------------------------------------------------------------------------