diff --git a/source/dynlink/include/dynlink/dynlink_type.h b/source/dynlink/include/dynlink/dynlink_type.h index 7661172e7..5347eb51a 100644 --- a/source/dynlink/include/dynlink/dynlink_type.h +++ b/source/dynlink/include/dynlink/dynlink_type.h @@ -62,6 +62,20 @@ typedef void (*dynlink_symbol_addr)(void); /**< Function pointer referrin \ } while (0) +#define dynlink_symbol_uncast(fn, result) \ + do \ + { \ + union \ + { \ + void *ptr; \ + dynlink_symbol_addr fn; \ + } cast; \ +\ + cast.fn = (fn); \ + (result) = cast.ptr; \ +\ + } while (0) + #ifdef __cplusplus } #endif diff --git a/source/metacall/include/metacall/metacall_link.h b/source/metacall/include/metacall/metacall_link.h index b0665a824..9d5f7d890 100644 --- a/source/metacall/include/metacall/metacall_link.h +++ b/source/metacall/include/metacall/metacall_link.h @@ -40,7 +40,38 @@ extern "C" { */ METACALL_API int metacall_link_initialize(void); -// TODO: Implement dlsym hook function table +/** +* @brief +* Register a function pointer in order to allow function +* interposition when loading a library, if you register a +* function @symbol called 'foo', when you try to dlsym (or the equivalent +* on every platform), you will get the pointer to @fn, even if +* the symbol does not exist in the library, it will work. +* Function interposition is required in order to hook into runtimes +* and dynamically interpose our functions. +* +* @param[in] symbol +* Name of the function to be interposed +* +* @param[in] fn +* Function pointer that will be returned by dlsym (or equivalent) when accessing to @symbol +* +* @return +* Zero if success, different from zero otherwise +*/ +METACALL_API int metacall_link_register(const char *symbol, void (*fn)(void)); + +/** +* @brief +* Remove the hook previously registered +* +* @param[in] symbol +* Name of the function to be removed +* +* @return +* Zero if success, different from zero otherwise +*/ +METACALL_API int metacall_link_unregister(const char *symbol); /** * @brief diff --git a/source/metacall/source/metacall_fork.c b/source/metacall/source/metacall_fork.c index 379a1a61c..6cbe972e6 100644 --- a/source/metacall/source/metacall_fork.c +++ b/source/metacall/source/metacall_fork.c @@ -1,8 +1,20 @@ /* * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * * Copyright (C) 2016 - 2024 Vicente Eduardo Ferrer Garcia * - * A library for providing a foreign function interface calls. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ diff --git a/source/metacall/source/metacall_link.c b/source/metacall/source/metacall_link.c index 82f0ca851..d1343c52d 100644 --- a/source/metacall/source/metacall_link.c +++ b/source/metacall/source/metacall_link.c @@ -1,26 +1,44 @@ -// /* -// * MetaCall Library by Parra Studios -// * Copyright (C) 2016 - 2024 Vicente Eduardo Ferrer Garcia -// * -// * A library for providing a foreign function interface calls. -// * -// */ - -// /* -- Headers -- */ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2024 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ #include #include #include +#include + #include +#include + #include /* -- Private Variables -- */ static detour_handle detour_link_handle = NULL; +static set metacall_link_table = NULL; + #if defined(WIN32) || defined(_WIN32) || \ defined(__CYGWIN__) || defined(__CYGWIN32__) || \ defined(__MINGW32__) || defined(__MINGW64__) @@ -38,7 +56,17 @@ FARPROC metacall_link_hook(HMODULE handle, LPCSTR symbol) metacall_link_func_ptr metacall_link_trampoline = (metacall_link_func_ptr)detour_trampoline(detour_link_handle); - // TODO: Intercept if any, iterate hash map elements and invoke them + /* Intercept if any */ + void *ptr = set_get(metacall_link_table, (set_key)symbol); + + if (ptr != NULL) + { + dynlink_symbol_addr addr; + + dynlink_symbol_cast(void *, ptr, addr); + + return (FARPROC)addr; + } return metacall_link_trampoline(handle, symbol); } @@ -61,7 +89,15 @@ void *metacall_link_hook(void *handle, const char *symbol) metacall_link_func_ptr metacall_link_trampoline = (metacall_link_func_ptr)detour_trampoline(detour_link_handle); - // TODO: Intercept if any, iterate hash map elements and invoke them + /* Intercept function if any */ + void *ptr = set_get(metacall_link_table, (set_key)symbol); + + log_write("metacall", LOG_LEVEL_DEBUG, "MetaCall detour link interception: %s -> %p", symbol, ptr); + + if (ptr != NULL) + { + return ptr; + } return metacall_link_trampoline(handle, symbol); } @@ -90,9 +126,47 @@ int metacall_link_initialize(void) } } + if (metacall_link_table == NULL) + { + metacall_link_table = set_create(&hash_callback_str, &comparable_callback_str); + + if (metacall_link_table == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "MetaCall failed to create link table"); + + metacall_link_destroy(); + + return 1; + } + } + return 0; } +int metacall_link_register(const char *symbol, void (*fn)(void)) +{ + void *ptr; + + if (metacall_link_table == NULL) + { + return 1; + } + + dynlink_symbol_uncast(fn, ptr); + + return set_insert(metacall_link_table, (set_key)symbol, ptr); +} + +int metacall_link_unregister(const char *symbol) +{ + if (metacall_link_table == NULL) + { + return 1; + } + + return (set_remove(metacall_link_table, (set_key)symbol) == NULL); +} + int metacall_link_destroy(void) { int result = 0; @@ -111,7 +185,12 @@ int metacall_link_destroy(void) detour_link_handle = NULL; } - // TODO: Destroy hash map + if (metacall_link_table != NULL) + { + set_destroy(metacall_link_table); + + metacall_link_table = NULL; + } return result; }