[Bug 48861] New: x64dbg fails to set debuggee command line via File -> Change Command Line menu

WineHQ Bugzilla wine-bugs at winehq.org
Mon Mar 30 18:00:59 CDT 2020


https://bugs.winehq.org/show_bug.cgi?id=48861

            Bug ID: 48861
           Summary: x64dbg fails to set debuggee command line via File ->
                    Change Command Line menu
           Product: Wine
           Version: 5.5
          Hardware: x86-64
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: P2
         Component: kernelbase
          Assignee: wine-bugs at winehq.org
          Reporter: focht at gmx.net
      Distribution: ---

Hello folks,

https://github.com/x64dbg/x64dbg/wiki/Frequently-Asked-Questions

--- quote ---
Q: Help, how do I pass arguments to the program I want to debug?

A: Use the File -> Change Command Line option:
--- quote ---

This doesn't work under Wine. One has to start the debuggee using an
undocumented way.

https://github.com/x64dbg/x64dbg/issues/210

The documentation task https://github.com/x64dbg/docs/issues/26 is still open
since 2016.

Examples:

--- snip --
# starts x64dbg with 64-bit debuggee and args
$ wine ./x96dbg.exe "c:\\windows\\system32\\cmd.exe" /k echo
"%PROCESSOR_ARCHITECTURE%"

# starts x32dbg with 32-bit debuggee and args
$ wine ./x96dbg.exe "c:\\windows\\syswow64\\cmd.exe" /k echo
"%PROCESSOR_ARCHITECTURE%"
--- snip ---

Anyway, the problem here is how Wine implements 'kernel32.GetCommandLine{A,W}'
functions.

Starting with x64dbg sources...

https://github.com/x64dbg/x64dbg/blob/d2c62e69c2cd4d42cc4509612ad3cfd34e91426b/src/dbg/debugger.cpp#L2406

--- snip ---
bool dbgsetcmdline(const char* cmd_line, cmdline_error_t* cmd_line_error)
{
    // Make sure cmd_line_error is a valid pointer
    cmdline_error_t cmd_line_error_aux;
    if(cmd_line_error == NULL)
        cmd_line_error = &cmd_line_error_aux;

    // Get the command line address
    if(!getcommandlineaddr(&cmd_line_error->addr, cmd_line_error))
        return false;
    auto command_line_addr = cmd_line_error->addr;

    // Convert the string to UTF-16
    auto command_linewstr = StringUtils::Utf8ToUtf16(cmd_line);
    if(command_linewstr.length() >= 32766) //32766 is maximum character count
for a null-terminated UNICODE_STRING
        command_linewstr.resize(32766);
    // Convert the UTF-16 string to ANSI
    auto command_linestr = Utf16ToAnsi(command_linewstr.c_str());

    // Fill the UNICODE_STRING to be set in the debuggee
    UNICODE_STRING new_command_line;
    new_command_line.Length = USHORT(command_linewstr.length() *
sizeof(WCHAR)); //max value: 32766 * 2 = 65532
    new_command_line.MaximumLength = new_command_line.Length + sizeof(WCHAR);
//max value: 65532 + 2 = 65534
    new_command_line.Buffer = PWSTR(command_linewstr.c_str()); //allow cast
from const because the UNICODE_STRING will not be used locally

    // Allocate remote memory for both the UNICODE_STRING.Buffer and the (null
terminated) ANSI buffer
    duint mem = MemAllocRemote(0, new_command_line.MaximumLength +
command_linestr.size());
    if(!mem)
    {
        cmd_line_error->type = CMDL_ERR_ALLOC_UNICODEANSI_COMMANDLINE;
        return false;
    }

    // Write the UNICODE_STRING.Buffer to the debuggee (UNICODE_STRING.Length
is used because the remote memory is zeroed)
    if(!MemWrite(mem, new_command_line.Buffer, new_command_line.Length))
    {
        cmd_line_error->addr = mem;
        cmd_line_error->type = CMDL_ERR_WRITE_UNICODE_COMMANDLINE;
        return false;
    }

    // Write the (null-terminated) ANSI buffer to the debuggee
    if(!MemWrite(mem + new_command_line.MaximumLength, command_linestr.data(),
command_linestr.size()))
    {
        cmd_line_error->addr = mem + new_command_line.MaximumLength;
        cmd_line_error->type = CMDL_ERR_WRITE_ANSI_COMMANDLINE;
        return false;
    }

    // Change the pointers to the command line
    if(!fixgetcommandlinesbase(mem, mem + new_command_line.MaximumLength,
cmd_line_error))
        return false;

    // Put the remote buffer address in the UNICODE_STRING and write it to the
PEB
    new_command_line.Buffer = PWSTR(mem);
    if(!MemWrite(command_line_addr, &new_command_line,
sizeof(new_command_line)))
    {
        cmd_line_error->addr = command_line_addr;
        cmd_line_error->type = CMDL_ERR_WRITE_PEBUNICODE_COMMANDLINE;
        return false;
    }

    // Copy command line
    copyCommandLine(cmd_line);

    return true;
}
--- snip ---

Corresponding Wine trace log snippet:

--- snip ---
...
0009:Call KERNEL32.VirtualAllocEx(0000024c,00000000,00000087,00003000,00000040)
ret=01dd445c
0009:Call
ntdll.NtAllocateVirtualMemory(0000024c,0032ce3c,00000000,0032ce58,00003000,00000040)
ret=7b022f18
0009: queue_apc( handle=024c,
call={APC_VIRTUAL_ALLOC,addr==00000000,size=00000087,zero_bits_64=0,op_type=3000,prot=40}
)
0045: *wakeup* signaled=256
0009: queue_apc() = 0 { handle=02e8, self=0 }
0045: select( flags=2, cookie=7ffdb10c, timeout=infinite, prev_apc=0000,
result={APC_VIRTUAL_QUERY,status=0,base=00330000,alloc_base=00330000,size=00001000,state=1,prot=2,alloc_prot=80,alloc_type=100},
data={WAIT,handles={0048}} )
0045: select() = KERNEL_APC { timeout=infinite,
call={APC_VIRTUAL_ALLOC,addr==00000000,size=00000087,zero_bits_64=0,op_type=3000,prot=40},
apc_handle=004c }
0009: select( flags=2, cookie=0032c9ac, timeout=infinite, prev_apc=0000,
result={}, data={WAIT_ALL,handles={02e8}} )
0009: select() = PENDING { timeout=infinite, call={APC_NONE}, apc_handle=0000 }
0045: select( flags=2, cookie=7ffdb10c, timeout=infinite, prev_apc=004c,
result={APC_VIRTUAL_ALLOC,status=0,addr=00380000,size=00001000},
data={WAIT,handles={0048}} )
0009: *wakeup* signaled=0
0045: select() = PENDING { timeout=infinite, call={APC_NONE}, apc_handle=0000 }
0009: get_apc_result( handle=02e8 )
0009: get_apc_result() = 0 {
result={APC_VIRTUAL_ALLOC,status=0,addr=00380000,size=00001000} }
0009:Ret  ntdll.NtAllocateVirtualMemory() retval=00000000 ret=7b022f18
0009:Ret  KERNEL32.VirtualAllocEx() retval=00380000 ret=01dd445c
0009:Call
KERNEL32.WriteProcessMemory(0000024c,00380000,05ab7250,00000058,0032cea8)
ret=0211b7b8
0009:Call
ntdll.NtWriteVirtualMemory(0000024c,00380000,05ab7250,00000058,0032cea8)
ret=7b023199
0009: write_process_memory( handle=024c, addr=00380000,
data={22,00,43,00,3a,00,5c,00,77,00,69,00,6e,00,64,00,6f,00,77,00,73,00,5c,00,73,00,79,00,73,00,77,00,6f,00,77,00,36,00,34,00,5c,00,77,00,69,00,6e,00,65,00,6d,00,69,00,6e,00,65,00,2e,00,65,00,78,00,65,00,22,00,20,00,2d,00,74,00,65,00,73,00,74,00,61,00,72,00,67,00,31,00}
)
0045: *signal* signal=19
0009: write_process_memory() = 0
0009:Ret  ntdll.NtWriteVirtualMemory() retval=00000000 ret=7b023199
0009:Ret  KERNEL32.WriteProcessMemory() retval=00000001 ret=0211b7b8
0009:Call
KERNEL32.WriteProcessMemory(0000024c,0038005a,05a989f8,0000002d,0032cea8)
ret=0211b7b8
0009:Call
ntdll.NtWriteVirtualMemory(0000024c,0038005a,05a989f8,0000002d,0032cea8)
ret=7b023199
0009: write_process_memory( handle=024c, addr=0038005a,
data={22,43,3a,5c,77,69,6e,64,6f,77,73,5c,73,79,73,77,6f,77,36,34,5c,77,69,6e,65,6d,69,6e,65,2e,65,78,65,22,20,2d,74,65,73,74,61,72,67,31,00}
)
0045: *signal* signal=19
0009: write_process_memory() = 0
0009:Ret  ntdll.NtWriteVirtualMemory() retval=00000000 ret=7b023199
0009:Ret  KERNEL32.WriteProcessMemory() retval=00000001 ret=0211b7b8 
--- snip ---

Both, ANSI and UNICODE buffers are allocated and filled in debuggee process
space.

https://github.com/x64dbg/x64dbg/blob/d2c62e69c2cd4d42cc4509612ad3cfd34e91426b/src/dbg/debugger.cpp#L2365

--- snip ---
static bool fixgetcommandlinesbase(duint new_command_line_unicode, duint
new_command_line_ascii, cmdline_error_t* cmd_line_error)
{
    duint getcommandline;

    if(!valfromstring("kernelBase:GetCommandLineA", &getcommandline))
    {
        if(!valfromstring("kernel32:GetCommandLineA", &getcommandline))
        {
            cmd_line_error->type = CMDL_ERR_GET_GETCOMMANDLINE;
            return false;
        }
    }
    if(!patchcmdline(getcommandline, new_command_line_ascii, cmd_line_error))
        return false;

    if(!valfromstring("kernelbase:GetCommandLineW", &getcommandline))
    {
        if(!valfromstring("kernel32:GetCommandLineW", &getcommandline))
        {
            cmd_line_error->type = CMDL_ERR_GET_GETCOMMANDLINE;
            return false;
        }
    }
    if(!patchcmdline(getcommandline, new_command_line_unicode, cmd_line_error))
        return false;

    return true;
}
--- snip ---

Corresponding Wine trace log snippet:

--- snip ---
...
0009:Call KERNEL32.LoadLibraryExW(05ab78d8
L"Z:\\home\\focht\\projects\\wine\\mainline-install-x86_64\\lib\\wine\\kernelbase.dll",00000000,00000001)
ret=01e0dbdf
...
0009:Ret  KERNEL32.LoadLibraryExW() retval=7b000000 ret=01e0dbdf
...
0009:Call KERNEL32.GetProcAddress(7b000000,05ab78bb "GetCommandLineA")
ret=01e0b046
0009:Ret  KERNEL32.GetProcAddress() retval=7b0036ec ret=01e0b046
0009:Call KERNEL32.FreeLibrary(7b000000) ret=01e0ddf9
0009:Ret  KERNEL32.FreeLibrary() retval=00000001 ret=01e0ddf9
...
0009:Call
KERNEL32.ReadProcessMemory(0000024c,7b0036ec,0032ce2c,00000064,0032ce08)
ret=0211b6bc
0009:Call
ntdll.NtReadVirtualMemory(0000024c,7b0036ec,0032ce2c,00000064,0032ce08)
ret=7b022cf9
0009: read_process_memory( handle=024c, addr=7b0036ec )
0045: *signal* signal=19
0009: read_process_memory() = 0 {
data={8b,ff,55,8b,ec,5d,68,36,01,0c,00,b8,00,50,07,7b,50,ff,50,04,c2,00,00,90,90,90,90,90,90,90,90,90,8b,ff,55,8b,ec,5d,68,37,01,0c,00,b8,00,50,07,7b,50,ff,50,04,c2,00,00,90,90,90,90,90,90,90,90,90,8b,ff,55,8b,ec,5d,68,38,01,0a,00,b8,00,50,07,7b,50,ff,50,04,c2,08,00,90,90,90,90,90,90,90,90,90,8b,ff,55,8b}
}
0009:Ret  ntdll.NtReadVirtualMemory() retval=00000000 ret=7b022cf9
0009:Ret  KERNEL32.ReadProcessMemory() retval=00000001 ret=0211b6bc 
...
--- snip ---

x32dbg reads the opcodes of 32-bit 'kernelbase.GetCommandLineA' and is not
happy about it.

--- snip ---
static bool patchcmdline(duint getcommandline, duint new_command_line,
cmdline_error_t* cmd_line_error)
{
    duint command_line_stored = 0;
    unsigned char data[100];

    cmd_line_error->addr = getcommandline;
    if(!MemRead(cmd_line_error->addr, & data, sizeof(data)))
    {
        cmd_line_error->type = CMDL_ERR_READ_GETCOMMANDLINEBASE;
        return false;
    }

#ifdef _WIN64
    /*
    00007FFC5B91E3C8 | 48 8B 05 19 1D 0E 00     | mov rax,qword ptr
ds:[7FFC5BA000E8]
    00007FFC5B91E3CF | C3                       | ret                          
          |
    This is a relative offset then to get the symbol: next instruction of
getmodulehandle (+7 bytes) + offset to symbol
    (the last 4 bytes of the instruction)
    */
    if(data[0] != 0x48 ||  data[1] != 0x8B || data[2] != 0x05 || data[7] !=
0xC3)
    {
        cmd_line_error->type = CMDL_ERR_CHECK_GETCOMMANDLINESTORED;
        return false;
    }
    DWORD offset = * ((DWORD*) & data[3]);
    command_line_stored = getcommandline + 7 + offset;
#else //x86
    /*
    750FE9CA | A1 CC DB 1A 75           | mov eax,dword ptr ds:[751ADBCC]      
  |
    750FE9CF | C3                       | ret                                  
  |
    */
    if(data[0] != 0xA1 ||  data[5] != 0xC3)
    {
        cmd_line_error->type = CMDL_ERR_CHECK_GETCOMMANDLINESTORED;
        return false;
    }
    command_line_stored = * ((duint*) & data[1]);
#endif

    //update the pointer in the debuggee
    if(!MemWrite(command_line_stored, &new_command_line,
sizeof(new_command_line)))
    {
        cmd_line_error->addr = command_line_stored;
        cmd_line_error->type = CMDL_ERR_WRITE_GETCOMMANDLINESTORED;
        return false;
    }

    return true;
}
--- snip ---

Wine source:

https://source.winehq.org/git/wine.git/blob/9bcb1f5195d1e65e0e7afb288d36fee716fe3a60:/dlls/kernelbase/process.c#l1008

--- snip ---
1008 static STARTUPINFOW startup_infoW;
1009 static char *command_lineA;
1010 static WCHAR *command_lineW;
--- snip ---

https://source.winehq.org/git/wine.git/blob/9bcb1f5195d1e65e0e7afb288d36fee716fe3a60:/dlls/kernelbase/process.c#l1043

--- snip ---
1043 /***********************************************************************
1044  *           GetCommandLineA   (kernelbase.@)
1045  */
1046 LPSTR WINAPI DECLSPEC_HOTPATCH GetCommandLineA(void)
1047 {
1048     return command_lineA;
1049 }
1050 
1051 
1052 /***********************************************************************
1053  *           GetCommandLineW   (kernelbase.@)
1054  */
1055 LPWSTR WINAPI DECLSPEC_HOTPATCH GetCommandLineW(void)
1056 {
1057     return NtCurrentTeb()->Peb->ProcessParameters->CommandLine.Buffer;
1058 }
--- snip ---

Depending on which compiler (GCC or LLVM/MingW) is used to build Wine, the
following code is generated:

=== GCC ===

--- snip --
$ gcc -v
Using built-in specs.
COLLECT_GCC=/usr/bin/gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/8/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap
--enable-languages=c,c++,fortran,objc,obj-c++,ada,go,lto --prefix=/usr
--mandir=/usr/share/man --infodir=/usr/share/info
--with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared
--enable-threads=posix --enable-checking=release --enable-multilib
--with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions
--enable-gnu-unique-object --enable-linker-build-id
--with-gcc-major-version-only --with-linker-hash-style=gnu --enable-plugin
--enable-initfini-array --with-isl --enable-libmpx
--enable-offload-targets=nvptx-none --without-cuda-driver
--enable-gnu-indirect-function --enable-cet --with-tune=generic
--with-arch_32=i686 --build=x86_64-redhat-linux
Thread model: posix
gcc version 8.3.1 20190223 (Red Hat 8.3.1-2) (GCC) 
--- snip ---

--- snip ---
$ gdb mainline-install-x86_64/lib64/wine/kernelbase.dll.so -batch -ex
'disassemble GetCommandLineA'
Dump of assembler code for function GetCommandLineA:
   0x000000007b05fd18 <+0>:    push   %rbp
   0x000000007b05fd19 <+1>:    mov    0x738a0(%rip),%rax        # 0x7b0d35c0
<command_lineA>
   0x000000007b05fd20 <+8>:    mov    %rsp,%rbp
   0x000000007b05fd23 <+11>:    and    $0xfffffffffffffff0,%rsp
   0x000000007b05fd27 <+15>:    leaveq 
   0x000000007b05fd28 <+16>:    retq
End of assembler dump.
--- snip ---

--- snip ---
$ gdb mainline-install-x86_64/lib64/wine/kernelbase.dll.so -batch -ex
'disassemble GetCommandLineW'
Dump of assembler code for function GetCommandLineW:
   0x000000007b05fd58 <+0>:    push   %rbp
   0x000000007b05fd59 <+1>:    mov    %gs:0x30,%rax
   0x000000007b05fd62 <+10>:    mov    0x60(%rax),%rax
   0x000000007b05fd66 <+14>:    mov    %rsp,%rbp
   0x000000007b05fd69 <+17>:    and    $0xfffffffffffffff0,%rsp
   0x000000007b05fd6d <+21>:    mov    0x20(%rax),%rax
   0x000000007b05fd71 <+25>:    mov    0x78(%rax),%rax
   0x000000007b05fd75 <+29>:    leaveq 
   0x000000007b05fd76 <+30>:    retq
End of assembler dump.
--- snip ---

NOTE: offset-0x4 to work around dwarf symbol processing bug.

--- snip ---
$ gdb mainline-install-x86_64/lib/wine/kernelbase.dll.so -batch -ex
'disassemble GetCommandLineA-0x4'
Dump of assembler code for function GetCommandLineA:
   0x7b060620 <+0>:    mov    %edi,%edi
   0x7b060622 <+2>:    push   %ebp
   0x7b060623 <+3>:    mov    %esp,%ebp
   0x7b060625 <+0>:    pop    %ebp
   0x7b060626 <+1>:    mov    0x7b0d4a20,%eax
   0x7b06062b <+6>:    ret    
End of assembler dump.
--- snip ---

--- snip ---
$ gdb mainline-install-x86_64/lib/wine/kernelbase.dll.so -batch -ex
'disassemble GetCommandLineW-0x4'
Dump of assembler code for function GetCommandLineW:
   0x7b060640 <+0>:    mov    %edi,%edi
   0x7b060642 <+2>:    push   %ebp
   0x7b060643 <+3>:    mov    %esp,%ebp
   0x7b060645 <+0>:    mov    %fs:0x18,%eax
   0x7b06064b <+6>:    mov    0x30(%eax),%eax
   0x7b06064e <+9>:    pop    %ebp
   0x7b06064f <+10>:    mov    0x10(%eax),%eax
   0x7b060652 <+13>:    mov    0x44(%eax),%eax
   0x7b060655 <+16>:    ret    
End of assembler dump.
--- snip ---

=== LLVM MinGW ===

--- snip ---
$ clang -v
clang version 10.0.0 (https://github.com/llvm/llvm-project.git
c49194969430f0ee817498a7000a979a7a0ded03)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /home/focht/projects/llvm-mingw-20191230-ubuntu-16.04/bin
Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/8
Selected GCC installation: /usr/lib/gcc/x86_64-redhat-linux/8
Candidate multilib: .;@m64
Candidate multilib: 32;@m32
Selected multilib: .;@m64
--- snip ---

--- snip ---
$ gdb mainline-install-x86_64/lib/wine/kernelbase.dll -batch -ex 'disassemble
GetCommandLineA'
Dump of assembler code for function _GetCommandLineA at 0:
   0x7b0313e0 <+0>:    push   %ebp
   0x7b0313e1 <+1>:    mov    %esp,%ebp
   0x7b0313e3 <+3>:    mov    0x7b076bf8,%eax
   0x7b0313e8 <+8>:    pop    %ebp
   0x7b0313e9 <+9>:    ret    
End of assembler dump.
--- snip ---

--- snip ---
$ gdb mainline-install-x86_64/lib/wine/kernelbase.dll -batch -ex 'disassemble
GetCommandLineW'
Dump of assembler code for function _GetCommandLineW at 0:
   0x7b0313f0 <+0>:    push   %ebp
   0x7b0313f1 <+1>:    mov    %esp,%ebp
   0x7b0313f3 <+3>:    mov    %fs:0x18,%eax
   0x7b0313f9 <+9>:    mov    0x30(%eax),%eax
   0x7b0313fc <+12>:    mov    0x10(%eax),%eax
   0x7b0313ff <+15>:    mov    0x44(%eax),%eax
   0x7b031402 <+18>:    pop    %ebp
   0x7b031403 <+19>:    ret    
End of assembler dump.
--- snip ---

--- snip ---
$ gdb mainline-install-x86_64/lib64/wine/kernelbase.dll -batch -ex 'disassemble
GetCommandLineA'
Dump of assembler code for function GetCommandLineA:
   0x000000007b0389b0 <+0>:    mov    0x4de49(%rip),%rax        # 0x7b086800
   0x000000007b0389b7 <+7>:    retq   
End of assembler dump.
--- snip ---

--- snip ---
$ gdb mainline-install-x86_64/lib64/wine/kernelbase.dll -batch -ex 'disassemble
GetCommandLineW'
Dump of assembler code for function GetCommandLineW:
   0x000000007b0389c0 <+0>:    mov    %gs:0x30,%rax
   0x000000007b0389c9 <+9>:    mov    0x60(%rax),%rax
   0x000000007b0389cd <+13>:    mov    0x20(%rax),%rax
   0x000000007b0389d1 <+17>:    mov    0x78(%rax),%rax
   0x000000007b0389d5 <+21>:    retq   
End of assembler dump.
--- snip ---

=====

General problems:

(1) asymmetric design of kernelbase GetCommandLineA/W commmand buffer handling

The ANSI variant returns the command line buffer address directly while the
UNICODE variant goes through the PEB.

The symmetric design was changed in 2003 with
https://source.winehq.org/git/wine.git/commitdiff/b53b5bcb50e75263e8bbffed34fc6ce48b4fdc24
("- fixed a couple of bugs in ntdll...") to be asymmetrical.

A big squash-like commit, doing major refactoring/various bugfixing = several
things in one go. No mentioning of this specific design change. Part of Wine
0.9 release.

(2) HOTPATCH entry

Commit
https://source.winehq.org/git/wine.git/commitdiff/911e50849a9bc05ab11d896a3b7f7a3b3462fabd
("kernel32: Move process startup information functions to kernelbase.") added
it. Part of Wine 4.16 release.

I couldn't find a bug report about hooking problems of GetCommandLineA/W which
requires those to be hotpatch-compatible entries. I guess this was added for no
special reason :|

(3) compiler choice/optimization settings, influencing generated code

---

Fixing above problems is not that easy by just returning 'command_lineW' in 'W'
variant and removing DECLSPEC_HOTPATCH because of (3).

Generated code:

--- snip ---
$ gdb mainline-install-x86_64/lib/wine/kernelbase.dll -batch -ex 'disassemble
GetCommandLineA'
Dump of assembler code for function _GetCommandLineA at 0:
   0x7b0313e0 <+0>:    push   %ebp
   0x7b0313e1 <+1>:    mov    %esp,%ebp
   0x7b0313e3 <+3>:    mov    0x7b076bf8,%eax
   0x7b0313e8 <+8>:    pop    %ebp
   0x7b0313e9 <+9>:    ret    
End of assembler dump.
--- snip ---

--- snip ---
$ gdb mainline-install-x86_64/lib/wine/kernelbase.dll -batch -ex 'disassemble
GetCommandLineW'
Dump of assembler code for function _GetCommandLineW at 0:
   0x7b0313f0 <+0>:    push   %ebp
   0x7b0313f1 <+1>:    mov    %esp,%ebp
   0x7b0313f3 <+3>:    mov    0x7b076bf4,%eax
   0x7b0313f8 <+8>:    pop    %ebp
   0x7b0313f9 <+9>:    ret    
End of assembler dump.
--- snip ---

--- snip ---
$ gdb mainline-install-x86_64/lib64/wine/kernelbase.dll -batch -ex 'disassemble
GetCommandLineA'
Dump of assembler code for function GetCommandLineA:
   0x000000007b0389b0 <+0>:    mov    0x4de49(%rip),%rax        # 0x7b086800
   0x000000007b0389b7 <+7>:    retq   
End of assembler dump.
--- snip ---

--- snip ---
$ gdb mainline-install-x86_64/lib64/wine/kernelbase.dll -batch -ex 'disassemble
GetCommandLineW'
Dump of assembler code for function GetCommandLineW:
   0x000000007b0389c0 <+0>:    mov    0x4de31(%rip),%rax        # 0x7b0867f8
   0x000000007b0389c7 <+7>:    retq   
End of assembler dump.
--- snip ---

The 64-bit variant looks good, but 32-bit is no-go. To have full control over
the generated code, small __i386__ and __x86_64__ assembly implementations for
A and W variant each are sufficient.

NOTE: the assembly for __x86_64__ needs to use RIP-relative addressing (PIE)

-> movq command_lineW(%rip), %rax

* encoding: [0x48,0x8b,0x05,A,A,A,A]
* kind: reloc_riprel_4byte_movq_load

With the small assembly functions in place, x64dbg and x32dbg were able to set
the debuggee command line via menu.

If assembly is not an option, consider changing A/W variant to be symmetrical
to make this bug report somewhat useful in the end (but still WONTFIX).

$ sha1sum snapshot_2020-02-11_01-07.zip 
03fa18ffb1cbdd46a6b3c9b7e19332e7e2024edc  snapshot_2020-02-11_01-07.zip

$ du -sh snapshot_2020-02-11_01-07.zip 
31M    snapshot_2020-02-11_01-07.zip

$ wine --version
wine-5.5

Regards

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.


More information about the wine-bugs mailing list