cedric pushed a commit to branch master. http://git.enlightenment.org/core/efl.git/commit/?id=92ff90ecca98f9e8e66a1f7a3ecf4e46f65913d4
commit 92ff90ecca98f9e8e66a1f7a3ecf4e46f65913d4 Author: Vincent Torri <[email protected]> Date: Wed Apr 29 10:50:11 2015 +0200 evil: fix dladdr() implementation, add 2 unit tests for evil. @fix Signed-off-by: Cedric BAIL <[email protected]> --- src/Makefile_Evil.am | 27 ++++++ src/lib/evil/dlfcn.c | 173 +++++++++++++++++++++++++++++++-------- src/lib/evil/dlfcn.h | 45 ++++++---- src/tests/evil/evil_suite.c | 138 +++++++++++++++++++++++++++++++ src/tests/evil/evil_suite.h | 41 ++++++++++ src/tests/evil/evil_test_dlfcn.c | 132 +++++++++++++++++++++++++++++ src/tests/evil/evil_test_main.c | 37 +++++++++ 7 files changed, 545 insertions(+), 48 deletions(-) diff --git a/src/Makefile_Evil.am b/src/Makefile_Evil.am index 3c6fd0e..4f9ae3f 100644 --- a/src/Makefile_Evil.am +++ b/src/Makefile_Evil.am @@ -113,6 +113,7 @@ bin/evil/evil_test_print.h \ bin/evil/evil_test_realpath.h \ bin/evil/evil_test_util.h + #suite_SOURCES += bin/evil/memcpy_glibc_i686.S # see EXTRA_DIST below! bin_evil_evil_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl @EVIL_CFLAGS@ @@ -123,7 +124,33 @@ bin_evil_test_evil_SOURCES = bin/evil/test_evil.c bin_evil_test_evil_LDADD = @USE_EVIL_LIBS@ bin_evil_test_evil_DEPENDENCIES = @USE_EVIL_INTERNAL_LIBS@ @DL_INTERNAL_LIBS@ +### Unit tests + +if EFL_ENABLE_TESTS + +check_PROGRAMS += tests/evil/evil_suite +TESTS += tests/evil/evil_suite + +tests_evil_evil_suite_SOURCES = \ +tests/evil/evil_suite.c \ +tests/evil/evil_suite.h \ +tests/evil/evil_test_dlfcn.c \ +tests/evil/evil_test_main.c + +tests_evil_evil_suite_CPPFLAGS = \ +-I$(top_builddir)/src/lib/efl \ +-DTESTS_SRC_DIR=\"$(top_srcdir)/src/tests/evil\" \ +-DPACKAGE_BUILD_DIR=\"$(abs_top_builddir)/src/tests/evil\" \ +-DTESTS_BUILD_DIR=PACKAGE_BUILD_DIR \ +@CHECK_CFLAGS@ \ +@EVIL_CFLAGS@ +tests_evil_evil_suite_LDADD = @USE_EVIL_LIBS@ @DL_LIBS@ @CHECK_LIBS@ +tests_evil_evil_suite_DEPENDENCIES = @USE_EVIL_INTERNAL_LIBS@ @DL_INTERNAL_LIBS@ + endif + +endif + EXTRA_DIST += \ lib/evil/regex/regerror.ih \ lib/evil/regex/engine.ih \ diff --git a/src/lib/evil/dlfcn.c b/src/lib/evil/dlfcn.c index c220003..ae8687e 100644 --- a/src/lib/evil/dlfcn.c +++ b/src/lib/evil/dlfcn.c @@ -171,21 +171,67 @@ dlsym(void *handle, const char *symbol) return fp; } +char * +dlerror (void) +{ + if (!_dl_err_viewed) + { + _dl_err_viewed = 1; + return _dl_err; + } + else + { + if (_dl_err) + free(_dl_err); + return NULL; + } +} + +#ifdef _GNU_SOURCE + +static char _dli_fname[MAX_PATH]; +static char _dli_sname[MAX_PATH]; /* a symbol should have at most 255 char */ + +static int +_dladdr_comp(const void *p1, const void *p2) +{ + return ( *(int *)p1 - *(int *)p2); +} + int dladdr (const void *addr EVIL_UNUSED, Dl_info *info) { - TCHAR tpath[PATH_MAX]; + TCHAR tpath[PATH_MAX]; MEMORY_BASIC_INFORMATION mbi; - char *path; + unsigned char *base; + char *path; size_t length; - int ret = 0; + + IMAGE_NT_HEADERS *nth; + IMAGE_EXPORT_DIRECTORY *ied; + DWORD *addresses; + WORD *ordinals; + DWORD *names; + DWORD *tmp; + DWORD res; + DWORD rva_addr; + DWORD i; if (!info) return 0; - length = VirtualQuery(addr, &mbi, sizeof(mbi)); - if (!length) - return 0; + info->dli_fname = NULL; + info->dli_fbase = NULL; + info->dli_sname = NULL; + info->dli_saddr = NULL; + + /* Get the name and base address of the module */ + + if (!VirtualQuery(addr, &mbi, sizeof(mbi))) + { + _dl_get_last_error("VirtualQuery returned: "); + return 0; + } if (mbi.State != MEM_COMMIT) return 0; @@ -193,49 +239,110 @@ dladdr (const void *addr EVIL_UNUSED, Dl_info *info) if (!mbi.AllocationBase) return 0; - ret = GetModuleFileName((HMODULE)mbi.AllocationBase, (LPTSTR)&tpath, PATH_MAX); - if (!ret) - return 0; + base = (unsigned char *)mbi.AllocationBase; -#ifdef UNICODE + if (!GetModuleFileName((HMODULE)base, (LPTSTR)&tpath, PATH_MAX)) + { + _dl_get_last_error("GetModuleFileName returned: "); + return 0; + } + +# ifdef UNICODE path = evil_wchar_to_char(tpath); -#else +# else path = tpath; -#endif /* ! UNICODE */ +# endif /* ! UNICODE */ - length = strlen (path); + length = strlen(path); if (length >= PATH_MAX) { length = PATH_MAX - 1; path[PATH_MAX - 1] = '\0'; } - EVIL_PATH_SEP_UNIX_TO_WIN32(path); + memcpy(_dli_fname, path, length + 1); + info->dli_fname = (const char *)_dli_fname; + info->dli_fbase = base; - memcpy (info->dli_fname, path, length + 1); - info->dli_fbase = NULL; - info->dli_sname = NULL; - info->dli_saddr = NULL; +# ifdef UNICODE + free(path); +# endif /* ! UNICODE */ -#ifdef UNICODE - free (path); -#endif /* ! UNICODE */ + /* get the name and the address of the required symbol */ - return 1; -} + if (((IMAGE_DOS_HEADER *)base)->e_magic != IMAGE_DOS_SIGNATURE) + { + SetLastError(1276); + return 0; + } -char * -dlerror (void) -{ - if (!_dl_err_viewed) + nth = (IMAGE_NT_HEADERS *)(base + ((IMAGE_DOS_HEADER *)base)->e_lfanew); + if (nth->Signature != IMAGE_NT_SIGNATURE) { - _dl_err_viewed = 1; - return _dl_err; + SetLastError(1276); + return 0; } - else + + /* no exported symbols ? it's an EXE and we exit without error */ + if (nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0) { - if (_dl_err) - free(_dl_err); - return NULL; + return 1; + } + + /* we assume now that the PE file is well-formed, so checks only when needed */ + ied = (IMAGE_EXPORT_DIRECTORY *)(base + nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + addresses = (DWORD *)(base + ied->AddressOfFunctions); + ordinals = (WORD *)(base + ied->AddressOfNameOrdinals); + names = (DWORD *)(base + ied->AddressOfNames); + + /* the addresses are not ordered, so we need to order them */ + tmp = malloc(ied->NumberOfFunctions * sizeof(DWORD)); + if (!tmp) + { + SetLastError(8); + return 0; + } + + memcpy(tmp, addresses, ied->NumberOfFunctions * sizeof(DWORD)); + qsort(tmp, ied->NumberOfFunctions, sizeof(DWORD), _dladdr_comp); + rva_addr = (unsigned char *)addr - base; + res = (DWORD)(-1); + for (i = 0; i < ied->NumberOfFunctions; i++) + { + if (tmp[i] < rva_addr) + continue; + + res = tmp[i]; + break; } + + /* if rva_addr is too high, we store the latest address */ + if (res == (DWORD)(-1)) + res = tmp[ied->NumberOfFunctions - 1]; + + free(tmp); + + for (i = 0; i < ied->NumberOfNames; i++) + { + if (addresses[ordinals[i]] == res) + { + char *name; + + name = (char *)(base + names[i]); + length = strlen(name); + if (length >= PATH_MAX) + { + length = PATH_MAX - 1; + name[PATH_MAX - 1] = '\0'; + } + memcpy(_dli_sname, name, length + 1); + info->dli_sname = (const char *)_dli_sname; + info->dli_saddr = base + res; + return 1; + } + } + + return 0; } + +#endif /* _GNU_SOURCE */ diff --git a/src/lib/evil/dlfcn.h b/src/lib/evil/dlfcn.h index 21de0b8..349a9b4 100644 --- a/src/lib/evil/dlfcn.h +++ b/src/lib/evil/dlfcn.h @@ -64,30 +64,40 @@ x * This header provides functions to load and unload dynamic-link */ #define RTLD_NODELETE 0x01000 /* do not delete object when closed. */ +#ifdef _GNU_SOURCE + /** * @def RTLD_DEFAULT * Symbols are searched in all the DLL opened by the current process. + * This symbol is defined only when _GNU_SOURCE was defined before + * including dlfcn.h. */ #define RTLD_DEFAULT ((void*)1) /* search the symbol on all the DLL of the current process */ /** * @typedef Dl_info * @brief A structure that stores infomation of a calling process. + * This typedef is defined only when _GNU_SOURCE was defined before + * including dlfcn.h. */ typedef struct Dl_info Dl_info; /** * @struct Dl_info * @brief A structure that stores infomation of a calling process. + * This structure is defined only when _GNU_SOURCE was defined before + * including dlfcn.h. */ struct Dl_info { - char dli_fname[PATH_MAX]; /**< Filename of defining object */ - void *dli_fbase; /**< Load address of that object */ - const char *dli_sname; /**< Name of nearest lower symbol */ - void *dli_saddr; /**< Exact value of nearest symbol */ + const char *dli_fname; /**< Filename of defining object */ + void *dli_fbase; /**< Load address of that object */ + const char *dli_sname; /**< Name of nearest lower symbol */ + void *dli_saddr; /**< Exact value of nearest symbol */ }; +#endif /* _GNU_SOURCE */ + /** * @brief Map a specified executable module (either a .dll or .exe file) * into the address space of the user process. @@ -197,30 +207,35 @@ EAPI int dlclose(void* handle); */ EAPI void *dlsym(void* handle, const char* symbol); +#ifdef _GNU_SOURCE + /** - * @brief Get the location of the current process (.exe) + * @brief Resolve module and function pointers from the given function + * pointer address. * - * @param addr Unused. - * @param info Pointer to the Dl_info to fill. + * @param addr A function pointer. + * @param info Pointer to the #Dl_info to fill. * @return 1 on success, 0 otherwise. * - * Fill the dli_fname member of @p info with the absolute name - * of the current calling process (.exe file that is executed). - * All other members are set to @c NULL. + * Fill @p info with the absolute name of the module which has the + * fonction pointer @p addr, the base address of that module, the name + * and address of the symbol. If no symbol matching @p addr could be + * found (as in an EXE file), then dli_sname and dli_saddr are set to + * NULL and the function returns 1. See #Dl_info for more informations. * - * Contrary to the unix function, the full name of the shared - * library is not returned, but insted the full name of the current - * calling process (.exe file). + * This function is available only when _GNU_SOURCE was defined before + * including dlfcn.h. * * Conformity: None. * - * Supported OS: Windows Vista, Windows XP or Windows 2000 - * Professional. + * Supported OS: Windows Vista, Windows XP. * * @ingroup Evil_Dlfcn */ EAPI int dladdr (const void *addr, Dl_info *info); +#endif /* _GNU_SOURCE */ + /** * @brief Get diagnostic information * diff --git a/src/tests/evil/evil_suite.c b/src/tests/evil/evil_suite.c new file mode 100644 index 0000000..2699bee --- /dev/null +++ b/src/tests/evil/evil_suite.c @@ -0,0 +1,138 @@ +/* EVIL - EFL library for Windows port + * Copyright (C) 2015 Vincent Torri + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; + * if not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <string.h> + +#include <Evil.h> + +#include "evil_suite.h" + +typedef struct _Evil_Test_Case Evil_Test_Case; +struct _Evil_Test_Case +{ + const char *test_case; + void (*build)(TCase *tc); +}; + +static const Evil_Test_Case etc[] = { + /* { "Dirent", evil_test_dirent }, */ + { "Dlfcn", evil_test_dlfcn }, + /* { "Fcntl", evil_test_fcntl }, */ + /* { "Fnmatch", evil_test_fnmatch }, */ + /* { "Inet", evil_test_inet }, */ + /* { "Langinfo", evil_test_langinfo }, */ + /* { "Link", evil_test_link }, */ + { "Main", evil_test_main }, + /* { "Mman", evil_test_mman }, */ + /* { "Pwd", evil_test_pwd }, */ + /* { "Stdio", evil_test_stdio }, */ + /* { "Stdlib", evil_test_stdlib }, */ + /* { "String", evil_test_string }, */ + /* { "Time", evil_test_time }, */ + /* { "Unistd", evil_test_unistd }, */ + /* { "Util", evil_test_util }, */ + { NULL, NULL } +}; + +static void +_list_tests(void) +{ + const Evil_Test_Case *itr = etc; + fputs("Available Test Cases:\n", stderr); + for (; itr->test_case; itr++) + fprintf(stderr, "\t%s\n", itr->test_case); +} + +static unsigned char +_use_test(int argc, const char **argv, const char *test_case) +{ + if (argc < 1) + return 1; + + for (; argc > 0; argc--, argv++) + if (strcmp(test_case, *argv) == 0) + return 1; + + return 0; +} + +Suite * +evil_build_suite(int argc, const char **argv) +{ + TCase *tc; + Suite *s; + int i; + + s = suite_create("Evil"); + + for (i = 0; etc[i].test_case; ++i) + { + if (!_use_test(argc, argv, etc[i].test_case)) + continue; + + tc = tcase_create(etc[i].test_case); +#ifndef _WIN32 + tcase_set_timeout(tc, 0); +#endif + + etc[i].build(tc); + suite_add_tcase(s, tc); + } + + return s; +} + +int +main(int argc, char **argv) +{ + Suite *s; + SRunner *sr; + int i, failed_count; + + for (i = 1; i < argc; i++) + if ((strcmp(argv[i], "-h") == 0) || + (strcmp(argv[i], "--help") == 0)) + { + fprintf(stderr, "Usage:\n\t%s [test_case1 .. [test_caseN]]\n", + argv[0]); + _list_tests(); + return 0; + } + else if ((strcmp(argv[i], "-l") == 0) || + (strcmp(argv[i], "--list") == 0)) + { + _list_tests(); + return 0; + } + + putenv("EFL_RUN_IN_TREE=1"); + + s = evil_build_suite(argc - 1, (const char **)argv + 1); + sr = srunner_create(s); + srunner_set_xml(sr, TESTS_BUILD_DIR "/check-results.xml"); + srunner_run_all(sr, CK_ENV); + failed_count = srunner_ntests_failed(sr); + srunner_free(sr); + + return (failed_count == 0) ? 0 : 255; +} diff --git a/src/tests/evil/evil_suite.h b/src/tests/evil/evil_suite.h new file mode 100644 index 0000000..17222fc --- /dev/null +++ b/src/tests/evil/evil_suite.h @@ -0,0 +1,41 @@ +/* EVIL - EFL library for Windows port + * Copyright (C) 2015 Vincent Torri + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; + * if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef EVIL_SUITE_H_ +#define EVIL_SUITE_H_ + +#include <check.h> + +/* void evil_test_dirent(TCase *tc); */ +void evil_test_dlfcn(TCase *tc); +/* void evil_test_fcntl(TCase *tc); */ +/* void evil_test_fnmatch(TCase *tc); */ +/* void evil_test_inet(TCase *tc); */ +/* void evil_test_langinfo(TCase *tc); */ +/* void evil_test_link(TCase *tc); */ +void evil_test_main(TCase *tc); +/* void evil_test_mman(TCase *tc); */ +/* void evil_test_pwd(TCase *tc); */ +/* void evil_test_stdio(TCase *tc); */ +/* void evil_test_stdlib(TCase *tc); */ +/* void evil_test_string(TCase *tc); */ +/* void evil_test_time(TCase *tc); */ +/* void evil_test_unistd(TCase *tc); */ +/* void evil_test_util(TCase *tc); */ + +#endif /* EVIL_SUITE_H_ */ diff --git a/src/tests/evil/evil_test_dlfcn.c b/src/tests/evil/evil_test_dlfcn.c new file mode 100644 index 0000000..28ee5b5 --- /dev/null +++ b/src/tests/evil/evil_test_dlfcn.c @@ -0,0 +1,132 @@ +/* EVIL - EFL library for Windows port + * Copyright (C) 2015 Vincent Torri + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; + * if not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <dlfcn.h> +#include <Evil.h> + +#include "evil_suite.h" + + +typedef int (*_evil_init)(void); +typedef int (*_evil_shutdwon)(void); + + +START_TEST(evil_dlfcn_dlopen_success) +{ + void *mod; + int res; + + mod = dlopen("c:\\windows\\system32\\kernel32.dll", 0); + fail_if(mod == NULL); + + res = dlclose(mod); + fail_if(res != 0); +} +END_TEST + +START_TEST(evil_dlfcn_dlopen_failure) +{ + void *mod; + + /* non existent DLL */ + mod = dlopen("c:\\windows\\system32\\kernel32.dl", 0); + fail_if(mod != NULL); +} +END_TEST + +START_TEST(evil_dlfcn_dlsym_success) +{ + _evil_init sym_init; + _evil_shutdwon sym_shutdown; + void *mod; + int res; + + mod = dlopen("libevil-1.dll", 0); + fail_if(mod == NULL); + + sym_init = dlsym(mod, "evil_init"); + fail_if(sym_init == NULL); + fail_if(sym_init() != 1); + + sym_shutdown = dlsym(mod, "evil_shutdown"); + fail_if(sym_shutdown == NULL); + fail_if(sym_shutdown() != 0); + + res = dlclose(mod); + fail_if(res != 0); +} +END_TEST + +START_TEST(evil_dlfcn_dlsym_failure) +{ + void *mod; + void *sym; + int res; + + mod = dlopen("libevil-1.dll", 0); + fail_if(mod == NULL); + + /* non-existent symbol */ + sym = dlsym(mod, "evil_ini"); + fail_if(sym != NULL); + + res = dlclose(mod); + fail_if(res != 0); +} +END_TEST + +START_TEST(evil_dlfcn_dladdr) +{ + Dl_info info; + void *mod; + void *sym; + char *dll; + int res; + + mod = dlopen("libevil-1.dll", 0); + fail_if(mod == NULL); + + sym = dlsym(mod, "evil_init"); + fail_if(sym == NULL); + + res = dladdr(sym, &info); + fail_if(res == 0); + + fail_if(mod != info.dli_fbase); + dll = strrchr(info.dli_fname, '\\') + 1; + fail_if(strcmp("libevil-1.dll", dll) != 0); + fail_if(sym != info.dli_saddr); + fail_if(strcmp("evil_init", info.dli_sname) != 0); + + res = dlclose(mod); + fail_if(res != 0); +} +END_TEST + +void evil_test_dlfcn(TCase *tc) +{ + tcase_add_test(tc, evil_dlfcn_dlopen_success); + tcase_add_test(tc, evil_dlfcn_dlopen_failure); + tcase_add_test(tc, evil_dlfcn_dlsym_success); + tcase_add_test(tc, evil_dlfcn_dlsym_failure); + tcase_add_test(tc, evil_dlfcn_dladdr); +} diff --git a/src/tests/evil/evil_test_main.c b/src/tests/evil/evil_test_main.c new file mode 100644 index 0000000..d116aef --- /dev/null +++ b/src/tests/evil/evil_test_main.c @@ -0,0 +1,37 @@ +/* EVIL - EFL library for Windows port + * Copyright (C) 2015 Vincent Torri + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; + * if not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <Evil.h> + +#include "evil_suite.h" + +START_TEST(evil_main_simple) +{ + fail_if(evil_init() != 1); + fail_if(evil_shutdown() != 0); +} +END_TEST + +void evil_test_main(TCase *tc) +{ + tcase_add_test(tc, evil_main_simple); +} --
