static void AppendAsChildren( const spINode & contextNode, const spINode & parsedNode ) { if ( !contextNode ) NOTIFY_ERROR( IError::kEDParser, kPECInvalidContextNode, "Context Node is invalid", IError::kESOperationFatal, false, false ); auto contextNodeType = contextNode->GetNodeType(); if ( contextNodeType != INode::kNTStructure && contextNodeType != INode::kNTArray ) NOTIFY_ERROR( IError::kEDParser, kPECContextNodeIsNonComposite, "Context Node is non composite", IError::kESOperationFatal, true, static_cast< sizet >( contextNodeType ) ); pICompositeNode compositeContextNode = contextNode->GetInterfacePointer< ICompositeNode >(); pIMetadata meta( NULL ); try { meta = parsedNode->GetInterfacePointer< IMetadata >(); } catch ( spcIError err ) { meta = NULL; } if ( meta ) { auto it = meta->Iterator(); while ( it ) { auto childNode = it->GetNode(); it = it->Next(); childNode = meta->GetIMetadata_I()->RemoveNode( childNode->GetNameSpace(), childNode->GetName() ); compositeContextNode->AppendNode( childNode ); } } else { compositeContextNode->AppendNode( parsedNode ); } }
static void InsertAfter( const spINode & contextNode, const spINode & parsedNode ) { if ( !contextNode ) NOTIFY_ERROR( IError::kEDParser, kPECInvalidContextNode, "Context Node is invalid", IError::kESOperationFatal, false, false ); if ( !contextNode->IsArrayItem() ) NOTIFY_ERROR( IError::kEDParser, kPECContextNodeParentIsNonArray, "Context Node's Parent is non array node", IError::kESOperationFatal, false, false ); pIMetadata meta( NULL ); try { meta = parsedNode->GetInterfacePointer< IMetadata >(); } catch ( spcIError err ) { meta = NULL; } pIArrayNode parentArrayNode = contextNode->GetINode_I()->GetRawParentPointer()->GetInterfacePointer< IArrayNode >(); if ( meta ) { auto it = meta->Iterator(); sizet index = contextNode->GetIndex() + 1; while ( it ) { auto childNode = it->GetNode(); it = it->Next(); childNode = meta->GetIMetadata_I()->RemoveNode( childNode->GetNameSpace(), childNode->GetName() ); parentArrayNode->InsertNodeAtIndex( childNode, index ); index = childNode->GetIndex() + 1; } } else { parentArrayNode->InsertNodeAtIndex( parsedNode, contextNode->GetIndex() + 1 ); } }
void symcache_init(const char *symcache_dir_in, size_t modsize_cache_threshold) { initialized = true; op_modsize_cache_threshold = modsize_cache_threshold; hashtable_init_ex(&symcache_table, SYMCACHE_MASTER_TABLE_HASH_BITS, IF_WINDOWS_ELSE(HASH_STRING_NOCASE, HASH_STRING), true/*strdup*/, false/*!synch*/, symcache_free_entry, NULL, NULL); symcache_lock = dr_mutex_create(); dr_snprintf(symcache_dir, BUFFER_SIZE_ELEMENTS(symcache_dir), "%s", symcache_dir_in); NULL_TERMINATE_BUFFER(symcache_dir); if (!dr_directory_exists(symcache_dir)) { if (!dr_create_dir(symcache_dir)) { /* check again in case of a race (i#616) */ if (!dr_directory_exists(symcache_dir)) { NOTIFY_ERROR("Unable to create symcache dir %s"NL, symcache_dir); ASSERT(false, "unable to create symcache dir"); dr_abort(); } } } }
void APICALL DOMParserImpl::ParseWithSpecificAction( const char * buffer, sizet bufferLength, eActionType actionType, spINode & node ) { auto parsedNode = ParseAsNode( buffer, bufferLength ); if ( parsedNode ) { switch ( actionType ) { case kATAppendAsChildren: AppendAsChildren( node, parsedNode ); break; case kATReplaceChildren: ReplaceChildren( node, parsedNode ); break; case kATAppendOrReplaceChildren: AppendOrReplaceChildren( node, parsedNode ); break; case kATInsertBefore: InsertBefore( node, parsedNode ); break; case kATInsertAfter: InsertAfter( node, parsedNode ); break; case kATReplace: ReplaceNode( node, parsedNode ); break; default: NOTIFY_ERROR( IError::kEDGeneral, kGECNotImplemented, "Not yet implemented", IError::kESOperationFatal, true, static_cast< sizet >( actionType ) ); } } }
static void ReplaceNode( spINode & node, const spINode & parsedNode ) { if ( node && node->IsArrayItem() && node->GetNodeType() != parsedNode->GetNodeType() ) { NOTIFY_ERROR( IError::kEDDataModel, kDMECArrayItemTypeDifferent, "node type is different than what currently array can hold", IError_v1::kESOperationFatal, true, static_cast< sizet >( node->GetNodeType() ), true, static_cast< sizet >( parsedNode->GetNodeType() ) ); } node = parsedNode; }
static void AppendOrReplaceChildren( const spINode & contextNode, const spINode & parsedNode ) { if ( !contextNode ) NOTIFY_ERROR( IError::kEDParser, kPECInvalidContextNode, "Context Node is invalid", IError::kESOperationFatal, false, false ); auto contextNodeType = contextNode->GetNodeType(); if ( contextNodeType != INode::kNTStructure && contextNodeType != INode::kNTArray ) NOTIFY_ERROR( IError::kEDParser, kPECContextNodeIsNonComposite, "Context Node is non composite", IError::kESOperationFatal, true, static_cast< sizet >( contextNodeType ) ); switch ( contextNodeType ) { case INode::kNTArray: ReplaceChildren( contextNode->GetInterfacePointer< IArrayNode >(), parsedNode ); break; case INode::kNTStructure: AppendOrReplaceChildren( contextNode->GetInterfacePointer< IStructureNode >(), parsedNode ); break; } }
void NotifyError( const char * errorMsg, const uint64 & key, eConfigurableErrorCode errorCode, IConfigurable::eDataType type, T1 v1, IConfigurable::eDataType oldType, T2 v2 ) { bool valuePresent = type != IConfigurable::kDTNone; bool oldValuePresent = oldType != IConfigurable::kDTNone; NOTIFY_ERROR( IError_v1::kEDConfigurable, errorCode, errorMsg, IError_v1::kESOperationFatal, true, key, valuePresent, static_cast< uint64 >( type ), oldValuePresent, static_cast< uint64 >( oldType ), valuePresent, v1, oldValuePresent, v2 ); }
spINode APICALL ClientDOMParserWrapperImpl::ParseAsNode( const char * buffer, sizet bufferLength ) { pcIError_base error( NULL ); uint32 unknownExceptionCaught( 0 ); auto pnode = mpClientParser->parse( buffer, bufferLength, this, &ReportErrorAndContinueABISafe, error, unknownExceptionCaught ); if ( error ) { auto spError = IError::MakeShared( error ); error->Release(); throw spError; } if ( unknownExceptionCaught ) NOTIFY_ERROR( IError::kEDGeneral, kGECUnknownExceptionCaught, "Unknown Exception caught in the client code", IError::kESOperationFatal, false, false ); return MakeUncheckedSharedPointer( pnode, __FILE__, __LINE__, false ); }
eConfigurableErrorCode APICALL ClientDOMSerializerWrapperImpl::ValidateValue( const uint64 & key, eDataType type, const CombinedDataValue & value ) const { pcIError_base error( NULL ); uint32 unknownExceptionCaught( 0 ); auto retValue = mpSerializer->validate( key, static_cast< uint32 >( type ), value, error, unknownExceptionCaught ); if ( error ) { auto spError = IError::MakeShared( error ); error->Release(); throw spError; } if ( unknownExceptionCaught ) NOTIFY_ERROR( IError::kEDGeneral, kGECUnknownExceptionCaught, "Unknown Exception caught in the client code", IError::kESOperationFatal, false, false ); return static_cast< eConfigurableErrorCode >( retValue ); }
bool HandleConstAlias( const spIMetadata & meta, spINode & destNode, const XMP_ExpandedXPath & expandedXPath, sizet & nodeIndex ) { if ( expandedXPath.empty() ) NOTIFY_ERROR( IError::kEDGeneral, kGECLogicalError, "Empty XPath", IError::kESOperationFatal, false, false ); if ( !( expandedXPath[ kSchemaStep ].options & kXMP_SchemaNode ) ) { return false; } else { XMP_VarString namespaceName = expandedXPath[ kSchemaStep ].step.c_str(); size_t colonPos = expandedXPath[ kRootPropStep ].step.find( ":" ); assert( colonPos != std::string::npos ); XMP_VarString propertyName = expandedXPath[ kRootPropStep ].step.substr( colonPos + 1 ); // here find the node with this name destNode = meta->GetNode( namespaceName.c_str(), namespaceName.size(), propertyName.c_str(), propertyName.size() ); if ( !destNode ) return false; if ( expandedXPath.size() == 2 ) return true; assert( destNode->GetNodeType() == INode::kNTArray ); if ( expandedXPath[ 2 ].options == kXMP_ArrayIndexStep ) { assert( expandedXPath[ 2 ].step == "[1]" ); destNode = destNode->ConvertToArrayNode()->GetNodeAtIndex( 1 ); auto actualNodeType = destNode->GetNodeType(); if ( destNode ) { if ( nodeIndex ) nodeIndex = 1; return true; } return false; } else if ( expandedXPath[ 2 ].options == kXMP_QualSelectorStep ) { assert( expandedXPath[ 2 ].step == "[?xml:lang=\"x-default\"]" ); if ( !destNode || destNode->GetNodeType() != INode::kNTArray ) return false; spINodeIterator iter = destNode->ConvertToArrayNode()->Iterator(); sizet index = 1; while ( iter ) { spINode node = iter->GetNode(); try { spISimpleNode qualNode = node->GetSimpleQualifier( "http://www.w3.org/XML/1998/namespace", AdobeXMPCommon::npos, "lang", AdobeXMPCommon::npos ); if ( qualNode->GetValue()->compare( "x-default" ) == 0 ) { destNode = node; if ( nodeIndex ) nodeIndex = index; return true; } } catch ( spcIError err ) { } catch ( ... ) {} index++; iter = iter->Next(); } return false; } return false; } }
bool ConfigurableImpl::GetParameter( const uint64 & actualKey, eDataType type, CombinedDataValue & value ) const { uint64 key = ModifyKey( actualKey ); if ( mTreatKeyAsCaseInsensitiveCharBuffer ) key = ConvertToLowerCase( key ); AutoSharedLock lock( GetMutex() ); auto it = mMap.find( key ); if ( it == mMap.end() ) return false; if ( it->second.first != type ) { NOTIFY_ERROR( IError_v1::kEDConfigurable, kCECValueTypeMismatch, "Type mismatch for a parameter", IError_v1::kESOperationFatal, true, key, true, static_cast< uint64 >( it->second.first ), true, static_cast< uint64 >( type ) ); return false; } value = it->second.second; return true; }
ClientDOMSerializerWrapperImpl::ClientDOMSerializerWrapperImpl( pIClientDOMSerializer serializer ) : mpSerializer( serializer ) { if ( serializer ) { pcIError_base error( NULL ); uint32 unknownExceptionCaught( 0 ); TreatKeyAsCaseInsensitive( serializer->areKeysCaseSensitive( error, unknownExceptionCaught ) == 0 ); if ( !error && unknownExceptionCaught == 0 ) serializer->initialize( this, error, unknownExceptionCaught ); if ( error ) { auto spError = IError::MakeShared( error ); error->Release(); throw spError; } if ( unknownExceptionCaught ) NOTIFY_ERROR( IError::kEDGeneral, kGECUnknownExceptionCaught, "Unknown Exception caught in the client code", IError::kESOperationFatal, false, false ); } }
spIUTF8String APICALL ClientDOMSerializerWrapperImpl::Serialize( const spINode & node, const spcINameSpacePrefixMap & map ) { spIUTF8String str( IUTF8String_I::CreateUTF8String( NULL, 0 ) ); pcIError_base error( NULL ); uint32 unknownExceptionCaught( 0 ); spcINameSpacePrefixMap mergedMap = INameSpacePrefixMap::GetDefaultNameSpacePrefixMap(); if ( map ) { spINameSpacePrefixMap newMergedMap = mergedMap->Clone(); newMergedMap->GetINameSpacePrefixMap_I()->Merge( map ); mergedMap = newMergedMap; } mpSerializer->serialize( node ? node->GetActualINode() : NULL, mergedMap ? mergedMap->GetActualINameSpacePrefixMap() : NULL, this, &ReportErrorAndContinueABISafe, str->GetActualIUTF8String(), error, unknownExceptionCaught ); if ( error ) { auto spError = IError::MakeShared( error ); error->Release(); throw spError; } if ( unknownExceptionCaught ) NOTIFY_ERROR( IError::kEDGeneral, kGECUnknownExceptionCaught, "Unknown Exception caught in the client code", IError::kESOperationFatal, false, false ); return str; }
spIMetadata APICALL DOMParserImpl::Parse( const char * buffer, sizet bufferLength ) { auto node = ParseAsNode( buffer, bufferLength ); if ( node ) { switch ( node->GetNodeType() ) { case INode::kNTSimple: case INode::kNTArray: { spIMetadata meta = IMetadata::CreateMetadata(); meta->AppendNode( node ); return meta; } break; case INode::kNTStructure: { pIMetadata meta( NULL ); try { meta = node->GetInterfacePointer< IMetadata >(); } catch ( spcIError err ) { meta = NULL; } if ( meta ) { return MakeUncheckedSharedPointer( meta, __FILE__, __LINE__ ); } else { spIMetadata meta = IMetadata::CreateMetadata(); meta->AppendNode( node ); return meta; } } break; default: NOTIFY_ERROR( IError::kEDGeneral, kGECInternalFailure, "Unhandled situation occured", IError::kESOperationFatal, false, false ); } } return spIMetadata(); }
spINode APICALL MetadataImpl::ReplaceNode( const spINode & node ) { if ( mSupportAliases ) { XMP_ExpandedXPath exPath; QualifiedName qName( node->GetNameSpace(), node->GetName() ); bool nodeIsAlias = IsNodeAlias( node->GetNameSpace()->c_str(), node->GetName()->c_str(), exPath ); if ( nodeIsAlias ) { spINode actualNodeToBeRemoved; sizet nodeIndex = 0; auto spSelf = MakeUncheckedSharedPointer( this, __FILE__, __LINE__, false ); if ( HandleConstAlias( spSelf, actualNodeToBeRemoved, exPath, nodeIndex ) ) { qName = QualifiedName( actualNodeToBeRemoved->GetNameSpace(), actualNodeToBeRemoved->GetName() ); } } if ( CheckSuitabilityToBeUsedAsChildNode( node ) && GetNode( qName.mNameSpace, qName.mName ) ) { auto retValue = RemoveNode( qName.mNameSpace, qName.mName ); spINode destNode = node; if ( nodeIsAlias ) { sizet destNodeIndex = 0; auto spSelf = MakeUncheckedSharedPointer( this, __FILE__, __LINE__, false ); if ( !HandleNonConstAlias( spSelf, exPath, true, 0, destNode, destNodeIndex, false, node ) ) { return destNode; } } InsertNode( destNode ); return retValue; } else { NOTIFY_ERROR( IError_v1::kEDDataModel, kDMECNoSuchNodeExists, "no such node exists with the specified qualified name", IError_v1::kESOperationFatal, true, node->GetNameSpace(), true, node->GetName() ); } return spINode(); } else { return StructureNodeImpl::ReplaceNode( node ); } }
bool HandleNonConstAlias( const spIMetadata & meta, XMP_ExpandedXPath & expandedXPath, bool createNodes, XMP_OptionBits leafOptions, spINode & destNode, sizet & nodeIndex, bool ignoreLastStep, const spINode & inputNode ) { destNode = meta; spcIUTF8String inputNodeValue; if ( inputNode && inputNode->GetNodeType() == INode::kNTSimple ) { inputNodeValue = inputNode->ConvertToSimpleNode()->GetValue(); } bool isAliasBeingCreated = expandedXPath.size() == 2; if ( expandedXPath.empty() ) NOTIFY_ERROR( IError::kEDDataModel, kDMECBadXPath, "Empty XPath", IError::kESOperationFatal, false, false ); if ( !( expandedXPath[ kSchemaStep ].options & kXMP_SchemaNode ) ) { return false; } else { XMP_VarString namespaceName = expandedXPath[ kSchemaStep ].step.c_str(); size_t colonPos = expandedXPath[ kRootPropStep ].step.find( ":" ); assert( colonPos != std::string::npos ); XMP_VarString propertyName = expandedXPath[ kRootPropStep ].step.substr( colonPos + 1 ); spcINode childNode = meta->GetNode( namespaceName.c_str(), namespaceName.size(), propertyName.c_str(), propertyName.size() ); if ( !childNode && !createNodes ) return false; if ( expandedXPath.size() == 2 ) { if ( childNode ) return true; XMP_OptionBits createOptions = 0; spINode tempNode; if ( isAliasBeingCreated ) tempNode = CreateTerminalNode( namespaceName.c_str(), propertyName.c_str(), leafOptions ); else tempNode = CreateTerminalNode( namespaceName.c_str(), propertyName.c_str(), createOptions ); if ( !tempNode ) return false; if ( inputNodeValue ) tempNode->ConvertToSimpleNode()->SetValue( inputNodeValue->c_str(), inputNodeValue->size() ); if ( destNode == meta ) { meta->InsertNode( tempNode ); } else { destNode->ConvertToStructureNode()->AppendNode( tempNode ); } destNode = tempNode; if ( destNode ) return true; return false; } XMP_Assert( expandedXPath.size() == 3 ); if ( expandedXPath[ 2 ].options == kXMP_ArrayIndexStep ) { XMP_Assert( expandedXPath[ 2 ].step == "[1]" ); destNode = meta->GetNode( namespaceName.c_str(), namespaceName.size(), propertyName.c_str(), propertyName.size() ); if ( !destNode && !createNodes ) return false; if ( !destNode ) { spINode arrayNode = CreateTerminalNode( namespaceName.c_str(), propertyName.c_str(), kXMP_PropArrayIsOrdered | kXMP_PropValueIsArray ); meta->AppendNode( arrayNode ); destNode = arrayNode; } if ( destNode->ConvertToArrayNode()->GetNodeAtIndex( 1 ) ) { destNode = destNode->ConvertToArrayNode()->GetNodeAtIndex( 1 ); if ( nodeIndex ) nodeIndex = 1; return true; } else { spISimpleNode indexNode = ISimpleNode::CreateSimpleNode( namespaceName.c_str(), namespaceName.size(), propertyName.c_str(), propertyName.size() ); if ( inputNodeValue ) { indexNode->SetValue( inputNodeValue->c_str(), inputNodeValue->size() ); } destNode->ConvertToArrayNode()->InsertNodeAtIndex( indexNode, 1 ); destNode = indexNode; return true; } return false; } else if ( expandedXPath[ 2 ].options == kXMP_QualSelectorStep ) { assert( expandedXPath[ 2 ].step == "[?xml:lang=\"x-default\"]" ); destNode = meta->GetNode( namespaceName.c_str(), namespaceName.size(), propertyName.c_str(), propertyName.size() ); if ( !destNode && !createNodes ) return false; spINode arrayNode = CreateTerminalNode( namespaceName.c_str(), propertyName.c_str(), kXMP_PropValueIsArray | kXMP_PropArrayIsAltText); meta->AppendNode( arrayNode ); destNode = arrayNode; auto iter = destNode->ConvertToArrayNode()->Iterator(); XMP_Index index = 1; while ( iter ) { spINode node = iter->GetNode(); spINode qualNode = node->GetQualifier( "http://www.w3.org/XML/1998/namespace", AdobeXMPCommon::npos, "lang", AdobeXMPCommon::npos ); if ( qualNode->GetNodeType() == INode::kNTSimple ) { if ( !qualNode->ConvertToSimpleNode()->GetValue()->compare( "x-default" ) ) { destNode = node; if ( nodeIndex ) nodeIndex = index; return true; } } index++; iter = iter->Next(); } spISimpleNode qualifierNode = ISimpleNode::CreateSimpleNode( "http://www.w3.org/XML/1998/namespace", AdobeXMPCommon::npos, "lang", AdobeXMPCommon::npos, "x-default", AdobeXMPCommon::npos ); if ( destNode->ConvertToArrayNode()->GetNodeAtIndex( 1 ) ) { destNode = destNode->ConvertToArrayNode()->GetNodeAtIndex( 1 ); if ( nodeIndex ) nodeIndex = 1; destNode->InsertQualifier( qualifierNode ); return true; } else { spISimpleNode indexNode = ISimpleNode::CreateSimpleNode( namespaceName.c_str(), AdobeXMPCommon::npos, propertyName.c_str(), AdobeXMPCommon::npos ); if ( inputNodeValue ) { indexNode->SetValue( inputNodeValue->c_str(), inputNodeValue->size() ); } destNode->ConvertToArrayNode()->InsertNodeAtIndex( indexNode, 1 ); destNode->InsertQualifier( qualifierNode ); destNode = indexNode; return true; } } } return false; }
DR_EXPORT drmf_status_t drsymcache_init(client_id_t client_id, const char *symcache_dir_in, size_t modsize_cache_threshold) { #ifdef WINDOWS module_data_t *mod; #endif drmf_status_t res; drmgr_priority_t pri_mod_load_cache = {sizeof(pri_mod_load_cache), DRMGR_PRIORITY_NAME_DRSYMCACHE, NULL, NULL, DRMGR_PRIORITY_MODLOAD_DRSYMCACHE_READ}; drmgr_priority_t pri_mod_unload_cache = {sizeof(pri_mod_unload_cache), DRMGR_PRIORITY_NAME_DRSYMCACHE, NULL, NULL, DRMGR_PRIORITY_MODUNLOAD_DRSYMCACHE}; drmgr_priority_t pri_mod_save_cache = {sizeof(pri_mod_save_cache), DRMGR_PRIORITY_NAME_DRSYMCACHE_SAVE, NULL, NULL, DRMGR_PRIORITY_MODLOAD_DRSYMCACHE_SAVE}; /* handle multiple sets of init/exit calls */ int count = dr_atomic_add32_return_sum(&symcache_init_count, 1); if (count > 1) return DRMF_WARNING_ALREADY_INITIALIZED; res = drmf_check_version(client_id); if (res != DRMF_SUCCESS) return res; drmgr_init(); drmgr_register_module_load_event_ex(symcache_module_load, &pri_mod_load_cache); drmgr_register_module_unload_event_ex(symcache_module_unload, &pri_mod_unload_cache); drmgr_register_module_load_event_ex(symcache_module_load_save, &pri_mod_save_cache); initialized = true; op_modsize_cache_threshold = modsize_cache_threshold; hashtable_init_ex(&symcache_table, SYMCACHE_MASTER_TABLE_HASH_BITS, IF_WINDOWS_ELSE(HASH_STRING_NOCASE, HASH_STRING), true/*strdup*/, false/*!synch*/, symcache_free_entry, NULL, NULL); symcache_lock = dr_mutex_create(); dr_snprintf(symcache_dir, BUFFER_SIZE_ELEMENTS(symcache_dir), "%s", symcache_dir_in); NULL_TERMINATE_BUFFER(symcache_dir); if (!dr_directory_exists(symcache_dir)) { if (!dr_create_dir(symcache_dir)) { /* check again in case of a race (i#616) */ if (!dr_directory_exists(symcache_dir)) { NOTIFY_ERROR("Unable to create symcache dir %s"NL, symcache_dir); ASSERT(false, "unable to create symcache dir"); dr_abort(); } } } #ifdef WINDOWS /* It's common for tools to query ntdll in their init routines so we add it * early here */ mod = dr_lookup_module_by_name("ntdll.dll"); if (mod != NULL) { symcache_module_load(dr_get_current_drcontext(), mod, true); dr_free_module_data(mod); } #endif return DRMF_SUCCESS; }
/* Sets modcache->has_debug_info. * No lock is needed as we assume the caller hasn't exposed modcache outside this * thread yet. */ static bool symcache_read_symfile(const module_data_t *mod, const char *modname, mod_cache_t *modcache) { hashtable_t *symtable = &modcache->table; bool res = false; const char *line, *next_line; char symbol[MAX_SYMLEN]; size_t offs; uint64 map_size; size_t actual_size; bool ok; void *map = NULL; char symfile[MAXIMUM_PATH]; file_t f; symcache_get_filename(modname, symfile, BUFFER_SIZE_ELEMENTS(symfile)); f = dr_open_file(symfile, DR_FILE_READ); if (f == INVALID_FILE) goto symcache_read_symfile_done; LOG(2, "processing symbol cache file for %s\n", modname); /* we avoid having to do our own buffering by just mapping the whole file */ ok = dr_file_size(f, &map_size); if (ok) { actual_size = (size_t) map_size; ASSERT(actual_size == map_size, "file size too large"); map = dr_map_file(f, &actual_size, 0, NULL, DR_MEMPROT_READ, 0); } if (!ok || map == NULL || actual_size < map_size) { NOTIFY_ERROR("Error mapping symcache file for %s"NL, modname); goto symcache_read_symfile_done; } if (strncmp((char *)map, SYMCACHE_FILE_HEADER, strlen(SYMCACHE_FILE_HEADER)) != 0) { WARN("WARNING: symbol cache file is corrupted\n"); goto symcache_read_symfile_done; } /* i#1057: We use dr_sscanf() because sscanf() from ntdll will call strlen() * and read off the end of the mapped file if it doesn't hit a null. */ if (dr_sscanf((char *)map + strlen(SYMCACHE_FILE_HEADER) + 1, "%d", (uint *)&offs) != 1 || /* neither forward nor backward compatible */ offs != SYMCACHE_VERSION) { WARN("WARNING: symbol cache file has wrong version\n"); goto symcache_read_symfile_done; } line = strchr((char *) map, '\n'); if (line != NULL) line++; if (line != NULL) { /* Module consistency checks */ uint cache_file_size; uint64 module_file_size; uint timestamp; #ifdef WINDOWS version_number_t file_version; version_number_t product_version; uint checksum; size_t module_internal_size; if (dr_sscanf(line, "%u,"UINT64_FORMAT_STRING","UINT64_FORMAT_STRING"," UINT64_FORMAT_STRING",%u,%u,%lu", &cache_file_size, &module_file_size, &file_version.version, &product_version.version, &checksum, ×tamp, &module_internal_size) != 7) { WARN("WARNING: %s symbol cache file has bad consistency header\n", modname); goto symcache_read_symfile_done; } if (module_file_size != modcache->module_file_size || file_version.version != modcache->file_version.version || product_version.version != modcache->product_version.version || checksum != modcache->checksum || timestamp != modcache->timestamp || module_internal_size != modcache->module_internal_size) { LOG(1, "module version mismatch: %s symbol cache file is stale\n", modname); LOG(2, "\t"UINT64_FORMAT_STRING" vs "UINT64_FORMAT_STRING", " UINT64_FORMAT_STRING" vs "UINT64_FORMAT_STRING", " UINT64_FORMAT_STRING" vs "UINT64_FORMAT_STRING", " "%u vs %u, %u vs %u, %lu vs %lu\n", module_file_size, modcache->module_file_size, file_version.version, modcache->file_version.version, product_version.version, modcache->product_version.version, checksum, modcache->checksum, timestamp, modcache->timestamp, module_internal_size, modcache->module_internal_size); goto symcache_read_symfile_done; } #elif defined(LINUX) if (dr_sscanf(line, "%u,"UINT64_FORMAT_STRING",%u", &cache_file_size, &module_file_size, ×tamp) != 3) { WARN("WARNING: %s symbol cache file has bad consistency header\n", modname); goto symcache_read_symfile_done; } if (module_file_size != modcache->module_file_size || timestamp != modcache->timestamp) { LOG(1, "module version mismatch: %s symbol cache file is stale\n", modname); goto symcache_read_symfile_done; } #elif defined(MACOS) uint current_version; uint compatibility_version; byte uuid[16]; /* XXX: if dr_sscanf supported %n maybe we could split these into * separate scans on the same string and share code w/ Linux. */ if (dr_sscanf(line, "%u,"UINT64_FORMAT_STRING",%u,%u,%u,%x,%x,%x,%x", &cache_file_size, &module_file_size, ×tamp, ¤t_version, &compatibility_version, (uint*)(&uuid[0]), (uint*)(&uuid[4]), (uint*)(&uuid[8]), (uint*)(&uuid[12])) != 9) { WARN("WARNING: %s symbol cache file has bad consistency header B\n", modname); goto symcache_read_symfile_done; } if (current_version != modcache->current_version || compatibility_version != modcache->compatibility_version || memcmp(uuid, modcache->uuid, sizeof(uuid)) != 0) { LOG(1, "module version mismatch: %s symbol cache file is stale\n", modname); goto symcache_read_symfile_done; } #endif /* We could go further w/ CRC or even MD5 but not worth it for dev tool */ if (cache_file_size != (uint)map_size) { WARN("WARNING: %s symbol cache file is corrupted: map=%d vs file=%d\n", modname, (uint)map_size, cache_file_size); goto symcache_read_symfile_done; } } line = strchr(line, '\n'); if (line != NULL) line++; if (line != NULL) { uint has_debug_info; if (dr_sscanf(line, "%u", &has_debug_info) != 1) { WARN("WARNING: %s symbol cache file has bad consistency header\n", modname); goto symcache_read_symfile_done; } if (has_debug_info) { /* We assume that the current availability of debug info doesn't matter */ modcache->has_debug_info = true; } else { /* We delay the costly check for symbols until we've read the symcache * b/c if its entry indicates symbols we don't need to look */ if (module_has_symbols(mod)) { LOG(1, "module now has debug info: %s symbol cache is stale\n", modname); goto symcache_read_symfile_done; } } } line = strchr(line, '\n'); if (line != NULL) line++; symbol[0] = '\0'; for (; line != NULL && line < ((char *)map) + map_size; line = next_line) { const char *comma = strchr(line, ','); const char *newline = strchr(line, '\n'); size_t symlen = (comma != NULL ? comma - line : 0); if (newline == NULL) { next_line = ((char *)map) + map_size + 1; /* handle EOF w/o trailing \n */ } else { next_line = newline + 1; } if (symlen > 0 && symlen < MAX_SYMLEN) { strncpy(symbol, line, symlen); symbol[symlen] = '\0'; } if (comma != NULL && symlen < MAX_SYMLEN && symbol[0] != '\0' && dr_sscanf(comma, ",0x%x", (uint *)&offs) == 1) { #ifdef WINDOWS /* Guard against corrupted files that cause DrMem to crash (i#1465) */ if (offs >= modcache->module_internal_size) { /* This one we want to know about */ NOTIFY("SYMCACHE ERROR: %s file has too-large entry "PIFX" for %s"NL, modname, offs, symbol); goto symcache_read_symfile_done; } #endif symcache_symbol_add(modname, symtable, symbol, offs); } else { WARN("WARNING: malformed symbol cache line \"%.*s\"\n", next_line - line - 1, line); /* We abort in case there were two dueling writes to the file * and it somehow got past the self-consistency check, * putting a header in the middle of the file, and we can't * trust subsequent lines since they may belong to a different * version of the module */ break; /* res should still be true */ } } res = true; symcache_read_symfile_done: if (map != NULL) dr_unmap_file(map, actual_size); if (f != INVALID_FILE) dr_close_file(f); if (!res) modcache->has_debug_info = module_has_symbols(mod); return res; }
/* caller must hold symcache_lock */ static void symcache_write_symfile(const char *modname, mod_cache_t *modcache) { uint i; file_t f; hashtable_t *symtable = &modcache->table; char buf[SYMCACHE_BUFFER_SIZE]; size_t sofar = 0; ssize_t len; size_t bsz = BUFFER_SIZE_ELEMENTS(buf); size_t filesz_loc; char symfile[MAXIMUM_PATH]; char symfile_tmp[MAXIMUM_PATH]; int64 file_size; ASSERT(dr_mutex_self_owns(symcache_lock), "missing symcache lock"); /* if from file, we assume it's a waste of time to re-write file: * the version matched after all, unless we appended to it. */ if (modcache->from_file && !modcache->appended) return; if (symtable->entries == 0) return; /* nothing to write */ /* Open the temp symcache that we will rename. */ symcache_get_filename(modname, symfile, BUFFER_SIZE_ELEMENTS(symfile)); f = INVALID_FILE; i = 0; while (f == INVALID_FILE && i < SYMCACHE_MAX_TMP_TRIES) { dr_snprintf(symfile_tmp, BUFFER_SIZE_ELEMENTS(symfile_tmp), "%s.%04d.tmp", symfile, i); NULL_TERMINATE_BUFFER(symfile_tmp); f = dr_open_file(symfile_tmp, DR_FILE_WRITE_REQUIRE_NEW); i++; } if (f == INVALID_FILE) { NOTIFY("WARNING: Unable to create symcache temp file %s"NL, symfile_tmp); return; } BUFFERED_WRITE(f, buf, bsz, sofar, len, "%s %d\n", SYMCACHE_FILE_HEADER, SYMCACHE_VERSION); /* Leave room for file size for self-consistency check */ filesz_loc = sofar; /* XXX: Assumes that the buffer hasn't been flushed. */ BUFFERED_WRITE(f, buf, bsz, sofar, len, "%"STRINGIFY(SYMCACHE_SIZE_DIGITS)"u,", 0); #ifdef WINDOWS BUFFERED_WRITE(f, buf, bsz, sofar, len, UINT64_FORMAT_STRING","UINT64_FORMAT_STRING"," UINT64_FORMAT_STRING",%u,%u,%lu\n", modcache->module_file_size, modcache->file_version.version, modcache->product_version.version, modcache->checksum, modcache->timestamp, modcache->module_internal_size); #else BUFFERED_WRITE(f, buf, bsz, sofar, len, UINT64_FORMAT_STRING",%u", modcache->module_file_size, modcache->timestamp); # ifdef MACOS BUFFERED_WRITE(f, buf, bsz, sofar, len, ",%u,%u,", modcache->current_version, modcache->compatibility_version); /* For easy sscanf we print as 4 ints */ for (i = 0; i < 4; i++) BUFFERED_WRITE(f, buf, bsz, sofar, len, "%08x,", *(int*)(&modcache->uuid[i*4])); # endif BUFFERED_WRITE(f, buf, bsz, sofar, len, "\n"); #endif BUFFERED_WRITE(f, buf, bsz, sofar, len, "%u\n", modcache->has_debug_info); for (i = 0; i < HASHTABLE_SIZE(symtable->table_bits); i++) { hash_entry_t *he; for (he = symtable->table[i]; he != NULL; he = he->next) { offset_list_t *olist = (offset_list_t *) he->payload; offset_entry_t *e; if (olist == NULL) continue; /* skip symbol in dup entries to save space */ BUFFERED_WRITE(f, buf, bsz, sofar, len, "%s", he->key); e = olist->list; while (e != NULL) { BUFFERED_WRITE(f, buf, bsz, sofar, len, ",0x%x\n", e->offs); e = e->next; } } } /* now update size */ FLUSH_BUFFER(f, buf, sofar); if ((file_size = dr_file_tell(f)) < 0 || dr_snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "%"STRINGIFY(SYMCACHE_SIZE_DIGITS)"u", (uint)file_size) < 0 || !dr_file_seek(f, filesz_loc, DR_SEEK_SET) || dr_write_file(f, buf, SYMCACHE_SIZE_DIGITS) != SYMCACHE_SIZE_DIGITS) { /* If any steps fail, warn and give up. */ NOTIFY("WARNING: Unable to write symcache file size."NL); dr_close_file(f); dr_delete_file(symfile_tmp); return; } else { LOG(3, "Wrote symcache %s file size %u to pos "SZFMT"\n", modname, (uint)file_size, filesz_loc); ASSERT(strlen(buf) <= SYMCACHE_SIZE_DIGITS, "not enough space for file size"); } dr_close_file(f); if (!dr_rename_file(symfile_tmp, symfile, /*replace*/true)) { NOTIFY_ERROR("WARNING: Failed to rename the symcache file."NL); dr_delete_file(symfile_tmp); } }
/* If an entry already exists and is 0, replaces it; else adds a new * offset for that symbol. * * If symtable is visible outside of this thread, the caller must hold symcache_lock. */ static bool symcache_symbol_add(const char *modname, hashtable_t *symtable, const char *symbol, size_t offs) { offset_list_t *olist; offset_entry_t *e; olist = (offset_list_t *) hashtable_lookup(symtable, (void *)symbol); if (olist != NULL) { if (olist->num == 1 && olist->list->offs == 0) { /* replace a single 0 entry */ if (olist->table != NULL) { ASSERT(olist->num >= OFFSET_LIST_MIN_TABLE, "table should be NULL"); hashtable_remove(olist->table, (void *)(olist->list->offs + 1)); hashtable_add(olist->table, (void *)(offs + 1), (void *)(offs + 1)); } olist->list->offs = offs; return true; } else if (olist->num == 1 && offs == 0) { /* XXX i#1465: temporary fatal error sanity check as we try to diagnose * our symbol cache errors. */ NOTIFY_ERROR("SYMCACHE ERROR: appending 0 to non-0 for %s!%s"NL, modname, symbol); dr_abort(); /* make sure we see this on bots */ } if (olist->table != NULL) { if (hashtable_lookup(olist->table, (void *)(offs + 1)) != NULL) { LOG(2, "%s: ignoring dup entry %s\n", __FUNCTION__, symbol); return false; } } else { for (e = olist->list; e != NULL; e = e->next) { if (e->offs == offs) { LOG(2, "%s: ignoring dup entry %s\n", __FUNCTION__, symbol); return false; } } } } else { olist = (offset_list_t *) global_alloc(sizeof(*olist), HEAPSTAT_HASHTABLE); olist->num = 0; olist->list = NULL; olist->list_last = NULL; olist->table = NULL; } LOG(2, "%s: %s \"%s\" @ "PIFX"\n", __FUNCTION__, modname, symbol, offs); /* we could verify by an addr lookup but we still need consistency info * in the file for the negative entries so we don't bother */ e = (offset_entry_t *) global_alloc(sizeof(*e), HEAPSTAT_HASHTABLE); e->offs = offs; e->next = NULL; /* append to avoid affecting iteration */ if (olist->list_last == NULL) { ASSERT(olist->list == NULL, "last not set"); olist->list = e; olist->list_last = e; } else { olist->list_last->next = e; olist->list_last = e; } olist->num++; if (olist->num >= OFFSET_LIST_MIN_TABLE) { if (olist->table == NULL) { /* enough entries that a table is worthwhile */ olist->table = (hashtable_t *) global_alloc(sizeof(*olist->table), HEAPSTAT_HASHTABLE); hashtable_init(olist->table, SYMCACHE_OLIST_TABLE_HASH_BITS, HASH_INTPTR, false/*strdup*/); for (e = olist->list; e != NULL; e = e->next) hashtable_add(olist->table, (void *)(e->offs + 1), (void *)(e->offs + 1)); } else hashtable_add(olist->table, (void *)(offs + 1), (void *)(offs + 1)); } hashtable_add(symtable, (void *)symbol, (void *)olist); /* clear any cached values */ olist->iter_idx = 0; olist->iter_entry = NULL; return true; }
static bool symcache_read_symfile(const module_data_t *mod, const char *modname, mod_cache_t *modcache) { hashtable_t *symtable = &modcache->table; bool res = false; const char *line, *next_line; char symbol[MAX_SYMLEN]; size_t offs; uint64 map_size; size_t actual_size; bool ok; void *map = NULL; char symfile[MAXIMUM_PATH]; file_t f; symcache_get_filename(modname, symfile, BUFFER_SIZE_ELEMENTS(symfile)); f = dr_open_file(symfile, DR_FILE_READ); if (f == INVALID_FILE) return res; LOG(2, "processing symbol cache file for %s\n", modname); /* we avoid having to do our own buffering by just mapping the whole file */ ok = dr_file_size(f, &map_size); if (ok) { actual_size = (size_t) map_size; ASSERT(actual_size == map_size, "file size too large"); map = dr_map_file(f, &actual_size, 0, NULL, DR_MEMPROT_READ, 0); } if (!ok || map == NULL || actual_size < map_size) { NOTIFY_ERROR("Error mapping symcache file for %s"NL, modname); goto symcache_read_symfile_done; } if (strncmp((char *)map, SYMCACHE_FILE_HEADER, strlen(SYMCACHE_FILE_HEADER)) != 0) { WARN("WARNING: symbol cache file is corrupted\n"); goto symcache_read_symfile_done; } if (sscanf((char *)map + strlen(SYMCACHE_FILE_HEADER) + 1, "%d", &offs) != 1 || /* neither forward nor backward compatible */ offs != SYMCACHE_VERSION) { WARN("WARNING: symbol cache file has wrong version\n"); goto symcache_read_symfile_done; } line = strchr((char *) map, '\n'); if (line != NULL) line++; if (line != NULL) { /* Module consistency checks */ uint cache_file_size; uint64 module_file_size; #ifdef WINDOWS version_number_t file_version; version_number_t product_version; uint checksum; uint timestamp; size_t module_internal_size; if (sscanf(line, " %u,"UINT64_FORMAT_STRING","UINT64_FORMAT_STRING"," UINT64_FORMAT_STRING",%u,%u,%lu", &cache_file_size, &module_file_size, &file_version.version, &product_version.version, &checksum, ×tamp, &module_internal_size) != 7) { WARN("WARNING: %s symbol cache file has bad consistency header\n", modname); goto symcache_read_symfile_done; } if (module_file_size != modcache->module_file_size || file_version.version != modcache->file_version.version || product_version.version != modcache->product_version.version || checksum != modcache->checksum || timestamp != modcache->timestamp || module_internal_size != modcache->module_internal_size) { LOG(1, "module version mismatch: %s symbol cache file is stale\n", modname); LOG(2, "\t"UINT64_FORMAT_STRING" vs "UINT64_FORMAT_STRING", " UINT64_FORMAT_STRING" vs "UINT64_FORMAT_STRING", " UINT64_FORMAT_STRING" vs "UINT64_FORMAT_STRING", " "%u vs %u, %u vs %u, %lu vs %lu\n", module_file_size, modcache->module_file_size, file_version.version, modcache->file_version.version, product_version.version, modcache->product_version.version, checksum, modcache->checksum, timestamp, modcache->timestamp, module_internal_size, modcache->module_internal_size); goto symcache_read_symfile_done; } #else if (sscanf(line, "%u,"UINT64_FORMAT_STRING, &cache_file_size, &module_file_size) != 2) { WARN("WARNING: %s symbol cache file has bad consistency header\n", modname); goto symcache_read_symfile_done; } if (module_file_size != modcache->module_file_size) { LOG(1, "module version mismatch: %s symbol cache file is stale\n", modname); goto symcache_read_symfile_done; } #endif /* We could go further w/ CRC or even MD5 but not worth it for dev tool */ if (cache_file_size != (uint)map_size) { WARN("WARNING: %s symbol cache file is corrupted\n", modname); goto symcache_read_symfile_done; } } line = strchr(line, '\n'); if (line != NULL) line++; symbol[0] = '\0'; for (; line != NULL && line < ((char *)map) + map_size; line = next_line) { const char *newline = strchr(line, '\n'); if (newline == NULL) { next_line = ((char *)map) + map_size + 1; /* handle EOF w/o trailing \n */ } else { next_line = newline + 1; } if (sscanf(line, "%"MAX_SYMLEN_MINUS_1_STR"[^,],0x%x", symbol, &offs) == 2) { symcache_symbol_add(modname, symtable, symbol, offs); } else if (symbol[0] != '\0' && sscanf(line, ",0x%x", &offs) == 1) { /* duplicate entries are allowed to not list the symbol, to save * space in the file (mainly for post-call caching i#669) */ symcache_symbol_add(modname, symtable, symbol, offs); } else { WARN("WARNING: malformed symbol cache line \"%.*s\"\n", next_line - line - 1, line); /* We abort in case there were two dueling writes to the file * and it somehow got past the self-consistency check, * putting a header in the middle of the file, and we can't * trust subsequent lines since they may belong to a different * version of the module */ break; /* res should still be true */ } } res = true; symcache_read_symfile_done: if (map != NULL) dr_unmap_file(map, actual_size); if (f != INVALID_FILE) dr_close_file(f); return res; }