|
From: | Josh Freeman |
Subject: | Re: crash upon startup in GWorkcenter, Recycler, ProjectCenter and Gorm |
Date: | Sat, 23 Dec 2017 00:19:33 -0500 |
On Dec 22, 2017, at 10:05 AM, Richard Frith-Macdonald wrote:
I think that's a possible indication of a bug in the compiler/ runtime nonfragile API support; it looks as if there may be an inconsistency in calculating the size of a structure used as an ivar.
After more testing, here's some additional info that might be useful:
-- Hardware (virtual): Clean, up-to-date Ubuntu 16.04 VM, 32bit-- GNUstep install: Used the current Ubuntu script from the wiki, with two changes: - Removed the script's workaround for the ivar-offset issue (so it builds using the current version of gnustep-make instead of an old version). - Built a version of gnustep-base from a couple days ago (Dec. 19) which still used the obsolete -fobjc-nonfragile-abi build flag. (As previously mentioned, the current version of base won't build with the Ubuntu script if the script's ivar-offset workaround is removed).
-- Attachments:Attached are 2 patches that print info about NSThread ivars at runtime, and 2 debugging-session outputs that were run with patches installed. The patches assume that the libs-base & libs-gui directories are in the same parent directory as the libobjc2 directory (as they are when running the Ubuntu script), because the patches add an #import "../../libobjc2/ivar.h" in order to use the runtime's ivar & ivar_list types.
The patches - one for base, and one for gui - each add a static C function that prints out the objc-runtime's NSThread ivar layout, obtained using the runtime API function, class_getIvarLayout(). The patch function also prints the locations in memory of the current thread's instance members, obtained using statements like: &thread- >_gcontext.
The base patch adds the diagnostic function to NSAutoreleasePool.m, & calls it from -[NSAutorelease dealloc] before the NSThread's _autorelease_vars is modified (which will leave a garbage value in _gcontext). The gui patch adds a matching function to NSGraphicsContext.m & calls it from -[NSGraphicsContext setCurrentContext:] before it frees the garbage value in _gcontext & segfaults.
The first output file, patched_base_session_output.txt, is the result of installing only the base patch & running a debug session.
The second output file, patched_base_and_gui_session_output.txt, is a followup session after also installing the gui patch. In this session, gnustep-base (previously patched) was not rebuilt, so base is the same binary as the first session.
-- Output summary:In the first session (patched_base_session_output.txt), the diagnostic output shows most of the NSThread members' addresses line up with the runtime's ivar layout - up until the first member after the _autorelease_vars struct, _gcontext.
The runtime's ivar-layout offset for NSThread's _autorelease_vars member is 44, and _gcontext's is 64. The 20-byte space for _autorelease_vars is the smallest possible size for that struct on 32bit, as the struct is made up of 5 members, each of which is 4 bytes (3 ints & 2 pointers - see struct autorelease_thread_vars definition in NSAutoreleasePool.h).
However, the memory address of thread->_gcontext is offset from the thread instance's address by 56 - not the 64 listed by the runtime API - which is only 12 bytes away from thread->_autorelease_vars. At that offset value, _gcontext occupies the same memory location as the _autorelease_vars struct member, pool_cache_size; When pool_cache_size is set in NSAutoreleasePool.m (base), the same value will be accessed later in NSGraphicsContext.m (gui) as _gcontext, causing a crash due to sending an objc message to a non-nil garbage value.
In the second session (patched_base_and_gui_session_output.txt), the NSThread ivar diagnostics are also printed from within gui. Two notes:
1) I had thought the "ivar-offset" issue was due to a mismatch between gui & base ivar offsets (gui using a different offset to _gcontext than base), but the output shows that this is not the case: gui & base actually match each other's objc2-runtime ivar layouts and also have matching memory addresses returned from &thread->member statements; The mismatch is between the runtime's ivar layout and the memory addresses returned by &thread->member statements.
2) NSThread's ivar layout (obtained from the runtime API) was the same between the first & second sessions. This was expected, because the only change made between sessions was to add a static C function to NSGraphicsContext.m, and a call to it from -[NSGraphicsContext setCurrentContext:].
Unexpectedly, the memory locations of NSThread members (as obtained from &thread->member statements) did change between the two sessions, and not just in gui, which was recompiled - it also changed in base, even though it was the same binary in both sessions.
The first session had memory address offsets (obtained from &thread->member statements) for _autorelease_vars & _gcontext at 44 & 56, respectively; In the second session, those members' offsets were 36 & 56 (_gcontext stayed in the same place, _autorelease_vars shifted 8 bytes down, and now both members' addresses disagreed with the runtime's ivar layout offsets of 44 & 64).
The changed offsets also changed the location of the crash, because the in the second session, _autorelease_vars & _gcontext were separated by 20 bytes instead of 12 bytes, so they no longer overlapped. Subsequently, the call to -[NSGraphicsContext setCurrentContext:] (where the first session crashed) returned successfully, because _gcontext wasn't overwritten by a garbage value when writing pool_cache_size. (The crash happened at a later point, in -[GSWindowDecorationView layout]; Stack trace is in the output file).
Hope this helps track down the issue. Cheers, JoshP.S Using the current Ubuntu script unmodified (leaving the workaround in place) still works fine - everything builds, and no crashes when starting apps.
patched_base_session_output.txt
Description: Text document
patched_base_and_gui_session_output.txt
Description: Text document
base-NSThread-diagnostic.diff
Description: Binary data
gui-NSThread-diagnostic.diff
Description: Binary data
[Prev in Thread] | Current Thread | [Next in Thread] |