summaryrefslogtreecommitdiff
path: root/src/lib/evil
diff options
context:
space:
mode:
authorVincent Torri <vincent.torri@gmail.com>2015-04-29 10:50:11 +0200
committerCedric BAIL <cedric@osg.samsung.com>2015-05-07 09:53:12 +0200
commit92ff90ecca98f9e8e66a1f7a3ecf4e46f65913d4 (patch)
tree3da7d8cbc50d35dd472c3e8cc5fddb5b17602ac8 /src/lib/evil
parent00a3de5c1f3268c9b7c98e5be39c3947b105ee6e (diff)
downloadefl-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.c173
-rw-r--r--src/lib/evil/dlfcn.h45
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
*