NaClErrorCode NaClAppLoadFile(struct Gio *gp, struct NaClApp *nap) { NaClErrorCode ret = LOAD_INTERNAL; NaClErrorCode subret; uintptr_t rodata_end; uintptr_t data_end; uintptr_t max_vaddr; struct NaClElfImage *image = NULL; struct NaClPerfCounter time_load_file; NaClPerfCounterCtor(&time_load_file, "NaClAppLoadFile"); /* NACL_MAX_ADDR_BITS < 32 */ if (nap->addr_bits > NACL_MAX_ADDR_BITS) { ret = LOAD_ADDR_SPACE_TOO_BIG; goto done; } nap->stack_size = NaClRoundAllocPage(nap->stack_size); /* temporay object will be deleted at end of function */ image = NaClElfImageNew(gp, &subret); if (NULL == image) { ret = subret; goto done; } subret = NaClElfImageValidateElfHeader(image); if (LOAD_OK != subret) { ret = subret; goto done; } subret = NaClElfImageValidateProgramHeaders(image, nap->addr_bits, &nap->static_text_end, &nap->rodata_start, &rodata_end, &nap->data_start, &data_end, &max_vaddr); if (LOAD_OK != subret) { ret = subret; goto done; } if (0 == nap->data_start) { if (0 == nap->rodata_start) { if (NaClRoundAllocPage(max_vaddr) - max_vaddr < NACL_HALT_SLED_SIZE) { /* * if no rodata and no data, we make sure that there is space for * the halt sled. */ max_vaddr += NACL_MAP_PAGESIZE; } } else { /* * no data, but there is rodata. this means max_vaddr is just * where rodata ends. this might not be at an allocation * boundary, and in this the page would not be writable. round * max_vaddr up to the next allocation boundary so that bss will * be at the next writable region. */ ; } max_vaddr = NaClRoundAllocPage(max_vaddr); } /* * max_vaddr -- the break or the boundary between data (initialized * and bss) and the address space hole -- does not have to be at a * page boundary. */ nap->break_addr = max_vaddr; nap->data_end = max_vaddr; NaClLog(4, "Values from NaClElfImageValidateProgramHeaders:\n"); NaClLog(4, "rodata_start = 0x%08"NACL_PRIxPTR"\n", nap->rodata_start); NaClLog(4, "rodata_end = 0x%08"NACL_PRIxPTR"\n", rodata_end); NaClLog(4, "data_start = 0x%08"NACL_PRIxPTR"\n", nap->data_start); NaClLog(4, "data_end = 0x%08"NACL_PRIxPTR"\n", data_end); NaClLog(4, "max_vaddr = 0x%08"NACL_PRIxPTR"\n", max_vaddr); /* We now support only one bundle size. */ nap->bundle_size = NACL_INSTR_BLOCK_SIZE; nap->initial_entry_pt = NaClElfImageGetEntryPoint(image); NaClLogAddressSpaceLayout(nap); if (!NaClAddrIsValidEntryPt(nap, nap->initial_entry_pt)) { ret = LOAD_BAD_ENTRY; goto done; } subret = NaClCheckAddressSpaceLayoutSanity(nap, rodata_end, data_end, max_vaddr); if (LOAD_OK != subret) { ret = subret; goto done; } NaClLog(2, "Allocating address space\n"); NaClPerfCounterMark(&time_load_file, "PreAllocAddrSpace"); NaClPerfCounterIntervalLast(&time_load_file); subret = NaClAllocAddrSpace(nap); NaClPerfCounterMark(&time_load_file, NACL_PERF_IMPORTANT_PREFIX "AllocAddrSpace"); NaClPerfCounterIntervalLast(&time_load_file); if (LOAD_OK != subret) { ret = subret; goto done; } /* * Make sure the static image pages are marked writable before we try * to write them. */ NaClLog(2, "Loading into memory\n"); ret = NaCl_mprotect((void *) (nap->mem_start + NACL_TRAMPOLINE_START), NaClRoundAllocPage(nap->data_end) - NACL_TRAMPOLINE_START, NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE); if (0 != ret) { NaClLog(LOG_FATAL, "NaClAppLoadFile: Failed to make image pages writable. " "Error code 0x%x\n", ret); } subret = NaClElfImageLoad(image, gp, nap->addr_bits, nap->mem_start); if (LOAD_OK != subret) { ret = subret; goto done; } /* * NB: mem_map object has been initialized, but is empty. * NaClMakeDynamicTextShared does not touch it. * * NaClMakeDynamicTextShared also fills the dynamic memory region * with the architecture-specific halt instruction. If/when we use * memory mapping to save paging space for the dynamic region and * lazily halt fill the memory as the pages become * readable/executable, we must make sure that the *last* * NACL_MAP_PAGESIZE chunk is nonetheless mapped and written with * halts. */ NaClLog(2, ("Replacing gap between static text and" " (ro)data with shareable memory\n")); subret = NaClMakeDynamicTextShared(nap); NaClPerfCounterMark(&time_load_file, NACL_PERF_IMPORTANT_PREFIX "MakeDynText"); NaClPerfCounterIntervalLast(&time_load_file); if (LOAD_OK != subret) { ret = subret; goto done; } /* * NaClFillEndOfTextRegion will fill with halt instructions the * padding space after the static text region. * * Shm-backed dynamic text space was filled with halt instructions * in NaClMakeDynamicTextShared. This extends to the rodata. For * non-shm-backed text space, this extend to the next page (and not * allocation page). static_text_end is updated to include the * padding. */ NaClFillEndOfTextRegion(nap); #if 0 == NACL_DANGEROUS_DEBUG_MODE_DISABLE_INNER_SANDBOX NaClLog(2, "Validating image\n"); subret = NaClValidateImage(nap); NaClPerfCounterMark(&time_load_file, NACL_PERF_IMPORTANT_PREFIX "ValidateImg"); NaClPerfCounterIntervalLast(&time_load_file); if (LOAD_OK != subret) { ret = subret; goto done; } #endif NaClLog(2, "Initializing arch switcher\n"); NaClInitSwitchToApp(nap); NaClLog(2, "Installing trampoline\n"); NaClLoadTrampoline(nap); /* * NaClMemoryProtect also initializes the mem_map w/ information * about the memory pages and their current protection value. * * The contents of the dynamic text region will get remapped as * non-writable. */ NaClLog(2, "Applying memory protection\n"); subret = NaClMemoryProtection(nap); if (LOAD_OK != subret) { ret = subret; goto done; } NaClLog(2, "NaClAppLoadFile done; "); NaClLogAddressSpaceLayout(nap); ret = LOAD_OK; done: NaClElfImageDelete(image); NaClPerfCounterMark(&time_load_file, "EndLoadFile"); NaClPerfCounterIntervalTotal(&time_load_file); return ret; }
NaClErrorCode NaClAppLoadFile(struct Gio *gp, struct NaClApp *nap, enum NaClAbiCheckOption check_abi) { NaClErrorCode ret = LOAD_INTERNAL; NaClErrorCode subret; uintptr_t rodata_end; uintptr_t data_end; uintptr_t max_vaddr; struct NaClElfImage *image = NULL; /* NACL_MAX_ADDR_BITS < 32 */ if (nap->addr_bits > NACL_MAX_ADDR_BITS) { ret = LOAD_ADDR_SPACE_TOO_BIG; goto done; } nap->stack_size = NaClRoundAllocPage(nap->stack_size); /* temporay object will be deleted at end of function */ image = NaClElfImageNew(gp, &subret); if (NULL == image) { ret = subret; goto done; } #if 0 == NACL_DANGEROUS_DEBUG_MODE_DISABLE_INNER_SANDBOX check_abi = NACL_ABI_CHECK_OPTION_CHECK; #endif if (NACL_ABI_CHECK_OPTION_CHECK == check_abi) { subret = NaClElfImageValidateAbi(image); if (subret != LOAD_OK) { ret = subret; goto done; } } subret = NaClElfImageValidateElfHeader(image); if (LOAD_OK != subret) { ret = subret; goto done; } subret = NaClElfImageValidateProgramHeaders(image, nap->addr_bits, &nap->static_text_end, &nap->rodata_start, &rodata_end, &nap->data_start, &data_end, &max_vaddr); if (LOAD_OK != subret) { ret = subret; goto done; } if (0 == nap->data_start) { if (0 == nap->rodata_start) { if (NaClRoundAllocPage(max_vaddr) - max_vaddr < NACL_HALT_SLED_SIZE) { /* * if no rodata and no data, we make sure that there is space for * the halt sled. */ max_vaddr += NACL_MAP_PAGESIZE; } } else { /* * no data, but there is rodata. this means max_vaddr is just * where rodata ends. this might not be at an allocation * boundary, and in this the page would not be writable. round * max_vaddr up to the next allocation boundary so that bss will * be at the next writable region. */ ; } max_vaddr = NaClRoundAllocPage(max_vaddr); } /* * max_vaddr -- the break or the boundary between data (initialized * and bss) and the address space hole -- does not have to be at a * page boundary. */ nap->break_addr = max_vaddr; nap->data_end = max_vaddr; NaClLog(4, "Values from NaClElfImageValidateProgramHeaders:\n"); NaClLog(4, "rodata_start = 0x%08"NACL_PRIxPTR"\n", nap->rodata_start); NaClLog(4, "rodata_end = 0x%08"NACL_PRIxPTR"\n", rodata_end); NaClLog(4, "data_start = 0x%08"NACL_PRIxPTR"\n", nap->data_start); NaClLog(4, "data_end = 0x%08"NACL_PRIxPTR"\n", data_end); NaClLog(4, "max_vaddr = 0x%08"NACL_PRIxPTR"\n", max_vaddr); #if 0 == NACL_DANGEROUS_DEBUG_MODE_DISABLE_INNER_SANDBOX nap->bundle_size = NaClElfImageGetBundleSize(image); if (nap->bundle_size == 0) { ret = LOAD_BAD_ABI; goto done; } #else /* pick some reasonable default for an un-sandboxed nexe */ nap->bundle_size = 32; #endif nap->entry_pt = NaClElfImageGetEntryPoint(image); NaClLog(2, "NaClApp addr space layout:\n"); NaClLog(2, "nap->static_text_end = 0x%016"NACL_PRIxPTR"\n", nap->static_text_end); NaClLog(2, "nap->dynamic_text_start = 0x%016"NACL_PRIxPTR"\n", nap->dynamic_text_start); NaClLog(2, "nap->dynamic_text_end = 0x%016"NACL_PRIxPTR"\n", nap->dynamic_text_end); NaClLog(2, "nap->rodata_start = 0x%016"NACL_PRIxPTR"\n", nap->rodata_start); NaClLog(2, "nap->data_start = 0x%016"NACL_PRIxPTR"\n", nap->data_start); NaClLog(2, "nap->data_end = 0x%016"NACL_PRIxPTR"\n", nap->data_end); NaClLog(2, "nap->break_addr = 0x%016"NACL_PRIxPTR"\n", nap->break_addr); NaClLog(2, "nap->entry_pt = 0x%016"NACL_PRIxPTR"\n", nap->entry_pt); NaClLog(2, "nap->bundle_size = 0x%x\n", nap->bundle_size); if (!NaClAddrIsValidEntryPt(nap, nap->entry_pt)) { ret = LOAD_BAD_ENTRY; goto done; } /* * Basic address space layout sanity check. */ if (0 != nap->data_start) { if (data_end != max_vaddr) { NaClLog(LOG_INFO, "data segment is not last\n"); ret = LOAD_DATA_NOT_LAST_SEGMENT; goto done; } } else if (0 != nap->rodata_start) { if (NaClRoundAllocPage(rodata_end) != max_vaddr) { /* * This should be unreachable, but we include it just for * completeness. * * Here is why it is unreachable: * * NaClPhdrChecks checks the test segment starting address. The * only allowed loaded segments are text, data, and rodata. * Thus unless the rodata is in the trampoline region, it must * be after the text. And NaClElfImageValidateProgramHeaders * ensures that all segments start after the trampoline region. */ NaClLog(LOG_INFO, "no data segment, but rodata segment is not last\n"); ret = LOAD_NO_DATA_BUT_RODATA_NOT_LAST_SEGMENT; goto done; } } if (0 != nap->rodata_start && 0 != nap->data_start) { if (rodata_end > nap->data_start) { NaClLog(LOG_INFO, "rodata_overlaps data.\n"); ret = LOAD_RODATA_OVERLAPS_DATA; goto done; } } if (0 != nap->rodata_start) { if (NaClRoundAllocPage(NaClEndOfStaticText(nap)) > nap->rodata_start) { ret = LOAD_TEXT_OVERLAPS_RODATA; goto done; } } else if (0 != nap->data_start) { if (NaClRoundAllocPage(NaClEndOfStaticText(nap)) > nap->data_start) { ret = LOAD_TEXT_OVERLAPS_DATA; goto done; } } if (0 != nap->rodata_start && NaClRoundAllocPage(nap->rodata_start) != nap->rodata_start) { NaClLog(LOG_INFO, "rodata_start not a multiple of allocation size\n"); ret = LOAD_BAD_RODATA_ALIGNMENT; goto done; } if (0 != nap->data_start && NaClRoundAllocPage(nap->data_start) != nap->data_start) { NaClLog(LOG_INFO, "data_start not a multiple of allocation size\n"); ret = LOAD_BAD_DATA_ALIGNMENT; goto done; } NaClLog(2, "Allocating address space\n"); subret = NaClAllocAddrSpace(nap); if (LOAD_OK != subret) { ret = subret; goto done; } /* * NB: mem_map object has been initialized, but is empty. * NaClMakeDynamicTextShared does not touch it. * * NaClMakeDynamicTextShared also fills the dynamic memory region * with the architecture-specific halt instruction. If/when we use * memory mapping to save paging space for the dynamic region and * lazily halt fill the memory as the pages become * readable/executable, we must make sure that the *last* * NACL_MAP_PAGESIZE chunk is nonetheless mapped and written with * halts. */ NaClLog(2, ("Replacing gap between static text and" " (ro)data with shareable memory\n")); subret = NaClMakeDynamicTextShared(nap); if (LOAD_OK != subret) { ret = subret; goto done; } /* * Make sure the static image pages are marked writable before we try * to write them. * TODO(ilewis): See if this can be enabled for Win32 as well. (issue 40077) */ NaClLog(2, "Loading into memory\n"); #if NACL_WINDOWS && NACL_ARCH_CPU_X86_64 ret = NaCl_mprotect((void*) nap->mem_start, NaClRoundAllocPage(nap->data_end), PROT_READ|PROT_WRITE); if (ret) { NaClLog(LOG_FATAL, "Couldn't get writeable pages for image. " "Error code 0x%X\n", ret); } #endif subret = NaClElfImageLoad(image, gp, nap->addr_bits, nap->mem_start); if (LOAD_OK != subret) { ret = subret; goto done; } /* * NaClFillEndOfTextRegion will fill with halt instructions the * padding space after the static text region. * * Shm-backed dynamic text space was filled with halt instructions * in NaClMakeDynamicTextShared. This extends to the rodata. For * non-shm-backed text space, this extend to the next page (and not * allocation page). static_text_end is updated to include the * padding. */ NaClFillEndOfTextRegion(nap); #if 0 == NACL_DANGEROUS_DEBUG_MODE_DISABLE_INNER_SANDBOX NaClLog(2, "Validating image\n"); subret = NaClValidateImage(nap); if (LOAD_OK != subret) { ret = subret; goto done; } #endif NaClLog(2, "Installing trampoline\n"); NaClLoadTrampoline(nap); NaClLog(2, "Installing springboard\n"); NaClLoadSpringboard(nap); NaClLog(2, "Applying memory protection\n"); /* * NaClMemoryProtect also initializes the mem_map w/ information * about the memory pages and their current protection value. * * The contents of the dynamic text region will get remapped as * non-writable. */ subret = NaClMemoryProtection(nap); if (LOAD_OK != subret) { ret = subret; goto done; } NaClLog(2, "NaClAppLoadFile done; addr space layout:\n"); NaClLog(2, "nap->static_text_end = 0x%016"NACL_PRIxPTR"\n", nap->static_text_end); NaClLog(2, "nap->dynamic_text_start = 0x%016"NACL_PRIxPTR"\n", nap->dynamic_text_start); NaClLog(2, "nap->dynamic_text_end = 0x%016"NACL_PRIxPTR"\n", nap->dynamic_text_end); NaClLog(2, "nap->rodata_start = 0x%016"NACL_PRIxPTR"\n", nap->rodata_start); NaClLog(2, "nap->data_start = 0x%016"NACL_PRIxPTR"\n", nap->data_start); NaClLog(2, "nap->data_end = 0x%016"NACL_PRIxPTR"\n", nap->data_end); NaClLog(2, "nap->break_addr = 0x%016"NACL_PRIxPTR"\n", nap->break_addr); NaClLog(2, "nap->entry_pt = 0x%016"NACL_PRIxPTR"\n", nap->entry_pt); NaClLog(2, "nap->bundle_size = 0x%x\n", nap->bundle_size); ret = LOAD_OK; done: NaClElfImageDelete(image); return ret; }
NaClErrorCode NaClAppLoadFileAslr(struct NaClDesc *ndp, struct NaClApp *nap, enum NaClAslrMode aslr_mode) { NaClErrorCode ret = LOAD_INTERNAL; NaClErrorCode subret = LOAD_INTERNAL; uintptr_t rodata_end; uintptr_t data_end; uintptr_t max_vaddr; struct NaClElfImage *image = NULL; struct NaClPerfCounter time_load_file; struct NaClElfImageInfo info; NaClPerfCounterCtor(&time_load_file, "NaClAppLoadFile"); /* NACL_MAX_ADDR_BITS < 32 */ if (nap->addr_bits > NACL_MAX_ADDR_BITS) { ret = LOAD_ADDR_SPACE_TOO_BIG; goto done; } nap->stack_size = NaClRoundAllocPage(nap->stack_size); /* temporay object will be deleted at end of function */ image = NaClElfImageNew(ndp, &subret); if (NULL == image || LOAD_OK != subret) { ret = subret; goto done; } subret = NaClElfImageValidateProgramHeaders(image, nap->addr_bits, &info); if (LOAD_OK != subret) { ret = subret; goto done; } if (nap->initial_nexe_max_code_bytes != 0) { size_t code_segment_size = info.static_text_end - NACL_TRAMPOLINE_END; if (code_segment_size > nap->initial_nexe_max_code_bytes) { NaClLog(LOG_ERROR, "NaClAppLoadFileAslr: " "Code segment size (%"NACL_PRIuS" bytes) exceeds limit (%" NACL_PRId32" bytes)\n", code_segment_size, nap->initial_nexe_max_code_bytes); ret = LOAD_CODE_SEGMENT_TOO_LARGE; goto done; } } nap->static_text_end = info.static_text_end; nap->rodata_start = info.rodata_start; rodata_end = info.rodata_end; nap->data_start = info.data_start; data_end = info.data_end; max_vaddr = info.max_vaddr; if (0 == nap->data_start) { if (0 == nap->rodata_start) { if (NaClRoundAllocPage(max_vaddr) - max_vaddr < NACL_HALT_SLED_SIZE) { /* * if no rodata and no data, we make sure that there is space for * the halt sled. */ max_vaddr += NACL_MAP_PAGESIZE; } } else { /* * no data, but there is rodata. this means max_vaddr is just * where rodata ends. this might not be at an allocation * boundary, and in this the page would not be writable. round * max_vaddr up to the next allocation boundary so that bss will * be at the next writable region. */ ; } max_vaddr = NaClRoundAllocPage(max_vaddr); } /* * max_vaddr -- the break or the boundary between data (initialized * and bss) and the address space hole -- does not have to be at a * page boundary. * * Memory allocation will use NaClRoundPage(nap->break_addr), but * the system notion of break is always an exact address. Even * though we must allocate and make accessible multiples of pages, * the linux-style brk system call (which returns current break on * failure) permits a non-aligned address as argument. */ nap->break_addr = max_vaddr; nap->data_end = max_vaddr; NaClLog(4, "Values from NaClElfImageValidateProgramHeaders:\n"); NaClLog(4, "rodata_start = 0x%08"NACL_PRIxPTR"\n", nap->rodata_start); NaClLog(4, "rodata_end = 0x%08"NACL_PRIxPTR"\n", rodata_end); NaClLog(4, "data_start = 0x%08"NACL_PRIxPTR"\n", nap->data_start); NaClLog(4, "data_end = 0x%08"NACL_PRIxPTR"\n", data_end); NaClLog(4, "max_vaddr = 0x%08"NACL_PRIxPTR"\n", max_vaddr); /* We now support only one bundle size. */ nap->bundle_size = NACL_INSTR_BLOCK_SIZE; nap->initial_entry_pt = NaClElfImageGetEntryPoint(image); NaClLogAddressSpaceLayout(nap); if (!NaClAddrIsValidEntryPt(nap, nap->initial_entry_pt)) { ret = LOAD_BAD_ENTRY; goto done; } subret = NaClCheckAddressSpaceLayoutSanity(nap, rodata_end, data_end, max_vaddr); if (LOAD_OK != subret) { ret = subret; goto done; } NaClLog(2, "Allocating address space\n"); NaClPerfCounterMark(&time_load_file, "PreAllocAddrSpace"); NaClPerfCounterIntervalLast(&time_load_file); subret = NaClAllocAddrSpaceAslr(nap, aslr_mode); NaClPerfCounterMark(&time_load_file, NACL_PERF_IMPORTANT_PREFIX "AllocAddrSpace"); NaClPerfCounterIntervalLast(&time_load_file); if (LOAD_OK != subret) { ret = subret; goto done; } /* * Make sure the static image pages are marked writable before we try * to write them. */ NaClLog(2, "Loading into memory\n"); ret = NaClMprotect((void *) (nap->mem_start + NACL_TRAMPOLINE_START), NaClRoundAllocPage(nap->data_end) - NACL_TRAMPOLINE_START, PROT_READ | PROT_WRITE); if (0 != ret) { NaClLog(LOG_FATAL, "NaClAppLoadFile: Failed to make image pages writable. " "Error code 0x%x\n", ret); } subret = NaClElfImageLoad(image, ndp, nap); NaClPerfCounterMark(&time_load_file, NACL_PERF_IMPORTANT_PREFIX "NaClElfImageLoad"); NaClPerfCounterIntervalLast(&time_load_file); if (LOAD_OK != subret) { ret = subret; goto done; } /* * NB: mem_map object has been initialized, but is empty. * NaClMakeDynamicTextShared does not touch it. * * NaClMakeDynamicTextShared also fills the dynamic memory region * with the architecture-specific halt instruction. If/when we use * memory mapping to save paging space for the dynamic region and * lazily halt fill the memory as the pages become * readable/executable, we must make sure that the *last* * NACL_MAP_PAGESIZE chunk is nonetheless mapped and written with * halts. */ NaClLog(2, ("Replacing gap between static text and" " (ro)data with shareable memory\n")); subret = NaClMakeDynamicTextShared(nap); NaClPerfCounterMark(&time_load_file, NACL_PERF_IMPORTANT_PREFIX "MakeDynText"); NaClPerfCounterIntervalLast(&time_load_file); if (LOAD_OK != subret) { ret = subret; goto done; } /* * NaClFillEndOfTextRegion will fill with halt instructions the * padding space after the static text region. * * Shm-backed dynamic text space was filled with halt instructions * in NaClMakeDynamicTextShared. This extends to the rodata. For * non-shm-backed text space, this extend to the next page (and not * allocation page). static_text_end is updated to include the * padding. */ NaClFillEndOfTextRegion(nap); if (nap->main_exe_prevalidated) { NaClLog(2, "Main executable segment hit validation cache and mapped in," " skipping validation.\n"); subret = LOAD_OK; } else { NaClLog(2, "Validating image\n"); subret = NaClValidateImage(nap); } NaClPerfCounterMark(&time_load_file, NACL_PERF_IMPORTANT_PREFIX "ValidateImg"); NaClPerfCounterIntervalLast(&time_load_file); if (LOAD_OK != subret) { ret = subret; goto done; } NaClLog(2, "Initializing arch switcher\n"); NaClInitSwitchToApp(nap); NaClLog(2, "Installing trampoline\n"); NaClLoadTrampoline(nap, aslr_mode); NaClLog(2, "Installing springboard\n"); NaClLoadSpringboard(nap); /* * NaClMemoryProtection also initializes the mem_map w/ information * about the memory pages and their current protection value. * * The contents of the dynamic text region will get remapped as * non-writable. */ NaClLog(2, "Applying memory protection\n"); subret = NaClMemoryProtection(nap); if (LOAD_OK != subret) { ret = subret; goto done; } NaClLog(2, "NaClAppLoadFile done; "); NaClLogAddressSpaceLayout(nap); ret = LOAD_OK; done: NaClElfImageDelete(image); NaClPerfCounterMark(&time_load_file, "EndLoadFile"); NaClPerfCounterIntervalTotal(&time_load_file); return ret; }