Debugging Win64 Cog Spur

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Debugging Win64 Cog Spur

Eliot Miranda-2
 
Hi Nicolas,

    the VM gets quite far before some unknown problem in path name manipulation.  I'm drunning the debug VM under gdb via
(gdb) run -trace=259 trunk50-64.image
(See Cogit>>sendTrace: for a definition of the flags)

and this is the output

...
UnixFileDirectory class>pathNameDelimiter
Array(Object)>at:
BlockClosure>value:
AcornFileDirectory class>isActiveDirectoryClass
SmalltalkImage>getSystemAttribute:
ByteString(String)>isString
ByteString(ArrayedCollection)>size
ByteString(ArrayedCollection)>size
SmallInteger>=
Array(Object)>at:
BlockClosure>value:
MacFileDirectory class>isActiveDirectoryClass
MacFileDirectory class>pathNameDelimiter
Character>=
Array(Object)>at:
BlockClosure>value:
DosFileDirectory class(FileDirectory class)>isActiveDirectoryClass
DosFileDirectory class>pathNameDelimiter
DosFileDirectory class(FileDirectory class)>primPathNameDelimiter
Character>=
FilePath class>pathName:
FilePath class>pathName:isEncoded:

Alas there's no debug information to be had:

(gdb) where
#0  0x00000000000008d4 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)

So my next step is to put a breakpoint for the selector #pathName:isEncoded: and step from there.

(gdb) b warning
Breakpoint 1 at 0x4015d9: file ../../spur64src/vm/gcc3x-cointerp.c, line 44.
(gdb) run -breaksel pathName:isEncoded: trunk50-64.image
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /cygdrive/z/oscogvm/build.win64x64/squeak.cog.spur/builddbg/vm/Squeak.exe -breaksel pathName:isEncoded: trunk50-64.image
[New Thread 4080.0x5ec]
[New Thread 4080.0xb30]
etc...

_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: Debugging Win64 Cog Spur

Nicolas Cellier
 
Great, you reproduced exact same behavior.
The problem I have is effectively where to put the breakpoint.
I think we can believe the output of (gdb) call printCallStack()

I've tried other means:
- analyze direct usage of registers RCX & co from VMMaker
  if ever it could conflicts with WIN64 logical register assignment
  But I did not find anything
- compile with MSVC 2017
  if ever the compiler could spit different warnings and give a clue
  alas it fails very early in readImageFromFileHeapSizeStartingAt (during checkAssumedCompactClasses)
 the failure is incomprehensible, because the debugger shows identical contents if I print:

        *((sqInt *)(classTableFirstPage+8+(51<<3)))    140697255509608    __int64
        *((sqInt *)(specialObjectsOop+8+(7<<3)))    140697255509608    __int64

nonetheless, the debugger enters into the if and execute
        invalidCompactClassError("Array");

I'll have to debug it at assembler level, but it's driving me away from the original problem...

2017-05-25 2:38 GMT+02:00 Eliot Miranda <[hidden email]>:
Hi Nicolas,

    the VM gets quite far before some unknown problem in path name manipulation.  I'm drunning the debug VM under gdb via
(gdb) run -trace=259 trunk50-64.image
(See Cogit>>sendTrace: for a definition of the flags)

and this is the output

...
UnixFileDirectory class>pathNameDelimiter
Array(Object)>at:
BlockClosure>value:
AcornFileDirectory class>isActiveDirectoryClass
SmalltalkImage>getSystemAttribute:
ByteString(String)>isString
ByteString(ArrayedCollection)>size
ByteString(ArrayedCollection)>size
SmallInteger>=
Array(Object)>at:
BlockClosure>value:
MacFileDirectory class>isActiveDirectoryClass
MacFileDirectory class>pathNameDelimiter
Character>=
Array(Object)>at:
BlockClosure>value:
DosFileDirectory class(FileDirectory class)>isActiveDirectoryClass
DosFileDirectory class>pathNameDelimiter
DosFileDirectory class(FileDirectory class)>primPathNameDelimiter
Character>=
FilePath class>pathName:
FilePath class>pathName:isEncoded:

Alas there's no debug information to be had:

(gdb) where
#0  0x00000000000008d4 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)

So my next step is to put a breakpoint for the selector #pathName:isEncoded: and step from there.

(gdb) b warning
Breakpoint 1 at 0x4015d9: file ../../spur64src/vm/gcc3x-cointerp.c, line 44.
(gdb) run -breaksel pathName:isEncoded: trunk50-64.image
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /cygdrive/z/oscogvm/build.win64x64/squeak.cog.spur/builddbg/vm/Squeak.exe -breaksel pathName:isEncoded: trunk50-64.image
[New Thread 4080.0x5ec]
[New Thread 4080.0xb30]
etc...

_,,,^..^,,,_
best, Eliot

Reply | Threaded
Open this post in threaded view
|

Re: Debugging Win64 Cog Spur

Nicolas Cellier
 


2017-05-25 8:28 GMT+02:00 Nicolas Cellier <[hidden email]>:
Great, you reproduced exact same behavior.
The problem I have is effectively where to put the breakpoint.
I think we can believe the output of (gdb) call printCallStack()

I've tried other means:
- analyze direct usage of registers RCX & co from VMMaker
  if ever it could conflicts with WIN64 logical register assignment
  But I did not find anything
- compile with MSVC 2017
  if ever the compiler could spit different warnings and give a clue
  alas it fails very early in readImageFromFileHeapSizeStartingAt (during checkAssumedCompactClasses)
 the failure is incomprehensible, because the debugger shows identical contents if I print:

        *((sqInt *)(classTableFirstPage+8+(51<<3)))    140697255509608    __int64
        *((sqInt *)(specialObjectsOop+8+(7<<3)))    140697255509608    __int64

nonetheless, the debugger enters into the if and execute
        invalidCompactClassError("Array");

I'll have to debug it at assembler level, but it's driving me away from the original problem...

and machine code seems wrong:

    /* begin checkAssumedCompactClasses */
    if ((longAt((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))) !=
00007FF6A0E4FC30  mov         rax,qword ptr [specialObjectsOop (07FF6A1052EF8h)] 
00007FF6A0E4FC37  mov         rcx,qword ptr [classTableFirstPage (07FF6A1052FB8h)] 
00007FF6A0E4FC3E  mov         rcx,qword ptr [rcx] 
00007FF6A0E4FC41  cmp         qword ptr [rax+40h],rcx 
00007FF6A0E4FC45  je          $l2+282h (07FF6A0E4FC53h) 
    /* begin fetchPointer:ofObject: */
    longAt((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassArrayCompactIndex) << (shiftForWord())))))) {
        invalidCompactClassError("Array");
00007FF6A0E4FC47  lea         rcx,[string "Array" (07FF6A0F30C14h)] 
00007FF6A0E4FC4E  call        invalidCompactClassError (07FF6A0C6FA95h) 
    }

I refuse to learn x86 assembler, but it seems that:
- the offset + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))) is OK [rax+40h] is 8+7*8
- the offset BaseHeaderSize) + ((((usqInt)(ClassArrayCompactIndex) << (shiftForWord())))) is completely missing!!!
  I would expect 8+51*8=52*8=26*2*8
00007FF6A0E4FC3E  mov         rcx,qword ptr [rcx+1A0h]

At this stage, there's not much I can do with MSVC...
 
2017-05-25 2:38 GMT+02:00 Eliot Miranda <[hidden email]>:
Hi Nicolas,

    the VM gets quite far before some unknown problem in path name manipulation.  I'm drunning the debug VM under gdb via
(gdb) run -trace=259 trunk50-64.image
(See Cogit>>sendTrace: for a definition of the flags)

and this is the output

...
UnixFileDirectory class>pathNameDelimiter
Array(Object)>at:
BlockClosure>value:
AcornFileDirectory class>isActiveDirectoryClass
SmalltalkImage>getSystemAttribute:
ByteString(String)>isString
ByteString(ArrayedCollection)>size
ByteString(ArrayedCollection)>size
SmallInteger>=
Array(Object)>at:
BlockClosure>value:
MacFileDirectory class>isActiveDirectoryClass
MacFileDirectory class>pathNameDelimiter
Character>=
Array(Object)>at:
BlockClosure>value:
DosFileDirectory class(FileDirectory class)>isActiveDirectoryClass
DosFileDirectory class>pathNameDelimiter
DosFileDirectory class(FileDirectory class)>primPathNameDelimiter
Character>=
FilePath class>pathName:
FilePath class>pathName:isEncoded:

Alas there's no debug information to be had:

(gdb) where
#0  0x00000000000008d4 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)

So my next step is to put a breakpoint for the selector #pathName:isEncoded: and step from there.

(gdb) b warning
Breakpoint 1 at 0x4015d9: file ../../spur64src/vm/gcc3x-cointerp.c, line 44.
(gdb) run -breaksel pathName:isEncoded: trunk50-64.image
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /cygdrive/z/oscogvm/build.win64x64/squeak.cog.spur/builddbg/vm/Squeak.exe -breaksel pathName:isEncoded: trunk50-64.image
[New Thread 4080.0x5ec]
[New Thread 4080.0xb30]
etc...

_,,,^..^,,,_
best, Eliot


Reply | Threaded
Open this post in threaded view
|

Re: Debugging Win64 Cog Spur

Eliot Miranda-2
In reply to this post by Nicolas Cellier
 
Hi Nicolas,

On Wed, May 24, 2017 at 11:28 PM, Nicolas Cellier <[hidden email]> wrote:
Great, you reproduced exact same behavior.
The problem I have is effectively where to put the breakpoint.
I think we can believe the output of (gdb) call printCallStack()

here's one issue; the computation to see if the frame pointer is in use fails.  I'm executing this at the compilation break point for FilePath class>pathName:isEncoded:

(gdb) print /x CStackPointer
$7 = 0xef91d0
(gdb) print /x CFramePointer
$8 = 0x0
(gdb) print cFramePointerInUse
$9 = 0
(gdb) info registers
rax            0x68588f 6838415
rbx            0xffffffff       4294967295
rcx            0x68588f 6838415
rdx            0xabababab003a643a       -6076574521274768326
rsi            0xfde9   65001
rdi            0x0      0
rbp            0xef5db0 0xef5db0
rsp            0xef5c50 0xef5c50
r8             0x0      0
r9             0xfffffffffbefbc48       -68174776
r10            0xe36e626d44726839       -2058599758222432199
r11            0x8101010101010100       -9151031864016699136
r12            0xffffffff       4294967295
r13            0x20     32
r14            0x7ffc202018f0   140720847460592
r15            0xf2faf0 15923952
rip            0x4015d9 0x4015d9 <warning+9>
eflags         0x206    [ PF IF ]
cs             0x33     51
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x53     83
gs             0x2b     43
(gdb)
 

I've tried other means:
- analyze direct usage of registers RCX & co from VMMaker
  if ever it could conflicts with WIN64 logical register assignment
  But I did not find anything
- compile with MSVC 2017
  if ever the compiler could spit different warnings and give a clue
  alas it fails very early in readImageFromFileHeapSizeStartingAt (during checkAssumedCompactClasses)
 the failure is incomprehensible, because the debugger shows identical contents if I print:

        *((sqInt *)(classTableFirstPage+8+(51<<3)))    140697255509608    __int64
        *((sqInt *)(specialObjectsOop+8+(7<<3)))    140697255509608    __int64

nonetheless, the debugger enters into the if and execute
        invalidCompactClassError("Array");

I'll have to debug it at assembler level, but it's driving me away from the original problem...

Hmmm.  I doubt this is a problem because the assert and debug VMs would print a warning if this were wrong and they seem to be doing fine (I'm using the clang build).
 

2017-05-25 2:38 GMT+02:00 Eliot Miranda <[hidden email]>:
Hi Nicolas,

    the VM gets quite far before some unknown problem in path name manipulation.  I'm drunning the debug VM under gdb via
(gdb) run -trace=259 trunk50-64.image
(See Cogit>>sendTrace: for a definition of the flags)

and this is the output

...
UnixFileDirectory class>pathNameDelimiter
Array(Object)>at:
BlockClosure>value:
AcornFileDirectory class>isActiveDirectoryClass
SmalltalkImage>getSystemAttribute:
ByteString(String)>isString
ByteString(ArrayedCollection)>size
ByteString(ArrayedCollection)>size
SmallInteger>=
Array(Object)>at:
BlockClosure>value:
MacFileDirectory class>isActiveDirectoryClass
MacFileDirectory class>pathNameDelimiter
Character>=
Array(Object)>at:
BlockClosure>value:
DosFileDirectory class(FileDirectory class)>isActiveDirectoryClass
DosFileDirectory class>pathNameDelimiter
DosFileDirectory class(FileDirectory class)>primPathNameDelimiter
Character>=
FilePath class>pathName:
FilePath class>pathName:isEncoded:

Alas there's no debug information to be had:

(gdb) where
#0  0x00000000000008d4 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)

So my next step is to put a breakpoint for the selector #pathName:isEncoded: and step from there.

(gdb) b warning
Breakpoint 1 at 0x4015d9: file ../../spur64src/vm/gcc3x-cointerp.c, line 44.
(gdb) run -breaksel pathName:isEncoded: trunk50-64.image
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /cygdrive/z/oscogvm/build.win64x64/squeak.cog.spur/builddbg/vm/Squeak.exe -breaksel pathName:isEncoded: trunk50-64.image
[New Thread 4080.0x5ec]
[New Thread 4080.0xb30]
etc...

_,,,^..^,,,_
best, Eliot




--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: Debugging Win64 Cog Spur

Nicolas Cellier
 


2017-05-25 17:49 GMT+02:00 Eliot Miranda <[hidden email]>:
Hi Nicolas,

On Wed, May 24, 2017 at 11:28 PM, Nicolas Cellier <[hidden email]> wrote:
Great, you reproduced exact same behavior.
The problem I have is effectively where to put the breakpoint.
I think we can believe the output of (gdb) call printCallStack()

here's one issue; the computation to see if the frame pointer is in use fails.  I'm executing this at the compilation break point for FilePath class>pathName:isEncoded:

(gdb) print /x CStackPointer
$7 = 0xef91d0
(gdb) print /x CFramePointer
$8 = 0x0
(gdb) print cFramePointerInUse
$9 = 0
(gdb) info registers
rax            0x68588f 6838415
rbx            0xffffffff       4294967295
rcx            0x68588f 6838415
rdx            0xabababab003a643a       -6076574521274768326
rsi            0xfde9   65001
rdi            0x0      0
rbp            0xef5db0 0xef5db0
rsp            0xef5c50 0xef5c50
r8             0x0      0
r9             0xfffffffffbefbc48       -68174776
r10            0xe36e626d44726839       -2058599758222432199
r11            0x8101010101010100       -9151031864016699136
r12            0xffffffff       4294967295
r13            0x20     32
r14            0x7ffc202018f0   140720847460592
r15            0xf2faf0 15923952
rip            0x4015d9 0x4015d9 <warning+9>
eflags         0x206    [ PF IF ]
cs             0x33     51
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x53     83
gs             0x2b     43
(gdb)
 

So ceCaptureStackPointers() must get the value of SP and eventually FP in the caller.
For getting caller SP, it must unstack the push RBX, and the return address.

But on Win64 ABI it's not enough, because stack space is reserved for the 4 register arguments even if there is less than 4...
So we must add 32 bytes more to callee SP in order to retrieve caller SP...

 

I've tried other means:
- analyze direct usage of registers RCX & co from VMMaker
  if ever it could conflicts with WIN64 logical register assignment
  But I did not find anything
- compile with MSVC 2017
  if ever the compiler could spit different warnings and give a clue
  alas it fails very early in readImageFromFileHeapSizeStartingAt (during checkAssumedCompactClasses)
 the failure is incomprehensible, because the debugger shows identical contents if I print:

        *((sqInt *)(classTableFirstPage+8+(51<<3)))    140697255509608    __int64
        *((sqInt *)(specialObjectsOop+8+(7<<3)))    140697255509608    __int64

nonetheless, the debugger enters into the if and execute
        invalidCompactClassError("Array");

I'll have to debug it at assembler level, but it's driving me away from the original problem...

Hmmm.  I doubt this is a problem because the assert and debug VMs would print a warning if this were wrong and they seem to be doing fine (I'm using the clang build).
 


This is a MSVC code generation bug.
 



--
_,,,^..^,,,_
best, Eliot

Reply | Threaded
Open this post in threaded view
|

Re: Debugging Win64 Cog Spur

Nicolas Cellier
 


2017-05-26 9:24 GMT+02:00 Nicolas Cellier <[hidden email]>:


2017-05-25 17:49 GMT+02:00 Eliot Miranda <[hidden email]>:
Hi Nicolas,

On Wed, May 24, 2017 at 11:28 PM, Nicolas Cellier <[hidden email]> wrote:
Great, you reproduced exact same behavior.
The problem I have is effectively where to put the breakpoint.
I think we can believe the output of (gdb) call printCallStack()

here's one issue; the computation to see if the frame pointer is in use fails.  I'm executing this at the compilation break point for FilePath class>pathName:isEncoded:

(gdb) print /x CStackPointer
$7 = 0xef91d0
(gdb) print /x CFramePointer
$8 = 0x0
(gdb) print cFramePointerInUse
$9 = 0
(gdb) info registers
rax            0x68588f 6838415
rbx            0xffffffff       4294967295
rcx            0x68588f 6838415
rdx            0xabababab003a643a       -6076574521274768326
rsi            0xfde9   65001
rdi            0x0      0
rbp            0xef5db0 0xef5db0
rsp            0xef5c50 0xef5c50
r8             0x0      0
r9             0xfffffffffbefbc48       -68174776
r10            0xe36e626d44726839       -2058599758222432199
r11            0x8101010101010100       -9151031864016699136
r12            0xffffffff       4294967295
r13            0x20     32
r14            0x7ffc202018f0   140720847460592
r15            0xf2faf0 15923952
rip            0x4015d9 0x4015d9 <warning+9>
eflags         0x206    [ PF IF ]
cs             0x33     51
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x53     83
gs             0x2b     43
(gdb)
 

So ceCaptureStackPointers() must get the value of SP and eventually FP in the caller.
For getting caller SP, it must unstack the push RBX, and the return address.

But on Win64 ABI it's not enough, because stack space is reserved for the 4 register arguments even if there is less than 4...
So we must add 32 bytes more to callee SP in order to retrieve caller SP...


Hmmm wrong guess from my side.
Some calls do explicitely reserve the stack space for saving the 4 register parameters like:

   0x518d06 <generateCaptureCStackPointers+150>:        sub    $0x20,%rsp
   0x518d0a <generateCaptureCStackPointers+154>:        callq  0x506220 <zeroOpcodeIndexForNewOpcodes>
   0x518d0f <generateCaptureCStackPointers+159>:        add    $0x20,%rsp

but some do not:

   0x4ed822 <generateStackPointerCapture+178>:  mov    0x2071f7(%rip),%rdx        # 0x6f4a20 <methodZoneBase>
   0x4ed829 <generateStackPointerCapture+185>:  mov    %rdx,0x70(%rsp)
   0x4ed82e <generateStackPointerCapture+190>:  mov    0x207203(%rip),%rdx        # 0x6f4a38 <trampolineTableIndex>
   0x4ed835 <generateStackPointerCapture+197>:  mov    %rdx,0x68(%rsp)
   0x4ed83a <generateStackPointerCapture+202>:  callq  0x518c70 <generateCaptureCStackPointers>
   0x4ed83f <generateStackPointerCapture+207>:  callq  *0x2b199b(%rip)        # 0x79f1e0 <ceCaptureCStackPointers>
   0x4ed845 <generateStackPointerCapture+213>:  callq  0x543650 <isCFramePointerInUse>
   0x4ed84a <generateStackPointerCapture+218>:  movslq %eax,%rcx
   0x4ed84d <generateStackPointerCapture+221>:  mov    %rcx,0x2b1844(%rip)        # 0x79f098 <cFramePointerInUse>

If I put a breakpoint:
(gdb) break generateStackPointerCapture

then I see that the frame pointer is not in use in this function:

(gdb) print $rbp
$6 = (void *) 0x0

then:

(gdb) print $rsp
$7 = (void *) 0xf2f250

if I step over ceCaptureCStackPointers()
=> 0x4e00010:   push   %rbx
   0x4e00011:   mov    $0x6dc018,%rbx
   0x4e00018:   mov    %rbp,0xc3158(%rbx)
   0x4e0001f:   mov    %rsp,%rax
   0x4e00022:   add    $0x30,%rax
   0x4e00026:   mov    %rax,0xc3140(%rbx)
   0x4e0002d:   pop    %rbx

i find:

(gdb) print CFramePointer
$10 = (void *) 0x0
(gdb) print CStackPointer
$9 = (void *) 0xf2f270

0x20 too much...

The other sender of ceCaptureCStackPointers does not use sub    $0x20,%rsp either

   0x428310 <enterSmalltalkExecutiveImplementation+144>:        xor    %eax,%eax
   0x428312 <enterSmalltalkExecutiveImplementation+146>:        mov    %al,%dl
   0x428314 <enterSmalltalkExecutiveImplementation+148>:        mov    %dl,0x4d(%rsp)
   0x428318 <enterSmalltalkExecutiveImplementation+152>:        mov    0x4d(%rsp),%al
   0x42831c <enterSmalltalkExecutiveImplementation+156>:        mov    %al,0x4c(%rsp)
   0x428320 <enterSmalltalkExecutiveImplementation+160>:        callq  *0x376eba(%rip)        # 0x79f1e0 <ceCaptureCStackPointers>
   0x428326 <enterSmalltalkExecutiveImplementation+166>:        lea    0x376c33(%rip),%rcx        # 0x79ef60 <reenterInterpreter>

So I will have to revert last VMMaker change...

 

I've tried other means:
- analyze direct usage of registers RCX & co from VMMaker
  if ever it could conflicts with WIN64 logical register assignment
  But I did not find anything
- compile with MSVC 2017
  if ever the compiler could spit different warnings and give a clue
  alas it fails very early in readImageFromFileHeapSizeStartingAt (during checkAssumedCompactClasses)
 the failure is incomprehensible, because the debugger shows identical contents if I print:

        *((sqInt *)(classTableFirstPage+8+(51<<3)))    140697255509608    __int64
        *((sqInt *)(specialObjectsOop+8+(7<<3)))    140697255509608    __int64

nonetheless, the debugger enters into the if and execute
        invalidCompactClassError("Array");

I'll have to debug it at assembler level, but it's driving me away from the original problem...

Hmmm.  I doubt this is a problem because the assert and debug VMs would print a warning if this were wrong and they seem to be doing fine (I'm using the clang build).
 


This is a MSVC code generation bug.
 



--
_,,,^..^,,,_
best, Eliot


Reply | Threaded
Open this post in threaded view
|

Re: Debugging Win64 Cog Spur

Eliot Miranda-2
 
Hi Nicolas,

    as far as I can see in https://msdn.microsoft.com/en-us/library/ms235286.aspx we do not simulate the shadow space convention so any bugs will not show up in the simulator.  I will modify the simulator to smash the shadow space on the stack and see how far we get.  That should break things and hence make it possible to debug and fix the issue.  The convention is pretty simple:

"The caller is responsible for allocating space for parameters to the callee, and must always allocate sufficient space to store four register parameters, even if the callee doesn’t take that many parameters. This simplifies support for unprototyped C-language functions, and vararg C/C++ functions. For vararg or unprototyped functions, any floating point values must be duplicated in the corresponding general-purpose register. Any parameters beyond the first four must be stored on the stack, above the shadow store for the first four, prior to the call. Vararg function details can be found in Varargs. Unprototyped function information is detailed in Unprototyped Functions."

There may be places where we simply call directly into the run-time and forget to make the shadow space available.

On Fri, May 26, 2017 at 5:55 AM, Nicolas Cellier <[hidden email]> wrote:


2017-05-26 9:24 GMT+02:00 Nicolas Cellier <[hidden email]>:


2017-05-25 17:49 GMT+02:00 Eliot Miranda <[hidden email]>:
Hi Nicolas,

On Wed, May 24, 2017 at 11:28 PM, Nicolas Cellier <[hidden email]> wrote:
Great, you reproduced exact same behavior.
The problem I have is effectively where to put the breakpoint.
I think we can believe the output of (gdb) call printCallStack()

here's one issue; the computation to see if the frame pointer is in use fails.  I'm executing this at the compilation break point for FilePath class>pathName:isEncoded:

(gdb) print /x CStackPointer
$7 = 0xef91d0
(gdb) print /x CFramePointer
$8 = 0x0
(gdb) print cFramePointerInUse
$9 = 0
(gdb) info registers
rax            0x68588f 6838415
rbx            0xffffffff       4294967295
rcx            0x68588f 6838415
rdx            0xabababab003a643a       -6076574521274768326
rsi            0xfde9   65001
rdi            0x0      0
rbp            0xef5db0 0xef5db0
rsp            0xef5c50 0xef5c50
r8             0x0      0
r9             0xfffffffffbefbc48       -68174776
r10            0xe36e626d44726839       -2058599758222432199
r11            0x8101010101010100       -9151031864016699136
r12            0xffffffff       4294967295
r13            0x20     32
r14            0x7ffc202018f0   140720847460592
r15            0xf2faf0 15923952
rip            0x4015d9 0x4015d9 <warning+9>
eflags         0x206    [ PF IF ]
cs             0x33     51
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x53     83
gs             0x2b     43
(gdb)
 

So ceCaptureStackPointers() must get the value of SP and eventually FP in the caller.
For getting caller SP, it must unstack the push RBX, and the return address.

But on Win64 ABI it's not enough, because stack space is reserved for the 4 register arguments even if there is less than 4...
So we must add 32 bytes more to callee SP in order to retrieve caller SP...


Hmmm wrong guess from my side.
Some calls do explicitely reserve the stack space for saving the 4 register parameters like:

   0x518d06 <generateCaptureCStackPointers+150>:        sub    $0x20,%rsp
   0x518d0a <generateCaptureCStackPointers+154>:        callq  0x506220 <zeroOpcodeIndexForNewOpcodes>
   0x518d0f <generateCaptureCStackPointers+159>:        add    $0x20,%rsp

but some do not:

   0x4ed822 <generateStackPointerCapture+178>:  mov    0x2071f7(%rip),%rdx        # 0x6f4a20 <methodZoneBase>
   0x4ed829 <generateStackPointerCapture+185>:  mov    %rdx,0x70(%rsp)
   0x4ed82e <generateStackPointerCapture+190>:  mov    0x207203(%rip),%rdx        # 0x6f4a38 <trampolineTableIndex>
   0x4ed835 <generateStackPointerCapture+197>:  mov    %rdx,0x68(%rsp)
   0x4ed83a <generateStackPointerCapture+202>:  callq  0x518c70 <generateCaptureCStackPointers>
   0x4ed83f <generateStackPointerCapture+207>:  callq  *0x2b199b(%rip)        # 0x79f1e0 <ceCaptureCStackPointers>
   0x4ed845 <generateStackPointerCapture+213>:  callq  0x543650 <isCFramePointerInUse>
   0x4ed84a <generateStackPointerCapture+218>:  movslq %eax,%rcx
   0x4ed84d <generateStackPointerCapture+221>:  mov    %rcx,0x2b1844(%rip)        # 0x79f098 <cFramePointerInUse>

If I put a breakpoint:
(gdb) break generateStackPointerCapture

then I see that the frame pointer is not in use in this function:

(gdb) print $rbp
$6 = (void *) 0x0

then:

(gdb) print $rsp
$7 = (void *) 0xf2f250

if I step over ceCaptureCStackPointers()
=> 0x4e00010:   push   %rbx
   0x4e00011:   mov    $0x6dc018,%rbx
   0x4e00018:   mov    %rbp,0xc3158(%rbx)
   0x4e0001f:   mov    %rsp,%rax
   0x4e00022:   add    $0x30,%rax
   0x4e00026:   mov    %rax,0xc3140(%rbx)
   0x4e0002d:   pop    %rbx

i find:

(gdb) print CFramePointer
$10 = (void *) 0x0
(gdb) print CStackPointer
$9 = (void *) 0xf2f270

0x20 too much...

The other sender of ceCaptureCStackPointers does not use sub    $0x20,%rsp either

   0x428310 <enterSmalltalkExecutiveImplementation+144>:        xor    %eax,%eax
   0x428312 <enterSmalltalkExecutiveImplementation+146>:        mov    %al,%dl
   0x428314 <enterSmalltalkExecutiveImplementation+148>:        mov    %dl,0x4d(%rsp)
   0x428318 <enterSmalltalkExecutiveImplementation+152>:        mov    0x4d(%rsp),%al
   0x42831c <enterSmalltalkExecutiveImplementation+156>:        mov    %al,0x4c(%rsp)
   0x428320 <enterSmalltalkExecutiveImplementation+160>:        callq  *0x376eba(%rip)        # 0x79f1e0 <ceCaptureCStackPointers>
   0x428326 <enterSmalltalkExecutiveImplementation+166>:        lea    0x376c33(%rip),%rcx        # 0x79ef60 <reenterInterpreter>

So I will have to revert last VMMaker change...

 

I've tried other means:
- analyze direct usage of registers RCX & co from VMMaker
  if ever it could conflicts with WIN64 logical register assignment
  But I did not find anything
- compile with MSVC 2017
  if ever the compiler could spit different warnings and give a clue
  alas it fails very early in readImageFromFileHeapSizeStartingAt (during checkAssumedCompactClasses)
 the failure is incomprehensible, because the debugger shows identical contents if I print:

        *((sqInt *)(classTableFirstPage+8+(51<<3)))    140697255509608    __int64
        *((sqInt *)(specialObjectsOop+8+(7<<3)))    140697255509608    __int64

nonetheless, the debugger enters into the if and execute
        invalidCompactClassError("Array");

I'll have to debug it at assembler level, but it's driving me away from the original problem...

Hmmm.  I doubt this is a problem because the assert and debug VMs would print a warning if this were wrong and they seem to be doing fine (I'm using the clang build).
 


This is a MSVC code generation bug.
 



--
_,,,^..^,,,_
best, Eliot





--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: Debugging Win64 Cog Spur

Eliot Miranda-2
 
Hi Nicolas,

    these commits fix start up:

[Vm-dev] VM Maker: VMMaker.oscog-eem.2225.mcz
0364ee: CogVM source as per VMMaker.oscog-eem.2227

The main issue was that there was a code generation error for JumpR that caused jmp %r8 to be generated as jmp %rax, which broke open PIC dispatch.  This didn't affect SysV because the reg assignments for ClassReg are different.

So now we have a working Cogit for Win64.  I still need to run full tests, but at least it starts up.  It would be good to check whether the change to compileInterpreterPrimitive:flags: is actually needed.  It seems like the right thing to do, but with the change to the C stack it might be irrelevant.

On Sat, May 27, 2017 at 8:21 AM, Eliot Miranda <[hidden email]> wrote:
Hi Nicolas,

    as far as I can see in https://msdn.microsoft.com/en-us/library/ms235286.aspx we do not simulate the shadow space convention so any bugs will not show up in the simulator.  I will modify the simulator to smash the shadow space on the stack and see how far we get.  That should break things and hence make it possible to debug and fix the issue.  The convention is pretty simple:

"The caller is responsible for allocating space for parameters to the callee, and must always allocate sufficient space to store four register parameters, even if the callee doesn’t take that many parameters. This simplifies support for unprototyped C-language functions, and vararg C/C++ functions. For vararg or unprototyped functions, any floating point values must be duplicated in the corresponding general-purpose register. Any parameters beyond the first four must be stored on the stack, above the shadow store for the first four, prior to the call. Vararg function details can be found in Varargs. Unprototyped function information is detailed in Unprototyped Functions."

There may be places where we simply call directly into the run-time and forget to make the shadow space available.

On Fri, May 26, 2017 at 5:55 AM, Nicolas Cellier <[hidden email]> wrote:


2017-05-26 9:24 GMT+02:00 Nicolas Cellier <[hidden email]>:


2017-05-25 17:49 GMT+02:00 Eliot Miranda <[hidden email]>:
Hi Nicolas,

On Wed, May 24, 2017 at 11:28 PM, Nicolas Cellier <[hidden email]> wrote:
Great, you reproduced exact same behavior.
The problem I have is effectively where to put the breakpoint.
I think we can believe the output of (gdb) call printCallStack()

here's one issue; the computation to see if the frame pointer is in use fails.  I'm executing this at the compilation break point for FilePath class>pathName:isEncoded:

(gdb) print /x CStackPointer
$7 = 0xef91d0
(gdb) print /x CFramePointer
$8 = 0x0
(gdb) print cFramePointerInUse
$9 = 0
(gdb) info registers
rax            0x68588f 6838415
rbx            0xffffffff       4294967295
rcx            0x68588f 6838415
rdx            0xabababab003a643a       -6076574521274768326
rsi            0xfde9   65001
rdi            0x0      0
rbp            0xef5db0 0xef5db0
rsp            0xef5c50 0xef5c50
r8             0x0      0
r9             0xfffffffffbefbc48       -68174776
r10            0xe36e626d44726839       -2058599758222432199
r11            0x8101010101010100       -9151031864016699136
r12            0xffffffff       4294967295
r13            0x20     32
r14            0x7ffc202018f0   140720847460592
r15            0xf2faf0 15923952
rip            0x4015d9 0x4015d9 <warning+9>
eflags         0x206    [ PF IF ]
cs             0x33     51
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x53     83
gs             0x2b     43
(gdb)
 

So ceCaptureStackPointers() must get the value of SP and eventually FP in the caller.
For getting caller SP, it must unstack the push RBX, and the return address.

But on Win64 ABI it's not enough, because stack space is reserved for the 4 register arguments even if there is less than 4...
So we must add 32 bytes more to callee SP in order to retrieve caller SP...


Hmmm wrong guess from my side.
Some calls do explicitely reserve the stack space for saving the 4 register parameters like:

   0x518d06 <generateCaptureCStackPointers+150>:        sub    $0x20,%rsp
   0x518d0a <generateCaptureCStackPointers+154>:        callq  0x506220 <zeroOpcodeIndexForNewOpcodes>
   0x518d0f <generateCaptureCStackPointers+159>:        add    $0x20,%rsp

but some do not:

   0x4ed822 <generateStackPointerCapture+178>:  mov    0x2071f7(%rip),%rdx        # 0x6f4a20 <methodZoneBase>
   0x4ed829 <generateStackPointerCapture+185>:  mov    %rdx,0x70(%rsp)
   0x4ed82e <generateStackPointerCapture+190>:  mov    0x207203(%rip),%rdx        # 0x6f4a38 <trampolineTableIndex>
   0x4ed835 <generateStackPointerCapture+197>:  mov    %rdx,0x68(%rsp)
   0x4ed83a <generateStackPointerCapture+202>:  callq  0x518c70 <generateCaptureCStackPointers>
   0x4ed83f <generateStackPointerCapture+207>:  callq  *0x2b199b(%rip)        # 0x79f1e0 <ceCaptureCStackPointers>
   0x4ed845 <generateStackPointerCapture+213>:  callq  0x543650 <isCFramePointerInUse>
   0x4ed84a <generateStackPointerCapture+218>:  movslq %eax,%rcx
   0x4ed84d <generateStackPointerCapture+221>:  mov    %rcx,0x2b1844(%rip)        # 0x79f098 <cFramePointerInUse>

If I put a breakpoint:
(gdb) break generateStackPointerCapture

then I see that the frame pointer is not in use in this function:

(gdb) print $rbp
$6 = (void *) 0x0

then:

(gdb) print $rsp
$7 = (void *) 0xf2f250

if I step over ceCaptureCStackPointers()
=> 0x4e00010:   push   %rbx
   0x4e00011:   mov    $0x6dc018,%rbx
   0x4e00018:   mov    %rbp,0xc3158(%rbx)
   0x4e0001f:   mov    %rsp,%rax
   0x4e00022:   add    $0x30,%rax
   0x4e00026:   mov    %rax,0xc3140(%rbx)
   0x4e0002d:   pop    %rbx

i find:

(gdb) print CFramePointer
$10 = (void *) 0x0
(gdb) print CStackPointer
$9 = (void *) 0xf2f270

0x20 too much...

The other sender of ceCaptureCStackPointers does not use sub    $0x20,%rsp either

   0x428310 <enterSmalltalkExecutiveImplementation+144>:        xor    %eax,%eax
   0x428312 <enterSmalltalkExecutiveImplementation+146>:        mov    %al,%dl
   0x428314 <enterSmalltalkExecutiveImplementation+148>:        mov    %dl,0x4d(%rsp)
   0x428318 <enterSmalltalkExecutiveImplementation+152>:        mov    0x4d(%rsp),%al
   0x42831c <enterSmalltalkExecutiveImplementation+156>:        mov    %al,0x4c(%rsp)
   0x428320 <enterSmalltalkExecutiveImplementation+160>:        callq  *0x376eba(%rip)        # 0x79f1e0 <ceCaptureCStackPointers>
   0x428326 <enterSmalltalkExecutiveImplementation+166>:        lea    0x376c33(%rip),%rcx        # 0x79ef60 <reenterInterpreter>

So I will have to revert last VMMaker change...

 

I've tried other means:
- analyze direct usage of registers RCX & co from VMMaker
  if ever it could conflicts with WIN64 logical register assignment
  But I did not find anything
- compile with MSVC 2017
  if ever the compiler could spit different warnings and give a clue
  alas it fails very early in readImageFromFileHeapSizeStartingAt (during checkAssumedCompactClasses)
 the failure is incomprehensible, because the debugger shows identical contents if I print:

        *((sqInt *)(classTableFirstPage+8+(51<<3)))    140697255509608    __int64
        *((sqInt *)(specialObjectsOop+8+(7<<3)))    140697255509608    __int64

nonetheless, the debugger enters into the if and execute
        invalidCompactClassError("Array");

I'll have to debug it at assembler level, but it's driving me away from the original problem...

Hmmm.  I doubt this is a problem because the assert and debug VMs would print a warning if this were wrong and they seem to be doing fine (I'm using the clang build).
 


This is a MSVC code generation bug.
 



--
_,,,^..^,,,_
best, Eliot





--
_,,,^..^,,,_
best, Eliot



--
_,,,^..^,,,_
best, Eliot