ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

module.c
Go to the documentation of this file.
00001 /*
00002  * File module.c - module handling for the wine debugger
00003  *
00004  * Copyright (C) 1993,      Eric Youngdale.
00005  *       2000-2007, Eric Pouech
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this library; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00020  */
00021 
00022 #include "config.h"
00023 #include <stdlib.h>
00024 #include <stdio.h>
00025 #include <string.h>
00026 #include <assert.h>
00027 
00028 #include "dbghelp_private.h"
00029 #include "psapi.h"
00030 #include "winternl.h"
00031 #include "wine/debug.h"
00032 
00033 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
00034 
00035 const WCHAR        S_ElfW[]         = {'<','e','l','f','>','\0'};
00036 const WCHAR        S_WineLoaderW[]  = {'<','w','i','n','e','-','l','o','a','d','e','r','>','\0'};
00037 static const WCHAR S_DotSoW[]       = {'.','s','o','\0'};
00038 static const WCHAR S_DotDylibW[]    = {'.','d','y','l','i','b','\0'};
00039 static const WCHAR S_DotPdbW[]      = {'.','p','d','b','\0'};
00040 static const WCHAR S_DotDbgW[]      = {'.','d','b','g','\0'};
00041 const WCHAR        S_SlashW[]       = {'/','\0'};
00042 
00043 static const WCHAR S_AcmW[] = {'.','a','c','m','\0'};
00044 static const WCHAR S_DllW[] = {'.','d','l','l','\0'};
00045 static const WCHAR S_DrvW[] = {'.','d','r','v','\0'};
00046 static const WCHAR S_ExeW[] = {'.','e','x','e','\0'};
00047 static const WCHAR S_OcxW[] = {'.','o','c','x','\0'};
00048 static const WCHAR S_VxdW[] = {'.','v','x','d','\0'};
00049 static const WCHAR * const ext[] = {S_AcmW, S_DllW, S_DrvW, S_ExeW, S_OcxW, S_VxdW, NULL};
00050 
00051 static int match_ext(const WCHAR* ptr, size_t len)
00052 {
00053     const WCHAR* const *e;
00054     size_t      l;
00055 
00056     for (e = ext; *e; e++)
00057     {
00058         l = strlenW(*e);
00059         if (l >= len) return FALSE;
00060         if (strncmpiW(&ptr[len - l], *e, l)) continue;
00061         return l;
00062     }
00063     return 0;
00064 }
00065 
00066 static const WCHAR* get_filename(const WCHAR* name, const WCHAR* endptr)
00067 {
00068     const WCHAR*        ptr;
00069 
00070     if (!endptr) endptr = name + strlenW(name);
00071     for (ptr = endptr - 1; ptr >= name; ptr--)
00072     {
00073         if (*ptr == '/' || *ptr == '\\') break;
00074     }
00075     return ++ptr;
00076 }
00077 
00078 static void module_fill_module(const WCHAR* in, WCHAR* out, size_t size)
00079 {
00080     const WCHAR *loader = get_wine_loader_name();
00081     const WCHAR *ptr, *endptr;
00082     size_t      len, l;
00083 
00084     ptr = get_filename(in, endptr = in + strlenW(in));
00085     len = min(endptr - ptr, size - 1);
00086     memcpy(out, ptr, len * sizeof(WCHAR));
00087     out[len] = '\0';
00088     if (len > 4 && (l = match_ext(out, len)))
00089         out[len - l] = '\0';
00090     else if (len > strlenW(loader) && !strcmpiW(out + len - strlenW(loader), loader))
00091         lstrcpynW(out, S_WineLoaderW, size);
00092     else
00093     {
00094         if (len > 3 && !strcmpiW(&out[len - 3], S_DotSoW) &&
00095             (l = match_ext(out, len - 3)))
00096             strcpyW(&out[len - l - 3], S_ElfW);
00097     }
00098     while ((*out = tolowerW(*out))) out++;
00099 }
00100 
00101 void module_set_module(struct module* module, const WCHAR* name)
00102 {
00103     module_fill_module(name, module->module.ModuleName, sizeof(module->module.ModuleName));
00104     WideCharToMultiByte(CP_ACP, 0, module->module.ModuleName, -1,
00105                         module->module_name, sizeof(module->module_name),
00106                         NULL, NULL);
00107 }
00108 
00109 const WCHAR *get_wine_loader_name(void)
00110 {
00111     static const int is_win64 = sizeof(void *) > sizeof(int); /* FIXME: should depend on target process */
00112     static const WCHAR wineW[] = {'w','i','n','e',0};
00113     static const WCHAR suffixW[] = {'6','4',0};
00114     static const WCHAR *loader;
00115 
00116     if (!loader)
00117     {
00118         WCHAR *p, *buffer;
00119         const char *ptr;
00120 
00121         /* All binaries are loaded with WINELOADER (if run from tree) or by the
00122          * main executable
00123          */
00124         if ((ptr = getenv("WINELOADER")))
00125         {
00126             DWORD len = 2 + MultiByteToWideChar( CP_UNIXCP, 0, ptr, -1, NULL, 0 );
00127             buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
00128             MultiByteToWideChar( CP_UNIXCP, 0, ptr, -1, buffer, len );
00129         }
00130         else
00131         {
00132             buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(wineW) + 2 * sizeof(WCHAR) );
00133             strcpyW( buffer, wineW );
00134         }
00135         p = buffer + strlenW( buffer ) - strlenW( suffixW );
00136         if (p > buffer && !strcmpW( p, suffixW ))
00137         {
00138             if (!is_win64) *p = 0;
00139         }
00140         else if (is_win64) strcatW( buffer, suffixW );
00141 
00142         TRACE( "returning %s\n", debugstr_w(buffer) );
00143         loader = buffer;
00144     }
00145     return loader;
00146 }
00147 
00148 static const char*      get_module_type(enum module_type type, BOOL virtual)
00149 {
00150     switch (type)
00151     {
00152     case DMT_ELF: return virtual ? "Virtual ELF" : "ELF";
00153     case DMT_PE: return virtual ? "Virtual PE" : "PE";
00154     case DMT_MACHO: return virtual ? "Virtual Mach-O" : "Mach-O";
00155     default: return "---";
00156     }
00157 }
00158 
00159 /***********************************************************************
00160  * Creates and links a new module to a process
00161  */
00162 struct module* module_new(struct process* pcs, const WCHAR* name,
00163                           enum module_type type, BOOL virtual,
00164                           DWORD64 mod_addr, DWORD64 size,
00165                           unsigned long stamp, unsigned long checksum)
00166 {
00167     struct module*      module;
00168     unsigned            i;
00169 
00170     assert(type == DMT_ELF || type == DMT_PE || type == DMT_MACHO);
00171     if (!(module = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*module))))
00172     return NULL;
00173 
00174     module->next = pcs->lmodules;
00175     pcs->lmodules = module;
00176 
00177     TRACE("=> %s %s-%s %s\n",
00178           get_module_type(type, virtual),
00179       wine_dbgstr_longlong(mod_addr), wine_dbgstr_longlong(mod_addr + size),
00180           debugstr_w(name));
00181 
00182     pool_init(&module->pool, 65536);
00183 
00184     module->process = pcs;
00185     module->module.SizeOfStruct = sizeof(module->module);
00186     module->module.BaseOfImage = mod_addr;
00187     module->module.ImageSize = size;
00188     module_set_module(module, name);
00189     module->module.ImageName[0] = '\0';
00190     lstrcpynW(module->module.LoadedImageName, name, sizeof(module->module.LoadedImageName) / sizeof(WCHAR));
00191     module->module.SymType = SymNone;
00192     module->module.NumSyms = 0;
00193     module->module.TimeDateStamp = stamp;
00194     module->module.CheckSum = checksum;
00195 
00196     memset(module->module.LoadedPdbName, 0, sizeof(module->module.LoadedPdbName));
00197     module->module.CVSig = 0;
00198     memset(module->module.CVData, 0, sizeof(module->module.CVData));
00199     module->module.PdbSig = 0;
00200     memset(&module->module.PdbSig70, 0, sizeof(module->module.PdbSig70));
00201     module->module.PdbAge = 0;
00202     module->module.PdbUnmatched = FALSE;
00203     module->module.DbgUnmatched = FALSE;
00204     module->module.LineNumbers = FALSE;
00205     module->module.GlobalSymbols = FALSE;
00206     module->module.TypeInfo = FALSE;
00207     module->module.SourceIndexed = FALSE;
00208     module->module.Publics = FALSE;
00209 
00210     module->reloc_delta       = 0;
00211     module->type              = type;
00212     module->is_virtual        = virtual ? TRUE : FALSE;
00213     for (i = 0; i < DFI_LAST; i++) module->format_info[i] = NULL;
00214     module->sortlist_valid    = FALSE;
00215     module->sorttab_size      = 0;
00216     module->addr_sorttab      = NULL;
00217     module->num_sorttab       = 0;
00218     module->num_symbols       = 0;
00219 
00220     vector_init(&module->vsymt, sizeof(struct symt*), 128);
00221     /* FIXME: this seems a bit too high (on a per module basis)
00222      * need some statistics about this
00223      */
00224     hash_table_init(&module->pool, &module->ht_symbols, 4096);
00225     hash_table_init(&module->pool, &module->ht_types,   4096);
00226     vector_init(&module->vtypes, sizeof(struct symt*),  32);
00227 
00228     module->sources_used      = 0;
00229     module->sources_alloc     = 0;
00230     module->sources           = 0;
00231     wine_rb_init(&module->sources_offsets_tree, &source_rb_functions);
00232 
00233     return module;
00234 }
00235 
00236 /***********************************************************************
00237  *  module_find_by_nameW
00238  *
00239  */
00240 struct module* module_find_by_nameW(const struct process* pcs, const WCHAR* name)
00241 {
00242     struct module*      module;
00243 
00244     for (module = pcs->lmodules; module; module = module->next)
00245     {
00246         if (!strcmpiW(name, module->module.ModuleName)) return module;
00247     }
00248     SetLastError(ERROR_INVALID_NAME);
00249     return NULL;
00250 }
00251 
00252 struct module* module_find_by_nameA(const struct process* pcs, const char* name)
00253 {
00254     WCHAR wname[MAX_PATH];
00255 
00256     MultiByteToWideChar(CP_ACP, 0, name, -1, wname, sizeof(wname) / sizeof(WCHAR));
00257     return module_find_by_nameW(pcs, wname);
00258 }
00259 
00260 /***********************************************************************
00261  *  module_is_already_loaded
00262  *
00263  */
00264 struct module* module_is_already_loaded(const struct process* pcs, const WCHAR* name)
00265 {
00266     struct module*      module;
00267     const WCHAR*        filename;
00268 
00269     /* first compare the loaded image name... */
00270     for (module = pcs->lmodules; module; module = module->next)
00271     {
00272         if (!strcmpiW(name, module->module.LoadedImageName))
00273             return module;
00274     }
00275     /* then compare the standard filenames (without the path) ... */
00276     filename = get_filename(name, NULL);
00277     for (module = pcs->lmodules; module; module = module->next)
00278     {
00279         if (!strcmpiW(filename, get_filename(module->module.LoadedImageName, NULL)))
00280             return module;
00281     }
00282     SetLastError(ERROR_INVALID_NAME);
00283     return NULL;
00284 }
00285 
00286 /***********************************************************************
00287  *           module_get_container
00288  *
00289  */
00290 static struct module* module_get_container(const struct process* pcs,
00291                                     const struct module* inner)
00292 {
00293     struct module*      module;
00294      
00295     for (module = pcs->lmodules; module; module = module->next)
00296     {
00297         if (module != inner &&
00298             module->module.BaseOfImage <= inner->module.BaseOfImage &&
00299             module->module.BaseOfImage + module->module.ImageSize >=
00300             inner->module.BaseOfImage + inner->module.ImageSize)
00301             return module;
00302     }
00303     return NULL;
00304 }
00305 
00306 /***********************************************************************
00307  *           module_get_containee
00308  *
00309  */
00310 struct module* module_get_containee(const struct process* pcs, 
00311                                     const struct module* outter)
00312 {
00313     struct module*      module;
00314      
00315     for (module = pcs->lmodules; module; module = module->next)
00316     {
00317         if (module != outter &&
00318             outter->module.BaseOfImage <= module->module.BaseOfImage &&
00319             outter->module.BaseOfImage + outter->module.ImageSize >=
00320             module->module.BaseOfImage + module->module.ImageSize)
00321             return module;
00322     }
00323     return NULL;
00324 }
00325 
00326 /******************************************************************
00327  *      module_get_debug
00328  *
00329  * get the debug information from a module:
00330  * - if the module's type is deferred, then force loading of debug info (and return
00331  *   the module itself)
00332  * - if the module has no debug info and has an ELF container, then return the ELF
00333  *   container (and also force the ELF container's debug info loading if deferred)
00334  * - otherwise return the module itself if it has some debug info
00335  */
00336 BOOL module_get_debug(struct module_pair* pair)
00337 {
00338     IMAGEHLP_DEFERRED_SYMBOL_LOADW64    idslW64;
00339 
00340     if (!pair->requested) return FALSE;
00341     /* for a PE builtin, always get info from container */
00342     if (!(pair->effective = module_get_container(pair->pcs, pair->requested)))
00343         pair->effective = pair->requested;
00344     /* if deferred, force loading */
00345     if (pair->effective->module.SymType == SymDeferred)
00346     {
00347         BOOL ret;
00348         
00349         if (pair->effective->is_virtual) ret = FALSE;
00350         else switch (pair->effective->type)
00351         {
00352         case DMT_ELF:
00353             ret = elf_load_debug_info(pair->effective);
00354             break;
00355         case DMT_PE:
00356             idslW64.SizeOfStruct = sizeof(idslW64);
00357             idslW64.BaseOfImage = pair->effective->module.BaseOfImage;
00358             idslW64.CheckSum = pair->effective->module.CheckSum;
00359             idslW64.TimeDateStamp = pair->effective->module.TimeDateStamp;
00360             memcpy(idslW64.FileName, pair->effective->module.ImageName,
00361                    sizeof(pair->effective->module.ImageName));
00362             idslW64.Reparse = FALSE;
00363             idslW64.hFile = INVALID_HANDLE_VALUE;
00364 
00365             pcs_callback(pair->pcs, CBA_DEFERRED_SYMBOL_LOAD_START, &idslW64);
00366             ret = pe_load_debug_info(pair->pcs, pair->effective);
00367             pcs_callback(pair->pcs,
00368                          ret ? CBA_DEFERRED_SYMBOL_LOAD_COMPLETE : CBA_DEFERRED_SYMBOL_LOAD_FAILURE,
00369                          &idslW64);
00370             break;
00371         case DMT_MACHO:
00372             ret = macho_load_debug_info(pair->effective, NULL);
00373             break;
00374         default:
00375             ret = FALSE;
00376             break;
00377         }
00378         if (!ret) pair->effective->module.SymType = SymNone;
00379         assert(pair->effective->module.SymType != SymDeferred);
00380         pair->effective->module.NumSyms = pair->effective->ht_symbols.num_elts;
00381     }
00382     return pair->effective->module.SymType != SymNone;
00383 }
00384 
00385 /***********************************************************************
00386  *  module_find_by_addr
00387  *
00388  * either the addr where module is loaded, or any address inside the 
00389  * module
00390  */
00391 struct module* module_find_by_addr(const struct process* pcs, unsigned long addr, 
00392                                    enum module_type type)
00393 {
00394     struct module*      module;
00395     
00396     if (type == DMT_UNKNOWN)
00397     {
00398         if ((module = module_find_by_addr(pcs, addr, DMT_PE)) ||
00399             (module = module_find_by_addr(pcs, addr, DMT_ELF)) ||
00400             (module = module_find_by_addr(pcs, addr, DMT_MACHO)))
00401             return module;
00402     }
00403     else
00404     {
00405         for (module = pcs->lmodules; module; module = module->next)
00406         {
00407             if (type == module->type && addr >= module->module.BaseOfImage &&
00408                 addr < module->module.BaseOfImage + module->module.ImageSize) 
00409                 return module;
00410         }
00411     }
00412     SetLastError(ERROR_INVALID_ADDRESS);
00413     return module;
00414 }
00415 
00416 /******************************************************************
00417  *      module_is_container_loaded
00418  *
00419  * checks whether the native container, for a (supposed) PE builtin is
00420  * already loaded
00421  */
00422 static BOOL module_is_container_loaded(const struct process* pcs,
00423                                        const WCHAR* ImageName, DWORD64 base)
00424 {
00425     size_t              len;
00426     struct module*      module;
00427     PCWSTR              filename, modname;
00428 
00429     if (!base) return FALSE;
00430     filename = get_filename(ImageName, NULL);
00431     len = strlenW(filename);
00432 
00433     for (module = pcs->lmodules; module; module = module->next)
00434     {
00435         if ((module->type == DMT_ELF || module->type == DMT_MACHO) &&
00436             base >= module->module.BaseOfImage &&
00437             base < module->module.BaseOfImage + module->module.ImageSize)
00438         {
00439             modname = get_filename(module->module.LoadedImageName, NULL);
00440             if (!strncmpiW(modname, filename, len) &&
00441                 !memcmp(modname + len, S_DotSoW, 3 * sizeof(WCHAR)))
00442             {
00443                 return TRUE;
00444             }
00445         }
00446     }
00447     /* likely a native PE module */
00448     WARN("Couldn't find container for %s\n", debugstr_w(ImageName));
00449     return FALSE;
00450 }
00451 
00452 /******************************************************************
00453  *      module_get_type_by_name
00454  *
00455  * Guesses a filename type from its extension
00456  */
00457 enum module_type module_get_type_by_name(const WCHAR* name)
00458 {
00459     int loader_len, len = strlenW(name);
00460     const WCHAR *loader;
00461 
00462     /* Skip all version extensions (.[digits]) regex: "(\.\d+)*$" */
00463     do
00464     {
00465         int i = len;
00466 
00467         while (i && isdigit(name[i - 1])) i--;
00468 
00469         if (i && name[i - 1] == '.')
00470             len = i - 1;
00471         else
00472             break;
00473     } while (len);
00474 
00475     /* check for terminating .so or .so.[digit] */
00476     /* FIXME: Can't rely solely on extension; have to check magic or
00477      *        stop using .so on Mac OS X.  For now, base on platform. */
00478     if (len > 3 && !memcmp(name + len - 3, S_DotSoW, 3))
00479 #ifdef __APPLE__
00480         return DMT_MACHO;
00481 #else
00482         return DMT_ELF;
00483 #endif
00484 
00485     if (len > 6 && !strncmpiW(name + len - 6, S_DotDylibW, 6))
00486         return DMT_MACHO;
00487 
00488     if (len > 4 && !strncmpiW(name + len - 4, S_DotPdbW, 4))
00489         return DMT_PDB;
00490 
00491     if (len > 4 && !strncmpiW(name + len - 4, S_DotDbgW, 4))
00492         return DMT_DBG;
00493 
00494     /* wine is also a native module (Mach-O on Mac OS X, ELF elsewhere) */
00495     loader = get_wine_loader_name();
00496     loader_len = strlenW( loader );
00497     if ((len == loader_len || (len > loader_len && name[len - loader_len - 1] == '/')) &&
00498         !strcmpiW(name + len - loader_len, loader))
00499     {
00500 #ifdef __APPLE__
00501         return DMT_MACHO;
00502 #else
00503         return DMT_ELF;
00504 #endif
00505     }
00506     return DMT_PE;
00507 }
00508 
00509 /******************************************************************
00510  *                      refresh_module_list
00511  */
00512 static BOOL refresh_module_list(struct process* pcs)
00513 {
00514     /* force transparent ELF and Mach-O loading / unloading */
00515     return elf_synchronize_module_list(pcs) || macho_synchronize_module_list(pcs);
00516 }
00517 
00518 /***********************************************************************
00519  *          SymLoadModule (DBGHELP.@)
00520  */
00521 DWORD WINAPI SymLoadModule(HANDLE hProcess, HANDLE hFile, PCSTR ImageName,
00522                            PCSTR ModuleName, DWORD BaseOfDll, DWORD SizeOfDll)
00523 {
00524     return SymLoadModuleEx(hProcess, hFile, ImageName, ModuleName, BaseOfDll,
00525                            SizeOfDll, NULL, 0);
00526 }
00527 
00528 /***********************************************************************
00529  *          SymLoadModuleEx (DBGHELP.@)
00530  */
00531 DWORD64 WINAPI  SymLoadModuleEx(HANDLE hProcess, HANDLE hFile, PCSTR ImageName,
00532                                 PCSTR ModuleName, DWORD64 BaseOfDll, DWORD DllSize,
00533                                 PMODLOAD_DATA Data, DWORD Flags)
00534 {
00535     PWSTR       wImageName, wModuleName;
00536     unsigned    len;
00537     DWORD64     ret;
00538 
00539     TRACE("(%p %p %s %s %s %08x %p %08x)\n",
00540           hProcess, hFile, debugstr_a(ImageName), debugstr_a(ModuleName),
00541           wine_dbgstr_longlong(BaseOfDll), DllSize, Data, Flags);
00542 
00543     if (ImageName)
00544     {
00545         len = MultiByteToWideChar(CP_ACP, 0, ImageName, -1, NULL, 0);
00546         wImageName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
00547         MultiByteToWideChar(CP_ACP, 0, ImageName, -1, wImageName, len);
00548     }
00549     else wImageName = NULL;
00550     if (ModuleName)
00551     {
00552         len = MultiByteToWideChar(CP_ACP, 0, ModuleName, -1, NULL, 0);
00553         wModuleName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
00554         MultiByteToWideChar(CP_ACP, 0, ModuleName, -1, wModuleName, len);
00555     }
00556     else wModuleName = NULL;
00557 
00558     ret = SymLoadModuleExW(hProcess, hFile, wImageName, wModuleName,
00559                           BaseOfDll, DllSize, Data, Flags);
00560     HeapFree(GetProcessHeap(), 0, wImageName);
00561     HeapFree(GetProcessHeap(), 0, wModuleName);
00562     return ret;
00563 }
00564 
00565 /***********************************************************************
00566  *          SymLoadModuleExW (DBGHELP.@)
00567  */
00568 DWORD64 WINAPI  SymLoadModuleExW(HANDLE hProcess, HANDLE hFile, PCWSTR wImageName,
00569                                  PCWSTR wModuleName, DWORD64 BaseOfDll, DWORD SizeOfDll,
00570                                  PMODLOAD_DATA Data, DWORD Flags)
00571 {
00572     struct process*     pcs;
00573     struct module*  module = NULL;
00574 
00575     TRACE("(%p %p %s %s %s %08x %p %08x)\n",
00576           hProcess, hFile, debugstr_w(wImageName), debugstr_w(wModuleName),
00577           wine_dbgstr_longlong(BaseOfDll), SizeOfDll, Data, Flags);
00578 
00579     if (Data)
00580         FIXME("Unsupported load data parameter %p for %s\n",
00581               Data, debugstr_w(wImageName));
00582     if (!validate_addr64(BaseOfDll)) return FALSE;
00583 
00584     if (!(pcs = process_find_by_handle(hProcess))) return FALSE;
00585 
00586     if (Flags & SLMFLAG_VIRTUAL)
00587     {
00588         if (!wImageName) return FALSE;
00589         module = module_new(pcs, wImageName, module_get_type_by_name(wImageName),
00590                             TRUE, BaseOfDll, SizeOfDll, 0, 0);
00591         if (!module) return FALSE;
00592         if (wModuleName) module_set_module(module, wModuleName);
00593         module->module.SymType = SymVirtual;
00594 
00595         return TRUE;
00596     }
00597     if (Flags & ~(SLMFLAG_VIRTUAL))
00598         FIXME("Unsupported Flags %08x for %s\n", Flags, debugstr_w(wImageName));
00599 
00600     refresh_module_list(pcs);
00601 
00602     /* this is a Wine extension to the API just to redo the synchronisation */
00603     if (!wImageName && !hFile) return 0;
00604 
00605     /* check if the module is already loaded, or if it's a builtin PE module with
00606      * an containing ELF module
00607      */
00608     if (wImageName)
00609     {
00610         module = module_is_already_loaded(pcs, wImageName);
00611         if (!module && module_is_container_loaded(pcs, wImageName, BaseOfDll))
00612         {
00613             /* force the loading of DLL as builtin */
00614             module = pe_load_builtin_module(pcs, wImageName, BaseOfDll, SizeOfDll);
00615         }
00616     }
00617     if (!module)
00618     {
00619         /* otherwise, try a regular PE module */
00620         if (!(module = pe_load_native_module(pcs, wImageName, hFile, BaseOfDll, SizeOfDll)) &&
00621             wImageName)
00622         {
00623             /* and finally an ELF or Mach-O module */
00624             switch (module_get_type_by_name(wImageName))
00625             {
00626                 case DMT_ELF:
00627                     module = elf_load_module(pcs, wImageName, BaseOfDll);
00628                     break;
00629                 case DMT_MACHO:
00630                     module = macho_load_module(pcs, wImageName, BaseOfDll);
00631                     break;
00632                 default:
00633                     /* Ignored */
00634                     break;
00635             }
00636         }
00637     }
00638     if (!module)
00639     {
00640         WARN("Couldn't locate %s\n", debugstr_w(wImageName));
00641         return 0;
00642     }
00643     module->module.NumSyms = module->ht_symbols.num_elts;
00644     /* by default module_new fills module.ModuleName from a derivation
00645      * of LoadedImageName. Overwrite it, if we have better information
00646      */
00647     if (wModuleName)
00648         module_set_module(module, wModuleName);
00649     if (wImageName)
00650         lstrcpynW(module->module.ImageName, wImageName,
00651               sizeof(module->module.ImageName) / sizeof(WCHAR));
00652 
00653     return module->module.BaseOfImage;
00654 }
00655 
00656 /***********************************************************************
00657  *                     SymLoadModule64 (DBGHELP.@)
00658  */
00659 DWORD64 WINAPI SymLoadModule64(HANDLE hProcess, HANDLE hFile, PCSTR ImageName,
00660                                PCSTR ModuleName, DWORD64 BaseOfDll, DWORD SizeOfDll)
00661 {
00662     if (!validate_addr64(BaseOfDll)) return FALSE;
00663     return SymLoadModule(hProcess, hFile, ImageName, ModuleName, (DWORD)BaseOfDll, SizeOfDll);
00664 }
00665 
00666 /******************************************************************
00667  *      module_remove
00668  *
00669  */
00670 BOOL module_remove(struct process* pcs, struct module* module)
00671 {
00672     struct module_format*modfmt;
00673     struct module**     p;
00674     unsigned            i;
00675 
00676     TRACE("%s (%p)\n", debugstr_w(module->module.ModuleName), module);
00677 
00678     for (i = 0; i < DFI_LAST; i++)
00679     {
00680         if ((modfmt = module->format_info[i]) && modfmt->remove)
00681             modfmt->remove(pcs, module->format_info[i]);
00682     }
00683     hash_table_destroy(&module->ht_symbols);
00684     hash_table_destroy(&module->ht_types);
00685     wine_rb_destroy(&module->sources_offsets_tree, NULL, NULL);
00686     HeapFree(GetProcessHeap(), 0, module->sources);
00687     HeapFree(GetProcessHeap(), 0, module->addr_sorttab);
00688     pool_destroy(&module->pool);
00689     /* native dbghelp doesn't invoke registered callback(,CBA_SYMBOLS_UNLOADED,) here
00690      * so do we
00691      */
00692     for (p = &pcs->lmodules; *p; p = &(*p)->next)
00693     {
00694         if (*p == module)
00695         {
00696             *p = module->next;
00697             HeapFree(GetProcessHeap(), 0, module);
00698             return TRUE;
00699         }
00700     }
00701     FIXME("This shouldn't happen\n");
00702     return FALSE;
00703 }
00704 
00705 /******************************************************************
00706  *      SymUnloadModule (DBGHELP.@)
00707  *
00708  */
00709 BOOL WINAPI SymUnloadModule(HANDLE hProcess, DWORD BaseOfDll)
00710 {
00711     struct process*     pcs;
00712     struct module*      module;
00713 
00714     pcs = process_find_by_handle(hProcess);
00715     if (!pcs) return FALSE;
00716     module = module_find_by_addr(pcs, BaseOfDll, DMT_UNKNOWN);
00717     if (!module) return FALSE;
00718     return module_remove(pcs, module);
00719 }
00720 
00721 /******************************************************************
00722  *      SymUnloadModule64 (DBGHELP.@)
00723  *
00724  */
00725 BOOL WINAPI SymUnloadModule64(HANDLE hProcess, DWORD64 BaseOfDll)
00726 {
00727     struct process*     pcs;
00728     struct module*      module;
00729 
00730     pcs = process_find_by_handle(hProcess);
00731     if (!pcs) return FALSE;
00732     if (!validate_addr64(BaseOfDll)) return FALSE;
00733     module = module_find_by_addr(pcs, (DWORD)BaseOfDll, DMT_UNKNOWN);
00734     if (!module) return FALSE;
00735     return module_remove(pcs, module);
00736 }
00737 
00738 /******************************************************************
00739  *      SymEnumerateModules (DBGHELP.@)
00740  *
00741  */
00742 struct enum_modW64_32
00743 {
00744     PSYM_ENUMMODULES_CALLBACK   cb;
00745     PVOID                       user;
00746     char                        module[MAX_PATH];
00747 };
00748 
00749 static BOOL CALLBACK enum_modW64_32(PCWSTR name, DWORD64 base, PVOID user)
00750 {
00751     struct enum_modW64_32*      x = user;
00752 
00753     WideCharToMultiByte(CP_ACP, 0, name, -1, x->module, sizeof(x->module), NULL, NULL);
00754     return x->cb(x->module, (DWORD)base, x->user);
00755 }
00756 
00757 BOOL  WINAPI SymEnumerateModules(HANDLE hProcess,
00758                                  PSYM_ENUMMODULES_CALLBACK EnumModulesCallback,  
00759                                  PVOID UserContext)
00760 {
00761     struct enum_modW64_32       x;
00762 
00763     x.cb = EnumModulesCallback;
00764     x.user = UserContext;
00765 
00766     return SymEnumerateModulesW64(hProcess, enum_modW64_32, &x);
00767 }
00768 
00769 /******************************************************************
00770  *      SymEnumerateModules64 (DBGHELP.@)
00771  *
00772  */
00773 struct enum_modW64_64
00774 {
00775     PSYM_ENUMMODULES_CALLBACK64 cb;
00776     PVOID                       user;
00777     char                        module[MAX_PATH];
00778 };
00779 
00780 static BOOL CALLBACK enum_modW64_64(PCWSTR name, DWORD64 base, PVOID user)
00781 {
00782     struct enum_modW64_64*      x = user;
00783 
00784     WideCharToMultiByte(CP_ACP, 0, name, -1, x->module, sizeof(x->module), NULL, NULL);
00785     return x->cb(x->module, base, x->user);
00786 }
00787 
00788 BOOL  WINAPI SymEnumerateModules64(HANDLE hProcess,
00789                                    PSYM_ENUMMODULES_CALLBACK64 EnumModulesCallback,  
00790                                    PVOID UserContext)
00791 {
00792     struct enum_modW64_64       x;
00793 
00794     x.cb = EnumModulesCallback;
00795     x.user = UserContext;
00796 
00797     return SymEnumerateModulesW64(hProcess, enum_modW64_64, &x);
00798 }
00799 
00800 /******************************************************************
00801  *      SymEnumerateModulesW64 (DBGHELP.@)
00802  *
00803  */
00804 BOOL  WINAPI SymEnumerateModulesW64(HANDLE hProcess,
00805                                     PSYM_ENUMMODULES_CALLBACKW64 EnumModulesCallback,
00806                                     PVOID UserContext)
00807 {
00808     struct process*     pcs = process_find_by_handle(hProcess);
00809     struct module*      module;
00810 
00811     if (!pcs) return FALSE;
00812     
00813     for (module = pcs->lmodules; module; module = module->next)
00814     {
00815         if (!(dbghelp_options & SYMOPT_WINE_WITH_NATIVE_MODULES) &&
00816             (module->type == DMT_ELF || module->type == DMT_MACHO))
00817             continue;
00818         if (!EnumModulesCallback(module->module.ModuleName,
00819                                  module->module.BaseOfImage, UserContext))
00820             break;
00821     }
00822     return TRUE;
00823 }
00824 
00825 /******************************************************************
00826  *      EnumerateLoadedModules64 (DBGHELP.@)
00827  *
00828  */
00829 struct enum_load_modW64_64
00830 {
00831     PENUMLOADED_MODULES_CALLBACK64      cb;
00832     PVOID                               user;
00833     char                                module[MAX_PATH];
00834 };
00835 
00836 static BOOL CALLBACK enum_load_modW64_64(PCWSTR name, DWORD64 base, ULONG size,
00837                                          PVOID user)
00838 {
00839     struct enum_load_modW64_64* x = user;
00840 
00841     WideCharToMultiByte(CP_ACP, 0, name, -1, x->module, sizeof(x->module), NULL, NULL);
00842     return x->cb(x->module, base, size, x->user);
00843 }
00844 
00845 BOOL  WINAPI EnumerateLoadedModules64(HANDLE hProcess,
00846                                       PENUMLOADED_MODULES_CALLBACK64 EnumLoadedModulesCallback,
00847                                       PVOID UserContext)
00848 {
00849     struct enum_load_modW64_64  x;
00850 
00851     x.cb = EnumLoadedModulesCallback;
00852     x.user = UserContext;
00853 
00854     return EnumerateLoadedModulesW64(hProcess, enum_load_modW64_64, &x);
00855 }
00856 
00857 /******************************************************************
00858  *      EnumerateLoadedModules (DBGHELP.@)
00859  *
00860  */
00861 struct enum_load_modW64_32
00862 {
00863     PENUMLOADED_MODULES_CALLBACK        cb;
00864     PVOID                               user;
00865     char                                module[MAX_PATH];
00866 };
00867 
00868 static BOOL CALLBACK enum_load_modW64_32(PCWSTR name, DWORD64 base, ULONG size,
00869                                          PVOID user)
00870 {
00871     struct enum_load_modW64_32* x = user;
00872     WideCharToMultiByte(CP_ACP, 0, name, -1, x->module, sizeof(x->module), NULL, NULL);
00873     return x->cb(x->module, (DWORD)base, size, x->user);
00874 }
00875 
00876 BOOL  WINAPI EnumerateLoadedModules(HANDLE hProcess,
00877                                     PENUMLOADED_MODULES_CALLBACK EnumLoadedModulesCallback,
00878                                     PVOID UserContext)
00879 {
00880     struct enum_load_modW64_32  x;
00881 
00882     x.cb = EnumLoadedModulesCallback;
00883     x.user = UserContext;
00884 
00885     return EnumerateLoadedModulesW64(hProcess, enum_load_modW64_32, &x);
00886 }
00887 
00888 /******************************************************************
00889  *      EnumerateLoadedModulesW64 (DBGHELP.@)
00890  *
00891  */
00892 BOOL  WINAPI EnumerateLoadedModulesW64(HANDLE hProcess,
00893                                        PENUMLOADED_MODULES_CALLBACKW64 EnumLoadedModulesCallback,
00894                                        PVOID UserContext)
00895 {
00896     HMODULE*    hMods;
00897     WCHAR       baseW[256], modW[256];
00898     DWORD       i, sz;
00899     MODULEINFO  mi;
00900 
00901     hMods = HeapAlloc(GetProcessHeap(), 0, 256 * sizeof(hMods[0]));
00902     if (!hMods) return FALSE;
00903 
00904     if (!EnumProcessModules(hProcess, hMods, 256 * sizeof(hMods[0]), &sz))
00905     {
00906         /* hProcess should also be a valid process handle !! */
00907         FIXME("If this happens, bump the number in mod\n");
00908         HeapFree(GetProcessHeap(), 0, hMods);
00909         return FALSE;
00910     }
00911     sz /= sizeof(HMODULE);
00912     for (i = 0; i < sz; i++)
00913     {
00914         if (!GetModuleInformation(hProcess, hMods[i], &mi, sizeof(mi)) ||
00915             !GetModuleBaseNameW(hProcess, hMods[i], baseW, sizeof(baseW) / sizeof(WCHAR)))
00916             continue;
00917         module_fill_module(baseW, modW, sizeof(modW) / sizeof(CHAR));
00918         EnumLoadedModulesCallback(modW, (DWORD_PTR)mi.lpBaseOfDll, mi.SizeOfImage,
00919                                   UserContext);
00920     }
00921     HeapFree(GetProcessHeap(), 0, hMods);
00922 
00923     return sz != 0 && i == sz;
00924 }
00925 
00926 /******************************************************************
00927  *      SymGetModuleInfo (DBGHELP.@)
00928  *
00929  */
00930 BOOL  WINAPI SymGetModuleInfo(HANDLE hProcess, DWORD dwAddr,
00931                               PIMAGEHLP_MODULE ModuleInfo)
00932 {
00933     IMAGEHLP_MODULE     mi;
00934     IMAGEHLP_MODULEW64  miw64;
00935 
00936     if (sizeof(mi) < ModuleInfo->SizeOfStruct) FIXME("Wrong size\n");
00937 
00938     miw64.SizeOfStruct = sizeof(miw64);
00939     if (!SymGetModuleInfoW64(hProcess, dwAddr, &miw64)) return FALSE;
00940 
00941     mi.SizeOfStruct  = miw64.SizeOfStruct;
00942     mi.BaseOfImage   = miw64.BaseOfImage;
00943     mi.ImageSize     = miw64.ImageSize;
00944     mi.TimeDateStamp = miw64.TimeDateStamp;
00945     mi.CheckSum      = miw64.CheckSum;
00946     mi.NumSyms       = miw64.NumSyms;
00947     mi.SymType       = miw64.SymType;
00948     WideCharToMultiByte(CP_ACP, 0, miw64.ModuleName, -1,
00949                         mi.ModuleName, sizeof(mi.ModuleName), NULL, NULL);
00950     WideCharToMultiByte(CP_ACP, 0, miw64.ImageName, -1,
00951                         mi.ImageName, sizeof(mi.ImageName), NULL, NULL);
00952     WideCharToMultiByte(CP_ACP, 0, miw64.LoadedImageName, -1,
00953                         mi.LoadedImageName, sizeof(mi.LoadedImageName), NULL, NULL);
00954 
00955     memcpy(ModuleInfo, &mi, ModuleInfo->SizeOfStruct);
00956 
00957     return TRUE;
00958 }
00959 
00960 /******************************************************************
00961  *      SymGetModuleInfoW (DBGHELP.@)
00962  *
00963  */
00964 BOOL  WINAPI SymGetModuleInfoW(HANDLE hProcess, DWORD dwAddr,
00965                                PIMAGEHLP_MODULEW ModuleInfo)
00966 {
00967     IMAGEHLP_MODULEW64  miw64;
00968     IMAGEHLP_MODULEW    miw;
00969 
00970     if (sizeof(miw) < ModuleInfo->SizeOfStruct) FIXME("Wrong size\n");
00971 
00972     miw64.SizeOfStruct = sizeof(miw64);
00973     if (!SymGetModuleInfoW64(hProcess, dwAddr, &miw64)) return FALSE;
00974 
00975     miw.SizeOfStruct  = miw64.SizeOfStruct;
00976     miw.BaseOfImage   = miw64.BaseOfImage;
00977     miw.ImageSize     = miw64.ImageSize;
00978     miw.TimeDateStamp = miw64.TimeDateStamp;
00979     miw.CheckSum      = miw64.CheckSum;
00980     miw.NumSyms       = miw64.NumSyms;
00981     miw.SymType       = miw64.SymType;
00982     strcpyW(miw.ModuleName, miw64.ModuleName);
00983     strcpyW(miw.ImageName, miw64.ImageName);
00984     strcpyW(miw.LoadedImageName, miw64.LoadedImageName);
00985     memcpy(ModuleInfo, &miw, ModuleInfo->SizeOfStruct);
00986 
00987     return TRUE;
00988 }
00989 
00990 /******************************************************************
00991  *      SymGetModuleInfo64 (DBGHELP.@)
00992  *
00993  */
00994 BOOL  WINAPI SymGetModuleInfo64(HANDLE hProcess, DWORD64 dwAddr,
00995                                 PIMAGEHLP_MODULE64 ModuleInfo)
00996 {
00997     IMAGEHLP_MODULE64   mi64;
00998     IMAGEHLP_MODULEW64  miw64;
00999 
01000     if (sizeof(mi64) < ModuleInfo->SizeOfStruct)
01001     {
01002         SetLastError(ERROR_MOD_NOT_FOUND); /* NOTE: native returns this error */
01003         WARN("Wrong size %u\n", ModuleInfo->SizeOfStruct);
01004         return FALSE;
01005     }
01006 
01007     miw64.SizeOfStruct = sizeof(miw64);
01008     if (!SymGetModuleInfoW64(hProcess, dwAddr, &miw64)) return FALSE;
01009 
01010     mi64.SizeOfStruct  = miw64.SizeOfStruct;
01011     mi64.BaseOfImage   = miw64.BaseOfImage;
01012     mi64.ImageSize     = miw64.ImageSize;
01013     mi64.TimeDateStamp = miw64.TimeDateStamp;
01014     mi64.CheckSum      = miw64.CheckSum;
01015     mi64.NumSyms       = miw64.NumSyms;
01016     mi64.SymType       = miw64.SymType;
01017     WideCharToMultiByte(CP_ACP, 0, miw64.ModuleName, -1,
01018                         mi64.ModuleName, sizeof(mi64.ModuleName), NULL, NULL);
01019     WideCharToMultiByte(CP_ACP, 0, miw64.ImageName, -1,
01020                         mi64.ImageName, sizeof(mi64.ImageName), NULL, NULL);
01021     WideCharToMultiByte(CP_ACP, 0, miw64.LoadedImageName, -1,
01022                         mi64.LoadedImageName, sizeof(mi64.LoadedImageName), NULL, NULL);
01023     WideCharToMultiByte(CP_ACP, 0, miw64.LoadedPdbName, -1,
01024                         mi64.LoadedPdbName, sizeof(mi64.LoadedPdbName), NULL, NULL);
01025 
01026     mi64.CVSig         = miw64.CVSig;
01027     WideCharToMultiByte(CP_ACP, 0, miw64.CVData, -1,
01028                         mi64.CVData, sizeof(mi64.CVData), NULL, NULL);
01029     mi64.PdbSig        = miw64.PdbSig;
01030     mi64.PdbSig70      = miw64.PdbSig70;
01031     mi64.PdbAge        = miw64.PdbAge;
01032     mi64.PdbUnmatched  = miw64.PdbUnmatched;
01033     mi64.DbgUnmatched  = miw64.DbgUnmatched;
01034     mi64.LineNumbers   = miw64.LineNumbers;
01035     mi64.GlobalSymbols = miw64.GlobalSymbols;
01036     mi64.TypeInfo      = miw64.TypeInfo;
01037     mi64.SourceIndexed = miw64.SourceIndexed;
01038     mi64.Publics       = miw64.Publics;
01039 
01040     memcpy(ModuleInfo, &mi64, ModuleInfo->SizeOfStruct);
01041 
01042     return TRUE;
01043 }
01044 
01045 /******************************************************************
01046  *      SymGetModuleInfoW64 (DBGHELP.@)
01047  *
01048  */
01049 BOOL  WINAPI SymGetModuleInfoW64(HANDLE hProcess, DWORD64 dwAddr,
01050                                  PIMAGEHLP_MODULEW64 ModuleInfo)
01051 {
01052     struct process*     pcs = process_find_by_handle(hProcess);
01053     struct module*      module;
01054     IMAGEHLP_MODULEW64  miw64;
01055 
01056     TRACE("%p %s %p\n", hProcess, wine_dbgstr_longlong(dwAddr), ModuleInfo);
01057 
01058     if (!pcs) return FALSE;
01059     if (ModuleInfo->SizeOfStruct > sizeof(*ModuleInfo)) return FALSE;
01060     module = module_find_by_addr(pcs, dwAddr, DMT_UNKNOWN);
01061     if (!module) return FALSE;
01062 
01063     miw64 = module->module;
01064 
01065     /* update debug information from container if any */
01066     if (module->module.SymType == SymNone)
01067     {
01068         module = module_get_container(pcs, module);
01069         if (module && module->module.SymType != SymNone)
01070         {
01071             miw64.SymType = module->module.SymType;
01072             miw64.NumSyms = module->module.NumSyms;
01073         }
01074     }
01075     memcpy(ModuleInfo, &miw64, ModuleInfo->SizeOfStruct);
01076     return TRUE;
01077 }
01078 
01079 /***********************************************************************
01080  *      SymGetModuleBase (DBGHELP.@)
01081  */
01082 DWORD WINAPI SymGetModuleBase(HANDLE hProcess, DWORD dwAddr)
01083 {
01084     DWORD64     ret;
01085 
01086     ret = SymGetModuleBase64(hProcess, dwAddr);
01087     return validate_addr64(ret) ? ret : 0;
01088 }
01089 
01090 /***********************************************************************
01091  *      SymGetModuleBase64 (DBGHELP.@)
01092  */
01093 DWORD64 WINAPI SymGetModuleBase64(HANDLE hProcess, DWORD64 dwAddr)
01094 {
01095     struct process*     pcs = process_find_by_handle(hProcess);
01096     struct module*      module;
01097 
01098     if (!pcs) return 0;
01099     module = module_find_by_addr(pcs, dwAddr, DMT_UNKNOWN);
01100     if (!module) return 0;
01101     return module->module.BaseOfImage;
01102 }
01103 
01104 /******************************************************************
01105  *      module_reset_debug_info
01106  * Removes any debug information linked to a given module.
01107  */
01108 void module_reset_debug_info(struct module* module)
01109 {
01110     module->sortlist_valid = TRUE;
01111     module->sorttab_size = 0;
01112     module->addr_sorttab = NULL;
01113     module->num_sorttab = module->num_symbols = 0;
01114     hash_table_destroy(&module->ht_symbols);
01115     module->ht_symbols.num_buckets = 0;
01116     module->ht_symbols.buckets = NULL;
01117     hash_table_destroy(&module->ht_types);
01118     module->ht_types.num_buckets = 0;
01119     module->ht_types.buckets = NULL;
01120     module->vtypes.num_elts = 0;
01121     hash_table_destroy(&module->ht_symbols);
01122     module->sources_used = module->sources_alloc = 0;
01123     module->sources = NULL;
01124 }
01125 
01126 /******************************************************************
01127  *              SymRefreshModuleList (DBGHELP.@)
01128  */
01129 BOOL WINAPI SymRefreshModuleList(HANDLE hProcess)
01130 {
01131     struct process*     pcs;
01132 
01133     TRACE("(%p)\n", hProcess);
01134 
01135     if (!(pcs = process_find_by_handle(hProcess))) return FALSE;
01136 
01137     return refresh_module_list(pcs);
01138 }
01139 
01140 /***********************************************************************
01141  *      SymFunctionTableAccess (DBGHELP.@)
01142  */
01143 PVOID WINAPI SymFunctionTableAccess(HANDLE hProcess, DWORD AddrBase)
01144 {
01145     return SymFunctionTableAccess64(hProcess, AddrBase);
01146 }
01147 
01148 /***********************************************************************
01149  *      SymFunctionTableAccess64 (DBGHELP.@)
01150  */
01151 PVOID WINAPI SymFunctionTableAccess64(HANDLE hProcess, DWORD64 AddrBase)
01152 {
01153     struct process*     pcs = process_find_by_handle(hProcess);
01154     struct module*      module;
01155 
01156     if (!pcs || !dbghelp_current_cpu->find_runtime_function) return NULL;
01157     module = module_find_by_addr(pcs, AddrBase, DMT_UNKNOWN);
01158     if (!module) return NULL;
01159 
01160     return dbghelp_current_cpu->find_runtime_function(module, AddrBase);
01161 }

Generated on Fri May 25 2012 04:21:22 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.