summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rts/Linker.c75
-rw-r--r--rts/include/rts/Linker.h1
2 files changed, 61 insertions, 15 deletions
diff --git a/rts/Linker.c b/rts/Linker.c
index 383f68abc8..cac82891ae 100644
--- a/rts/Linker.c
+++ b/rts/Linker.c
@@ -98,11 +98,11 @@
Note [runtime-linker-phases]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Broadly the behavior of the runtime linker can be
- split into the following four phases:
+ split into the following five phases:
- Indexing (e.g. ocVerifyImage and ocGetNames)
- - Initialization (e.g. ocResolve and ocRunInit)
- - Resolve (e.g. resolveObjs())
+ - Initialization (e.g. ocResolve)
+ - RunInit (e.g. ocRunInit)
- Lookup (e.g. lookupSymbol)
This is to enable lazy loading of symbols. Eager loading is problematic
@@ -132,14 +132,22 @@
* During resolve we attempt to resolve all the symbols needed for the
initial link. This essentially means, that for any ObjectCode given
- directly to the command-line we perform lookupSymbols on the required
- symbols. lookupSymbols may trigger the loading of additional ObjectCode
- if required.
+ directly to the command-line we perform lookupSymbol on the required
+ symbols. lookupSymbol may trigger the loading of additional ObjectCode
+ if required. After resolving an object we mark its text as executable and
+ not writable.
This phase will produce ObjectCode with status `OBJECT_RESOLVED` if
the previous status was `OBJECT_NEEDED`.
- * lookupSymbols is used to lookup any symbols required, both during initial
+ * During RunInit we run the initializers ("constructors") of the objects
+ that are in `OBJECT_RESOLVED` state and move them to `OBJECT_READY` state.
+ This must be in a separate phase since we must ensure that all needed
+ objects have been fully resolved before we can run their initializers.
+ This is particularly tricky in the presence of cyclic dependencies (see
+ #21253).
+
+ * lookupSymbol is used to lookup any symbols required, both during initial
link and during statement and expression compilations in the REPL.
Declaration of e.g. a foreign import, will eventually call lookupSymbol
which will either fail (symbol unknown) or succeed (and possibly trigger a
@@ -170,8 +178,11 @@ StrHashTable *symhash;
Mutex linker_mutex;
#endif
-/* Generic wrapper function to try and Resolve and RunInit oc files */
-int ocTryLoad( ObjectCode* oc );
+/* Generic wrapper function to try and resolve oc files */
+static int ocTryLoad( ObjectCode* oc );
+/* Run initializers */
+static int ocRunInit( ObjectCode* oc );
+static int runPendingInitializers (void);
static void ghciRemoveSymbolTable(StrHashTable *table, const SymbolName* key,
ObjectCode *owner)
@@ -282,6 +293,7 @@ int ghciInsertSymbolTable(
return 1;
}
else if ( pinfo->owner
+ && pinfo->owner->status != OBJECT_READY
&& pinfo->owner->status != OBJECT_RESOLVED
&& pinfo->owner->status != OBJECT_NEEDED)
{
@@ -296,7 +308,9 @@ int ghciInsertSymbolTable(
This is essentially emulating the behavior of a linker wherein it will always
link in object files that are .o file arguments, but only take object files
from archives as needed. */
- if (owner && (owner->status == OBJECT_NEEDED || owner->status == OBJECT_RESOLVED)) {
+ if (owner && (owner->status == OBJECT_NEEDED
+ || owner->status == OBJECT_RESOLVED
+ || owner->status == OBJECT_READY)) {
pinfo->value = data;
pinfo->owner = owner;
pinfo->strength = strength;
@@ -991,6 +1005,10 @@ SymbolAddr* lookupSymbol( SymbolName* lbl )
IF_DEBUG(linker, printLoadedObjects());
fflush(stderr);
}
+
+ if (!runPendingInitializers()) {
+ errorBelch("lookupSymbol: Failed to run initializers.");
+ }
RELEASE_LOCK(&linker_mutex);
return r;
}
@@ -1622,12 +1640,24 @@ int ocTryLoad (ObjectCode* oc) {
m32_allocator_flush(oc->rw_m32);
#endif
- // run init/init_array/ctors/mod_init_func
+ IF_DEBUG(linker, ocDebugBelch(oc, "resolved\n"));
+ oc->status = OBJECT_RESOLVED;
+
+ return 1;
+}
+
+// run init/init_array/ctors/mod_init_func
+int ocRunInit(ObjectCode *oc)
+{
+ if (oc->status != OBJECT_RESOLVED) {
+ return 1;
+ }
IF_DEBUG(linker, ocDebugBelch(oc, "running initializers\n"));
// See Note [Tracking foreign exports] in ForeignExports.c
foreignExportsLoadingObject(oc);
+ int r;
#if defined(OBJFORMAT_ELF)
r = ocRunInit_ELF ( oc );
#elif defined(OBJFORMAT_PEi386)
@@ -1640,10 +1670,22 @@ int ocTryLoad (ObjectCode* oc) {
foreignExportsFinishedLoadingObject();
if (!r) { return r; }
+ oc->status = OBJECT_READY;
- IF_DEBUG(linker, ocDebugBelch(oc, "resolved\n"));
- oc->status = OBJECT_RESOLVED;
+ return 1;
+}
+int runPendingInitializers (void)
+{
+ for (ObjectCode *oc = objects; oc; oc = oc->next) {
+ int r = ocRunInit(oc);
+ if (!r) {
+ errorBelch("Could not run initializers of Object Code %" PATH_FMT ".\n", OC_INFORMATIVE_FILENAME(oc));
+ IF_DEBUG(linker, printLoadedObjects());
+ fflush(stderr);
+ return r;
+ }
+ }
return 1;
}
@@ -1658,8 +1700,7 @@ static HsInt resolveObjs_ (void)
for (ObjectCode *oc = objects; oc; oc = oc->next) {
int r = ocTryLoad(oc);
- if (!r)
- {
+ if (!r) {
errorBelch("Could not load Object Code %" PATH_FMT ".\n", OC_INFORMATIVE_FILENAME(oc));
IF_DEBUG(linker, printLoadedObjects());
fflush(stderr);
@@ -1667,6 +1708,10 @@ static HsInt resolveObjs_ (void)
}
}
+ if (!runPendingInitializers()) {
+ return 0;
+ }
+
#if defined(PROFILING)
// collect any new cost centres & CCSs that were defined during runInit
refreshProfilingCCSs();
diff --git a/rts/include/rts/Linker.h b/rts/include/rts/Linker.h
index 143f1328d5..ae463bc05e 100644
--- a/rts/include/rts/Linker.h
+++ b/rts/include/rts/Linker.h
@@ -52,6 +52,7 @@ typedef enum {
OBJECT_LOADED,
OBJECT_NEEDED,
OBJECT_RESOLVED,
+ OBJECT_READY,
OBJECT_UNLOADED,
OBJECT_DONT_RESOLVE,
OBJECT_NOT_LOADED /* The object was either never loaded or has been