1 /**
2  * Written in the D programming language.
3  * Module initialization routines.
4  *
5  * Copyright: Copyright Digital Mars 2000 - 2013.
6  * License: Distributed under the
7  *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
8  *    (See accompanying file LICENSE)
9  * Authors:   Walter Bright, Sean Kelly
10  * Source: $(DRUNTIMESRC src/rt/_minfo.d)
11  */
12 
13 module rt.minfo;
14 
15 import core.stdc.stdlib;  // alloca
16 import core.stdc.string;  // memcpy
17 import rt.sections;
18 
19 enum
20 {
21     MIctorstart  = 0x1,   // we've started constructing it
22     MIctordone   = 0x2,   // finished construction
23     MIstandalone = 0x4,   // module ctor does not depend on other module
24                         // ctors being done first
25     MItlsctor    = 8,
26     MItlsdtor    = 0x10,
27     MIctor       = 0x20,
28     MIdtor       = 0x40,
29     MIxgetMembers = 0x80,
30     MIictor      = 0x100,
31     MIunitTest   = 0x200,
32     MIimportedModules = 0x400,
33     MIlocalClasses = 0x800,
34     MIname       = 0x1000,
35 }
36 
37 /*****
38  * A ModuleGroup is an unordered collection of modules.
39  * There is exactly one for:
40  *  1. all statically linked in D modules, either directely or as shared libraries
41  *  2. each call to rt_loadLibrary()
42  */
43 
44 struct ModuleGroup
45 {
46     this(immutable(ModuleInfo*)[] modules) nothrow @nogc
47     {
48         _modules = modules;
49     }
50 
51     @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
52     {
53         return _modules;
54     }
55 
56     // this function initializes the bookeeping necessary to create the
57     // cycle path, and then creates it. It is a precondition that src and
58     // target modules are involved in a cycle.
59     //
60     // The return value is malloc'd using C, so it must be freed after use.
61     private size_t[] genCyclePath(size_t srcidx, size_t targetidx, int[][] edges)
62     {
63         import core.bitop : bt, btc, bts;
64 
65         // set up all the arrays.
66         size_t[] cyclePath = (cast(size_t*)malloc(size_t.sizeof * _modules.length * 2))[0 .. _modules.length * 2];
67         size_t totalMods;
68         int[] distance = (cast(int*)malloc(int.sizeof * _modules.length))[0 .. _modules.length];
69         scope(exit)
70             .free(distance.ptr);
71 
72         // determine the shortest path between two modules. Uses dijkstra
73         // without a priority queue. (we can be a bit slow here, in order to
74         // get a better printout).
75         void shortest(size_t start, size_t target)
76         {
77             // initial setup
78             distance[] = int.max;
79             int curdist = 0;
80             distance[start] = 0;
81             while (true)
82             {
83                 bool done = true;
84                 foreach (i, x; distance)
85                 {
86                     if (x == curdist)
87                     {
88                         if (i == target)
89                         {
90                             done = true;
91                             break;
92                         }
93                         foreach (n; edges[i])
94                         {
95                             if (distance[n] == int.max)
96                             {
97                                 distance[n] = curdist + 1;
98                                 done = false;
99                             }
100                         }
101                     }
102                 }
103                 if (done)
104                     break;
105                 ++curdist;
106             }
107             // it should be impossible to not get to target, this is just a
108             // sanity check. Not an assert, because druntime is compiled in
109             // release mode.
110             if (distance[target] != curdist)
111             {
112                 throw new Error("internal error printing module cycle");
113             }
114 
115             // determine the path. This is tricky, because we have to
116             // follow the edges in reverse to get back to the original. We
117             // don't have a reverse mapping, so it takes a bit of looping.
118             totalMods += curdist;
119             auto subpath = cyclePath[totalMods - curdist .. totalMods];
120             while (true)
121             {
122                 --curdist;
123                 subpath[curdist] = target;
124                 if (curdist == 0)
125                     break;
126             distloop:
127                 // search for next (previous) module in cycle.
128                 foreach (int m, d; distance)
129                 {
130                     if (d == curdist)
131                     {
132                         // determine if m can reach target
133                         foreach (e; edges[m])
134                         {
135                             if (e == target)
136                             {
137                                 // recurse
138                                 target = m;
139                                 break distloop;
140                             }
141                         }
142                     }
143                 }
144             }
145         }
146 
147         // first get to the target
148         shortest(srcidx, targetidx);
149         // now get back.
150         shortest(targetidx, srcidx);
151 
152         return cyclePath[0 .. totalMods];
153     }
154 
155     /******************************
156      * Allocate and fill in _ctors[] and _tlsctors[].
157      * Modules are inserted into the arrays in the order in which the constructors
158      * need to be run.
159      *
160      * Params:
161      *  cycleHandling - string indicating option for cycle handling
162      * Throws:
163      *  Exception if it fails.
164      */
165     void sortCtors(string cycleHandling)
166     {
167         import core.bitop : bts, btr, bt, BitRange;
168         import rt.util.container.hashtab;
169 
170         enum OnCycle
171         {
172             deprecate,
173             abort,
174             print,
175             ignore
176         }
177 
178         auto onCycle = OnCycle.abort;
179 
180         switch (cycleHandling) with(OnCycle)
181         {
182         case "deprecate":
183             onCycle = deprecate;
184             break;
185         case "abort":
186             onCycle = abort;
187             break;
188         case "print":
189             onCycle = print;
190             break;
191         case "ignore":
192             onCycle = ignore;
193             break;
194         case "":
195             // no option passed
196             break;
197         default:
198             // invalid cycle handling option.
199             throw new Error("DRT invalid cycle handling option: " ~ cycleHandling);
200         }
201 
202         debug (printModuleDependencies)
203         {
204             import core.stdc.stdio : printf;
205 
206             foreach (_m; _modules)
207             {
208                 printf("%s%s%s:", _m.name.ptr, (_m.flags & MIstandalone)
209                         ? "+".ptr : "".ptr, (_m.flags & (MIctor | MIdtor)) ? "*".ptr : "".ptr);
210                 foreach (_i; _m.importedModules)
211                     printf(" %s", _i.name.ptr);
212                 printf("\n");
213             }
214         }
215 
216         immutable uint len = cast(uint) _modules.length;
217         if (!len)
218             return; // nothing to do.
219 
220         // allocate some stack arrays that will be used throughout the process.
221         immutable nwords = (len + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof);
222         immutable flagbytes = nwords * size_t.sizeof;
223         auto ctorstart = cast(size_t*) malloc(flagbytes); // ctor/dtor seen
224         auto ctordone = cast(size_t*) malloc(flagbytes); // ctor/dtor processed
225         auto relevant = cast(size_t*) malloc(flagbytes); // has ctors/dtors
226         scope (exit)
227         {
228             .free(ctorstart);
229             .free(ctordone);
230             .free(relevant);
231         }
232 
233         void clearFlags(size_t* flags)
234         {
235             memset(flags, 0, flagbytes);
236         }
237 
238 
239         // build the edges between each module. We may need this for printing,
240         // and also allows avoiding keeping a hash around for module lookups.
241         int[][] edges = (cast(int[]*)malloc((int[]).sizeof * _modules.length))[0 .. _modules.length];
242         {
243             HashTab!(immutable(ModuleInfo)*, int) modIndexes;
244             foreach (i, m; _modules)
245                 modIndexes[m] = cast(int) i;
246 
247             auto reachable = cast(size_t*) malloc(flagbytes);
248             scope(exit)
249                 .free(reachable);
250 
251             foreach (i, m; _modules)
252             {
253                 // use bit array to prevent duplicates
254                 // https://issues.dlang.org/show_bug.cgi?id=16208
255                 clearFlags(reachable);
256                 // preallocate enough space to store all the indexes
257                 int *edge = cast(int*)malloc(int.sizeof * _modules.length);
258                 size_t nEdges = 0;
259                 foreach (imp; m.importedModules)
260                 {
261                     if (imp is m) // self-import
262                         continue;
263                     if (auto impidx = imp in modIndexes)
264                     {
265                         if (!bts(reachable, *impidx))
266                             edge[nEdges++] = *impidx;
267                     }
268                 }
269                 // trim space to what is needed.
270                 edges[i] = (cast(int*)realloc(edge, int.sizeof * nEdges))[0 .. nEdges];
271             }
272         }
273 
274         // free all the edges after we are done
275         scope(exit)
276         {
277             foreach (e; edges)
278                 if (e.ptr)
279                     .free(e.ptr);
280             .free(edges.ptr);
281         }
282 
283         void buildCycleMessage(size_t sourceIdx, size_t cycleIdx, scope void delegate(string) sink)
284         {
285             version (Windows)
286                 enum EOL = "\r\n";
287             else
288                 enum EOL = "\n";
289 
290             sink("Cyclic dependency between module ");
291             sink(_modules[sourceIdx].name);
292             sink(" and ");
293             sink(_modules[cycleIdx].name);
294             sink(EOL);
295             auto cyclePath = genCyclePath(sourceIdx, cycleIdx, edges);
296             scope(exit) .free(cyclePath.ptr);
297 
298             sink(_modules[sourceIdx].name);
299             sink("* ->" ~ EOL);
300             foreach (x; cyclePath[0 .. $ - 1])
301             {
302                 sink(_modules[x].name);
303                 sink(bt(relevant, x) ? "* ->" ~ EOL : " ->" ~ EOL);
304             }
305             sink(_modules[sourceIdx].name);
306             sink("*" ~ EOL);
307         }
308 
309         // find all the non-trivial dependencies (that is, dependencies that have a
310         // ctor or dtor) of a given module.  Doing this, we can 'skip over' the
311         // trivial modules to get at the non-trivial ones.
312         //
313         // If a cycle is detected, returns the index of the module that completes the cycle.
314         // Returns: true for success, false for a deprecated cycle error
315         bool findDeps(size_t idx, size_t* reachable)
316         {
317             static struct stackFrame
318             {
319                 size_t curMod;
320                 size_t curDep;
321             }
322 
323             // initialize "stack"
324             auto stack = cast(stackFrame*) malloc(stackFrame.sizeof * len);
325             scope (exit)
326                 .free(stack);
327             auto stacktop = stack + len;
328             auto sp = stack;
329             sp.curMod = cast(int) idx;
330             sp.curDep = 0;
331 
332             // initialize reachable by flagging source module
333             clearFlags(reachable);
334             bts(reachable, idx);
335 
336             for (;;)
337             {
338                 auto m = _modules[sp.curMod];
339                 if (sp.curDep >= edges[sp.curMod].length)
340                 {
341                     // return
342                     if (sp == stack) // finished the algorithm
343                         break;
344                     --sp;
345                 }
346                 else
347                 {
348                     auto midx = edges[sp.curMod][sp.curDep];
349                     if (!bts(reachable, midx))
350                     {
351                         if (bt(relevant, midx))
352                         {
353                             // need to process this node, don't recurse.
354                             if (bt(ctorstart, midx))
355                             {
356                                 // was already started, this is a cycle.
357                                 final switch (onCycle) with(OnCycle)
358                                 {
359                                 case deprecate:
360                                     // check with old algorithm
361                                     if (sortCtorsOld(edges))
362                                     {
363                                         // unwind to print deprecation message.
364                                         return false;   // deprecated cycle error
365                                     }
366                                     goto case abort; // fall through
367                                 case abort:
368 
369                                     string errmsg = "";
370                                     buildCycleMessage(idx, midx, (string x) {errmsg ~= x;});
371                                     throw new Error(errmsg, __FILE__, __LINE__);
372                                 case ignore:
373                                     break;
374                                 case print:
375                                     // print the message
376                                     buildCycleMessage(idx, midx, (string x) {
377                                                       import core.stdc.stdio : fprintf, stderr;
378                                                       fprintf(stderr, "%.*s", cast(int) x.length, x.ptr);
379                                                       });
380                                     // continue on as if this is correct.
381                                     break;
382                                 }
383                             }
384                         }
385                         else if (!bt(ctordone, midx))
386                         {
387                             // non-relevant, and hasn't been exhaustively processed, recurse.
388                             if (++sp >= stacktop)
389                             {
390                                 // stack overflow, this shouldn't happen.
391                                 import core.internal.abort : abort;
392 
393                                 abort("stack overflow on dependency search");
394                             }
395                             sp.curMod = midx;
396                             sp.curDep = 0;
397                             continue;
398                         }
399                     }
400                 }
401 
402                 // next dependency
403                 ++sp.curDep;
404             }
405             return true; // success
406         }
407 
408         // The list of constructors that will be returned by the sorting.
409         immutable(ModuleInfo)** ctors;
410         // current element being inserted into ctors list.
411         size_t ctoridx = 0;
412 
413         // This function will determine the order of construction/destruction and
414         // check for cycles. If a cycle is found, the cycle path is transformed
415         // into a string and thrown as an error.
416         //
417         // Each call into this function is given a module that has static
418         // ctor/dtors that must be dealt with. It recurses only when it finds
419         // dependencies that also have static ctor/dtors.
420         // Returns: true for success, false for a deprecated cycle error
421         bool processMod(size_t curidx)
422         {
423             immutable ModuleInfo* current = _modules[curidx];
424 
425             // First, determine what modules are reachable.
426             auto reachable = cast(size_t*) malloc(flagbytes);
427             scope (exit)
428                 .free(reachable);
429             if (!findDeps(curidx, reachable))
430                 return false;   // deprecated cycle error
431 
432             // process the dependencies. First, we process all relevant ones
433             bts(ctorstart, curidx);
434             auto brange = BitRange(reachable, len);
435             foreach (i; brange)
436             {
437                 // note, don't check for cycles here, because the config could have been set to ignore cycles.
438                 // however, don't recurse if there is one, so still check for started ctor.
439                 if (i != curidx && bt(relevant, i) && !bt(ctordone, i) && !bt(ctorstart, i))
440                 {
441                     if (!processMod(i))
442                         return false; // deprecated cycle error
443                 }
444             }
445 
446             // now mark this node, and all nodes reachable from this module as done.
447             bts(ctordone, curidx);
448             btr(ctorstart, curidx);
449             foreach (i; brange)
450             {
451                 // Since relevant dependencies are already marked as done
452                 // from recursion above (or are going to be handled up the call
453                 // stack), no reason to check for relevance, that is a wasted
454                 // op.
455                 bts(ctordone, i);
456             }
457 
458             // add this module to the construction order list
459             ctors[ctoridx++] = current;
460             return true;
461         }
462 
463         // returns `false` if deprecated cycle error otherwise set `result`.
464         bool doSort(size_t relevantFlags, ref immutable(ModuleInfo)*[] result)
465         {
466             clearFlags(relevant);
467             clearFlags(ctorstart);
468             clearFlags(ctordone);
469 
470             // pre-allocate enough space to hold all modules.
471             ctors = (cast(immutable(ModuleInfo)**).malloc(len * (void*).sizeof));
472             ctoridx = 0;
473             foreach (int idx, m; _modules)
474             {
475                 if (m.flags & relevantFlags)
476                 {
477                     if (m.flags & MIstandalone)
478                     {
479                         // can run at any time. Just run it first.
480                         ctors[ctoridx++] = m;
481                     }
482                     else
483                     {
484                         bts(relevant, idx);
485                     }
486                 }
487             }
488 
489             // now run the algorithm in the relevant ones
490             foreach (idx; BitRange(relevant, len))
491             {
492                 if (!bt(ctordone, idx))
493                 {
494                     if (!processMod(idx))
495                         return false;
496                 }
497             }
498 
499             if (ctoridx == 0)
500             {
501                 // no ctors in the list.
502                 .free(ctors);
503             }
504             else
505             {
506                 ctors = cast(immutable(ModuleInfo)**).realloc(ctors, ctoridx * (void*).sizeof);
507                 if (ctors is null)
508                     assert(0);
509                 result = ctors[0 .. ctoridx];
510             }
511             return true;
512         }
513 
514         // finally, do the sorting for both shared and tls ctors. If either returns false,
515         // print the deprecation warning.
516         if (!doSort(MIctor | MIdtor, _ctors) ||
517             !doSort(MItlsctor | MItlsdtor, _tlsctors))
518         {
519             // print a warning
520             import core.stdc.stdio : fprintf, stderr;
521             fprintf(stderr, "Deprecation 16211 warning:\n"
522                 ~ "A cycle has been detected in your program that was undetected prior to DMD\n"
523                 ~ "2.072. This program will continue, but will not operate when using DMD 2.074\n"
524                 ~ "to compile. Use runtime option --DRT-oncycle=print to see the cycle details.\n");
525 
526         }
527     }
528 
529     /// ditto
530     void sortCtors()
531     {
532         import rt.config : rt_configOption;
533         sortCtors(rt_configOption("oncycle"));
534     }
535 
536     /******************************
537      * This is the old ctor sorting algorithm that does not find all cycles.
538      *
539      * It is here to allow the deprecated behavior from the original algorithm
540      * until people have fixed their code.
541      *
542      * If no cycles are found, the _ctors and _tlsctors are replaced with the
543      * ones generated by this algorithm to preserve the old incorrect ordering
544      * behavior.
545      *
546      * Params:
547      *   edges - The module edges as found in the `importedModules` member of
548      *          each ModuleInfo. Generated in sortCtors.
549      * Returns:
550      *   true if no cycle is found, false if one was.
551      */
552     bool sortCtorsOld(int[][] edges)
553     {
554         immutable len = edges.length;
555         assert(len == _modules.length);
556 
557         static struct StackRec
558         {
559             @property int mod()
560             {
561                 return _mods[_idx];
562             }
563 
564             int[] _mods;
565             size_t         _idx;
566         }
567 
568         auto stack = (cast(StackRec*).calloc(len, StackRec.sizeof))[0 .. len];
569         // TODO: reuse GCBits by moving it to rt.util.container or core.internal
570         immutable nwords = (len + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof);
571         auto ctorstart = cast(size_t*).malloc(nwords * size_t.sizeof);
572         auto ctordone = cast(size_t*).malloc(nwords * size_t.sizeof);
573         int[] initialEdges = (cast(int *)malloc(int.sizeof * len))[0 .. len];
574         if (!stack.ptr || ctorstart is null || ctordone is null || !initialEdges.ptr)
575             assert(0);
576         scope (exit)
577         {
578             .free(stack.ptr);
579             .free(ctorstart);
580             .free(ctordone);
581             .free(initialEdges.ptr);
582         }
583 
584         // initialize the initial edges
585         foreach (int i, ref v; initialEdges)
586             v = i;
587 
588         bool sort(ref immutable(ModuleInfo)*[] ctors, uint mask)
589         {
590             import core.bitop;
591 
592             ctors = (cast(immutable(ModuleInfo)**).malloc(len * size_t.sizeof))[0 .. len];
593             if (!ctors.ptr)
594                 assert(0);
595 
596             // clean flags
597             memset(ctorstart, 0, nwords * size_t.sizeof);
598             memset(ctordone, 0, nwords * size_t.sizeof);
599             size_t stackidx = 0;
600             size_t cidx;
601 
602             int[] mods = initialEdges;
603 
604             size_t idx;
605             while (true)
606             {
607                 while (idx < mods.length)
608                 {
609                     auto m = mods[idx];
610 
611                     if (bt(ctordone, m))
612                     {
613                         // this module has already been processed, skip
614                         ++idx;
615                         continue;
616                     }
617                     else if (bt(ctorstart, m))
618                     {
619                         /* Trace back to the begin of the cycle.
620                          */
621                         bool ctorInCycle;
622                         size_t start = stackidx;
623                         while (start--)
624                         {
625                             auto sm = stack[start].mod;
626                             if (sm == m)
627                                 break;
628                             assert(sm >= 0);
629                             if (bt(ctorstart, sm))
630                                 ctorInCycle = true;
631                         }
632                         assert(stack[start].mod == m);
633                         if (ctorInCycle)
634                         {
635                             return false;
636                         }
637                         else
638                         {
639                             /* This is also a cycle, but the import chain does not constrain
640                              * the order of initialization, either because the imported
641                              * modules have no ctors or the ctors are standalone.
642                              */
643                             ++idx;
644                         }
645                     }
646                     else
647                     {
648                         auto curmod = _modules[m];
649                         if (curmod.flags & mask)
650                         {
651                             if (curmod.flags & MIstandalone || !edges[m].length)
652                             {   // trivial ctor => sort in
653                                 ctors[cidx++] = curmod;
654                                 bts(ctordone, m);
655                             }
656                             else
657                             {   // non-trivial ctor => defer
658                                 bts(ctorstart, m);
659                             }
660                         }
661                         else    // no ctor => mark as visited
662                         {
663                             bts(ctordone, m);
664                         }
665 
666                         if (edges[m].length)
667                         {
668                             /* Internal runtime error, recursion exceeds number of modules.
669                              */
670                             (stackidx < len) || assert(0);
671 
672                             // recurse
673                             stack[stackidx++] = StackRec(mods, idx);
674                             idx  = 0;
675                             mods = edges[m];
676                         }
677                     }
678                 }
679 
680                 if (stackidx)
681                 {   // pop old value from stack
682                     --stackidx;
683                     mods    = stack[stackidx]._mods;
684                     idx     = stack[stackidx]._idx;
685                     auto m  = mods[idx++];
686                     if (bt(ctorstart, m) && !bts(ctordone, m))
687                         ctors[cidx++] = _modules[m];
688                 }
689                 else // done
690                     break;
691             }
692             // store final number and shrink array
693             ctors = (cast(immutable(ModuleInfo)**).realloc(ctors.ptr, cidx * size_t.sizeof))[0 .. cidx];
694             return true;
695         }
696 
697         /* Do two passes: ctor/dtor, tlsctor/tlsdtor
698          */
699         immutable(ModuleInfo)*[] _ctors2;
700         immutable(ModuleInfo)*[] _tlsctors2;
701         auto result = sort(_ctors2, MIctor | MIdtor) && sort(_tlsctors2, MItlsctor | MItlsdtor);
702         if (result) // no cycle
703         {
704             // fall back to original ordering as part of the deprecation.
705             if (_ctors.ptr)
706                 .free(_ctors.ptr);
707             _ctors = _ctors2;
708             if (_tlsctors.ptr)
709                 .free(_tlsctors.ptr);
710             _tlsctors = _tlsctors2;
711         }
712         else
713         {
714             // free any allocated memory that will be forgotten
715             if (_ctors2.ptr)
716                 .free(_ctors2.ptr);
717             if (_tlsctors2.ptr)
718                 .free(_tlsctors2.ptr);
719         }
720         return result;
721     }
722 
723     void runCtors()
724     {
725         // run independent ctors
726         runModuleFuncs!(m => m.ictor)(_modules);
727         // sorted module ctors
728         runModuleFuncs!(m => m.ctor)(_ctors);
729     }
730 
731     void runTlsCtors()
732     {
733         runModuleFuncs!(m => m.tlsctor)(_tlsctors);
734     }
735 
736     void runTlsDtors()
737     {
738         runModuleFuncsRev!(m => m.tlsdtor)(_tlsctors);
739     }
740 
741     void runDtors()
742     {
743         runModuleFuncsRev!(m => m.dtor)(_ctors);
744     }
745 
746     void free()
747     {
748         if (_ctors.ptr)
749             .free(_ctors.ptr);
750         _ctors = null;
751         if (_tlsctors.ptr)
752             .free(_tlsctors.ptr);
753         _tlsctors = null;
754         // _modules = null; // let the owner free it
755     }
756 
757 private:
758     immutable(ModuleInfo*)[]  _modules;
759     immutable(ModuleInfo)*[]    _ctors;
760     immutable(ModuleInfo)*[] _tlsctors;
761 }
762 
763 
764 /********************************************
765  * Iterate over all module infos.
766  */
767 
768 int moduleinfos_apply(scope int delegate(immutable(ModuleInfo*)) dg)
769 {
770     foreach (ref sg; SectionGroup)
771     {
772         foreach (m; sg.modules)
773         {
774             // TODO: Should null ModuleInfo be allowed?
775             if (m !is null)
776             {
777                 if (auto res = dg(m))
778                     return res;
779             }
780         }
781     }
782     return 0;
783 }
784 
785 /********************************************
786  * Module constructor and destructor routines.
787  */
788 
789 extern (C)
790 {
791 void rt_moduleCtor()
792 {
793     foreach (ref sg; SectionGroup)
794     {
795         sg.moduleGroup.sortCtors();
796         sg.moduleGroup.runCtors();
797     }
798 }
799 
800 void rt_moduleTlsCtor()
801 {
802     foreach (ref sg; SectionGroup)
803     {
804         sg.moduleGroup.runTlsCtors();
805     }
806 }
807 
808 void rt_moduleTlsDtor()
809 {
810     foreach_reverse (ref sg; SectionGroup)
811     {
812         sg.moduleGroup.runTlsDtors();
813     }
814 }
815 
816 void rt_moduleDtor()
817 {
818     foreach_reverse (ref sg; SectionGroup)
819     {
820         sg.moduleGroup.runDtors();
821         sg.moduleGroup.free();
822     }
823 }
824 
825 version (Win32)
826 {
827     // Alternate names for backwards compatibility with older DLL code
828     void _moduleCtor()
829     {
830         rt_moduleCtor();
831     }
832 
833     void _moduleDtor()
834     {
835         rt_moduleDtor();
836     }
837 
838     void _moduleTlsCtor()
839     {
840         rt_moduleTlsCtor();
841     }
842 
843     void _moduleTlsDtor()
844     {
845         rt_moduleTlsDtor();
846     }
847 }
848 }
849 
850 /********************************************
851  */
852 
853 void runModuleFuncs(alias getfp)(const(immutable(ModuleInfo)*)[] modules)
854 {
855     foreach (m; modules)
856     {
857         if (auto fp = getfp(m))
858             (*fp)();
859     }
860 }
861 
862 void runModuleFuncsRev(alias getfp)(const(immutable(ModuleInfo)*)[] modules)
863 {
864     foreach_reverse (m; modules)
865     {
866         if (auto fp = getfp(m))
867             (*fp)();
868     }
869 }
870 
871 unittest
872 {
873     static void assertThrown(T : Throwable, E)(lazy E expr, string msg)
874     {
875         try
876             expr;
877         catch (T)
878             return;
879         assert(0, msg);
880     }
881 
882     static void stub()
883     {
884     }
885 
886     static struct UTModuleInfo
887     {
888         this(uint flags)
889         {
890             mi._flags = flags;
891         }
892 
893         void setImports(immutable(ModuleInfo)*[] imports...)
894         {
895             import core.bitop;
896             assert(flags & MIimportedModules);
897 
898             immutable nfuncs = popcnt(flags & (MItlsctor|MItlsdtor|MIctor|MIdtor|MIictor));
899             immutable size = nfuncs * (void function()).sizeof +
900                 size_t.sizeof + imports.length * (ModuleInfo*).sizeof;
901             assert(size <= pad.sizeof);
902 
903             pad[nfuncs] = imports.length;
904             .memcpy(&pad[nfuncs+1], imports.ptr, imports.length * imports[0].sizeof);
905         }
906 
907         immutable ModuleInfo mi;
908         size_t[8] pad;
909         alias mi this;
910     }
911 
912     static UTModuleInfo mockMI(uint flags)
913     {
914         auto mi = UTModuleInfo(flags | MIimportedModules);
915         auto p = cast(void function()*)&mi.pad;
916         if (flags & MItlsctor) *p++ = &stub;
917         if (flags & MItlsdtor) *p++ = &stub;
918         if (flags & MIctor) *p++ = &stub;
919         if (flags & MIdtor) *p++ = &stub;
920         if (flags & MIictor) *p++ = &stub;
921         *cast(size_t*)p++ = 0; // number of imported modules
922         assert(cast(void*)p <= &mi + 1);
923         return mi;
924     }
925 
926     static void checkExp2(string testname, bool shouldThrow, string oncycle,
927         immutable(ModuleInfo*)[] modules,
928         immutable(ModuleInfo*)[] dtors=null,
929         immutable(ModuleInfo*)[] tlsdtors=null)
930     {
931         auto mgroup = ModuleGroup(modules);
932         mgroup.sortCtors(oncycle);
933 
934         // if we are expecting sort to throw, don't throw because of unexpected
935         // success!
936         if (!shouldThrow)
937         {
938             foreach (m; mgroup._modules)
939                 assert(!(m.flags & (MIctorstart | MIctordone)), testname);
940             assert(mgroup._ctors    == dtors, testname);
941             assert(mgroup._tlsctors == tlsdtors, testname);
942         }
943     }
944 
945     static void checkExp(string testname, bool shouldThrow,
946         immutable(ModuleInfo*)[] modules,
947         immutable(ModuleInfo*)[] dtors=null,
948         immutable(ModuleInfo*)[] tlsdtors=null)
949     {
950         checkExp2(testname, shouldThrow, "abort", modules, dtors, tlsdtors);
951     }
952 
953 
954     {
955         auto m0 = mockMI(0);
956         auto m1 = mockMI(0);
957         auto m2 = mockMI(0);
958         checkExp("no ctors", false, [&m0.mi, &m1.mi, &m2.mi]);
959     }
960 
961     {
962         auto m0 = mockMI(MIictor);
963         auto m1 = mockMI(0);
964         auto m2 = mockMI(MIictor);
965         auto mgroup = ModuleGroup([&m0.mi, &m1.mi, &m2.mi]);
966         checkExp("independent ctors", false, [&m0.mi, &m1.mi, &m2.mi]);
967     }
968 
969     {
970         auto m0 = mockMI(MIstandalone | MIctor);
971         auto m1 = mockMI(0);
972         auto m2 = mockMI(0);
973         auto mgroup = ModuleGroup([&m0.mi, &m1.mi, &m2.mi]);
974         checkExp("standalone ctor", false, [&m0.mi, &m1.mi, &m2.mi], [&m0.mi]);
975     }
976 
977     {
978         auto m0 = mockMI(MIstandalone | MIctor);
979         auto m1 = mockMI(MIstandalone | MIctor);
980         auto m2 = mockMI(0);
981         m1.setImports(&m0.mi);
982         checkExp("imported standalone => no dependency", false,
983                  [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]);
984     }
985 
986     {
987         auto m0 = mockMI(MIstandalone | MIctor);
988         auto m1 = mockMI(MIstandalone | MIctor);
989         auto m2 = mockMI(0);
990         m0.setImports(&m1.mi);
991         checkExp("imported standalone => no dependency (2)", false,
992                 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]);
993     }
994 
995     {
996         auto m0 = mockMI(MIstandalone | MIctor);
997         auto m1 = mockMI(MIstandalone | MIctor);
998         auto m2 = mockMI(0);
999         m0.setImports(&m1.mi);
1000         m1.setImports(&m0.mi);
1001         checkExp("standalone may have cycle", false,
1002                 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]);
1003     }
1004 
1005     {
1006         auto m0 = mockMI(MIctor);
1007         auto m1 = mockMI(MIctor);
1008         auto m2 = mockMI(0);
1009         m1.setImports(&m0.mi);
1010         checkExp("imported ctor => ordered ctors", false,
1011                 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi], []);
1012     }
1013 
1014     {
1015         auto m0 = mockMI(MIctor);
1016         auto m1 = mockMI(MIctor);
1017         auto m2 = mockMI(0);
1018         m0.setImports(&m1.mi);
1019         checkExp("imported ctor => ordered ctors (2)", false,
1020                 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], []);
1021     }
1022 
1023     {
1024         auto m0 = mockMI(MIctor);
1025         auto m1 = mockMI(MIctor);
1026         auto m2 = mockMI(0);
1027         m0.setImports(&m1.mi);
1028         m1.setImports(&m0.mi);
1029         assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]),
1030                 "detects ctors cycles");
1031         assertThrown!Throwable(checkExp2("", true, "deprecate",
1032                                         [&m0.mi, &m1.mi, &m2.mi]),
1033                 "detects ctors cycles (dep)");
1034     }
1035 
1036     {
1037         auto m0 = mockMI(MIctor);
1038         auto m1 = mockMI(MIctor);
1039         auto m2 = mockMI(0);
1040         m0.setImports(&m2.mi);
1041         m1.setImports(&m2.mi);
1042         m2.setImports(&m0.mi, &m1.mi);
1043         assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]),
1044                 "detects cycle with repeats");
1045     }
1046 
1047     {
1048         auto m0 = mockMI(MIctor);
1049         auto m1 = mockMI(MIctor);
1050         auto m2 = mockMI(MItlsctor);
1051         m0.setImports(&m1.mi, &m2.mi);
1052         checkExp("imported ctor/tlsctor => ordered ctors/tlsctors", false,
1053                 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi]);
1054     }
1055 
1056     {
1057         auto m0 = mockMI(MIctor | MItlsctor);
1058         auto m1 = mockMI(MIctor);
1059         auto m2 = mockMI(MItlsctor);
1060         m0.setImports(&m1.mi, &m2.mi);
1061         checkExp("imported ctor/tlsctor => ordered ctors/tlsctors (2)", false,
1062                 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi, &m0.mi]);
1063     }
1064 
1065     {
1066         auto m0 = mockMI(MIctor);
1067         auto m1 = mockMI(MIctor);
1068         auto m2 = mockMI(MItlsctor);
1069         m0.setImports(&m1.mi, &m2.mi);
1070         m2.setImports(&m0.mi);
1071         checkExp("no cycle between ctors/tlsctors", false,
1072                 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi]);
1073     }
1074 
1075     {
1076         auto m0 = mockMI(MItlsctor);
1077         auto m1 = mockMI(MIctor);
1078         auto m2 = mockMI(MItlsctor);
1079         m0.setImports(&m2.mi);
1080         m2.setImports(&m0.mi);
1081         assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]),
1082                 "detects tlsctors cycle");
1083         assertThrown!Throwable(checkExp2("", true, "deprecate",
1084                                          [&m0.mi, &m1.mi, &m2.mi]),
1085                 "detects tlsctors cycle (dep)");
1086     }
1087 
1088     {
1089         auto m0 = mockMI(MItlsctor);
1090         auto m1 = mockMI(MIctor);
1091         auto m2 = mockMI(MItlsctor);
1092         m0.setImports(&m1.mi);
1093         m1.setImports(&m0.mi, &m2.mi);
1094         m2.setImports(&m1.mi);
1095         assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]),
1096                 "detects tlsctors cycle with repeats");
1097     }
1098 
1099     {
1100         auto m0 = mockMI(MIctor);
1101         auto m1 = mockMI(MIstandalone | MIctor);
1102         auto m2 = mockMI(MIstandalone | MIctor);
1103         m0.setImports(&m1.mi);
1104         m1.setImports(&m2.mi);
1105         m2.setImports(&m0.mi);
1106         // NOTE: this is implementation dependent, sorted order shouldn't be tested.
1107         checkExp("closed ctors cycle", false, [&m0.mi, &m1.mi, &m2.mi],
1108                 [&m1.mi, &m2.mi, &m0.mi]);
1109         //checkExp("closed ctors cycle", false, [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi, &m2.mi]);
1110     }
1111 }
1112 
1113 version (CRuntime_Microsoft)
1114 {
1115     // Dummy so Win32 code can still call it
1116     extern(C) void _minit() { }
1117 }
1118