diff --git a/compiler/src/dmd/backend/backconfig.d b/compiler/src/dmd/backend/backconfig.d index a848ac4c1654..34cc42bbcb4e 100644 --- a/compiler/src/dmd/backend/backconfig.d +++ b/compiler/src/dmd/backend/backconfig.d @@ -78,6 +78,7 @@ void out_config_init( bool useModuleInfo, bool useTypeInfo, bool useExceptions, + bool unwindTables, ubyte dwarf, string _version, exefmt_t exefmt, @@ -159,15 +160,16 @@ void out_config_init( cfg.avx = avx; if (model == 64) { - cfg.ehmethod = useExceptions ? EHmethod.EH_DWARF : EHmethod.EH_NONE; + cfg.ehmethod = (useExceptions || unwindTables) ? EHmethod.EH_DWARF : EHmethod.EH_NONE; } else { - cfg.ehmethod = useExceptions ? EHmethod.EH_DWARF : EHmethod.EH_NONE; + cfg.ehmethod = (useExceptions || unwindTables) ? EHmethod.EH_DWARF : EHmethod.EH_NONE; if (!exe) cfg.flags |= CFGromable; // put switch tables in code segment } - cfg.flags |= CFGnoebp; + if (!unwindTables) + cfg.flags |= CFGnoebp; switch (pic) { case 0: // PIC.fixed @@ -193,7 +195,7 @@ void out_config_init( { cfg.fpxmmregs = true; cfg.avx = avx; - cfg.ehmethod = useExceptions ? EHmethod.EH_DWARF : EHmethod.EH_NONE; + cfg.ehmethod = (useExceptions || unwindTables) ? EHmethod.EH_DWARF : EHmethod.EH_NONE; cfg.flags |= CFGnoebp; if (!exe) { @@ -211,13 +213,13 @@ void out_config_init( { if (model == 64) { - cfg.ehmethod = useExceptions ? EHmethod.EH_DWARF : EHmethod.EH_NONE; + cfg.ehmethod = (useExceptions || unwindTables) ? EHmethod.EH_DWARF : EHmethod.EH_NONE; cfg.fpxmmregs = true; cfg.avx = avx; } else { - cfg.ehmethod = useExceptions ? EHmethod.EH_DWARF : EHmethod.EH_NONE; + cfg.ehmethod = (useExceptions || unwindTables) ? EHmethod.EH_DWARF : EHmethod.EH_NONE; if (!exe) cfg.flags |= CFGromable; // put switch tables in code segment } @@ -247,13 +249,13 @@ void out_config_init( if (!exe) cfg.flags3 |= CFG3pic; cfg.objfmt = OBJ_ELF; - cfg.ehmethod = useExceptions ? EHmethod.EH_DWARF : EHmethod.EH_NONE; + cfg.ehmethod = (useExceptions || unwindTables) ? EHmethod.EH_DWARF : EHmethod.EH_NONE; } if (cfg.exe == EX_DRAGONFLYBSD64) { if (model == 64) { - cfg.ehmethod = useExceptions ? EHmethod.EH_DWARF : EHmethod.EH_NONE; + cfg.ehmethod = (useExceptions || unwindTables) ? EHmethod.EH_DWARF : EHmethod.EH_NONE; cfg.fpxmmregs = true; cfg.avx = avx; } @@ -286,7 +288,7 @@ void out_config_init( if (!exe) cfg.flags3 |= CFG3pic; cfg.objfmt = OBJ_ELF; - cfg.ehmethod = useExceptions ? EHmethod.EH_DWARF : EHmethod.EH_NONE; + cfg.ehmethod = (useExceptions || unwindTables) ? EHmethod.EH_DWARF : EHmethod.EH_NONE; } cfg.flags2 |= CFG2nodeflib; // no default library @@ -357,7 +359,8 @@ static if (0) cfg.useModuleInfo = useModuleInfo; cfg.useTypeInfo = useTypeInfo; - cfg.useExceptions = useExceptions; + cfg.useExceptions = useExceptions || unwindTables; + cfg.unwindTables = unwindTables; cod3_setdefault(); if (arm) diff --git a/compiler/src/dmd/backend/cdef.d b/compiler/src/dmd/backend/cdef.d index 836aad2891b0..b41b7dae78fb 100644 --- a/compiler/src/dmd/backend/cdef.d +++ b/compiler/src/dmd/backend/cdef.d @@ -567,6 +567,7 @@ struct Config bool useModuleInfo; // implement ModuleInfo bool useTypeInfo; // implement TypeInfo bool useExceptions; // implement exception handling + bool unwindTables; // emit unwind tables for stack tracing ubyte dwarf; // DWARF version } diff --git a/compiler/src/dmd/backend/dwarfdbginf.d b/compiler/src/dmd/backend/dwarfdbginf.d index afc2133106df..d1fb0ffb1930 100644 --- a/compiler/src/dmd/backend/dwarfdbginf.d +++ b/compiler/src/dmd/backend/dwarfdbginf.d @@ -109,16 +109,20 @@ static if (1) */ bool doUnwindEhFrame() { - if (funcsym_p.Sfunc.Fflags3 & Feh_none) - { - return (config.exe & (EX_FREEBSD | EX_FREEBSD64 | EX_OPENBSD | EX_OPENBSD64 | EX_DRAGONFLYBSD64)) != 0; - } - /* FreeBSD fails when having some frames as having unwinding info and some not. * (It hangs in unittests for std.datetime.) * g++ on FreeBSD does not generate mixed frames, while g++ on OSX and Linux does. */ assert(!(cgstate.usednteh & ~(EHtry | EHcleanup))); + + if (config.unwindTables) + return true; + + if (funcsym_p.Sfunc.Fflags3 & Feh_none) + { + return (config.exe & (EX_FREEBSD | EX_FREEBSD64 | EX_OPENBSD | EX_OPENBSD64 | EX_DRAGONFLYBSD64)) != 0; + } + return (cgstate.usednteh & (EHtry | EHcleanup)) || (config.exe & (EX_FREEBSD | EX_FREEBSD64 | EX_OPENBSD | EX_OPENBSD64 | EX_DRAGONFLYBSD64)) && config.useExceptions; } @@ -1759,11 +1763,16 @@ static if (1) OutBuffer* buf = SegData[dfseg].SDbuf; buf.reserve(1000); - uint* poffset = ehunwind ? &CIE_offset_unwind : &CIE_offset_no_unwind; + // When unwind tables are enabled, don't reference personality function (no druntime) + bool usePersonality = ehunwind && !config.unwindTables; + uint* poffset = usePersonality ? &CIE_offset_unwind : &CIE_offset_no_unwind; if (*poffset == ~0) - *poffset = writeEhFrameHeader(dfseg, buf, getRtlsymPersonality(), ehunwind); + { + Symbol* personality = usePersonality ? getRtlsymPersonality() : null; + *poffset = writeEhFrameHeader(dfseg, buf, personality, usePersonality); + } - writeEhFrameFDE(dfseg, sfunc, ehunwind, *poffset); + writeEhFrameFDE(dfseg, sfunc, usePersonality, *poffset); } if (!config.fulltypes) return; diff --git a/compiler/src/dmd/backend/x86/cgcod.d b/compiler/src/dmd/backend/x86/cgcod.d index 4fff4ca46cb6..a171d4e93f7d 100644 --- a/compiler/src/dmd/backend/x86/cgcod.d +++ b/compiler/src/dmd/backend/x86/cgcod.d @@ -642,6 +642,7 @@ void prolog(ref CGstate cg, ref CodeBuilder cdb) * so need frame if function can possibly throw */ !(config.exe == EX_WIN32) && !(funcsym_p.Sfunc.Fflags3 & Fnothrow) || + (config.unwindTables && config.ehmethod == EHmethod.EH_DWARF) || cg.accessedTLS || sv64 || (0 && cg.calledafunc && cg.AArch64) diff --git a/compiler/src/dmd/cli.d b/compiler/src/dmd/cli.d index 9dd11ff08b8c..6c46706aa3d7 100644 --- a/compiler/src/dmd/cli.d +++ b/compiler/src/dmd/cli.d @@ -430,6 +430,13 @@ dmd -cov -unittest myprog.d Option("gs", "always emit stack frame" ), + Option("gu", + "generate unwind tables", + "Generate unwind tables for all functions. + This enables the emission of metadata required for stack walking, + allowing backtrace() and debuggers to function even when + exception handling is disabled or in `-betterC` code.", + ), Option("gx", "add stack stomp code", `Adds stack stomp code, which overwrites the stack frame memory upon function exit.`, diff --git a/compiler/src/dmd/dmdparams.d b/compiler/src/dmd/dmdparams.d index 12a0a31cc171..31a55191c44a 100644 --- a/compiler/src/dmd/dmdparams.d +++ b/compiler/src/dmd/dmdparams.d @@ -41,6 +41,7 @@ enum SymImport : ubyte struct DMDparams { bool alwaysframe; // always emit standard stack frame + bool unwindTables; // emit unwind tables for stack tracing ubyte dwarf; // DWARF version bool map; // generate linker .map file bool vasm; // print generated assembler for each function diff --git a/compiler/src/dmd/dmsc.d b/compiler/src/dmd/dmsc.d index 5ffa48651927..4715c9e574ed 100644 --- a/compiler/src/dmd/dmsc.d +++ b/compiler/src/dmd/dmsc.d @@ -88,7 +88,8 @@ void backend_init(const ref Param params, const ref DMDparams driverParams, cons driverParams.pic, params.useModuleInfo && Module.moduleinfo, params.useTypeInfo && Type.dtypeinfo, - params.useExceptions && ClassDeclaration.throwable, + params.useExceptions && (ClassDeclaration.throwable || driverParams.unwindTables), + driverParams.unwindTables, driverParams.dwarf, global.versionString(), exfmt, diff --git a/compiler/src/dmd/glue/toir.d b/compiler/src/dmd/glue/toir.d index 89768fc471a7..594c945ad04e 100644 --- a/compiler/src/dmd/glue/toir.d +++ b/compiler/src/dmd/glue/toir.d @@ -106,9 +106,9 @@ struct IRState this.params = params; this.target = target; this.eSink = eSink; - mayThrow = params.useExceptions - && ClassDeclaration.throwable - && !(fd && fd.hasNoEH); + bool exceptionsEnabled = params.useExceptions && + (ClassDeclaration.throwable || driverParams.unwindTables); + mayThrow = exceptionsEnabled && !(fd && fd.hasNoEH); this.Cfile = m.filetype == FileType.c; } diff --git a/compiler/src/dmd/mars.d b/compiler/src/dmd/mars.d index ad7ce89869e4..fd30edcfca9d 100644 --- a/compiler/src/dmd/mars.d +++ b/compiler/src/dmd/mars.d @@ -985,6 +985,10 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, out Param } else if (arg == "-gs") // https://dlang.org/dmd.html#switch-gs driverParams.alwaysframe = true; + else if (arg == "-gu") // https://dlang.org/dmd.html#switch-gu + { + driverParams.unwindTables = true; + } else if (arg == "-gx") // https://dlang.org/dmd.html#switch-gx driverParams.stackstomp = true; else if (arg == "-lowmem") // https://dlang.org/dmd.html#switch-lowmem