Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions changelog/crt-constructor.dd
Original file line number Diff line number Diff line change
@@ -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");
}
---
27 changes: 8 additions & 19 deletions src/ddmd/backend/elfobj.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**************************************
Expand All @@ -1647,24 +1640,20 @@ 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);
}

/***************************************
* Stuff pointer to function in its own segment.
* 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;
}


Expand Down
46 changes: 12 additions & 34 deletions src/ddmd/backend/machobj.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**************************************
Expand All @@ -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);
}


Expand All @@ -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;
}


Expand Down
3 changes: 3 additions & 0 deletions src/ddmd/backend/melf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
45 changes: 12 additions & 33 deletions src/ddmd/backend/mscoffobj.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**************************************
Expand All @@ -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);
}


Expand All @@ -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;
}


Expand Down
6 changes: 6 additions & 0 deletions src/ddmd/dsymbolsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions src/ddmd/id.d
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ immutable Msgtable[] msgtable =
{ "mangle" },
{ "msg" },
{ "startaddress" },
{ "crt_constructor" },
{ "crt_destructor" },

// For special functions
{ "tohash", "toHash" },
Expand Down
36 changes: 34 additions & 2 deletions src/ddmd/toobj.d
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -1482,5 +1516,3 @@ private size_t emitVtbl(ref DtBuilder dtb, BaseClass *b, ref FuncDeclarations bv
}
return id_vtbl_dim * Target.ptrsize;
}


24 changes: 24 additions & 0 deletions test/fail_compilation/test17868.d
Original file line number Diff line number Diff line change
@@ -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 {}
16 changes: 16 additions & 0 deletions test/fail_compilation/test17868b.d
Original file line number Diff line number Diff line change
@@ -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()
{
}
12 changes: 12 additions & 0 deletions test/runnable/extra-files/test17868-postscript.sh
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions test/runnable/extra-files/test17868.d.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
init
init
main
fini
fini
35 changes: 35 additions & 0 deletions test/runnable/test17868.d
Original file line number Diff line number Diff line change
@@ -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;
}
Loading