Index: vm/vmcore/include/jarfile_support.h =================================================================== --- vm/vmcore/include/jarfile_support.h (revision 699565) +++ vm/vmcore/include/jarfile_support.h (working copy) @@ -244,6 +244,8 @@ } // parses JAR file and stores its structure inside bool Parse( const char* filename, bool do_map ); + // parses JAR file and stores its structure inside as well as extract mapping info between module and library + bool Parse( const char* filename, apr_file_t *mapFile, bool do_map ); // returns entry cache (either shared or owned) JarEntryCache* GetCache() { return m_entries; } // returns if this JarFile owns entry cache it holds Index: vm/vmcore/include/classloader.h =================================================================== --- vm/vmcore/include/classloader.h (revision 699565) +++ vm/vmcore/include/classloader.h (working copy) @@ -378,7 +378,9 @@ const char* class_name_in_jar, const String* class_name, bool* not_found); void SetClasspathFromString(char* prop_string, apr_pool_t *tmp_pool); void SetClasspathFromJarFile(JarFile *jar, apr_pool_t *tmp_pool); + void SetClasspathFromString(char* prop_string, apr_file_t *mapFile, apr_pool_t *tmp_pool); void SetBCPElement(const char *path, apr_pool_t *tmp_pool); + void SetBCPElement(const char *path, apr_file_t *mapFile, apr_pool_t *tmp_pool); BCPElements m_BCPElements; Global_Env* m_env; Index: vm/vmcore/include/environment.h =================================================================== --- vm/vmcore/include/environment.h (revision 699565) +++ vm/vmcore/include/environment.h (working copy) @@ -21,7 +21,7 @@ #include #include #include - +#include #include "open/hythread.h" #include "open/compmgr.h" #include "open/em_vm.h" @@ -36,7 +36,7 @@ typedef struct NSOTableItem NSOTableItem; typedef struct DynamicCode DynamicCode; typedef struct Assertion_Registry Assertion_Registry; - +typedef std::map JarFilePackageMapping; #ifdef USE_COMPRESSED_VTABLE_POINTERS typedef VirtualMemoryPool VTablePool; @@ -73,6 +73,7 @@ hythread_library_t hythread_lib; String_Pool string_pool; // string table JavaVMInitArgs vm_arguments; + JarFilePackageMapping pending_jar_set; /** Index: vm/vmcore/src/class_support/classloader.cpp =================================================================== --- vm/vmcore/src/class_support/classloader.cpp (revision 699565) +++ vm/vmcore/src/class_support/classloader.cpp (working copy) @@ -1170,6 +1170,23 @@ return; } // BootstrapClassLoader::SetClasspathFromProperty +inline void +BootstrapClassLoader::SetClasspathFromString(char* bcp, + apr_file_t *mapFile, apr_pool_t* tmp_pool) +{ + assert(bcp); + + // set bootclasspath elements + char *path_name = strtok(bcp, PORT_PATH_SEPARATOR_STR); + while (path_name) + { + SetBCPElement(path_name, mapFile, tmp_pool); + path_name = strtok(NULL, PORT_PATH_SEPARATOR_STR); + } + + return; +} // BootstrapClassLoader::SetClasspathFromProperty + inline void BootstrapClassLoader::SetBCPElement(const char *path, apr_pool_t *tmp_pool) { BCPElement* element; @@ -1223,6 +1240,59 @@ return; } // BootstrapClassLoader::SetBCPElement +inline void BootstrapClassLoader::SetBCPElement(const char *path, apr_file_t *mapFile, apr_pool_t *tmp_pool) +{ + BCPElement* element; + // check existence of a given path in bootstrap classpath + const char* canoname = port_filepath_canonical(path, tmp_pool); + const String* new_path = m_env->string_pool.lookup(canoname); + for( element = m_BCPElements.m_first; element; element = element->m_next ) { + if( element->m_path == new_path ) { + // found such path + return; + } + } + + // check existence of a given path + apr_finfo_t finfo; + if(apr_stat(&finfo, new_path->bytes, APR_FINFO_SIZE, tmp_pool) != APR_SUCCESS) { + // broken path to the file + return; + } + + element = (BCPElement*)apr_palloc(pool, sizeof(BCPElement) ); + element->m_path = new_path; + element->m_next = NULL; + element->m_isJarFile = file_is_archive(new_path->bytes); + if(element->m_isJarFile) { + JarEntryCache* jec = NULL; + if(m_env->use_common_jar_cache + &&m_BCPElements.m_last + && m_BCPElements.m_last->m_isJarFile) + { + // if previous boot class path element is jar, reuse the same cache + jec = m_BCPElements.m_last->m_jar->GetCache(); + } + void* mem_JarFile = apr_palloc(pool, sizeof(JarFile)); + element->m_jar = new (mem_JarFile) JarFile(jec); + + if(element->m_jar && element->m_jar->Parse(new_path->bytes, mapFile, m_env->map_bootsrtap_jars)) + { + TRACE2("classloader.jar", "opened archive: " << new_path ); + } + } + + // insert element into collection + if( NULL == m_BCPElements.m_first ) { + m_BCPElements.m_first = element; + } else { + m_BCPElements.m_last->m_next = element; + } + m_BCPElements.m_last = element; + + return; +} // BootstrapClassLoader::SetBCPElement + inline void BootstrapClassLoader::SetClasspathFromJarFile(JarFile *jar, apr_pool_t* tmp_pool) { // get classpath from archive file @@ -1576,7 +1646,128 @@ } m_env->VmProperties()->destroy(lib_list); + // create temp pool for apr functions + apr_pool_t *tmp_pool; + apr_pool_create(&tmp_pool, NULL); + /* + * Extract the lib boot directory for on-demand class parsing. + * The module library mapping file is saved in the lib boot directory + */ + char* javaHome = m_env->JavaProperties()->get("java.home"); + char* bootDirectory = + (char*)STD_MALLOC(strlen(javaHome) + + strlen(PORT_FILE_SEPARATOR_STR) + + strlen("bin") + + strlen(PORT_FILE_SEPARATOR_STR) + + strlen("default") + + strlen(PORT_FILE_SEPARATOR_STR) + + 1); + strcpy(bootDirectory, javaHome); + strcat(bootDirectory, PORT_FILE_SEPARATOR_STR); + strcat(bootDirectory, "bin"); + strcat(bootDirectory, PORT_FILE_SEPARATOR_STR); + strcat(bootDirectory, "default" ); + strcat(bootDirectory, PORT_FILE_SEPARATOR_STR); + + char* mapPropertyFile = + (char*)STD_MALLOC(strlen(bootDirectory) + + strlen("modulelibrarymapping.properties") + + 1); + strcpy(mapPropertyFile, bootDirectory); + strcat(mapPropertyFile, "modulelibrarymapping.properties"); + + char* signaturePropertyFile = + (char*)STD_MALLOC(strlen(bootDirectory) + + strlen("signature.properties") + + 1); + strcpy(signaturePropertyFile, bootDirectory); + strcat(signaturePropertyFile, "signature.properties"); + + + /** + * Check existence of the signature file and mapping file + * If the signature file exists, we then compare it with boot libraries in luni path + * If the comparion fails, then we disable the on-demand class loading + */ + char *luni_path = m_env->JavaProperties()->get(O_A_H_BOOT_CLASS_PATH); + apr_finfo_t finfo; + bool mapFileExist = true; // By default, assume the mapping file exists + apr_file_t *mappingFile = NULL; + apr_file_t *signatureFile = NULL; + + if (luni_path == NULL) { + // If there is no luni lib, then no need to do on-demand jar parsing + mapFileExist = false; + } else if(apr_stat(&finfo, signaturePropertyFile, APR_FINFO_SIZE, tmp_pool) != APR_SUCCESS) { + // If the signature file doesn't exist, then on-demand jar parsing is disabled + mapFileExist = false; + } else if(apr_stat(&finfo, mapPropertyFile, APR_FINFO_SIZE, tmp_pool) != APR_SUCCESS) { + // If the mapping info file doesn't exist, then on-demand jar parsing is disabled as well + mapFileExist = false; + } else { + // Both signature and mapping files exist, we check whether there is change for each module + // If there is some change, then on-demand jar parsing is disabled + Properties signatureprops; + load_properties(bootDirectory, "signature", signatureprops); + char* luniPathCopy = (char*)apr_palloc(tmp_pool, strlen(luni_path) + 1); + strcpy(luniPathCopy, luni_path); + char *path_name = strtok(luniPathCopy, PORT_PATH_SEPARATOR_STR); + bool match = true; //By default, we assume the match will succeed as people seldom update modules + while (path_name) + { + // Format the classpath to a standard absolute path. + const char* canoname = port_filepath_canonical(path_name, tmp_pool); + apr_finfo_t finfo; + + /** + * Only compare the existing jars. For jars contained in bootclasspath.properties, + * but don't exist in fact, no need to process them (such as orb.jar) + */ + if(apr_stat(&finfo, canoname, APR_FINFO_SIZE|APR_FINFO_MTIME, tmp_pool) == APR_SUCCESS) { + if (signatureprops.get(canoname) != NULL) { + // Extract signature for this module file + char timeStamp[20]; + char fileSize[20]; + _snprintf(timeStamp, 19, "%lld", finfo.mtime); + _snprintf(fileSize, 19, "%lld", finfo.size); + char* signatureBuf = (char*)apr_palloc(tmp_pool, strlen(timeStamp) + + strlen(fileSize) + + 2); + strcpy(signatureBuf, timeStamp); + strcat(signatureBuf, PORT_PATH_SEPARATOR_STR); + strcat(signatureBuf, fileSize); + if (strcmp(signatureBuf, signatureprops.get(canoname)) !=0) { + match = false; + break; + } + } else { + match = false; + break; + } + } + path_name = strtok(NULL, PORT_PATH_SEPARATOR_STR); + } + + if (match) { + mapFileExist = true; + // Read the map between module and libraries and save them in a software cache + Properties modlibprops; + load_properties(bootDirectory, "modulelibrarymapping", modlibprops); + char** keys = modlibprops.get_keys(); + char** cur_key = keys; + char *value = NULL; + while(*cur_key) { + value = modlibprops.get(*cur_key); + m_env->pending_jar_set.insert(std::make_pair(m_env->string_pool.lookup(*cur_key), m_env->string_pool.lookup(value)));; + modlibprops.destroy(value); + cur_key++; + } + if (*keys != NULL) + modlibprops.destroy(keys); + } + } + /* * At this point, LUNI is loaded, so we can use the boot class path * generated there to set vm.boot.class.path since we didn't do * it before. We also need to add what is listed in harmonyvm.properties @@ -1593,6 +1784,7 @@ size_t bcp_p_len = (prepend ? strlen(prepend) + strlen(PORT_PATH_SEPARATOR_STR) : 0); size_t bcp_a_len = (append ? strlen(PORT_PATH_SEPARATOR_STR) + strlen(append) : 0); char* vmboot = NULL; + char* requiredLib = NULL; // If boot classpath is overridden, just use that value. if(xbcp) { vmboot = (char*)STD_MALLOC(strlen(xbcp) + bcp_a_len + bcp_p_len + 1); @@ -1607,8 +1799,12 @@ // Second, read in harmonyvm.properties char* kernel_bcp = get_kernel_path(m_env); // Third, what is provided by LUNI as class library classes - char *luni_path = m_env->JavaProperties()->get(O_A_H_BOOT_CLASS_PATH); - + requiredLib = (char*)STD_MALLOC( + bcp_p_len + + (comp_path ? strlen(comp_path) : 0) + + strlen(kernel_bcp) + + bcp_a_len + + 1); vmboot = (char*)STD_MALLOC( bcp_p_len + (comp_path ? strlen(comp_path) : 0) @@ -1617,20 +1813,24 @@ + bcp_a_len + 1); char* vmboot_start = vmboot + bcp_p_len; + char* requiredLib_start = requiredLib + bcp_p_len; *vmboot = '\0'; // make allocated buffer look like a C-string *vmboot_start = '\0'; + *requiredLib = '\0'; + *requiredLib_start = '\0'; if(comp_path != NULL) { strcpy(vmboot_start, comp_path); + strcpy(requiredLib_start, comp_path); STD_FREE(comp_path); } strcat(vmboot_start, kernel_bcp); + strcat(requiredLib_start, kernel_bcp); STD_FREE(kernel_bcp); if(luni_path != NULL) { strcat(vmboot_start, PORT_PATH_SEPARATOR_STR); strcat(vmboot_start, luni_path); - m_env->JavaProperties()->destroy(luni_path); } } @@ -1642,12 +1842,16 @@ if (prepend) { strcpy(vmboot, prepend); vmboot[bcp_p_len-1] = PORT_PATH_SEPARATOR; + strcpy(requiredLib, prepend); + requiredLib[bcp_p_len-1] = PORT_PATH_SEPARATOR; m_env->VmProperties()->destroy(prepend); } if (append) { strcat(vmboot, PORT_PATH_SEPARATOR_STR); strcat(vmboot, append); + strcat(requiredLib, PORT_PATH_SEPARATOR_STR); + strcat(requiredLib, append); m_env->VmProperties()->destroy(append); } } @@ -1655,19 +1859,86 @@ /* * set VM_BOOT_CLASS_PATH and SUN_BOOT_CLASS_PATH for any code * that needs it + * Before doing that, we first exclude any repeated elements */ - m_env->VmProperties()->set(VM_BOOT_CLASS_PATH, vmboot); - m_env->JavaProperties()->set(VM_BOOT_CLASS_PATH, vmboot); - m_env->JavaProperties()->set(SUN_BOOT_CLASS_PATH, vmboot); + char *cleanVMBoot = (char*)STD_MALLOC(strlen(vmboot) + 1); + cleanVMBoot[0] = '\0'; + char *path_name = strtok(vmboot, PORT_PATH_SEPARATOR_STR); + while (path_name) + { + // Format the classpath to a standard absolute path. + const char* canoname = port_filepath_canonical(path_name, tmp_pool); + if (strstr(cleanVMBoot, canoname) == NULL) { + strcat(cleanVMBoot, canoname); + strcat(cleanVMBoot, PORT_PATH_SEPARATOR_STR); + } + path_name = strtok(NULL, PORT_PATH_SEPARATOR_STR); + } + m_env->VmProperties()->set(VM_BOOT_CLASS_PATH, cleanVMBoot); + m_env->JavaProperties()->set(VM_BOOT_CLASS_PATH, cleanVMBoot); + m_env->JavaProperties()->set(SUN_BOOT_CLASS_PATH, cleanVMBoot); - // create temp pool for apr functions - apr_pool_t *tmp_pool; - apr_pool_create(&tmp_pool, NULL); // create a bootclasspath collection - - SetClasspathFromString(vmboot, tmp_pool); + /** + * First, we load all necessary class libraries, which is saved in requiredLib + * Second, we perform on-demand class parsing for boot class libraries. There are + * two cases we should consider. (a) If the mapping info between module and libraries + * doesn't exist, we parse these modules and collect mapping info; (b) if the mapping info + * has been established, we just read them into software cache for on-demand use. + * We should note the real case is more complex because we need determine whether + * the target library has been changed by checking signature like file data and size + */ + SetClasspathFromString(requiredLib, tmp_pool); + if (luni_path!= NULL && !mapFileExist) { + apr_status_t status = + apr_file_open(&mappingFile, mapPropertyFile, APR_WRITE | APR_CREATE | APR_TRUNCATE, APR_OS_DEFAULT, tmp_pool); + assert(!status); + status = + apr_file_open(&signatureFile, signaturePropertyFile, APR_WRITE | APR_CREATE | APR_TRUNCATE, APR_OS_DEFAULT, tmp_pool); + assert(!status); + path_name = strtok(luni_path, PORT_PATH_SEPARATOR_STR); + while (path_name) + { + // Format the classpath to a standard absolute path. + const char* canoname = port_filepath_canonical(path_name, tmp_pool); + apr_finfo_t finfo; + if(apr_stat(&finfo, canoname, APR_FINFO_SIZE|APR_FINFO_MTIME, tmp_pool) == APR_SUCCESS) { + // If file exists, we parse it, and save its mapping info into mapping file + SetBCPElement(canoname, mappingFile, tmp_pool); + // Extract and save signature to signature file + char timeStamp[20]; + char fileSize[20]; + _snprintf(timeStamp, 19, "%lld", finfo.mtime); + _snprintf(fileSize, 19, "%lld", finfo.size); + char* signatureBuf = (char*)apr_palloc(tmp_pool, strlen(canoname) + + strlen(timeStamp) + + strlen(fileSize) + + strlen(PORT_PATH_SEPARATOR_STR) + + 3); // 3 includes =, \n, and null + strcpy(signatureBuf, canoname); + strcat(signatureBuf, "="); + strcat(signatureBuf, timeStamp); + strcat(signatureBuf, PORT_PATH_SEPARATOR_STR); + strcat(signatureBuf, fileSize); + strcat(signatureBuf, "\n"); + apr_size_t len = strlen(signatureBuf); + apr_file_write(signatureFile, signatureBuf, &len); + } + path_name = strtok(NULL, PORT_PATH_SEPARATOR_STR); + } + + SetClasspathFromString(luni_path, mappingFile, tmp_pool); + apr_file_close(mappingFile); + apr_file_close(signatureFile); + } + m_env->JavaProperties()->destroy(luni_path); + STD_FREE(vmboot); + STD_FREE(requiredLib); + STD_FREE(mapPropertyFile); + STD_FREE(bootDirectory); + STD_FREE(cleanVMBoot); // check if vm.bootclasspath.appendclasspath property is set to true if( TRUE == vm_property_get_boolean("vm.bootclasspath.appendclasspath", FALSE, VM_PROPERTIES) ) { @@ -2101,12 +2372,63 @@ // find archive entry in archive file *not_found = false; const JarEntry *entry = jar_file->Lookup(class_name_in_jar); + /* if(!entry) { // file was not found *not_found = true; return NULL; - } - + }*/ + if(!entry) { + Global_Env *env = VM_Global_State::loader_env; + JarFilePackageMapping::iterator it= env->pending_jar_set.begin(); + if (it != env->pending_jar_set.end()) {//indicate there are some un-parsed modules + int packageSeperator = '/'; + const char *pdest = strrchr( class_name_in_jar, packageSeperator ); + char *pkgName = NULL; + if (pdest != NULL) { + int preChaNum = (int)(pdest - class_name_in_jar) + 1; + pkgName = (char*)STD_MALLOC(preChaNum + 1); + strncpy(pkgName, class_name_in_jar, preChaNum); + pkgName[preChaNum] = '\0'; + } + // Find which jar exports this package + if (pkgName != NULL) { + apr_pool_t *tmp_pool; + apr_pool_create(&tmp_pool, NULL); + while (it != env->pending_jar_set.end()) { + pdest = strstr( (*it).second->bytes, pkgName ); + if (pdest != NULL) { + // Open this found jar, and read all classes contained in this jar + SetBCPElement((*it).first->bytes, tmp_pool); + // Erase the found jar from pending jar list as it has been parsed just now + env->pending_jar_set.erase(it++); + } else { + it ++; + } + } + STD_FREE(pkgName); + // destroy temp pool + apr_pool_destroy(tmp_pool); + + // Check whether we found the required class. + entry = jar_file->Lookup(class_name_in_jar); + if(!entry) { + *not_found = true; + return NULL; + } + } else { + /** + * We assume the class without any package info the main class. + * It is the responsibility of user classloader for such class + */ + *not_found = true; + return NULL; + } + }else { + *not_found = true; + return NULL; + } + } // unpack entry unsigned size = entry->GetContentSize(); unsigned char* buffer = (unsigned char*)STD_MALLOC(size); Index: vm/vmcore/src/util/jarfile_support.cpp =================================================================== --- vm/vmcore/src/util/jarfile_support.cpp (revision 699565) +++ vm/vmcore/src/util/jarfile_support.cpp (working copy) @@ -19,7 +19,7 @@ * @version $Revision: 1.1.2.2.4.4 $ */ #include "jarfile_support.h" - +#include "port_filepath.h" #include "port_malloc.h" #include #include @@ -258,7 +258,151 @@ return true; } +bool JarFile::Parse( const char* fileName, apr_file_t *mapFile, bool do_map ) +{ + int fp; + long off; + unsigned char* buf; + unsigned long fsize = 0; + m_use_mmap = do_map; + m_name = fileName; + + if(!m_use_mmap) { + int flags = O_RDONLY; +#ifndef PLATFORM_POSIX + flags |= O_BINARY; +#endif + fp = open( fileName, flags, 0 ); + if( fp == -1 ) return false; + + m_file_handle = fp; + + struct stat fs; + if(stat(fileName, &fs) == -1) return false; + fsize = fs.st_size; + + int cd_size = fsize < 67000 ? fsize : 67000; + lseek(fp, -cd_size, SEEK_END); + buf = (unsigned char*)STD_ALLOCA(cd_size); + off = read(fp, buf, cd_size) - 22; // 22 - EOD size + } else { + apr_pool_create(&m_mappool, NULL); + + apr_status_t status = + apr_file_open(&m_jarfile, fileName, APR_READ, APR_OS_DEFAULT, m_mappool); + assert(!status); + + apr_off_t _fsize = 0; + status = apr_file_seek(m_jarfile, APR_END, &_fsize); + assert(!status); + fsize = (unsigned long)_fsize; + + status = apr_mmap_create(&m_mmap, m_jarfile, 0, (apr_size_t)fsize, + APR_MMAP_READ, m_mappool); + assert(!status); + + int cd_size = fsize < 67000 ? fsize : 67000; + off = cd_size - 22; + buf = (unsigned char*)m_mmap->mm + fsize - cd_size; + } + + long offsetCD; // start of the Central Dir + int number; // number of entries + + m_jars.push_back(this); + + JarEntry je; + je.m_jarFileIdx = (int)(m_jars.size() - 1); + + while (1){ + if (je.IsPK(buf + off)){ + if (je.IsEODMagic(buf + off + 2)){ + offsetCD = buf[off + 16] + ((unsigned int)buf[off + 17]<<8) + + ((unsigned int)buf[off + 18]<<16) + ((unsigned int)buf[off + 19]<<24); + number = buf[off + 10] + ((unsigned int)buf[off + 11]<<8); + break; + } + } + if (--off < 0) + return false; + } + + if(m_ownCache) { + void* jec_mem = m_pool.alloc(sizeof(JarEntryCache)); + m_entries = new (jec_mem) JarEntryCache(); + } + + if(!m_use_mmap) { + lseek(fp, offsetCD, SEEK_SET); + + buf = (unsigned char *)STD_MALLOC(fsize - offsetCD); + fsize = read(fp, buf, fsize - offsetCD); + } else { + buf = (unsigned char*)m_mmap->mm + offsetCD; + } + + apr_pool_t *tmp_pool; + apr_pool_create(&tmp_pool, NULL); + // The number of 3 includes character, like =, \n, and null + int mapLength = strlen(fileName) + 3; + int moduleNameLength = mapLength; + char* mapBuf = (char*)apr_palloc(tmp_pool, mapLength); + strcpy(mapBuf, fileName); + strcat(mapBuf, "="); + + off = 0; + for (int i = 0; i < number; i++){ + if (!je.IsPK(buf + off) || !je.IsDirEntryMagic( buf + off + 2)) + return false; + + je.ConstructFixed(buf + off); + je.m_fileName = (char *)m_pool.alloc(je.m_nameLength + 1); + strncpy(je.m_fileName, (const char *)buf + off + JAR_DIRECTORYENTRY_LEN, je.m_nameLength ); + je.m_fileName[je.m_nameLength] = '\0'; + je.m_contentOffset = je.m_relOffset + JarEntry::sizeFixed + je.m_nameLength + je.m_extraLength; + if(!strcmp(je.m_fileName, "META-INF/MANIFEST.MF")) { + // parse manifest + m_manifest = new Manifest(); + if(!m_manifest) return false; + if(!m_manifest->Parse(&je) || !(*m_manifest)) { + delete m_manifest; + return false; + } + } else { + // Insertion in common cache preserves the order of inserted + // elements with the same hash value, so lookup will return + // the first occurrence. Although more memory is spent here, + // we do not need to lookup on each entry. + if (strstr(je.m_fileName, ".class") == NULL && (je.m_fileName[strlen(je.m_fileName)-1] == '/')){ + char *oldMap = mapBuf; + mapBuf = (char*)apr_palloc(tmp_pool, strlen(oldMap) + + strlen(je.m_fileName) + + 3); + strcpy(mapBuf, oldMap); + strcat(mapBuf, je.m_fileName); + strcat(mapBuf, PORT_PATH_SEPARATOR_STR); + mapLength += strlen(je.m_fileName) + 1; + } + m_entries->Insert(je); + } + off += JAR_DIRECTORYENTRY_LEN + je.m_nameLength + je.m_extraLength; + } + if(!m_use_mmap) { + STD_FREE(buf); + } + if (moduleNameLength != mapLength) { // Means we get the mapping info + // Write the mapping info between module and exported libraries into file + strcat(mapBuf, "\n"); + apr_size_t len = strlen(mapBuf); + apr_file_write(mapFile, mapBuf, &len); + } + // destroy temp pool + apr_pool_destroy(tmp_pool); + + return true; +} + bool JarFile::ReadEntry(unsigned char* buf, long entry_offset, int entry_length) { if(!m_use_mmap) {