* ldd.cc: Rework to detect missing DLLs.
(start_process): Change to expect windows filename as input. (tocyg): New function - convert cygwin fn to windows fn. (print_dlls_and_kill_inferior): Accept extra argument denoting whether to open input and look for nonexistent DLLs. Use tocyg to convert filename and pass it to start_process. (report): Flag when an DLL-not-found exception occurs and pass this information to print_dlls_and_kill_inferior. (filelist): New structure. (saw_file): New function. (dump_import_directory): Ditto. (map_file): Ditto. (skip_dos_stub): Ditto. (get_directory_index): Ditto. (process_file): Ditto.
This commit is contained in:
		| @@ -1,3 +1,21 @@ | |||||||
|  | 2009-03-14  Christopher Faylor  <me+cygwin@cgf.cx> | ||||||
|  |  | ||||||
|  | 	* ldd.cc: Rework to detect missing DLLs. | ||||||
|  | 	(start_process): Change to expect windows filename as input. | ||||||
|  | 	(tocyg): New function - convert cygwin fn to windows fn. | ||||||
|  | 	(print_dlls_and_kill_inferior): Accept extra argument denoting whether | ||||||
|  | 	to open input and look for nonexistent DLLs.  Use tocyg to convert | ||||||
|  | 	filename and pass it to start_process. | ||||||
|  | 	(report): Flag when an DLL-not-found exception occurs and pass this | ||||||
|  | 	information to print_dlls_and_kill_inferior. | ||||||
|  | 	(filelist): New structure. | ||||||
|  | 	(saw_file): New function. | ||||||
|  | 	(dump_import_directory): Ditto. | ||||||
|  | 	(map_file): Ditto. | ||||||
|  | 	(skip_dos_stub): Ditto. | ||||||
|  | 	(get_directory_index): Ditto. | ||||||
|  | 	(process_file): Ditto. | ||||||
|  |  | ||||||
| 2009-03-09  Corinna Vinschen  <corinna@vinschen.de> | 2009-03-09  Corinna Vinschen  <corinna@vinschen.de> | ||||||
|  |  | ||||||
| 	* utils.sgml: Fix typo. | 	* utils.sgml: Fix typo. | ||||||
|   | |||||||
| @@ -32,12 +32,17 @@ | |||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <sys/cygwin.h> | #include <sys/cygwin.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
|  | #include <libgen.h> | ||||||
|  |  | ||||||
| #define _WIN32_WINNT 0x0501 | #define _WIN32_WINNT 0x0501 | ||||||
| #include <windows.h> | #include <windows.h> | ||||||
| #include <imagehlp.h> | #include <imagehlp.h> | ||||||
| #include <psapi.h> | #include <psapi.h> | ||||||
|  |  | ||||||
|  | #ifndef STATUS_DLL_NOT_FOUND | ||||||
|  | #define STATUS_DLL_NOT_FOUND (0xC0000135L) | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #define VERSION "1.0" | #define VERSION "1.0" | ||||||
|  |  | ||||||
| struct option longopts[] = | struct option longopts[] = | ||||||
| @@ -50,6 +55,8 @@ struct option longopts[] = | |||||||
|   {0, no_argument, NULL, 0} |   {0, no_argument, NULL, 0} | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | static int process_file (const char *); | ||||||
|  |  | ||||||
| static int | static int | ||||||
| usage (const char *fmt, ...) | usage (const char *fmt, ...) | ||||||
| { | { | ||||||
| @@ -79,18 +86,10 @@ static HANDLE hProcess; | |||||||
| static int | static int | ||||||
| start_process (const char *fn) | start_process (const char *fn) | ||||||
| { | { | ||||||
|   ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_A, fn, NULL, 0); |  | ||||||
|   if (len <= 0) |  | ||||||
|     print_errno_error_and_return (fn); |  | ||||||
|  |  | ||||||
|   char fn_win[len + 1]; |  | ||||||
|   if (cygwin_conv_path (CCP_POSIX_TO_WIN_A, fn, fn_win, len)) |  | ||||||
|     print_errno_error_and_return (fn); |  | ||||||
|  |  | ||||||
|   STARTUPINFO si = {}; |   STARTUPINFO si = {}; | ||||||
|   PROCESS_INFORMATION pi; |   PROCESS_INFORMATION pi; | ||||||
|   si.cb = sizeof (si); |   si.cb = sizeof (si); | ||||||
|   if (CreateProcess (NULL, fn_win, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi)) |   if (CreateProcess (NULL, (CHAR *) fn, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi)) | ||||||
|     { |     { | ||||||
|       hProcess = pi.hProcess; |       hProcess = pi.hProcess; | ||||||
|       DebugSetProcessKillOnExit (true); |       DebugSetProcessKillOnExit (true); | ||||||
| @@ -124,8 +123,32 @@ struct dlls | |||||||
|     struct dlls *next; |     struct dlls *next; | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|  | #define SLOP strlen (" (?)") | ||||||
|  | char * | ||||||
|  | tocyg (char *win_fn) | ||||||
|  | { | ||||||
|  |   win_fn[MAX_PATH] = '\0'; | ||||||
|  |   ssize_t cwlen = cygwin_conv_path (CCP_WIN_A_TO_POSIX, win_fn, NULL, 0); | ||||||
|  |   char *fn; | ||||||
|  |   if (cwlen <= 0) | ||||||
|  |     fn = strdup (win_fn); | ||||||
|  |   else | ||||||
|  |     { | ||||||
|  |       char *fn_cyg = (char *) malloc (cwlen + SLOP + 1); | ||||||
|  |       if (cygwin_conv_path (CCP_WIN_A_TO_POSIX, win_fn, fn_cyg, cwlen) == 0) | ||||||
|  | 	fn = fn_cyg; | ||||||
|  |       else | ||||||
|  | 	{ | ||||||
|  | 	  free (fn_cyg); | ||||||
|  | 	  fn = (char *) malloc (strlen (win_fn) + SLOP + 1); | ||||||
|  | 	  strcpy (fn, win_fn); | ||||||
|  | 	} | ||||||
|  |     } | ||||||
|  |   return fn; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int | static int | ||||||
| print_dlls_and_kill_inferior (dlls *dll) | print_dlls_and_kill_inferior (dlls *dll, const char *process_fn) | ||||||
| { | { | ||||||
|   while ((dll = dll->next)) |   while ((dll = dll->next)) | ||||||
|     { |     { | ||||||
| @@ -135,27 +158,13 @@ print_dlls_and_kill_inferior (dlls *dll) | |||||||
|       if (!len) |       if (!len) | ||||||
| 	fn = strdup ("???"); | 	fn = strdup ("???"); | ||||||
|       else |       else | ||||||
| 	{ | 	fn = tocyg (fnbuf); | ||||||
| 	  fnbuf[MAX_PATH] = '\0'; |       printf ("\t%s => %s (%p)\n", basename (fn), fn, dll->lpBaseOfDll); | ||||||
| 	  ssize_t cwlen = cygwin_conv_path (CCP_WIN_A_TO_POSIX, fnbuf, NULL, 0); |  | ||||||
| 	  if (cwlen <= 0) |  | ||||||
| 	    fn = strdup (fnbuf); |  | ||||||
| 	  else |  | ||||||
| 	    { |  | ||||||
| 	      char *fn_cyg = (char *) malloc (cwlen + 1); |  | ||||||
| 	      if (cygwin_conv_path (CCP_WIN_A_TO_POSIX, fnbuf, fn_cyg, cwlen) == 0) |  | ||||||
| 		fn = fn_cyg; |  | ||||||
| 	      else |  | ||||||
| 		{ |  | ||||||
| 		  free (fn_cyg); |  | ||||||
| 		  fn = strdup (fnbuf); |  | ||||||
| 		} |  | ||||||
| 	    } |  | ||||||
| 	} |  | ||||||
|       printf ("\t%s (%p)\n", fn, dll->lpBaseOfDll); |  | ||||||
|       free (fn); |       free (fn); | ||||||
|     } |     } | ||||||
|   TerminateProcess (hProcess, 0); |   TerminateProcess (hProcess, 0); | ||||||
|  |   if (process_fn) | ||||||
|  |     return process_file (process_fn); | ||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -165,7 +174,18 @@ report (const char *in_fn, bool multiple) | |||||||
|   if (multiple) |   if (multiple) | ||||||
|     printf ("%s:\n", in_fn); |     printf ("%s:\n", in_fn); | ||||||
|   char *fn = realpath (in_fn, NULL); |   char *fn = realpath (in_fn, NULL); | ||||||
|   if (!fn || start_process (fn)) |   if (!fn) | ||||||
|  |     print_errno_error_and_return (in_fn); | ||||||
|  |  | ||||||
|  |   ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_A, fn, NULL, 0); | ||||||
|  |   if (len <= 0) | ||||||
|  |     print_errno_error_and_return (fn); | ||||||
|  |  | ||||||
|  |   char fn_win[len + 1]; | ||||||
|  |   if (cygwin_conv_path (CCP_POSIX_TO_WIN_A, fn, fn_win, len)) | ||||||
|  |     print_errno_error_and_return (fn); | ||||||
|  |  | ||||||
|  |   if (!fn || start_process (fn_win)) | ||||||
|     print_errno_error_and_return (in_fn); |     print_errno_error_and_return (in_fn); | ||||||
|  |  | ||||||
|   DEBUG_EVENT ev; |   DEBUG_EVENT ev; | ||||||
| @@ -174,6 +194,7 @@ report (const char *in_fn, bool multiple) | |||||||
|  |  | ||||||
|   dlls dll_list = {}; |   dlls dll_list = {}; | ||||||
|   dlls *dll_last = &dll_list; |   dlls *dll_last = &dll_list; | ||||||
|  |   const char *process_fn = NULL; | ||||||
|   while (1) |   while (1) | ||||||
|     { |     { | ||||||
|       if (WaitForDebugEvent (&ev, 1000)) |       if (WaitForDebugEvent (&ev, 1000)) | ||||||
| @@ -198,7 +219,10 @@ report (const char *in_fn, bool multiple) | |||||||
| 	  dll_last = dll_last->next; | 	  dll_last = dll_last->next; | ||||||
| 	  break; | 	  break; | ||||||
| 	case EXCEPTION_DEBUG_EVENT: | 	case EXCEPTION_DEBUG_EVENT: | ||||||
| 	  print_dlls_and_kill_inferior (&dll_list); | 	  if (ev.u.Exception.ExceptionRecord.ExceptionCode == STATUS_DLL_NOT_FOUND) | ||||||
|  | 	    process_fn = fn_win; | ||||||
|  | 	  else | ||||||
|  | 	    print_dlls_and_kill_inferior (&dll_list, process_fn); | ||||||
| 	  break; | 	  break; | ||||||
| 	default: | 	default: | ||||||
| 	  break; | 	  break; | ||||||
| @@ -263,3 +287,278 @@ main (int argc, char **argv) | |||||||
|       ret = 1; |       ret = 1; | ||||||
|   exit (ret); |   exit (ret); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static struct filelist | ||||||
|  | { | ||||||
|  |   struct filelist *next; | ||||||
|  |   char *name; | ||||||
|  | } *head; | ||||||
|  |  | ||||||
|  | static bool printing = false; | ||||||
|  |  | ||||||
|  | static bool | ||||||
|  | saw_file (char *name) | ||||||
|  | { | ||||||
|  |  | ||||||
|  |   struct filelist *p; | ||||||
|  |  | ||||||
|  |   for (p=head; p; p = p->next) | ||||||
|  |     if (strcasecmp (name, p->name) == 0) | ||||||
|  |       return true; | ||||||
|  |  | ||||||
|  |   p = (filelist *) malloc(sizeof (struct filelist)); | ||||||
|  |   p->next = head; | ||||||
|  |   p->name = strdup (name); | ||||||
|  |   head = p; | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* dump of import directory | ||||||
|  |    section begins at pointer 'section base' | ||||||
|  |    section RVA is 'section_rva' | ||||||
|  |    import directory begins at pointer 'imp' */ | ||||||
|  | static int | ||||||
|  | dump_import_directory (const void *const section_base, | ||||||
|  | 		       const DWORD section_rva, | ||||||
|  | 		       const IMAGE_IMPORT_DESCRIPTOR *imp) | ||||||
|  | { | ||||||
|  |   /* get memory address given the RVA */ | ||||||
|  |   #define adr(rva) ((const void*) ((char*) section_base+((DWORD) (rva))-section_rva)) | ||||||
|  |  | ||||||
|  |   /* continue until address inaccessible or there's no DLL name */ | ||||||
|  |   for (; !IsBadReadPtr (imp, sizeof (*imp)) && imp->Name; imp++) | ||||||
|  |     { | ||||||
|  |       char full_path[MAX_PATH]; | ||||||
|  |       char *dummy; | ||||||
|  |       char *fn = (char *) adr (imp->Name); | ||||||
|  |  | ||||||
|  |       if (saw_file (fn)) | ||||||
|  | 	continue; | ||||||
|  |  | ||||||
|  |       /* output DLL's name */ | ||||||
|  |       char *print_fn; | ||||||
|  |       if (!SearchPath (NULL, fn, NULL, sizeof (full_path), full_path, &dummy)) | ||||||
|  | 	{ | ||||||
|  | 	  print_fn = strdup ("not found"); | ||||||
|  | 	  printing = true; | ||||||
|  | 	} | ||||||
|  |       else if (!printing) | ||||||
|  | 	continue; | ||||||
|  |       else | ||||||
|  | 	{ | ||||||
|  | 	  print_fn = tocyg (full_path); | ||||||
|  | 	  strcat (print_fn, " (?)"); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |       printf ("\t%s => %s\n", (char *) fn, print_fn); | ||||||
|  |       free (print_fn); | ||||||
|  |     } | ||||||
|  |   #undef adr | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* load a file in RAM (memory-mapped) | ||||||
|  |    return pointer to loaded file | ||||||
|  |    0 if no success  */ | ||||||
|  | static void * | ||||||
|  | map_file (const char *filename) | ||||||
|  | { | ||||||
|  |   HANDLE hFile, hMapping; | ||||||
|  |   void *basepointer; | ||||||
|  |   if ((hFile = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, | ||||||
|  | 			   0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0)) == INVALID_HANDLE_VALUE) | ||||||
|  |     { | ||||||
|  |       fprintf (stderr, "couldn't open %s\n", filename); | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  |   if (!(hMapping = CreateFileMapping (hFile, 0, PAGE_READONLY | SEC_COMMIT, 0, 0, 0))) | ||||||
|  |     { | ||||||
|  |       fprintf (stderr, "CreateFileMapping failed with windows error %lu\n", GetLastError ()); | ||||||
|  |       CloseHandle (hFile); | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  |   if (!(basepointer = MapViewOfFile (hMapping, FILE_MAP_READ, 0, 0, 0))) | ||||||
|  |     { | ||||||
|  |       fprintf (stderr, "MapViewOfFile failed with windows error %lu\n", GetLastError ()); | ||||||
|  |       CloseHandle (hMapping); | ||||||
|  |       CloseHandle (hFile); | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  |    | ||||||
|  |   CloseHandle (hMapping); | ||||||
|  |   CloseHandle (hFile); | ||||||
|  |  | ||||||
|  |   return basepointer; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* this will return a pointer immediatly behind the DOS-header | ||||||
|  |    0 if error */ | ||||||
|  | static void * | ||||||
|  | skip_dos_stub (const IMAGE_DOS_HEADER *dos_ptr) | ||||||
|  | { | ||||||
|  |   /* look there's enough space for a DOS-header */ | ||||||
|  |   if (IsBadReadPtr (dos_ptr, sizeof (*dos_ptr))) | ||||||
|  |       { | ||||||
|  | 	fprintf (stderr, "not enough space for DOS-header\n"); | ||||||
|  | 	return 0; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |    /* validate MZ */ | ||||||
|  |    if (dos_ptr->e_magic != IMAGE_DOS_SIGNATURE) | ||||||
|  |       { | ||||||
|  | 	fprintf (stderr, "not a DOS-stub\n"); | ||||||
|  | 	return 0; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |   /* ok, then, go get it */ | ||||||
|  |   return (char*) dos_ptr + dos_ptr->e_lfanew; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* find the directory's section index given the RVA | ||||||
|  |    Returns -1 if impossible */ | ||||||
|  | static int | ||||||
|  | get_directory_index (const unsigned dir_rva, | ||||||
|  | 		     const unsigned dir_length, | ||||||
|  | 		     const int number_of_sections, | ||||||
|  | 		     const IMAGE_SECTION_HEADER *sections) | ||||||
|  | { | ||||||
|  |   int sect; | ||||||
|  |   for (sect = 0; sect < number_of_sections; sect++) | ||||||
|  |   { | ||||||
|  |     /* compare directory RVA to section RVA */ | ||||||
|  |     if (sections[sect].VirtualAddress <= dir_rva | ||||||
|  |        && dir_rva < sections[sect].VirtualAddress+sections[sect].SizeOfRawData) | ||||||
|  |       return sect; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* dump imports of a single file | ||||||
|  |    Returns 0 if successful, !=0 else */ | ||||||
|  | static int | ||||||
|  | process_file (const char *filename) | ||||||
|  | { | ||||||
|  |   void *basepointer;    /* Points to loaded PE file | ||||||
|  | 			 * This is memory mapped stuff | ||||||
|  | 			 */ | ||||||
|  |   int number_of_sections; | ||||||
|  |   DWORD import_rva;           /* RVA of import directory */ | ||||||
|  |   DWORD import_length;        /* length of import directory */ | ||||||
|  |   int import_index;           /* index of section with import directory */ | ||||||
|  |  | ||||||
|  |   /* ensure byte-alignment for struct tag_header */ | ||||||
|  |   #include <pshpack1.h> | ||||||
|  |  | ||||||
|  |   const struct tag_header | ||||||
|  |     { | ||||||
|  |       DWORD signature; | ||||||
|  |       IMAGE_FILE_HEADER file_head; | ||||||
|  |       IMAGE_OPTIONAL_HEADER opt_head; | ||||||
|  |       IMAGE_SECTION_HEADER section_header[1];  /* this is an array of unknown length | ||||||
|  | 					          actual number in file_head.NumberOfSections | ||||||
|  | 					          if your compiler objects to it length 1 should work */ | ||||||
|  |     } *header; | ||||||
|  |  | ||||||
|  |   /* revert to regular alignment */ | ||||||
|  |   #include <poppack.h> | ||||||
|  |  | ||||||
|  |   head = NULL;			/* FIXME: memory leak */ | ||||||
|  |   printing = false; | ||||||
|  |  | ||||||
|  |   /* first, load file */ | ||||||
|  |   basepointer = map_file (filename); | ||||||
|  |   if (!basepointer) | ||||||
|  |       { | ||||||
|  | 	puts ("cannot load file"); | ||||||
|  | 	return 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |   /* get header pointer; validate a little bit */ | ||||||
|  |   header = (struct tag_header *) skip_dos_stub ((IMAGE_DOS_HEADER *) basepointer); | ||||||
|  |   if (!header) | ||||||
|  |       { | ||||||
|  | 	puts ("cannot skip DOS stub"); | ||||||
|  | 	UnmapViewOfFile (basepointer); | ||||||
|  | 	return 2; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |   /* look there's enough space for PE headers */ | ||||||
|  |   if (IsBadReadPtr (header, sizeof (*header))) | ||||||
|  |       { | ||||||
|  | 	puts ("not enough space for PE headers"); | ||||||
|  | 	UnmapViewOfFile (basepointer); | ||||||
|  | 	return 3; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |   /* validate PE signature */ | ||||||
|  |   if (header->signature!=IMAGE_NT_SIGNATURE) | ||||||
|  |       { | ||||||
|  | 	puts ("not a PE file"); | ||||||
|  | 	UnmapViewOfFile (basepointer); | ||||||
|  | 	return 4; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |   /* get number of sections */ | ||||||
|  |   number_of_sections = header->file_head.NumberOfSections; | ||||||
|  |  | ||||||
|  |   /* check there are sections... */ | ||||||
|  |   if (number_of_sections<1) | ||||||
|  |       { | ||||||
|  | 	UnmapViewOfFile (basepointer); | ||||||
|  | 	return 5; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |   /* validate there's enough space for section headers */ | ||||||
|  |   if (IsBadReadPtr (header->section_header, number_of_sections*sizeof (IMAGE_SECTION_HEADER))) | ||||||
|  |       { | ||||||
|  | 	puts ("not enough space for section headers"); | ||||||
|  | 	UnmapViewOfFile (basepointer); | ||||||
|  | 	return 6; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |   /* get RVA and length of import directory */ | ||||||
|  |   import_rva = header->opt_head.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; | ||||||
|  |   import_length = header->opt_head.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size; | ||||||
|  |  | ||||||
|  |   /* check there's stuff to care about */ | ||||||
|  |   if (!import_rva || !import_length) | ||||||
|  |       { | ||||||
|  | 	UnmapViewOfFile (basepointer); | ||||||
|  | 	return 0;       /* success! */ | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   /* get import directory pointer */ | ||||||
|  |   import_index = get_directory_index (import_rva,import_length,number_of_sections,header->section_header); | ||||||
|  |  | ||||||
|  |   /* check directory was found */ | ||||||
|  |   if (import_index <0) | ||||||
|  |       { | ||||||
|  | 	puts ("couldn't find import directory in sections"); | ||||||
|  | 	UnmapViewOfFile (basepointer); | ||||||
|  | 	return 7; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |   /* ok, we've found the import directory... action! */ | ||||||
|  |   { | ||||||
|  |       /* The pointer to the start of the import directory's section */ | ||||||
|  |     const void *section_address = (char*) basepointer + header->section_header[import_index].PointerToRawData; | ||||||
|  |     if (dump_import_directory (section_address, | ||||||
|  | 			     header->section_header[import_index].VirtualAddress, | ||||||
|  | 				      /* the last parameter is the pointer to the import directory: | ||||||
|  | 				         section address + (import RVA - section RVA) | ||||||
|  | 				         The difference is the offset of the import directory in the section */ | ||||||
|  | 			     (const IMAGE_IMPORT_DESCRIPTOR *) ((char *) section_address+import_rva-header->section_header[import_index].VirtualAddress))) | ||||||
|  |       { | ||||||
|  | 	UnmapViewOfFile (basepointer); | ||||||
|  | 	return 8; | ||||||
|  |       } | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   UnmapViewOfFile (basepointer); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user