From 276df5b9e95012475077f20a6662a0e386f9d68e Mon Sep 17 00:00:00 2001 From: Martin Nowak Date: Wed, 4 Oct 2017 01:16:45 +0200 Subject: [PATCH 1/2] fix Issue 17868 - add pragma(crt_con-/destructor) - allows to run con-/destructors before/after CRT startup/shutdown - primary use-case is implementing modular startup in druntime itself --- src/ddmd/backend/elfobj.c | 27 ++++------- src/ddmd/backend/machobj.c | 46 +++++-------------- src/ddmd/backend/melf.h | 3 ++ src/ddmd/backend/mscoffobj.c | 45 +++++------------- src/ddmd/dsymbolsem.d | 6 +++ src/ddmd/id.d | 2 + src/ddmd/toobj.d | 36 ++++++++++++++- test/fail_compilation/test17868.d | 24 ++++++++++ test/fail_compilation/test17868b.d | 16 +++++++ .../extra-files/test17868-postscript.sh | 12 +++++ test/runnable/extra-files/test17868.d.out | 5 ++ test/runnable/test17868.d | 35 ++++++++++++++ test/runnable/test17868b.d | 42 +++++++++++++++++ 13 files changed, 211 insertions(+), 88 deletions(-) create mode 100644 test/fail_compilation/test17868.d create mode 100644 test/fail_compilation/test17868b.d create mode 100755 test/runnable/extra-files/test17868-postscript.sh create mode 100644 test/runnable/extra-files/test17868.d.out create mode 100644 test/runnable/test17868.d create mode 100644 test/runnable/test17868b.d diff --git a/src/ddmd/backend/elfobj.c b/src/ddmd/backend/elfobj.c index 1666cfd72f90..3f676e0563f3 100644 --- a/src/ddmd/backend/elfobj.c +++ b/src/ddmd/backend/elfobj.c @@ -1625,16 +1625,9 @@ void Obj::compiler() * 3: compiler */ -void Obj::staticctor(Symbol *s,int dtor,int none) +void Obj::staticctor(Symbol *s, int, int) { - // Static constructors and destructors - //dbg_printf("Obj::staticctor(%s) offset %x\n",s->Sident,s->Soffset); - //symbol_print(s); - const IDXSEC seg = s->Sseg = - ElfObj::getsegment(".ctors", NULL, SHT_PROGBITS, SHF_ALLOC|SHF_WRITE, 4); - const unsigned relinfo = I64 ? R_X86_64_64 : R_386_32; - const size_t sz = ElfObj::writerel(seg, SegData[seg]->SDoffset, relinfo, STI_TEXT, s->Soffset); - SegData[seg]->SDoffset += sz; + setModuleCtorDtor(s, true); } /************************************** @@ -1647,14 +1640,7 @@ void Obj::staticctor(Symbol *s,int dtor,int none) void Obj::staticdtor(Symbol *s) { - //dbg_printf("Obj::staticdtor(%s) offset %x\n",s->Sident,s->Soffset); - //symbol_print(s); - // Why does this sequence differ from staticctor, looks like a bug? - const IDXSEC seg = - ElfObj::getsegment(".dtors", NULL, SHT_PROGBITS, SHF_ALLOC|SHF_WRITE, 4); - const unsigned relinfo = I64 ? R_X86_64_64 : R_386_32; - const size_t sz = ElfObj::writerel(seg, SegData[seg]->SDoffset, relinfo, s->Sxtrnnum, s->Soffset); - SegData[seg]->SDoffset += sz; + setModuleCtorDtor(s, false); } /*************************************** @@ -1662,9 +1648,12 @@ void Obj::staticdtor(Symbol *s) * Used for static ctor and dtor lists. */ -void Obj::setModuleCtorDtor(Symbol *s, bool isCtor) +void Obj::setModuleCtorDtor(Symbol *sfunc, bool isCtor) { - //dbg_printf("Obj::setModuleCtorDtor(%s) \n",s->Sident); + IDXSEC seg = ElfObj::getsegment(isCtor ? ".ctors" : ".dtors", NULL, SHT_PROGBITS, SHF_ALLOC|SHF_WRITE, NPTRSIZE); + const unsigned reltype = I64 ? R_X86_64_64 : R_386_32; + const size_t sz = ElfObj::writerel(seg, SegData[seg]->SDoffset, reltype, sfunc->Sxtrnnum, 0); + SegData[seg]->SDoffset += sz; } diff --git a/src/ddmd/backend/machobj.c b/src/ddmd/backend/machobj.c index 858f32df01be..2e256ec3b417 100644 --- a/src/ddmd/backend/machobj.c +++ b/src/ddmd/backend/machobj.c @@ -1662,24 +1662,9 @@ void Obj::compiler() * 3: compiler */ -void Obj::staticctor(Symbol *s,int dtor,int none) +void Obj::staticctor(Symbol *s, int, int) { -#if 0 - IDXSEC seg; - Outbuffer *buf; - - //dbg_printf("Obj::staticctor(%s) offset %x\n",s->Sident,s->Soffset); - //symbol_print(s); - s->Sseg = seg = - ElfObj::getsegment(".ctors", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE, 4); - buf = SegData[seg]->SDbuf; - if (I64) - buf->write64(s->Soffset); - else - buf->write32(s->Soffset); - MachObj::addrel(seg, SegData[seg]->SDoffset, s, RELaddr); - SegData[seg]->SDoffset = buf->size(); -#endif + setModuleCtorDtor(s, true); } /************************************** @@ -1692,21 +1677,7 @@ void Obj::staticctor(Symbol *s,int dtor,int none) void Obj::staticdtor(Symbol *s) { -#if 0 - IDXSEC seg; - Outbuffer *buf; - - //dbg_printf("Obj::staticdtor(%s) offset %x\n",s->Sident,s->Soffset); - //symbol_print(s); - seg = ElfObj::getsegment(".dtors", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE, 4); - buf = SegData[seg]->SDbuf; - if (I64) - buf->write64(s->Soffset); - else - buf->write32(s->Soffset); - MachObj::addrel(seg, SegData[seg]->SDoffset, s, RELaddr); - SegData[seg]->SDoffset = buf->size(); -#endif + setModuleCtorDtor(s, false); } @@ -1715,9 +1686,16 @@ void Obj::staticdtor(Symbol *s) * Used for static ctor and dtor lists. */ -void Obj::setModuleCtorDtor(Symbol *s, bool isCtor) +void Obj::setModuleCtorDtor(Symbol *sfunc, bool isCtor) { - //dbg_printf("Obj::setModuleCtorDtor(%s) \n",s->Sident); + const char *secname = isCtor ? "__mod_init_func" : "__mod_term_func"; + const int align = I64 ? 3 : 2; // align to NPTRSIZE + const int flags = isCtor ? S_MOD_INIT_FUNC_POINTERS : S_MOD_TERM_FUNC_POINTERS; + IDXSEC seg = MachObj::getsegment(secname, "__DATA", align, flags); + + const int relflags = I64 ? CFoff | CFoffset64 : CFoff; + const int sz = Obj::reftoident(seg, SegData[seg]->SDoffset, sfunc, 0, relflags); + SegData[seg]->SDoffset += sz; } diff --git a/src/ddmd/backend/melf.h b/src/ddmd/backend/melf.h index 527a83bb8745..12194b36a65b 100644 --- a/src/ddmd/backend/melf.h +++ b/src/ddmd/backend/melf.h @@ -108,6 +108,9 @@ typedef struct #define SHT_REL 9 /* Relocations no addends */ #define SHT_RESTYPE 10 /* Reserved section type*/ #define SHT_DYNTAB 11 /* Dynamic linker symbol table */ + #define SHT_INIT_ARRAY 14 /* Array of constructors */ + #define SHT_FINI_ARRAY 15 /* Array of destructors */ + #define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ #define SHT_GROUP 17 /* Section group (COMDAT) */ #define SHT_SYMTAB_SHNDX 18 /* Extended section indices */ Elf32_Word sh_flags; /* Section attribute flags */ diff --git a/src/ddmd/backend/mscoffobj.c b/src/ddmd/backend/mscoffobj.c index ed05baa40336..7ae1aeeae0f7 100644 --- a/src/ddmd/backend/mscoffobj.c +++ b/src/ddmd/backend/mscoffobj.c @@ -1129,22 +1129,7 @@ void MsCoffObj::compiler() void MsCoffObj::staticctor(Symbol *s,int dtor,int none) { -#if 0 - IDXSEC seg; - Outbuffer *buf; - - //dbg_printf("MsCoffObj::staticctor(%s) offset %x\n",s->Sident,s->Soffset); - //symbol_print(s); - s->Sseg = seg = - MsCoffObj::getsegment(".ctors", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE, 4); - buf = SegData[seg]->SDbuf; - if (I64) - buf->write64(s->Soffset); - else - buf->write32(s->Soffset); - MsCoffObj::addrel(seg, SegData[seg]->SDoffset, s, RELaddr); - SegData[seg]->SDoffset = buf->size(); -#endif + setModuleCtorDtor(s, true); } /************************************** @@ -1157,21 +1142,7 @@ void MsCoffObj::staticctor(Symbol *s,int dtor,int none) void MsCoffObj::staticdtor(Symbol *s) { -#if 0 - IDXSEC seg; - Outbuffer *buf; - - //dbg_printf("MsCoffObj::staticdtor(%s) offset %x\n",s->Sident,s->Soffset); - //symbol_print(s); - seg = MsCoffObj::getsegment(".dtors", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE, 4); - buf = SegData[seg]->SDbuf; - if (I64) - buf->write64(s->Soffset); - else - buf->write32(s->Soffset); - MsCoffObj::addrel(seg, SegData[seg]->SDoffset, s, RELaddr); - SegData[seg]->SDoffset = buf->size(); -#endif + setModuleCtorDtor(s, false); } @@ -1180,9 +1151,17 @@ void MsCoffObj::staticdtor(Symbol *s) * Used for static ctor and dtor lists. */ -void MsCoffObj::setModuleCtorDtor(Symbol *s, bool isCtor) +void MsCoffObj::setModuleCtorDtor(Symbol *sfunc, bool isCtor) { - //dbg_printf("MsCoffObj::setModuleCtorDtor(%s) \n",s->Sident); + // Also see https://blogs.msdn.microsoft.com/vcblog/2006/10/20/crt-initialization/ + // and http://www.codeguru.com/cpp/misc/misc/applicationcontrol/article.php/c6945/Running-Code-Before-and-After-Main.htm + const int align = I64 ? IMAGE_SCN_ALIGN_8BYTES : IMAGE_SCN_ALIGN_4BYTES; + const int attr = IMAGE_SCN_CNT_INITIALIZED_DATA | align | IMAGE_SCN_MEM_READ; + const int seg = MsCoffObj::getsegment(isCtor ? ".CRT$XCU" : ".CRT$XPU", attr); + + const int relflags = I64 ? CFoff | CFoffset64 : CFoff; + const int sz = MsCoffObj::reftoident(seg, SegData[seg]->SDoffset, sfunc, 0, relflags); + SegData[seg]->SDoffset += sz; } diff --git a/src/ddmd/dsymbolsem.d b/src/ddmd/dsymbolsem.d index dfd4cb13702b..f99eaa1aefb1 100644 --- a/src/ddmd/dsymbolsem.d +++ b/src/ddmd/dsymbolsem.d @@ -3048,6 +3048,12 @@ extern(C++) final class DsymbolSemanticVisitor : Visitor } } } + else if (pd.ident == Id.crt_constructor || pd.ident == Id.crt_destructor) + { + if (pd.args && pd.args.dim != 0) + pd.error("takes no argument"); + goto Ldecl; + } else if (global.params.ignoreUnsupportedPragmas) { if (global.params.verbose) diff --git a/src/ddmd/id.d b/src/ddmd/id.d index c60ebc8fdff2..84d58d44639b 100644 --- a/src/ddmd/id.d +++ b/src/ddmd/id.d @@ -298,6 +298,8 @@ immutable Msgtable[] msgtable = { "mangle" }, { "msg" }, { "startaddress" }, + { "crt_constructor" }, + { "crt_destructor" }, // For special functions { "tohash", "toHash" }, diff --git a/src/ddmd/toobj.d b/src/ddmd/toobj.d index ff7c39651e6e..950c1de7cf33 100644 --- a/src/ddmd/toobj.d +++ b/src/ddmd/toobj.d @@ -1115,7 +1115,41 @@ void toObjFile(Dsymbol ds, bool multiobj) Symbol *s = toSymbol(f); obj_startaddress(s); } + visit(cast(AttribDeclaration)pd); + + if (pd.ident == Id.crt_constructor || pd.ident == Id.crt_destructor) + { + immutable isCtor = pd.ident == Id.crt_constructor; + + static uint recurse(Dsymbol s, bool isCtor) + { + if (auto ad = s.isAttribDeclaration()) + { + uint nestedCount; + auto decls = ad.include(null, null); + if (decls) + { + for (size_t i = 0; i < decls.dim; ++i) + nestedCount += recurse((*decls)[i], isCtor); + } + return nestedCount; + } + else if (auto f = s.isFuncDeclaration()) + { + objmod.setModuleCtorDtor(s.csym, isCtor); + if (f.linkage != LINKc) + f.error("must be extern(C) for pragma %s", isCtor ? "crt_constructor".ptr : "crt_destructor".ptr); + return 1; + } + else + return 0; + assert(0); + } + + if (recurse(pd, isCtor) > 1) + pd.error("can only apply to a single declaration"); + } } override void visit(TemplateInstance ti) @@ -1482,5 +1516,3 @@ private size_t emitVtbl(ref DtBuilder dtb, BaseClass *b, ref FuncDeclarations bv } return id_vtbl_dim * Target.ptrsize; } - - diff --git a/test/fail_compilation/test17868.d b/test/fail_compilation/test17868.d new file mode 100644 index 000000000000..efcfe7b48fb9 --- /dev/null +++ b/test/fail_compilation/test17868.d @@ -0,0 +1,24 @@ +/* +TEST_OUTPUT: +---- +fail_compilation/test17868.d(10): Error: pragma crt_constructor takes no argument +fail_compilation/test17868.d(11): Error: pragma crt_constructor takes no argument +fail_compilation/test17868.d(12): Error: pragma crt_constructor takes no argument +fail_compilation/test17868.d(13): Error: pragma crt_constructor takes no argument +---- + */ +pragma(crt_constructor, ctfe()) +pragma(crt_constructor, 1.5f) +pragma(crt_constructor, "foobar") +pragma(crt_constructor, S()) +void foo() +{ +} + +int ctfe() +{ + __gshared int val; + return val; +} + +struct S {} diff --git a/test/fail_compilation/test17868b.d b/test/fail_compilation/test17868b.d new file mode 100644 index 000000000000..8c550532b377 --- /dev/null +++ b/test/fail_compilation/test17868b.d @@ -0,0 +1,16 @@ +/* +TEST_OUTPUT: +---- +fail_compilation/test17868b.d(10): Error: function test17868b.foo must be extern(C) for pragma crt_constructor +fail_compilation/test17868b.d(14): Error: function test17868b.bar must be extern(C) for pragma crt_constructor +fail_compilation/test17868b.d(9): Error: pragma crt_constructor can only apply to a single declaration +---- + */ +pragma(crt_constructor): +void foo() +{ +} + +void bar() +{ +} diff --git a/test/runnable/extra-files/test17868-postscript.sh b/test/runnable/extra-files/test17868-postscript.sh new file mode 100755 index 000000000000..7b4185497865 --- /dev/null +++ b/test/runnable/extra-files/test17868-postscript.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# trim off the first line which contains the path of the file which differs between windows and non-windows +# also trim off compiler debug message +grep -v 'runnable\|DEBUG' $1 > ${RESULTS_DIR}/runnable/test17868.d.out.2 + +diff --strip-trailing-cr runnable/extra-files/test17868.d.out ${RESULTS_DIR}/runnable/test17868.d.out.2 +if [ $? -ne 0 ]; then + exit 1; +fi + +rm ${RESULTS_DIR}/runnable/test17868.d.out.2 diff --git a/test/runnable/extra-files/test17868.d.out b/test/runnable/extra-files/test17868.d.out new file mode 100644 index 000000000000..03244829e2c4 --- /dev/null +++ b/test/runnable/extra-files/test17868.d.out @@ -0,0 +1,5 @@ +init +init +main +fini +fini diff --git a/test/runnable/test17868.d b/test/runnable/test17868.d new file mode 100644 index 000000000000..f0144736f0a2 --- /dev/null +++ b/test/runnable/test17868.d @@ -0,0 +1,35 @@ +// REQUIRED_ARGS: -betterC +// POST_SCRIPT: runnable/extra-files/test17868-postscript.sh +import core.stdc.stdio; + +extern(C): + +pragma(crt_constructor) +void init() +{ + puts("init"); +} + +pragma(crt_destructor) +void fini2() +{ + puts("fini"); +} + +pragma(crt_constructor) +void foo() +{ + puts("init"); +} + +pragma(crt_destructor) +void bar() +{ + puts("fini"); +} + +int main() +{ + puts("main"); + return 0; +} diff --git a/test/runnable/test17868b.d b/test/runnable/test17868b.d new file mode 100644 index 000000000000..eca2803e8034 --- /dev/null +++ b/test/runnable/test17868b.d @@ -0,0 +1,42 @@ +// REQUIRED_ARGS: -betterC +// POST_SCRIPT: runnable/extra-files/test17868-postscript.sh +import core.stdc.stdio; + +extern(C): + +pragma(crt_constructor) +pragma(crt_destructor) +void ctor_dtor_1() +{ + __gshared bool initialized; + puts(initialized ? "fini" : "init"); + initialized = true; +} + +pragma(crt_constructor) +__gshared int var; // ignored for anything but functions + +pragma(crt_constructor) +{ + version (all) void init() + { + puts("init"); + } +} + +template fini() +{ + pragma(crt_destructor) + void fini() + { + puts("fini"); + } +} + +alias instantiate = fini!(); + +int main() +{ + puts("main"); + return 0; +} From 086bb2976125855b12705963d184981a5c3c240b Mon Sep 17 00:00:00 2001 From: Martin Nowak Date: Fri, 6 Oct 2017 12:30:44 +0200 Subject: [PATCH 2/2] add changelog entry for pragma(crt_con-/destructor) --- changelog/crt-constructor.dd | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 changelog/crt-constructor.dd diff --git a/changelog/crt-constructor.dd b/changelog/crt-constructor.dd new file mode 100644 index 000000000000..d5e25282ded8 --- /dev/null +++ b/changelog/crt-constructor.dd @@ -0,0 +1,33 @@ +pragma(crt_constructor) and pragma(crt_destructor) were added + +This allows programs to run initialization code before or cleanup code after C +main. + +In particular those pragmas can be used in $(TT -betterC) code as substitutes +for $(LINK2 $(ROOT_DIR)spec/class.html#shared_static_constructors, `shared static this()`) and +$(LINK2 $(ROOT_DIR)spec/class.html#shared_static_destructors, `shared static ~this()`). + +$(RED Note:) At the time of execution druntime is not initialized. + +$(RED Note:) The order in which constructors are executed is unspecified. + +--- +import core.stdc.stdio; + +pragma(crt_constructor) +void init() +{ + puts("init"); +} + +pragma(crt_destructor) +void fini() +{ + puts("fini"); +} + +extern(C) int main() +{ + puts("main"); +} +---