Dalvik Source Code Reading
Upcoming SlideShare
Loading in...5
×
 

Dalvik Source Code Reading

on

  • 5,281 views

横浜AndroidPF部で発表しようとして、できなかったときの資料です。

横浜AndroidPF部で発表しようとして、できなかったときの資料です。
http://silentworlds.info/pukiwiki/?%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89%E3%83%AA%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0

Statistics

Views

Total Views
5,281
Views on SlideShare
4,971
Embed Views
310

Actions

Likes
5
Downloads
63
Comments
0

4 Embeds 310

http://d.hatena.ne.jp 302
url_unknown 6
http://s.deeeki.com 1
http://webcache.googleusercontent.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Dalvik Source Code Reading Dalvik Source Code Reading Presentation Transcript

  • Dalvik (1) @kishima http://silentworlds.info/ 2011/6/5 AndroidPF ( )2011 6 12 1
  • ( ) Zygote fork VM dex dexopt2011 6 12 2
  • dalvik/ dalvikvm/ DalvikVM main() dexdump/ dex dexlist/ frameworks/base/cmds/app_process/ ? dexopt/ Zygote(system-server) dex dex docs/ dvz/ zygote system/core/libcutils/zygote.c dx/ zygote dex hit/ ? libdex/ dex (vm ) libnativehelper/ ? tests/ tools/ vm/ VM2011 6 12 3
  • dalvik/ vm/ alloc/ GC analysis/ arch/ compiler/ JIT hprof/ interp/ mterp jdwp/ mterp/ native/ JNI oo/ reflect/ test/ VM2011 6 12 4
  • Zygote init.rc service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system- server2011 6 12 5
  • Zygote frameworks/base/cmds/app_process/app_main.cpp int main(int argc, const char* const argv[]) { --- init // Next arg is startup classname or "--zygote" if (i < argc) { Zygote arg = argv[i++]; if (0 == strcmp("--zygote", arg)) { bool startSystemServer = (i < argc) ? strcmp(argv[i], "--start-system-server") == 0 : false; setArgv0(argv0, "zygote"); set_process_name("zygote"); runtime.start("com.android.internal.os.ZygoteInit", startSystemServer); ---2011 6 12 6
  • Zygote frameworks/base/core/jni/AndroidRuntime.cpp /* * Start the Android runtime. This involves starting the virtual machine * and calling the "static void main(String[] args)" method in the class * named by "className". */ void AndroidRuntime::start(const char* className, const bool startSystemServer) { --- /* start the virtual machine */ VM if (startVm(&mJavaVM, &env) != 0) --- /* * Start VM. This thread becomes the main thread of the VM, and will * not return until the VM exits. */ --- “com.android.internal.os.ZygoteInit” startClass = env->FindClass(slashClassName); --- startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V"); --- env->CallStaticVoidMethod(startClass, startMeth, strArray); --- } JNI Java main2011 6 12 7
  • Zygote AndroidRuntime.cpp JNI frameworks/base/core/java/com/android/internal/os/ZygoteInit.java public static void main(String argv[]) { if (argv[1].equals("true")) { try { startSystemServer(); VMRuntime.getRuntime().setMinimumHeapSize(5 * 1024 * 1024); } else if (!argv[1].equals("false")) { throw new RuntimeException(argv[0] + USAGE_STRING); // Start profiling the zygote initialization. } SamplingProfilerIntegration.start(); Log.i(TAG, "Accepting command socket connections"); registerZygoteSocket(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, if (ZYGOTE_FORK_MODE) { SystemClock.uptimeMillis()); runForkMode(); preloadClasses(); PreLoad } else { Fork? preloadResources(); runSelectLoopMode(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, } SystemClock.uptimeMillis()); closeServerSocket(); // Finish profiling the zygote initialization. } catch (MethodAndArgsCaller caller) { SamplingProfilerIntegration.writeZygoteSnapshot(); caller.run(); } catch (RuntimeException ex) { // Do an initial gc to clean up after startup Log.e(TAG, "Zygote died with exception", ex); gc(); closeServerSocket(); throw ex; // If requested, start system server directly from Zygote } if (argv.length != 2) { } throw new RuntimeException(argv[0] + USAGE_STRING); }2011 6 12 8
  • dvz dalvik/dvz int main (int argc, const char **argv) { --- err = zygote_run_wait(argc - 1, argv + 1, post_run_func); system/core/libcutils/zygote.c int zygote_run_wait(int argc, const char **argv, void (*post_run_func)(int)) /dec/socket/zygote { fd = socket_local_client(ZYGOTE_SOCKET, ANDROID_SOCKET_NAMESPACE_RESERVED, AF_LOCAL); --- // The command socket is passed to the peer as close-on-exec // and will close when the peer dies zygote newargv[0] = "--peer-wait"; memcpy(newargv + 1, argv, argc * sizeof(*argv)); pid = send_request(fd, 1, argc + 1, newargv); --- }2011 6 12 9
  • system/core/libcutils/zygote.c static int send_request(int fd, int sendStdio, int argc, const char **argv) { --- struct msghdr msg; --- ret = sendmsg(fd, &msg, MSG_NOSIGNAL); sendmsg --- // replace any newlines with spaces and send the args 1.stdio For (i = 0; i < argc; i++) { --- 2.argv toprint = argv[i]; --- 3.PID ivs[0].iov_base = (char *)toprint; ivs[0].iov_len = strlen(toprint); ( fork PID ivs[1].iov_base = (char *)newline_string; // ”¥n” ) ivs[1].iov_len = 1; msg.msg_iovlen = 2; do { ret = sendmsg(fd, &msg, MSG_NOSIGNAL); } while (ret < 0 && errno == EINTR); --- // Read the pid, as a 4-byte network-order integer ivs[0].iov_base = &pid; --- ret = recvmsg(fd, &msg, MSG_NOSIGNAL | MSG_WAITALL);2011 6 12 10
  • VM frameworks/base/core/jni/AndroidRuntime.cpp int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) { --- if (executionMode == kEMIntPortable) { JIT opt.optionString = "-Xint:portable"; mOptions.add(opt); } else if (executionMode == kEMIntFast) { opt.optionString = "-Xint:fast"; mOptions.add(opt); #if defined(WITH_JIT) } else if (executionMode == kEMJitCompiler) { opt.optionString = "-Xint:jit"; mOptions.add(opt); #endif --- /* * Initialize the VM. * * The JavaVM* is essentially per-process, and the JNIEnv* is per-thread. * If this call succeeds, the VM is ready, and we can start issuing * JNI calls. */ if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) { VM --- }2011 6 12 11
  • VM dalvik/vm/Jni.c jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) VM { /* * Set up structures for JNIEnv and VM. */ pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt)); memset(pVM, 0, sizeof(JavaVMExt)); pVM->funcTable = &gInvokeInterface; pVM->envList = pEnv; --- VM /* set this up before initializing VM, so it can create some JNIEnvs */ gDvm.vmList = (JavaVM*) pVM; ※ /* * Create an env for main thread. We need to have something set up * here because some of the class initialization we do when starting * up the VM will call into native code. */ JNI pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL); --- /* initialize VM */ gDvm.initializing = true; if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) { --- } --- *p_env = (JNIEnv*) pEnv; *p_vm = (JavaVM*) pVM; --- }2011 6 12 12
  • VM JavaVM VM libnativehelper/include/nativehelper/jni.h struct JNIInvokeInterface{ jint (*DestroyJavaVM)(JavaVM*); jint (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*); jint (*DetachCurrentThread)(JavaVM*); jint (*GetEnv)(JavaVM*, void**, jint); jint (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*); } JNIEnv JNI libnativehelper/include/nativehelper/jni.h struct JNINativeInterface{} VM2011 6 12 13
  • VM struct JavaVMExt; typedef struct JNIEnvExt { const struct JNINativeInterface* funcTable; /* must be first */ const struct JNINativeInterface* baseFuncTable; /* pointer to the VM we are a part of */ struct JavaVMExt* vm; u4 envThreadId; Thread* self; /* if nonzero, we are in a "critical" JNI call */ int critical; /* keep a copy of this here for speed */ bool forceDataCopy; struct JNIEnvExt* prev; struct JNIEnvExt* next; } JNIEnvExt; typedef struct JavaVMExt { const struct JNIInvokeInterface* funcTable; /* must be first */ const struct JNIInvokeInterface* baseFuncTable; /* if multiple VMs are desired, add doubly-linked list stuff here */ /* per-VM feature flags */ bool useChecked; bool warnError; bool forceDataCopy; /* head of list of JNIEnvs associated with this VM */ JNIEnvExt* envList; pthread_mutex_t envListLock; } JavaVMExt;2011 6 12 14
  • dex 1 dalvik/vm/JarFile.c /* classes.dex * Open a Jar file. Its okay if its just a Zip archive without all of * the Jar trimmings, but we do insist on finding "classes.dex" inside .odex * or an appropriately-named ".odex" file alongside. * * If "isBootstrap" is not set, the optimizer/verifier regards this DEX as * being part of a different class loader. */ int dvmJarFileOpen(const char* fileName, const char* odexOutputName, JarFile** ppJarFile, bool isBootstrap) { --- /* First, look for a ".odex" alongside the jar file. It will * have the same name/path except for the extension. .odex */ fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName); .odex --- /* * Pre-created .odex absent or stale. Look inside the jar for a * "classes.dex". */ classes.dex entry = dexZipFindEntry(&archive, kDexInJarName);2011 6 12 15
  • dex 2 dalvik/vm/JarFile.c /* classes.dex * Weve found the one we want. See if theres an up-to-date copy * in the cache. * * On return, "fd" will be seeked just past the "opt" header. ( fd opt * * If a stale .odex file is present and classes.dex exists in ) * the archive, this will *not* return an fd pointing to the ( odex * .odex file; the fd will point into dalvik-cache like any classes.dex fd odex * other jar. */ jar ) if (odexOutputName == NULL) { cachedName = dexOptGenerateCacheFileName(fileName, kDexInJarName); --- /* * If fd points to a new file (because there was no cached version, * or the cached version was stale), generate the optimized DEX. * The file descriptor returned is still locked, and is positioned * just past the optimization header. */ if (newFile) { --- DEX result = dvmOptimizeDexFile(fd, dexOffset, dexGetZipEntryUncompLen(&archive, entry), ( odex?) fileName, dexGetZipEntryModTime(&archive, entry), dexGetZipEntryCrc32(&archive, entry), odex isBootstrap); ---2011 6 12 16
  • dex 3 dalvik/vm/JarFile.c --- /* * Map the cached version. This immediately rewinds the fd, so it * doesnt have to be seeked anywhere in particular. mmap */ if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) { seek --- *ppJarFile = (JarFile*) calloc(1, sizeof(JarFile)); (*ppJarFile)->archive = archive; (*ppJarFile)->cacheFileName = cachedName; (*ppJarFile)->pDvmDex = pDvmDex; --- ( ) dalvik/libdex/SysUtil.c } fd dalvik/DvmDex.c dvmDexFileOpenFromFd() (writable read-only) pDvmDex mmap { mmap prot=PROT_READ | PROT_WRITE SHA-1 flags=MAP_FILE | MAP_PRIVATE (copy-on-write) mprotect } PROT_READ2011 6 12 17
  • dex dalvik/vm/analysis/DexPrepare.c /* fd * Given a descriptor for a file with DEX data in it, produce an * optimized version. * * The file pointed to by "fd" is expected to be a locked shared resource OK * (or private); we make no efforts to enforce multi-process correctness * here. * * "fileName" is only used for debug output. "modWhen" and "crc" are stored * in the dependency set. * bootstrap * The "isBootstrap" flag determines how the optimizer and verifier handle * package-scope access checks. When optimizing, we only load the bootstrap ※ * class DEX files and the target DEX, so the flag determines whether the * target DEX classes are given a (synthetic) non-NULL classLoader pointer. * This only really matters if the target DEX contains classes that claim to * be in the same package as bootstrap classes. dex * * The optimizer will need to load every class in the target DEX file. * This is generally undesirable, so we start a subprocess to do the * work and wait for it to complete. * * Returns "true" on success. All data will have been written to "fd". ※ */ bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength, const char* fileName, u4 modWhen, u4 crc, bool isBootstrap) fd2011 6 12 18
  • dex dalvik/vm/analysis/DexPrepare.c bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength, const char* fileName, u4 modWhen, u4 crc, bool isBootstrap) { --- pid = fork(); if (pid == 0) { static const int kUseValgrind = 0; static const char* kDexOptBin = "/bin/dexopt"; ※fork static const char* kValgrinder = "/usr/bin/valgrind"; --- strcpy(execFile, androidRoot); dexopt strcat(execFile, kDexOptBin); execv() --- if (kUseValgrind) dexopt execv(kValgrinder, argv); else execv(execFile, argv); --- } else { ※fork --- /* * Wait for the optimization process to finish. We go into VMWAIT * mode here so GC suspension wont have to wait for us. */ VM VMWAIT oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT); ※GC --- } }2011 6 12 19
  • 1( ) dalvik/ vm/ mterp/ gen-mterp.py config-xxx xxx config-yyy yyy config-portstd rebuild.sh xxx/ xxx xxx yyy/ yyy yyy c/ C out/ OP out2011 6 12 20
  • dalvik/vm/mterp/out/InterpC-portstd.c ( C ) /* File: portable/entry.c */ * Main interpreter loop. bool INTERP_FUNC_NAME(Thread* self, InterpState* interpState) { #define FETCH(_offset) (pc[(_offset)]) --- #define INST_INST(_inst) ((_inst) & 0xff) /* copy state in */ # define HANDLE_OPCODE(_op) case _op: curMethod = interpState->method; # define ADJUST_PC(_offset) do { pc = interpState->pc; pc += _offset; fp = interpState->fp; EXPORT_EXTRA_PC(); --- } while (false) methodClassDex = curMethod->clazz->pDvmDex; # define FINISH(_offset) { ADJUST_PC(_offset); break; } --- while (1) { --- /* fetch the next 16 bits from the instruction stream */ inst = FETCH(0); switch (INST_INST(inst)) { PC( ) (instruction) --- /* File: c/OP_NOP.c */ HANDLE_OPCODE(OP_NOP) OP FINISH(1); OP_END OP case --- PC --- } JNI2011 6 12 21
  • ( ) Zygote fork dexopt VM ->fork->dex -> JNI JIT ↓ http://silentworlds.info/pukiwiki/2011 6 12 22