void unregister_intercept_flag(const String& name, char *flag) { Lock lock(s_mutex); RegisteredFlagsMap::iterator iter = s_registered_flags.find(name); if (iter != s_registered_flags.end()) { std::vector<char*> &flags = iter->second.second; for (int i = flags.size(); i--; ) { if (flag == flags[i]) { flags.erase(flags.begin() + i); break; } } } }
bool register_intercept(CStrRef name, CVarRef callback, CVarRef data) { StringIMap<Variant> &handlers = s_intercept_data->m_intercept_handlers; if (!callback.toBoolean()) { if (name.empty()) { s_intercept_data->m_global_handler.reset(); handlers.clear(); } else { handlers.erase(name); } return true; } EventHook::EnableIntercept(); Array handler = CREATE_VECTOR2(callback, data); if (name.empty()) { s_intercept_data->m_global_handler = handler; handlers.clear(); } else { handlers[name] = handler; } Lock lock(s_mutex); if (name.empty()) { for (RegisteredFlagsMap::iterator iter = s_registered_flags.begin(); iter != s_registered_flags.end(); ++iter) { flag_maybe_interrupted(iter->second); } } else { RegisteredFlagsMap::iterator iter = s_registered_flags.find(name); if (iter != s_registered_flags.end()) { flag_maybe_interrupted(iter->second); } } return true; }
namespace HPHP { TRACE_SET_MOD(intercept); class InterceptRequestData : public RequestEventHandler { public: InterceptRequestData() : m_use_allowed_functions(false) { } void clear() { m_use_allowed_functions = false; m_allowed_functions.clear(); m_renamed_functions.clear(); m_global_handler.reset(); m_intercept_handlers.clear(); } virtual void requestInit() { clear(); } virtual void requestShutdown() { clear(); } public: bool m_use_allowed_functions; StringISet m_allowed_functions; StringIMap<String> m_renamed_functions; Variant m_global_handler; StringIMap<Variant> m_intercept_handlers; }; IMPLEMENT_STATIC_REQUEST_LOCAL(InterceptRequestData, s_intercept_data); static Mutex s_mutex; /* * The bool indicates whether fb_intercept has ever been called * on a function with this name. * The vector contains a list of maybeIntercepted flags for functions * with this name. */ typedef StringIMap<std::pair<bool,std::vector<char*>>> RegisteredFlagsMap; static RegisteredFlagsMap s_registered_flags; /////////////////////////////////////////////////////////////////////////////// static void flag_maybe_intercepted(std::vector<char*> &flags) { for (auto flag : flags) { *flag = 1; } } bool register_intercept(const String& name, CVarRef callback, CVarRef data) { StringIMap<Variant> &handlers = s_intercept_data->m_intercept_handlers; if (!callback.toBoolean()) { if (name.empty()) { s_intercept_data->m_global_handler.reset(); handlers.clear(); } else { handlers.erase(name); } return true; } EventHook::EnableIntercept(); Array handler = make_packed_array(callback, data); if (name.empty()) { s_intercept_data->m_global_handler = handler; handlers.clear(); } else { handlers[name] = handler; } Lock lock(s_mutex); if (name.empty()) { for (auto& entry : s_registered_flags) { flag_maybe_intercepted(entry.second.second); } } else { StringData* sd = name.get(); if (!sd->isStatic()) { sd = makeStaticString(sd); } auto &entry = s_registered_flags[StrNR(sd)]; entry.first = true; flag_maybe_intercepted(entry.second); } return true; } static Variant *get_enabled_intercept_handler(const String& name) { Variant *handler = nullptr; StringIMap<Variant> &handlers = s_intercept_data->m_intercept_handlers; StringIMap<Variant>::iterator iter = handlers.find(name); if (iter != handlers.end()) { handler = &iter->second; } else { handler = &s_intercept_data->m_global_handler; if (handler->isNull()) { return nullptr; } } return handler; } Variant *get_intercept_handler(const String& name, char* flag) { TRACE(1, "get_intercept_handler %s flag is %d\n", name.get()->data(), (int)*flag); if (*flag == -1) { Lock lock(s_mutex); if (*flag == -1) { StringData *sd = name.get(); if (!sd->isStatic()) { sd = makeStaticString(sd); } auto &entry = s_registered_flags[StrNR(sd)]; entry.second.push_back(flag); *flag = entry.first; } if (!*flag) return nullptr; } Variant *handler = get_enabled_intercept_handler(name); if (handler == nullptr) { return nullptr; } assert(*flag); return handler; } void unregister_intercept_flag(const String& name, char *flag) { Lock lock(s_mutex); RegisteredFlagsMap::iterator iter = s_registered_flags.find(name); if (iter != s_registered_flags.end()) { std::vector<char*> &flags = iter->second.second; for (int i = flags.size(); i--; ) { if (flag == flags[i]) { flags.erase(flags.begin() + i); break; } } } } /////////////////////////////////////////////////////////////////////////////// // fb_rename_function() void rename_function(const String& old_name, const String& new_name) { auto const old = old_name.get(); auto const n3w = new_name.get(); auto const oldNe = const_cast<NamedEntity*>(Unit::GetNamedEntity(old)); auto const newNe = const_cast<NamedEntity*>(Unit::GetNamedEntity(n3w)); Func* func = Unit::lookupFunc(oldNe); if (!func) { // It's the caller's responsibility to ensure that the old function // exists. not_reached(); } if (!(func->attrs() & AttrDynamicInvoke)) { // When EvalJitEnableRenameFunction is false, the translator may wire // non-DynamicInvoke Func*'s into the TC. Don't rename functions. if (RuntimeOption::EvalJit && !RuntimeOption::EvalJitEnableRenameFunction) { raise_error("You must explicitly enable fb_rename_function in the JIT " "(-v Eval.JitEnableRenameFunction=true)"); } } Func *fnew = Unit::lookupFunc(newNe); if (fnew && fnew != func) { // To match hphpc, we silently ignore functions defined in user code that // have the same name as a function defined in a separable extension if (!fnew->isAllowOverride()) { raise_error("Function already defined: %s", n3w->data()); } return; } oldNe->setCachedFunc(nullptr); newNe->m_cachedFunc.bind(); newNe->setCachedFunc(func); if (RuntimeOption::EvalJit) { JIT::invalidateForRenameFunction(old); } } /////////////////////////////////////////////////////////////////////////////// }
namespace HPHP { static const Trace::Module TRACEMOD = Trace::intercept; class InterceptRequestData : public RequestEventHandler { public: InterceptRequestData() : m_use_allowed_functions(false) { } void clear() { *s_hasRenamedFunction = false; m_use_allowed_functions = false; m_allowed_functions.clear(); m_renamed_functions.clear(); m_global_handler.reset(); m_intercept_handlers.clear(); } virtual void requestInit() { clear(); } virtual void requestShutdown() { clear(); } public: bool m_use_allowed_functions; StringISet m_allowed_functions; StringIMap<String> m_renamed_functions; Variant m_global_handler; StringIMap<Variant> m_intercept_handlers; }; IMPLEMENT_STATIC_REQUEST_LOCAL(InterceptRequestData, s_intercept_data); IMPLEMENT_THREAD_LOCAL_NO_CHECK(bool, s_hasRenamedFunction); static Mutex s_mutex; typedef StringIMap<vector<char*> > RegisteredFlagsMap; static RegisteredFlagsMap s_registered_flags; /////////////////////////////////////////////////////////////////////////////// static void flag_maybe_interrupted(vector<char*> &flags) { for (int i = flags.size() - 1; i >= 0; i--) { *flags[i] = 1; } } bool register_intercept(CStrRef name, CVarRef callback, CVarRef data) { StringIMap<Variant> &handlers = s_intercept_data->m_intercept_handlers; if (!callback.toBoolean()) { if (name.empty()) { s_intercept_data->m_global_handler.reset(); handlers.clear(); } else { handlers.erase(name); } return true; } EventHook::EnableIntercept(); Array handler = CREATE_VECTOR2(callback, data); if (name.empty()) { s_intercept_data->m_global_handler = handler; handlers.clear(); } else { handlers[name] = handler; } Lock lock(s_mutex); if (name.empty()) { for (RegisteredFlagsMap::iterator iter = s_registered_flags.begin(); iter != s_registered_flags.end(); ++iter) { flag_maybe_interrupted(iter->second); } } else { RegisteredFlagsMap::iterator iter = s_registered_flags.find(name); if (iter != s_registered_flags.end()) { flag_maybe_interrupted(iter->second); } } return true; } Variant *get_enabled_intercept_handler(CStrRef name) { Variant *handler = nullptr; StringIMap<Variant> &handlers = s_intercept_data->m_intercept_handlers; StringIMap<Variant>::iterator iter = handlers.find(name); if (iter != handlers.end()) { handler = &iter->second; } else { handler = &s_intercept_data->m_global_handler; if (handler->isNull()) { return nullptr; } } return handler; } Variant *get_intercept_handler(CStrRef name, char* flag) { TRACE(1, "get_intercept_handler %s flag is %d\n", name.get()->data(), (int)*flag); if (*flag == -1) { Lock lock(s_mutex); if (*flag == -1) { StringData *sd = name.get(); if (!sd->isStatic()) { sd = StringData::GetStaticString(sd); } s_registered_flags[StrNR(sd)].push_back(flag); *flag = 0; } } Variant *handler = get_enabled_intercept_handler(name); if (handler == nullptr) { return nullptr; } *flag = 1; return handler; } void unregister_intercept_flag(CStrRef name, char *flag) { Lock lock(s_mutex); RegisteredFlagsMap::iterator iter = s_registered_flags.find(name); if (iter != s_registered_flags.end()) { vector<char*> &flags = iter->second; for (int i = flags.size(); i--; ) { if (flag == flags[i]) { flags.erase(flags.begin() + i); break; } } } } /////////////////////////////////////////////////////////////////////////////// // fb_rename_function() void check_renamed_functions(CArrRef names) { g_vmContext->addRenameableFunctions(names.get()); } bool check_renamed_function(CStrRef name) { return g_vmContext->isFunctionRenameable(name.get()); } void rename_function(CStrRef old_name, CStrRef new_name) { g_vmContext->renameFunction(old_name.get(), new_name.get()); } String get_renamed_function(CStrRef name) { HPHP::Func* f = HPHP::Unit::lookupFunc(name.get()); if (f) { return f->nameRef(); } return name; } /////////////////////////////////////////////////////////////////////////////// }
namespace HPHP { TRACE_SET_MOD(intercept); struct InterceptRequestData final : RequestEventHandler { InterceptRequestData() : m_use_allowed_functions(false) { } void clear() { m_use_allowed_functions = false; m_allowed_functions.clear(); m_renamed_functions.clear(); m_global_handler.releaseForSweep(); m_intercept_handlers.clear(); } void requestInit() override { clear(); } void requestShutdown() override { clear(); } void vscan(IMarker& mark) const override { // maybe better to teach heap-trace and IMarker about hphp_hash_set/map for (auto& s : m_allowed_functions) mark(s); for (auto& p : m_renamed_functions) mark(p); mark(m_global_handler); for (auto& p : m_intercept_handlers) mark(p); } public: bool m_use_allowed_functions; StringISet m_allowed_functions; StringIMap<String> m_renamed_functions; Variant m_global_handler; StringIMap<Variant> m_intercept_handlers; }; IMPLEMENT_STATIC_REQUEST_LOCAL(InterceptRequestData, s_intercept_data); static Mutex s_mutex; /* * The bool indicates whether fb_intercept has ever been called * on a function with this name. * The vector contains a list of maybeIntercepted flags for functions * with this name. */ typedef StringIMap<std::pair<bool,std::vector<int8_t*>>> RegisteredFlagsMap; static RegisteredFlagsMap s_registered_flags; /////////////////////////////////////////////////////////////////////////////// static void flag_maybe_intercepted(std::vector<int8_t*> &flags) { for (auto flag : flags) { *flag = 1; } } bool register_intercept(const String& name, const Variant& callback, const Variant& data) { StringIMap<Variant> &handlers = s_intercept_data->m_intercept_handlers; if (!callback.toBoolean()) { if (name.empty()) { s_intercept_data->m_global_handler.unset(); StringIMap<Variant> empty; handlers.swap(empty); } else { auto tmp = handlers[name]; auto it = handlers.find(name); if (it != handlers.end()) { auto tmp = it->second; handlers.erase(it); } } return true; } EventHook::EnableIntercept(); Array handler = make_packed_array(callback, data); if (name.empty()) { s_intercept_data->m_global_handler = handler; StringIMap<Variant> empty; handlers.swap(empty); } else { handlers[name] = handler; } Lock lock(s_mutex); if (name.empty()) { for (auto& entry : s_registered_flags) { flag_maybe_intercepted(entry.second.second); } } else { StringData* sd = name.get(); if (!sd->isStatic()) { sd = makeStaticString(sd); } auto &entry = s_registered_flags[StrNR(sd)]; entry.first = true; flag_maybe_intercepted(entry.second); } return true; } static Variant *get_enabled_intercept_handler(const String& name) { Variant *handler = nullptr; StringIMap<Variant> &handlers = s_intercept_data->m_intercept_handlers; StringIMap<Variant>::iterator iter = handlers.find(name); if (iter != handlers.end()) { handler = &iter->second; } else { handler = &s_intercept_data->m_global_handler; if (handler->isNull()) { return nullptr; } } return handler; } Variant *get_intercept_handler(const String& name, int8_t* flag) { TRACE(1, "get_intercept_handler %s flag is %d\n", name.get()->data(), (int)*flag); if (*flag == -1) { Lock lock(s_mutex); if (*flag == -1) { StringData *sd = name.get(); if (!sd->isStatic()) { sd = makeStaticString(sd); } auto &entry = s_registered_flags[StrNR(sd)]; entry.second.push_back(flag); *flag = entry.first; } if (!*flag) return nullptr; } Variant *handler = get_enabled_intercept_handler(name); if (handler == nullptr) { return nullptr; } assert(*flag); return handler; } void unregister_intercept_flag(const String& name, int8_t *flag) { Lock lock(s_mutex); RegisteredFlagsMap::iterator iter = s_registered_flags.find(name); if (iter != s_registered_flags.end()) { std::vector<int8_t*> &flags = iter->second.second; for (int i = flags.size(); i--; ) { if (flag == flags[i]) { flags.erase(flags.begin() + i); break; } } } } /////////////////////////////////////////////////////////////////////////////// // fb_rename_function() void rename_function(const String& old_name, const String& new_name) { auto const old = old_name.get(); auto const n3w = new_name.get(); auto const oldNe = const_cast<NamedEntity*>(NamedEntity::get(old)); auto const newNe = const_cast<NamedEntity*>(NamedEntity::get(n3w)); Func* func = Unit::lookupFunc(oldNe); if (!func) { // It's the caller's responsibility to ensure that the old function // exists. not_reached(); } // Interceptable functions can be renamed even when // JitEnableRenameFunction is false. if (!(func->attrs() & AttrInterceptable)) { if (!RuntimeOption::EvalJitEnableRenameFunction) { // When EvalJitEnableRenameFunction is false, the translator may // wire non-AttrInterceptable Func*'s into the TC. Don't rename // functions. raise_error("fb_rename_function must be explicitly enabled" "(-v Eval.JitEnableRenameFunction=true)"); } } auto const fnew = Unit::lookupFunc(newNe); if (fnew && fnew != func) { raise_error("Function already defined: %s", n3w->data()); } always_assert(!rds::isPersistentHandle(oldNe->getFuncHandle())); oldNe->setCachedFunc(nullptr); newNe->m_cachedFunc.bind(); newNe->setCachedFunc(func); if (RuntimeOption::EvalJit) { jit::invalidateForRenameFunction(old); } } /////////////////////////////////////////////////////////////////////////////// }