Forward reference to thread variable segfaults
Open Dylan 2013.1 OS X
In testworks I was rearranging the code into different files and started getting a segfault during library initialization. It was fairly hard to debug. Turned out to be a forward reference to a thread variable in a different file. i.e., the file occurred later in the LID file. Here's a library that reproduces the problem:
==> library.dylan <==
module: dylan-user
define library segfault
use common-dylan;
use io;
end library;
define module segfault
use common-dylan, exclude: { format-to-string };
use format-out;
==> segfault1.dylan <==
module: segfault
define function main (name :: <string>, arguments :: <vector>)
format-out("Hello, %s!\n", *thread-variable*);
exit-application(0);
end;
main(application-name(), application-arguments());
==> segfault2.dylan <==
module: segfault
define thread variable *thread-variable* = "world";
==> segfault.lid <==
library: segfault
files: library
segfault1
segfault2
cgay-macbookpro2:segfault cgay$ lldb _build/bin/segfault
Current executable set to '_build/bin/segfault' (i386).
(lldb) process launch
process launch
Process 63839 launched: '/Users/cgay/dylan/src/segfault/_build/bin/segfault' (i386)
Process 63839 stopped
* thread #1: tid = 0x1d2c87, 0x00109cd2 libdylan.dylib`primitive_read_thread_variable(h=0x001389c4) + 34 at posix-threads.c:1548, queue = 'com.apple.main-thread, stop reason = EXC_BAD_ACCESS (code=1, address=0x922630)
frame #0: 0x00109cd2 libdylan.dylib`primitive_read_thread_variable(h=0x001389c4) + 34 at posix-threads.c:1548
(lldb) bt
bt
* thread #1: tid = 0x1d2c87, 0x00109cd2 libdylan.dylib`primitive_read_thread_variable(h=0x001389c4) + 34 at posix-threads.c:1548, queue = 'com.apple.main-thread, stop reason = EXC_BAD_ACCESS (code=1, address=0x922630)
frame #0: 0x00109cd2 libdylan.dylib`primitive_read_thread_variable(h=0x001389c4) + 34 at posix-threads.c:1548
frame #1: 0x00004e71 libsegfault.dylib`_Init_segfault__X_segfault1_for_user [inlined] KmainVsegfaultI + 42 at segfault1.c:112
frame #2: 0x00004e47 libsegfault.dylib`_Init_segfault__X_segfault1_for_user + 23 at segfault1.c:144
frame #3: 0x00004d1a libsegfault.dylib`_Init_segfault_ + 74 at _glue.c:24
frame #4: 0x8fe13cda dyld`ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) + 230
frame #5: 0x8fe13fde dyld`ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&) + 64
frame #6: 0x8fe10268 dyld`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&) + 356
frame #7: 0x8fe101cc dyld`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&) + 200
frame #8: 0x8fe100ba dyld`ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 62
frame #9: 0x8fe01e05 dyld`dyld::initializeMainExecutable() + 211
frame #10: 0x8fe05adb dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 3050
frame #11: 0x8fe01376 dyld`dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*) + 704
frame #12: 0x8fe01077 dyld`_dyld_start + 71
The variable/constant needn't be a thread variable. I just ran into the same problem with protocol buffers, in which I tried to update an $introspection-data table with top-level code that occurred in a file before it was defined.
Since we do library-at-a-time compilation, I assume that we have a view of all the definitions regardless of file. We may or may not currently be able to infer a complete ordering? It would be great if we could emit a serious warning for such code ("attempt to use $introspection-data which is not yet initialized") and not segfault.
I'm dropping this here on the theory that it's a similar/related issue.
I have top-level code that makes an instance of <dylan-field-descriptor-proto>, which is defined in a subsequent file. (I'm pretty certain this is illegal, so this is really just about improving the failure mode.) The test suite dies with this error when it attempts to access a field in the class via field-descriptor-proto-name. So I'm guessing that the top-level code that makes the instance somehow executes without error but hasn't correctly initialized the class object?
$ $PB/../_build/bin/protocol-buffers-test-suite --debug crashes --test 'test-<file-descriptor-set>-introspection'
Test test-<file-descriptor-set>-introspection:
#f is not of type {<class>: <integer>}
Backtrace:
0
default-handler:dylan:dylan##1 + 0x12
0x79643a6e616c7964
default-last-handler:common-dylan-internals:common-dylan##0 + 0x2f6
error:dylan:dylan##0 + 0x27
type-check-error:internal:dylan + 0x6d
make-slot-access-engine-node:dispatch-engine-internal:dylan + 0x238
transmogrify-method-list-grounded:dispatch-engine-internal:dylan + 0x28c
compute-terminal-engine-node:dispatch-engine-internal:dylan + 0x8b
compute-discriminator-for-arg:dispatch-engine-internal:dylan + 0x2a4
compute-dispatch-from-root:dispatch-engine-internal:dylan + 0x2b
Kanonymous_of_PPtest_Lfile_descriptor_setG_introspectionF51I + 0x13
do-check-equal:%testworks:testworks + 0x335
%%test-<file-descriptor-set>-introspection:protocol-buffers-test-suite:protocol-buffers-test-suite + 0x261
Kanonymous_of_execute_componentF95I + 0x360
do-with-profiling:common-dylan-internals:common-dylan + 0x2f
execute-component:%testworks:testworks##1 + 0x2cd
maybe-execute-component:%testworks:testworks##0 + 0x193
run-tests:testworks:testworks + 0x88
run-or-list-tests:%testworks:testworks + 0x71
run-test-application:testworks:testworks + 0x413
main + 0x19
__libc_start_main + 0xea
_start + 0x2a