BlogMatrix
 

Patching wxMutexInternal

edit David Janes 2004-06-28 23:29 UTC  ·

Last week I was having a pretty serious problem with wxPython on the Macintosh. The following block of code in wxPython is just plain wrong and after a few hours of operations the m_waiters array would be corrupted because of multiple threads contending for the memory.

If you're running wxPython 2.5.1.5, seriously considering patching your library as per these instructions.

while ( m_owner != kNoThreadID && m_owner != current)
{
 m_waiters.Add(current);
 ::SetThreadStateEndCritical(kCurrentThreadID, kStoppedThreadState, m_owner);
 ::ThreadBeginCritical();
}

It should read (as far as I can tell)

while ( m_owner != kNoThreadID && m_owner != current)
{
 ::SetThreadStateEndCritical(kCurrentThreadID, kStoppedThreadState, m_owner);
 m_waiters.Add(current);
 ::ThreadBeginCritical();
}

I knew that rebuilding wxPython libraries from scratch could end up being more trouble than it's worth, so I decided to take a different approach. Last Friday night I did an extending hacking session on wxPython's libraries. I downloaded Apple's Xcode developer tools and disassembled (using objdump) the broken module. Looking at the PowerPC opcodes, I figured one little tweak would do the trick. I needed to change this:

675f8:   38 a0 00 01     li      r5,1
675fc:   4b fb 17 39     bl      [wxBaseArrayLong::Add]

67600:   38 60 00 01     li      r3,1
67604:   38 80 00 01     li      r4,1
67608:   80 be 00 00     lwz     r5,0(r30)
6760c:   48 01 30 69     bl      [::SetThreadStateEndCritical]

67610:   48 01 30 a5     bl      [::ThreadBeginCritical]
to this:
67610:   48 01 30 a5     bl      [::ThreadBeginCritical]

675f8:   38 a0 00 01     li      r5,1
675fc:   4b fb 17 39     bl      [wxBaseArrayLong::Add]

67600:   38 60 00 01     li      r3,1
67604:   38 80 00 01     li      r4,1
67608:   80 be 00 00     lwz     r5,0(r30)
6760c:   48 01 30 69     bl      [::SetThreadStateEndCritical]

Note that I had to figure out those symbolic names — the objdump program didn't do that for me. Here's a Python program to make the fix:

old_data = [
 0x38, 0xa0, 0x00, 0x01, 
 0x4b, 0xfb, 0x17, 0x39,  # wxArrayLong::Add
 0x38, 0x60, 0x00, 0x01,
 0x38, 0x80, 0x00, 0x01,
 0x80, 0xbe, 0x00, 0x00,
 0x48, 0x01, 0x30, 0x69,  # ::SetThreadStateEndCritical
 0x48, 0x01, 0x30, 0xa5,  # ::ThreadBeginCritical
]

new_data = [
 0x48, 0x01, 0x30, 0xa5,  # ::ThreadBeginCritical
 0x38, 0xa0, 0x00, 0x01, 
 0x4b, 0xfb, 0x17, 0x39,  # wxArrayLong::Add
 0x38, 0x60, 0x00, 0x01,
 0x38, 0x80, 0x00, 0x01,
 0x80, 0xbe, 0x00, 0x00,
 0x48, 0x01, 0x30, 0x69,  # ::SetThreadStateEndCritical
]

fio = open('build/Jaeger.app/Contents/Frameworks/libwx_base_carbond-2.5.1.0.0.dylib', 'rb+')
fio.seek(start)

f_in = fio.read(len(old_data))

for i in xrange(len(old_data)):
 if old_data[i] != ord(f_in[i]):
  print >> sys.stderr, "data did not match"
  print >> sys.stderr, "%d: %02x %02x" % ( i, old_data[i], ord(f_in[i]) )
  sys.exit(1)

fio.seek(start)

for i in xrange(len(new_data)):
 fio.write(chr(new_data[i]))
 
fio.close()

There was one other piece of code causing a crash in Jäger. My advice — never never ever try to modify wxPython objects from outside their thread: it ain't ever going to work.