Wine
- Wine User's Guide - WineHQ Wiki
- Wine Developer's Guide/Architecture Overview - WineHQ Wiki - VC程序员看看这个也是极好的,如Kernel32 DLL是在用户态的
Wine makes it possible to run Windows programs alongside any Unix-like operating system, particularly Linux. At its heart, Wine is an implementation of the Windows Application Programing Interface (API) library, acting as a bridge between the Windows program and Linux. Think of Wine as a compatibility layer, when a Windows program tries to perform a function that Linux doesn't normally understand, Wine will translate that program's instruction into one supported by the system. For example, if a program asks the system to create a Windows pushbutton or text-edit field, Wine will convert that instruction into its Linux equivalent in the form of a command to the window manager using the standard X11 protocol.
If you have access to the Windows program source code, Wine can also be used to recompile a program into a format that Linux can understand more easily. Wine is still needed to launch the program in its recompiled form, however there are many advantages to compiling a Windows program natively within Linux. For more information, see the Winelib User Guide.
You can simply invoke the wine command to get a small help message:
Usage: wine PROGRAM [ARGUMENTS...] Run the specified program wine --help Display this help and exit wine --version Output version information and exit
The first argument should be the name of the file you want wine to execute. Most binary Wine packages will associate Wine with .exe files for you. If that is the case, you should be able to simply double-click on the .exe file in your file manager, just like in Windows. You can also right-click on the file, choose "Run with", and choose "Wine".
Windows的.exe和.dll是PE (Portable Executable)格式,Linux的是ELF(Executable and Linkable Format)格式。可执行程序wine是ELF格式,wine去执行的notepad.exe是PE格式。
wine的源码里有多个名为*loader.c的,还有个pe.c:
* PE dumping utility switch (mach) { case IMAGE_FILE_MACHINE_UNKNOWN: return "Unknown"; case IMAGE_FILE_MACHINE_I860: return "i860"; case IMAGE_FILE_MACHINE_I386: return "i386"; case IMAGE_FILE_MACHINE_R3000: return "R3000"; case IMAGE_FILE_MACHINE_R4000: return "R4000"; case IMAGE_FILE_MACHINE_R10000: return "R10000"; case IMAGE_FILE_MACHINE_ALPHA: return "Alpha"; case IMAGE_FILE_MACHINE_POWERPC: return "PowerPC"; case IMAGE_FILE_MACHINE_AMD64: return "AMD64"; case IMAGE_FILE_MACHINE_IA64: return "IA64"; case IMAGE_FILE_MACHINE_ARM64: return "ARM64"; case IMAGE_FILE_MACHINE_ARM: return "ARM"; case IMAGE_FILE_MACHINE_ARMNT: return "ARMNT"; case IMAGE_FILE_MACHINE_THUMB: return "ARM Thumb"; }
The Mips R10000 is a dynamic, superscalar microprocessor that implements the 64-bit Mips 4 instruction set architecture. It fetches and decodes four instructions per cycle and dynamically issues them to five fully-pipelined, low-latency execution units. Instructions can be fetched and executed speculatively beyond branches. Quite possibly the world's fastest CPU when released, code-named the "T5" after Terminator 2 which featured graphics designed on Silicon Graphics systems, the MIPS R10000 (Do you sense the play on T-1000 ?) was released in late 1995 and was first avaliable on SGI's Indigo2 systems. 这个叫“终结者”。HP和Intel搞了EPIC(史诗), EPYC读音和EPIC一样吧?中国文化认为“名贱好养活” :-)
AMD Server 'EPYC' CPU Market Share. Starting with the server-side, AMD has reported a share of 8%, almost double compared to 2018. The stats from Mercury Research highlighted a market share of 4.5% till Q4 2019 so this is where we see a large desperate between what research firms are claiming and what AMD's own internal estimates are showing. [link]
名字太长不像是好主意:The Z8000 / Z80,000 / Z16C00 CPU homepage (kranenborg.org)
The R4000 is a microprocessor developed by MIPS Computer Systems that implements the MIPS III instruction set architecture. Officially announced on 1 October 1991, it was one of the first 64-bit microprocessors and the first MIPS III implementation. In the early 1990s, when RISC microprocessors were expected to replace CISC microprocessors such as the Intel i486, the R4000 was selected to be the microprocessor of the Advanced Computing Environment, an industry standard that intended to define a common RISC platform. ACE ultimately failed for a number of reasons, but the R4000 found success in the workstation and server markets.
The frequently asked questions about BogoMips (tldp.org) As a very approximate guide, the BogoMips can be calculated by:
System BogoMips Comparison Intel 8088 clock * 0.004 0.02 Intel/AMD 386SX clock * 0.14 0.8 Intel/AMD 386DX clock * 0.18 1 (definition) Motorola 68030 clock * 0.25 1.4 Cyrix/IBM 486 clock * 0.34 1.8 Intel Pentium clock * 0.40 2.2 Intel 486 clock * 0.50 2.8 AMD 5x86 clock * 0.50 2.8 Mips R4000/R4400 clock * 0.50 2.8 Motorola 68040 clock * 0.67 3.7 PowerPC 603 clock * 0.67 3.7 PowerPC 601 clock * 0.84 4.7 Alpha 21064/21064A clock * 0.99 5.5 Alpha 21066/21066A clock * 0.99 5.5 Alpha 21164/21164A clock * 0.99 5.5 Intel Pentium Pro clock * 0.99 5.5 Cyrix 5x86/6x86 clock * 1.00 5.6 Intel Pentium II/III clock * 1.00 5.6 AMD K7/Athlon clock * 1.00 5.6 Intel Celeron clock * 1.00 5.6 Intel Itanium clock * 1.00 5.6 Mips R4600 clock * 1.00 5.6 Intel Itanium 2 clock * 1.49 8.3 Alpha 21264 clock * 1.99 11.1 Centaur VIA clock * 1.99 11.1 AMD K5/K6/K6-2/K6-III clock * 2.00 11.1 AMD Athlon XP/Athlon 64 clock * 2.00 11.1 AMD Duron/Opteron clock * 2.00 11.1 UltraSparc II clock * 2.00 11.1 Pentium MMX clock * 2.00 11.1
Wine will not allow running native Windows drivers under Unix. This comes mainly because (look at the generic architecture schemas) Wine doesn't implement the kernel features of Windows (kernel here really means the kernel, not the KERNEL32 DLL), but rather sets up a proxy layer on top of the Unix kernel to provide the NTDLL and KERNEL32 features. This means that Wine doesn't provide the inner infrastructure to run native drivers, either from the Win9x family or from the NT family.
In other words, Wine will only be able to provide access to a specific device, if and only if, 1/ this device is supported in Unix (there is Unix-driver to talk to it), 2/ Wine has implemented the proxy code to make the glue between the API of a Windows driver, and the Unix interface of the Unix driver.
Wine, however, tries to implement in the various DLLs needing to access devices to do it through the standard Windows APIs for device drivers in user space. This is for example the case for the multimedia drivers, where Wine loads Wine builtin DLLs to talk to the OSS interface, or the ALSA interface. Those DLLs implement the same interface as any user space audio driver in Windows.
dlls/ntdll/unix/loader.c里"Usage: wine PROGRAM [ARGUMENTS...] Run the specified program\n",

NTSTATUS exec_wineloader( char **argv, int socketfd, const pe_image_info_t *pe_info ) { int is_child_64bit = (pe_info->cpu == CPU_x86_64 || pe_info->cpu == CPU_ARM64); ULONGLONG res_start = pe_info->base; ULONGLONG res_end = pe_info->base + pe_info->map_size; const char *loader = argv0; const char *loader_env = getenv( "WINELOADER" ); char preloader_reserve[64], socket_env[64]; if (!is_win64 ^ !is_child_64bit) { /* remap WINELOADER to the alternate 32/64-bit version if necessary */ if (loader_env) { int len = strlen( loader_env ); char *env = malloc( sizeof("WINELOADER=") + len + 2 ); if (!env) return STATUS_NO_MEMORY; strcpy( env, "WINELOADER=" ); strcat( env, loader_env ); if (is_child_64bit) { strcat( env, "64" ); } else { len += sizeof("WINELOADER=") - 1; if (!strcmp( env + len - 2, "64" )) env[len - 2] = 0; } loader = env; putenv( env ); } else loader = is_child_64bit ? "wine64" : "wine"; } signal( SIGPIPE, SIG_DFL ); sprintf( socket_env, "WINESERVERSOCKET=%u", socketfd ); sprintf( preloader_reserve, "WINEPRELOADRESERVE=%x%08x-%x%08x", (ULONG)(res_start >> 32), (ULONG)res_start, (ULONG)(res_end >> 32), (ULONG)res_end ); putenv( preloader_reserve ); putenv( socket_env ); return loader_exec( loader, argv, pe_info->cpu ); } static NTSTATUS map_so_dll( const IMAGE_NT_HEADERS *nt_descr, HMODULE module ) { static const char builtin_signature[32] = "Wine builtin DLL"; IMAGE_DATA_DIRECTORY *dir; IMAGE_DOS_HEADER *dos; IMAGE_NT_HEADERS *nt; IMAGE_SECTION_HEADER *sec; BYTE *addr = (BYTE *)module; DWORD code_start, code_end, data_start, data_end, align_mask; int delta, nb_sections = 2; /* code + data */ unsigned int i; DWORD size = (sizeof(IMAGE_DOS_HEADER) + sizeof(builtin_signature) + sizeof(IMAGE_NT_HEADERS) + nb_sections * sizeof(IMAGE_SECTION_HEADER)); if (anon_mmap_fixed( addr, size, PROT_READ | PROT_WRITE, 0 ) != addr) return STATUS_NO_MEMORY; dos = (IMAGE_DOS_HEADER *)addr; nt = (IMAGE_NT_HEADERS *)((BYTE *)(dos + 1) + sizeof(builtin_signature)); sec = (IMAGE_SECTION_HEADER *)(nt + 1); /* build the DOS and NT headers */ dos->e_magic = IMAGE_DOS_SIGNATURE; dos->e_cblp = 0x90; dos->e_cp = 3; dos->e_cparhdr = (sizeof(*dos) + 0xf) / 0x10; dos->e_minalloc = 0; dos->e_maxalloc = 0xffff; dos->e_ss = 0x0000; dos->e_sp = 0x00b8; dos->e_lfanew = sizeof(*dos) + sizeof(builtin_signature); *nt = *nt_descr; delta = (const BYTE *)nt_descr - addr; align_mask = nt->OptionalHeader.SectionAlignment - 1; code_start = (size + align_mask) & ~align_mask; data_start = delta & ~align_mask; #ifdef __APPLE__ { Dl_info dli; unsigned long data_size; /* need the mach_header, not the PE header, to give to getsegmentdata(3) */ dladdr(addr, &dli); code_end = getsegmentdata(dli.dli_fbase, "__DATA", &data_size) - addr; data_end = (code_end + data_size + align_mask) & ~align_mask; } #else code_end = data_start; data_end = (nt->OptionalHeader.SizeOfImage + delta + align_mask) & ~align_mask; #endif fixup_rva_ptrs( &nt->OptionalHeader.AddressOfEntryPoint, addr, 1 ); nt->FileHeader.NumberOfSections = nb_sections; nt->OptionalHeader.BaseOfCode = code_start; #ifndef _WIN64 nt->OptionalHeader.BaseOfData = data_start; #endif nt->OptionalHeader.SizeOfCode = code_end - code_start; nt->OptionalHeader.SizeOfInitializedData = data_end - data_start; nt->OptionalHeader.SizeOfUninitializedData = 0; nt->OptionalHeader.SizeOfImage = data_end; nt->OptionalHeader.ImageBase = (ULONG_PTR)addr; /* build the code section */ memcpy( sec->Name, ".text", sizeof(".text") ); sec->SizeOfRawData = code_end - code_start; sec->Misc.VirtualSize = sec->SizeOfRawData; sec->VirtualAddress = code_start; sec->PointerToRawData = code_start; sec->Characteristics = (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ); sec++; /* build the data section */ memcpy( sec->Name, ".data", sizeof(".data") ); sec->SizeOfRawData = data_end - data_start; sec->Misc.VirtualSize = sec->SizeOfRawData; sec->VirtualAddress = data_start; sec->PointerToRawData = data_start; sec->Characteristics = (IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ); sec++; for (i = 0; i < nt->OptionalHeader.NumberOfRvaAndSizes; i++) fixup_rva_dwords( &nt->OptionalHeader.DataDirectory[i].VirtualAddress, delta, 1 ); /* build the import directory */ dir = &nt->OptionalHeader.DataDirectory[IMAGE_FILE_IMPORT_DIRECTORY]; if (dir->Size) { IMAGE_IMPORT_DESCRIPTOR *imports = (IMAGE_IMPORT_DESCRIPTOR *)(addr + dir->VirtualAddress); while (imports->Name) { fixup_rva_dwords( &imports->u.OriginalFirstThunk, delta, 1 ); fixup_rva_dwords( &imports->Name, delta, 1 ); fixup_rva_dwords( &imports->FirstThunk, delta, 1 ); if (imports->u.OriginalFirstThunk) fixup_rva_names( (UINT_PTR *)(addr + imports->u.OriginalFirstThunk), delta ); if (imports->FirstThunk) fixup_rva_names( (UINT_PTR *)(addr + imports->FirstThunk), delta ); imports++; } } /* build the resource directory */ dir = &nt->OptionalHeader.DataDirectory[IMAGE_FILE_RESOURCE_DIRECTORY]; if (dir->Size) { void *ptr = addr + dir->VirtualAddress; fixup_so_resources( ptr, ptr, delta ); } /* build the export directory */ dir = &nt->OptionalHeader.DataDirectory[IMAGE_FILE_EXPORT_DIRECTORY]; if (dir->Size) { IMAGE_EXPORT_DIRECTORY *exports = (IMAGE_EXPORT_DIRECTORY *)(addr + dir->VirtualAddress); fixup_rva_dwords( &exports->Name, delta, 1 ); fixup_rva_dwords( &exports->AddressOfFunctions, delta, 1 ); fixup_rva_dwords( &exports->AddressOfNames, delta, 1 ); fixup_rva_dwords( &exports->AddressOfNameOrdinals, delta, 1 ); fixup_rva_dwords( (DWORD *)(addr + exports->AddressOfNames), delta, exports->NumberOfNames ); fixup_rva_ptrs( addr + exports->AddressOfFunctions, addr, exports->NumberOfFunctions ); } return STATUS_SUCCESS; } static ULONG_PTR find_pe_export( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports, const IMAGE_IMPORT_BY_NAME *name ) { const WORD *ordinals = (const WORD *)((BYTE *)module + exports->AddressOfNameOrdinals); const DWORD *names = (const DWORD *)((BYTE *)module + exports->AddressOfNames); if (name->Hint < exports->NumberOfNames) { char *ename = (char *)module + names[name->Hint]; if (!strcmp( ename, (char *)name->Name )) return find_ordinal_export( module, exports, ordinals[name->Hint] ); } return find_named_export( module, exports, (char *)name->Name ); } static NTSTATUS fixup_ntdll_imports( const char *name, HMODULE module ) { const IMAGE_NT_HEADERS *nt; const IMAGE_IMPORT_DESCRIPTOR *descr; const IMAGE_DATA_DIRECTORY *dir; const IMAGE_THUNK_DATA *import_list; IMAGE_THUNK_DATA *thunk_list; nt = get_rva( module, ((IMAGE_DOS_HEADER *)module)->e_lfanew ); dir = &nt->OptionalHeader.DataDirectory[IMAGE_FILE_IMPORT_DIRECTORY]; if (!dir->VirtualAddress || !dir->Size) return STATUS_SUCCESS; descr = get_rva( module, dir->VirtualAddress ); for (; descr->Name && descr->FirstThunk; descr++) { thunk_list = get_rva( module, descr->FirstThunk ); /* ntdll must be the only import */ if (strcmp( get_rva( module, descr->Name ), "ntdll.dll" )) { ERR( "module %s is importing %s\n", debugstr_a(name), (char *)get_rva( module, descr->Name )); return STATUS_PROCEDURE_NOT_FOUND; } if (descr->u.OriginalFirstThunk) import_list = get_rva( module, descr->u.OriginalFirstThunk ); else import_list = thunk_list; while (import_list->u1.Ordinal) { if (IMAGE_SNAP_BY_ORDINAL( import_list->u1.Ordinal )) { int ordinal = IMAGE_ORDINAL( import_list->u1.Ordinal ) - ntdll_exports->Base; thunk_list->u1.Function = find_ordinal_export( ntdll_module, ntdll_exports, ordinal ); if (!thunk_list->u1.Function) ERR( "%s: ntdll.%u not found\n", debugstr_a(name), ordinal ); } else /* import by name */ { IMAGE_IMPORT_BY_NAME *pe_name = get_rva( module, import_list->u1.AddressOfData ); thunk_list->u1.Function = find_pe_export( ntdll_module, ntdll_exports, pe_name ); if (!thunk_list->u1.Function) ERR( "%s: ntdll.%s not found\n", debugstr_a(name), pe_name->Name ); } import_list++; thunk_list++; } } return STATUS_SUCCESS; } static void load_ntdll_functions( HMODULE module ) { void **ptr; ntdll_exports = get_export_dir( module ); assert( ntdll_exports ); #define GET_FUNC(name) \ if (!(p##name = (void *)find_named_export( module, ntdll_exports, #name ))) \ ERR( "%s not found\n", #name ) GET_FUNC( DbgUiRemoteBreakin ); GET_FUNC( KiRaiseUserExceptionDispatcher ); GET_FUNC( KiUserExceptionDispatcher ); GET_FUNC( KiUserApcDispatcher ); GET_FUNC( LdrInitializeThunk ); GET_FUNC( RtlUserThreadStart ); GET_FUNC( __wine_set_unix_funcs ); #undef GET_FUNC #define SET_PTR(name,val) \ if ((ptr = (void *)find_named_export( module, ntdll_exports, #name ))) *ptr = val; \ else ERR( "%s not found\n", #name ) SET_PTR( __wine_syscall_dispatcher, __wine_syscall_dispatcher ); #ifdef __i386__ SET_PTR( __wine_ldt_copy, &__wine_ldt_copy ); #endif #undef SET_PTR } static void check_command_line( int argc, char *argv[] ) { static const char usage[] = "Usage: wine PROGRAM [ARGUMENTS...] Run the specified program\n" " wine --help Display this help and exit\n" " wine --version Output version information and exit"; /*********************************************************************** * __wine_main * * Main entry point called by the wine loader. */ void __wine_main( int argc, char *argv[], char *envp[] ) { init_paths( argv ); if (!getenv( "WINELOADERNOEXEC" )) /* first time around */ { check_command_line( argc, argv ); if (pre_exec()) { static char noexec[] = "WINELOADERNOEXEC=1"; char **new_argv = malloc( (argc + 2) * sizeof(*argv) ); memcpy( new_argv + 1, argv, (argc + 1) * sizeof(*argv) ); putenv( noexec ); loader_exec( argv0, new_argv, client_cpu ); fatal_error( "could not exec the wine loader\n" ); } } #ifdef RLIMIT_NOFILE set_max_limit( RLIMIT_NOFILE ); #endif #ifdef RLIMIT_AS set_max_limit( RLIMIT_AS ); #endif virtual_init(); init_environment( argc, argv, envp ); #ifdef __APPLE__ apple_main_thread(); #endif start_main_thread(); }
loader/main.c:

extern char **environ; /* the preloader will set this variable */ const struct wine_preload_info *wine_main_preload_info = NULL; /* canonicalize path and return its directory name */ static char *realpath_dirname( const char *name ) { char *p, *fullpath = realpath( name, NULL ); if (fullpath) { p = strrchr( fullpath, '/' ); if (p == fullpath) p++; if (p) *p = 0; } return fullpath; } /* if string ends with tail, remove it */ static char *remove_tail( const char *str, const char *tail ) { size_t len = strlen( str ); size_t tail_len = strlen( tail ); char *ret; if (len < tail_len) return NULL; if (strcmp( str + len - tail_len, tail )) return NULL; ret = malloc( len - tail_len + 1 ); memcpy( ret, str, len - tail_len ); ret[len - tail_len] = 0; return ret; } /* build a path from the specified dir and name */ static char *build_path( const char *dir, const char *name ) { size_t len = strlen( dir ); char *ret = malloc( len + strlen( name ) + 2 ); memcpy( ret, dir, len ); if (len && ret[len - 1] != '/') ret[len++] = '/'; strcpy( ret + len, name ); return ret; } static const char *get_self_exe( char *argv0 ) { #if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) return "/proc/self/exe"; #elif defined (__FreeBSD__) || defined(__DragonFly__) return "/proc/curproc/file"; #else if (!strchr( argv0, '/' )) /* search in PATH */ { char *p, *path = getenv( "PATH" ); if (!path || !(path = strdup(path))) return NULL; for (p = strtok( path, ":" ); p; p = strtok( NULL, ":" )) { char *name = build_path( p, argv0 ); if (!access( name, X_OK )) { free( path ); return name; } free( name ); } free( path ); return NULL; } return argv0; #endif } static void *try_dlopen( const char *dir, const char *name ) { char *path = build_path( dir, name ); void *handle = dlopen( path, RTLD_NOW ); free( path ); return handle; } static void *load_ntdll( char *argv0 ) { const char *self = get_self_exe( argv0 ); char *path, *p; void *handle = NULL; if (self && ((path = realpath_dirname( self )))) { if ((p = remove_tail( path, "/loader" ))) { handle = try_dlopen( p, "dlls/ntdll/ntdll.so" ); free( p ); } else handle = try_dlopen( path, BIN_TO_DLLDIR "/ntdll.so" ); free( path ); } if (!handle && (path = getenv( "WINEDLLPATH" ))) { path = strdup( path ); for (p = strtok( path, ":" ); p; p = strtok( NULL, ":" )) { handle = try_dlopen( p, "ntdll.so" ); if (handle) break; } free( path ); } if (!handle && !self) handle = try_dlopen( DLLDIR, "ntdll.so" ); return handle; } /********************************************************************** * main */ int main( int argc, char *argv[] ) { void *handle; if ((handle = load_ntdll( argv[0] ))) { void (*init_func)(int, char **, char **) = dlsym( handle, "__wine_main" ); if (init_func) init_func( argc, argv, environ ); fprintf( stderr, "wine: __wine_main function not found in ntdll.so\n" ); exit(1); } fprintf( stderr, "wine: could not load ntdll.so: %s\n", dlerror() ); pthread_detach( pthread_self() ); /* force importing libpthread for OpenGL */ exit(1); }
六级/考研单词: implement, compatible, norm, translate, instruct, convert, equivalent, protocol, compile, usage, execute, parcel, dump, utility, thumb, dynamic, issue, compute, ace, seldom, indigenous, outer, infrastructure, audio
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理