diff options
author | Vincent Torri <vincent.torri@gmail.com> | 2015-04-29 10:50:11 +0200 |
---|---|---|
committer | Cedric BAIL <cedric@osg.samsung.com> | 2015-05-07 09:53:12 +0200 |
commit | 92ff90ecca98f9e8e66a1f7a3ecf4e46f65913d4 (patch) | |
tree | 3da7d8cbc50d35dd472c3e8cc5fddb5b17602ac8 /src/lib/evil | |
parent | 00a3de5c1f3268c9b7c98e5be39c3947b105ee6e (diff) | |
download | efl-92ff90ecca98f9e8e66a1f7a3ecf4e46f65913d4.tar.gz |
evil: fix dladdr() implementation, add 2 unit tests for evil.
@fix
Signed-off-by: Cedric BAIL <cedric@osg.samsung.com>
Diffstat (limited to 'src/lib/evil')
-rw-r--r-- | src/lib/evil/dlfcn.c | 173 | ||||
-rw-r--r-- | src/lib/evil/dlfcn.h | 45 |
2 files changed, 170 insertions, 48 deletions
diff --git a/src/lib/evil/dlfcn.c b/src/lib/evil/dlfcn.c index c220003de6..ae8687ebf5 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 21de0b8ccc..349a9b4356 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 * |