1 module bc.core.system.linux.dwarf;
2 
3 version (D_BetterC) {}
4 else version (linux):
5 
6 import bc.core.system.linux.elf : Image;
7 import core.exception : onOutOfMemoryErrorNoGC;
8 import core.internal.traits : dtorIsNothrow;
9 import core.stdc.stdio : snprintf;
10 import core.stdc.string : strlen;
11 
12 // Selective copy from normally unavailable: https://github.com/dlang/druntime/blob/master/src/rt/backtrace/dwarf.d
13 // Also combined (and noted) with some only here used module parts
14 
15 size_t getFirstFrame(const(void*)[] callstack, const char** frameList) nothrow @nogc
16 {
17     import core.internal.execinfo : getMangledSymbolName;
18 
19     version (LDC) enum BaseExceptionFunctionName = "_d_throw_exception";
20     else enum BaseExceptionFunctionName = "_d_throwdwarf";
21 
22     foreach (i; 0..callstack.length)
23     {
24         auto proc = getMangledSymbolName(frameList[i][0 .. strlen(frameList[i])]);
25         if (proc == BaseExceptionFunctionName) return i+1;
26     }
27     return 0;
28 }
29 
30 /// Our customized @nogc variant of https://github.com/dlang/druntime/blob/master/src/rt/backtrace/dwarf.d#L94
31 size_t dumpCallstack(S)(ref S sink, ref Image image, const(void*)[] callstack, const char** frameList,
32     const(ubyte)[] debugLineSectionData) nothrow @nogc
33 {
34     // find address -> file, line mapping using dwarf debug_line
35     Array!Location locations;
36     if (debugLineSectionData)
37     {
38         locations.length = callstack.length;
39         foreach (size_t i; 0 .. callstack.length)
40             locations[i].address = cast(size_t) callstack[i];
41 
42         resolveAddresses(debugLineSectionData, locations[], image.baseAddress);
43     }
44 
45     size_t ret = 0;
46     foreach (size_t i; 0 .. callstack.length)
47     {
48         char[1536] buffer = void;
49         size_t bufferLength = 0;
50 
51         void appendToBuffer(Args...)(const(char)* format, Args args)
52         {
53             const count = snprintf(buffer.ptr + bufferLength, buffer.length - bufferLength, format, args);
54             assert(count >= 0);
55             bufferLength += count;
56             if (bufferLength >= buffer.length)
57                 bufferLength = buffer.length - 1;
58         }
59 
60         if (i) { sink.put('\n'); ret++; }
61 
62         if (locations.length > 0 && locations[i].line != -1)
63         {
64             bool includeSlash = locations[i].directory.length > 0 && locations[i].directory[$ - 1] != '/';
65             if (locations[i].line)
66             {
67                 string printFormat = includeSlash ? "%.*s/%.*s:%d " : "%.*s%.*s:%d ";
68                 appendToBuffer(
69                     printFormat.ptr,
70                     cast(int) locations[i].directory.length, locations[i].directory.ptr,
71                     cast(int) locations[i].file.length, locations[i].file.ptr,
72                     locations[i].line,
73                 );
74             }
75             else
76             {
77                 string printFormat = includeSlash ? "%.*s/%.*s " : "%.*s%.*s ";
78                 appendToBuffer(
79                     printFormat.ptr,
80                     cast(int) locations[i].directory.length, locations[i].directory.ptr,
81                     cast(int) locations[i].file.length, locations[i].file.ptr,
82                 );
83             }
84         }
85         else
86         {
87             buffer[0 .. 5] = "??:? ";
88             bufferLength = 5;
89         }
90 
91         char[1024] symbolBuffer = void;
92         auto symbol = getDemangledSymbol(frameList[i][0 .. strlen(frameList[i])], symbolBuffer);
93         if (symbol.length > 0)
94             appendToBuffer("%.*s ", cast(int) symbol.length, symbol.ptr);
95 
96         const addressLength = 20;
97         const maxBufferLength = buffer.length - addressLength;
98         if (bufferLength > maxBufferLength)
99         {
100             buffer[maxBufferLength-4 .. maxBufferLength] = "... ";
101             bufferLength = maxBufferLength;
102         }
103         appendToBuffer("[0x%zx]", callstack[i]);
104 
105         auto output = buffer[0 .. bufferLength];
106         sink.put(output);
107         ret += bufferLength;
108         if (symbol == "_Dmain") break;
109     }
110 
111     return ret;
112 }
113 
114 // Copy: https://github.com/dlang/druntime/blob/master/src/rt/backtrace/dwarf.d#L172
115 // the lifetime of the Location data is bound to the lifetime of debugLineSectionData
116 void resolveAddresses(const(ubyte)[] debugLineSectionData, Location[] locations, size_t baseAddress) @nogc nothrow
117 {
118     debug(DwarfDebugMachine) import core.stdc.stdio;
119 
120     size_t numberOfLocationsFound = 0;
121 
122     const(ubyte)[] dbg = debugLineSectionData;
123     while (dbg.length > 0)
124     {
125         debug(DwarfDebugMachine) printf("new debug program\n");
126         const lp = readLineNumberProgram(dbg);
127 
128         LocationInfo lastLoc = LocationInfo(-1, -1);
129         size_t lastAddress = 0x0;
130 
131         debug(DwarfDebugMachine) printf("program:\n");
132         runStateMachine(lp,
133             (size_t address, LocationInfo locInfo, bool isEndSequence)
134             {
135                 // adjust to ASLR offset
136                 address += baseAddress;
137                 debug (DwarfDebugMachine)
138                     printf("-- offsetting 0x%zx to 0x%zx\n", address - baseAddress, address);
139 
140                 foreach (ref loc; locations)
141                 {
142                     // If loc.line != -1, then it has been set previously.
143                     // Some implementations (eg. dmd) write an address to
144                     // the debug data multiple times, but so far I have found
145                     // that the first occurrence to be the correct one.
146                     if (loc.line != -1)
147                         continue;
148 
149                     // Can be called with either `locInfo` or `lastLoc`
150                     void update(const ref LocationInfo match)
151                     {
152                         const sourceFile = lp.sourceFiles[match.file - 1];
153                         debug (DwarfDebugMachine)
154                         {
155                             printf("-- found for [0x%zx]:\n", loc.address);
156                             printf("--   file: %.*s\n",
157                                    cast(int) sourceFile.file.length, sourceFile.file.ptr);
158                             printf("--   line: %d\n", match.line);
159                         }
160                         // DMD emits entries with FQN, but other implmentations
161                         // (e.g. LDC) make use of directories
162                         // See https://github.com/dlang/druntime/pull/2945
163                         if (sourceFile.dirIndex != 0)
164                             loc.directory = lp.includeDirectories[sourceFile.dirIndex - 1];
165 
166                         loc.file = sourceFile.file;
167                         loc.line = match.line;
168                         numberOfLocationsFound++;
169                     }
170 
171                     // The state machine will not contain an entry for each
172                     // address, as consecutive addresses with the same file/line
173                     // are merged together to save on space, so we need to
174                     // check if our address is within two addresses we get
175                     // called with.
176                     //
177                     // Specs (DWARF v4, Section 6.2, PDF p.109) says:
178                     // "We shrink it with two techniques. First, we delete from
179                     // the matrix each row whose file, line, source column and
180                     // discriminator information is identical with that of its
181                     // predecessors.
182                     if (loc.address == address)
183                         update(locInfo);
184                     else if (lastAddress &&
185                              loc.address > lastAddress && loc.address < address)
186                         update(lastLoc);
187                 }
188 
189                 if (isEndSequence)
190                 {
191                     lastAddress = 0;
192                 }
193                 else
194                 {
195                     lastAddress = address;
196                     lastLoc = locInfo;
197                 }
198 
199                 return numberOfLocationsFound < locations.length;
200             }
201         );
202 
203         if (numberOfLocationsFound == locations.length) return;
204     }
205 }
206 
207 const(char)[] getDemangledSymbol(const(char)[] btSymbol, return ref char[1024] buffer) nothrow @nogc
208 {
209     //import core.demangle; // isn't @nogc :(
210     import bc.core.demangle : demangle;
211     import core.internal.execinfo : getMangledSymbolName;
212 
213     const mangledName = getMangledSymbolName(btSymbol);
214     return !mangledName.length ? buffer[0..0] : demangle(mangledName, buffer[]);
215     // return mangledName;
216 }
217 
218 struct LineNumberProgram
219 {
220     ulong unitLength;
221     ushort dwarfVersion;
222     ulong headerLength;
223     ubyte minimumInstructionLength;
224     ubyte maximumOperationsPerInstruction;
225     bool defaultIsStatement;
226     byte lineBase;
227     ubyte lineRange;
228     ubyte opcodeBase;
229     const(ubyte)[] standardOpcodeLengths;
230     Array!(const(char)[]) includeDirectories;
231     Array!SourceFile sourceFiles;
232     const(ubyte)[] program;
233 }
234 
235 struct SourceFile
236 {
237     const(char)[] file;
238     size_t dirIndex;
239 }
240 
241 struct LocationInfo
242 {
243     int file;
244     int line;
245 }
246 
247 LineNumberProgram readLineNumberProgram(ref const(ubyte)[] data) @nogc nothrow
248 {
249     // import core.stdc.stdio : printf;
250     // printf("!my readLineNumberProgram: ");
251     // foreach (b; data[0..data.length > 256 ? 256 : $]) printf("%02X", b);
252     // printf("\n");
253 
254     const originalData = data;
255 
256     LineNumberProgram lp;
257 
258     bool is64bitDwarf = false;
259     lp.unitLength = data.read!uint();
260     if (lp.unitLength == uint.max)
261     {
262         is64bitDwarf = true;
263         lp.unitLength = data.read!ulong();
264     }
265 
266     const dwarfVersionFieldOffset = cast(size_t) (data.ptr - originalData.ptr);
267     lp.dwarfVersion = data.read!ushort();
268     debug(DwarfDebugMachine) printf("DWARF version: %d\n", lp.dwarfVersion);
269     assert(lp.dwarfVersion < 5, "DWARF v5+ not supported yet");
270 
271     lp.headerLength = (is64bitDwarf ? data.read!ulong() : data.read!uint());
272 
273     const minimumInstructionLengthFieldOffset = cast(size_t) (data.ptr - originalData.ptr);
274     lp.minimumInstructionLength = data.read!ubyte();
275 
276     lp.maximumOperationsPerInstruction = (lp.dwarfVersion >= 4 ? data.read!ubyte() : 1);
277     lp.defaultIsStatement = (data.read!ubyte() != 0);
278     lp.lineBase = data.read!byte();
279     lp.lineRange = data.read!ubyte();
280     lp.opcodeBase = data.read!ubyte();
281 
282     lp.standardOpcodeLengths = data[0 .. lp.opcodeBase - 1];
283     data = data[lp.opcodeBase - 1 .. $];
284 
285     // A sequence ends with a null-byte.
286     static auto readSequence(alias ReadEntry)(ref const(ubyte)[] data)
287     {
288         alias ResultType = typeof(ReadEntry(data));
289 
290         static size_t count(const(ubyte)[] data)
291         {
292             size_t count = 0;
293             while (data.length && data[0] != 0)
294             {
295                 ReadEntry(data);
296                 ++count;
297             }
298             return count;
299         }
300 
301         const numEntries = count(data);
302 
303         Array!ResultType result;
304         result.length = numEntries;
305 
306         foreach (i; 0 .. numEntries)
307             result[i] = ReadEntry(data);
308 
309         data = data[1 .. $]; // skip over sequence-terminating null
310 
311         return result;
312     }
313 
314     static const(char)[] readIncludeDirectoryEntry(ref const(ubyte)[] data)
315     {
316         const length = strlen(cast(char*) data.ptr);
317         auto result = cast(const(char)[]) data[0 .. length];
318         debug(DwarfDebugMachine) printf("dir: %.*s\n", cast(int) length, result.ptr);
319         data = data[length + 1 .. $];
320         return result;
321     }
322     lp.includeDirectories = readSequence!readIncludeDirectoryEntry(data);
323 
324     static SourceFile readFileNameEntry(ref const(ubyte)[] data)
325     {
326         const length = strlen(cast(char*) data.ptr);
327         auto file = cast(const(char)[]) data[0 .. length];
328         debug(DwarfDebugMachine) printf("file: %.*s\n", cast(int) length, file.ptr);
329         data = data[length + 1 .. $];
330 
331         auto dirIndex = cast(size_t) data.readULEB128();
332 
333         data.readULEB128(); // last mod
334         data.readULEB128(); // file len
335 
336         return SourceFile(
337             file,
338             dirIndex,
339         );
340     }
341     lp.sourceFiles = readSequence!readFileNameEntry(data);
342 
343     const programStart = cast(size_t) (minimumInstructionLengthFieldOffset + lp.headerLength);
344     const programEnd = cast(size_t) (dwarfVersionFieldOffset + lp.unitLength);
345     lp.program = originalData[programStart .. programEnd];
346 
347     data = originalData[programEnd .. $];
348 
349     return lp;
350 }
351 
352 T read(T)(ref const(ubyte)[] buffer) @nogc nothrow
353 {
354     version (X86)         enum hasUnalignedLoads = true;
355     else version (X86_64) enum hasUnalignedLoads = true;
356     else                  enum hasUnalignedLoads = false;
357 
358     static if (hasUnalignedLoads || T.alignof == 1)
359     {
360         T result = *(cast(T*) buffer.ptr);
361     }
362     else
363     {
364         import core.stdc.string : memcpy;
365         T result = void;
366         memcpy(&result, buffer.ptr, T.sizeof);
367     }
368 
369     buffer = buffer[T.sizeof .. $];
370     return result;
371 }
372 
373 ulong readULEB128(ref const(ubyte)[] buffer) @nogc nothrow
374 {
375     ulong val = 0;
376     uint shift = 0;
377 
378     while (true)
379     {
380         ubyte b = buffer.read!ubyte();
381 
382         val |= (b & 0x7f) << shift;
383         if ((b & 0x80) == 0) break;
384         shift += 7;
385     }
386 
387     return val;
388 }
389 
390 long readSLEB128(ref const(ubyte)[] buffer) @nogc nothrow
391 {
392     long val = 0;
393     uint shift = 0;
394     int size = 8 << 3;
395     ubyte b;
396 
397     while (true)
398     {
399         b = buffer.read!ubyte();
400         val |= (b & 0x7f) << shift;
401         shift += 7;
402         if ((b & 0x80) == 0)
403             break;
404     }
405 
406     if (shift < size && (b & 0x40) != 0)
407         val |= -(1 << shift);
408 
409     return val;
410 }
411 
412 alias RunStateMachineCallback =
413     bool delegate(size_t address, LocationInfo info, bool isEndSequence)
414     @nogc nothrow;
415 
416 enum StandardOpcode : ubyte
417 {
418     extendedOp = 0,
419     copy = 1,
420     advancePC = 2,
421     advanceLine = 3,
422     setFile = 4,
423     setColumn = 5,
424     negateStatement = 6,
425     setBasicBlock = 7,
426     constAddPC = 8,
427     fixedAdvancePC = 9,
428     setPrologueEnd = 10,
429     setEpilogueBegin = 11,
430     setISA = 12,
431 }
432 
433 enum ExtendedOpcode : ubyte
434 {
435     endSequence = 1,
436     setAddress = 2,
437     defineFile = 3,
438     setDiscriminator = 4,
439 }
440 
441 struct StateMachine
442 {
443     size_t address = 0;
444     uint operationIndex = 0;
445     uint fileIndex = 1;
446     uint line = 1;
447     uint column = 0;
448     uint isa = 0;
449     uint discriminator = 0;
450     bool isStatement;
451     bool isBasicBlock = false;
452     bool isEndSequence = false;
453     bool isPrologueEnd = false;
454     bool isEpilogueBegin = false;
455 }
456 
457 bool runStateMachine(ref const(LineNumberProgram) lp, scope RunStateMachineCallback callback) @nogc nothrow
458 {
459     StateMachine machine;
460     machine.isStatement = lp.defaultIsStatement;
461 
462     const(ubyte)[] program = lp.program;
463     while (program.length > 0)
464     {
465         size_t advanceAddressAndOpIndex(size_t operationAdvance)
466         {
467             const addressIncrement = lp.minimumInstructionLength * ((machine.operationIndex + operationAdvance) / lp.maximumOperationsPerInstruction);
468             machine.address += addressIncrement;
469             machine.operationIndex = (machine.operationIndex + operationAdvance) % lp.maximumOperationsPerInstruction;
470             return addressIncrement;
471         }
472 
473         ubyte opcode = program.read!ubyte();
474         if (opcode < lp.opcodeBase)
475         {
476             switch (opcode) with (StandardOpcode)
477             {
478                 case extendedOp:
479                     size_t len = cast(size_t) program.readULEB128();
480                     ubyte eopcode = program.read!ubyte();
481 
482                     switch (eopcode) with (ExtendedOpcode)
483                     {
484                         case endSequence:
485                             machine.isEndSequence = true;
486                             debug(DwarfDebugMachine) printf("endSequence 0x%zx\n", machine.address);
487                             if (!callback(machine.address, LocationInfo(machine.fileIndex, machine.line), true)) return true;
488                             machine = StateMachine.init;
489                             machine.isStatement = lp.defaultIsStatement;
490                             break;
491 
492                         case setAddress:
493                             size_t address = program.read!size_t();
494                             debug(DwarfDebugMachine) printf("setAddress 0x%zx\n", address);
495                             machine.address = address;
496                             machine.operationIndex = 0;
497                             break;
498 
499                         case defineFile: // TODO: add proper implementation
500                             debug(DwarfDebugMachine) printf("defineFile\n");
501                             program = program[len - 1 .. $];
502                             break;
503 
504                         case setDiscriminator:
505                             const discriminator = cast(uint) program.readULEB128();
506                             debug(DwarfDebugMachine) printf("setDiscriminator %d\n", discriminator);
507                             machine.discriminator = discriminator;
508                             break;
509 
510                         default:
511                             // unknown opcode
512                             debug(DwarfDebugMachine) printf("unknown extended opcode %d\n", cast(int) eopcode);
513                             program = program[len - 1 .. $];
514                             break;
515                     }
516 
517                     break;
518 
519                 case copy:
520                     debug(DwarfDebugMachine) printf("copy 0x%zx\n", machine.address);
521                     if (!callback(machine.address, LocationInfo(machine.fileIndex, machine.line), false)) return true;
522                     machine.isBasicBlock = false;
523                     machine.isPrologueEnd = false;
524                     machine.isEpilogueBegin = false;
525                     machine.discriminator = 0;
526                     break;
527 
528                 case advancePC:
529                     const operationAdvance = cast(size_t) readULEB128(program);
530                     advanceAddressAndOpIndex(operationAdvance);
531                     debug(DwarfDebugMachine) printf("advancePC %d to 0x%zx\n", cast(int) operationAdvance, machine.address);
532                     break;
533 
534                 case advanceLine:
535                     long ad = readSLEB128(program);
536                     machine.line += ad;
537                     debug(DwarfDebugMachine) printf("advanceLine %d to %d\n", cast(int) ad, cast(int) machine.line);
538                     break;
539 
540                 case setFile:
541                     uint index = cast(uint) readULEB128(program);
542                     debug(DwarfDebugMachine) printf("setFile to %d\n", cast(int) index);
543                     machine.fileIndex = index;
544                     break;
545 
546                 case setColumn:
547                     uint col = cast(uint) readULEB128(program);
548                     debug(DwarfDebugMachine) printf("setColumn %d\n", cast(int) col);
549                     machine.column = col;
550                     break;
551 
552                 case negateStatement:
553                     debug(DwarfDebugMachine) printf("negateStatement\n");
554                     machine.isStatement = !machine.isStatement;
555                     break;
556 
557                 case setBasicBlock:
558                     debug(DwarfDebugMachine) printf("setBasicBlock\n");
559                     machine.isBasicBlock = true;
560                     break;
561 
562                 case constAddPC:
563                     const operationAdvance = (255 - lp.opcodeBase) / lp.lineRange;
564                     advanceAddressAndOpIndex(operationAdvance);
565                     debug(DwarfDebugMachine) printf("constAddPC 0x%zx\n", machine.address);
566                     break;
567 
568                 case fixedAdvancePC:
569                     const add = program.read!ushort();
570                     machine.address += add;
571                     machine.operationIndex = 0;
572                     debug(DwarfDebugMachine) printf("fixedAdvancePC %d to 0x%zx\n", cast(int) add, machine.address);
573                     break;
574 
575                 case setPrologueEnd:
576                     machine.isPrologueEnd = true;
577                     debug(DwarfDebugMachine) printf("setPrologueEnd\n");
578                     break;
579 
580                 case setEpilogueBegin:
581                     machine.isEpilogueBegin = true;
582                     debug(DwarfDebugMachine) printf("setEpilogueBegin\n");
583                     break;
584 
585                 case setISA:
586                     machine.isa = cast(uint) readULEB128(program);
587                     debug(DwarfDebugMachine) printf("setISA %d\n", cast(int) machine.isa);
588                     break;
589 
590                 default:
591                     debug(DwarfDebugMachine) printf("unknown opcode %d\n", cast(int) opcode);
592                     return false;
593             }
594         }
595         else
596         {
597             opcode -= lp.opcodeBase;
598             const operationAdvance = opcode / lp.lineRange;
599             const addressIncrement = advanceAddressAndOpIndex(operationAdvance);
600             const lineIncrement = lp.lineBase + (opcode % lp.lineRange);
601             machine.line += lineIncrement;
602 
603             debug (DwarfDebugMachine)
604                 printf("special %d %d to 0x%zx line %d\n", cast(int) addressIncrement,
605                        cast(int) lineIncrement, machine.address, machine.line);
606 
607             if (!callback(machine.address, LocationInfo(machine.fileIndex, machine.line), false)) return true;
608 
609             machine.isBasicBlock = false;
610             machine.isPrologueEnd = false;
611             machine.isEpilogueBegin = false;
612             machine.discriminator = 0;
613         }
614     }
615 
616     return true;
617 }
618 
619 struct Location
620 {
621     const(char)[] file = null;
622     const(char)[] directory = null;
623     int line = -1;
624     size_t address;
625 }
626 
627 // See: module rt.util.container.array;
628 struct Array(T)
629 {
630 nothrow:
631     @disable this(this);
632 
633     ~this()
634     {
635         reset();
636     }
637 
638     void reset()
639     {
640         length = 0;
641     }
642 
643     @property size_t length() const
644     {
645         return _length;
646     }
647 
648     @property void length(size_t nlength)
649     {
650         import core.checkedint : mulu;
651 
652         bool overflow = false;
653         size_t reqsize = mulu(T.sizeof, nlength, overflow);
654         if (!overflow)
655         {
656             if (nlength < _length)
657                 foreach (ref val; _ptr[nlength .. _length]) .destroy(val);
658             _ptr = cast(T*).xrealloc(_ptr, reqsize);
659             if (nlength > _length)
660                 foreach (ref val; _ptr[_length .. nlength]) .initialize(val);
661             _length = nlength;
662         }
663         else
664             onOutOfMemoryErrorNoGC();
665 
666     }
667 
668     @property bool empty() const
669     {
670         return !length;
671     }
672 
673     @property ref inout(T) front() inout
674     in { assert(!empty); }
675     do
676     {
677         return _ptr[0];
678     }
679 
680     @property ref inout(T) back() inout
681     in { assert(!empty); }
682     do
683     {
684         return _ptr[_length - 1];
685     }
686 
687     ref inout(T) opIndex(size_t idx) inout
688     in { assert(idx < length); }
689     do
690     {
691         return _ptr[idx];
692     }
693 
694     inout(T)[] opSlice() inout
695     {
696         return _ptr[0 .. _length];
697     }
698 
699     inout(T)[] opSlice(size_t a, size_t b) inout
700     in { assert(a < b && b <= length); }
701     do
702     {
703         return _ptr[a .. b];
704     }
705 
706     alias length opDollar;
707 
708     void insertBack()(auto ref T val)
709     {
710         import core.checkedint : addu;
711 
712         bool overflow = false;
713         size_t newlength = addu(length, 1, overflow);
714         if (!overflow)
715         {
716             length = newlength;
717             back = val;
718         }
719         else
720             onOutOfMemoryErrorNoGC();
721     }
722 
723     void popBack()
724     {
725         length = length - 1;
726     }
727 
728     void remove(size_t idx)
729     in { assert(idx < length); }
730     do
731     {
732         foreach (i; idx .. length - 1)
733             _ptr[i] = _ptr[i+1];
734         popBack();
735     }
736 
737     void swap(ref Array other)
738     {
739         auto ptr = _ptr;
740         _ptr = other._ptr;
741         other._ptr = ptr;
742         immutable len = _length;
743         _length = other._length;
744         other._length = len;
745     }
746 
747     invariant
748     {
749         assert(!_ptr == !_length);
750     }
751 
752 private:
753     T* _ptr;
754     size_t _length;
755 }
756 
757 import core.stdc.stdlib : malloc, realloc, free;
758 
759 // See: rt.util.container.common
760 void* xrealloc(void* ptr, size_t sz) nothrow @nogc
761 {
762     import core.exception;
763 
764     if (!sz) { .free(ptr); return null; }
765     if (auto nptr = .realloc(ptr, sz)) return nptr;
766     .free(ptr); onOutOfMemoryErrorNoGC();
767     assert(0);
768 }
769 
770 void destroy(T)(ref T t) if (is(T == struct) && dtorIsNothrow!T)
771 {
772     scope (failure) assert(0); // nothrow hack
773     object.destroy(t);
774 }
775 
776 void destroy(T)(ref T t) if (!is(T == struct))
777 {
778     t = T.init;
779 }
780 
781 void initialize(T)(ref T t) if (is(T == struct))
782 {
783     import core.stdc.string;
784     static if (__traits(isPOD, T)) // implies !hasElaborateAssign!T && !hasElaborateDestructor!T
785         t = T.init;
786     else static if (__traits(isZeroInit, T))
787         memset(&t, 0, T.sizeof);
788     else
789         memcpy(&t, typeid(T).initializer().ptr, T.sizeof);
790 }
791 
792 void initialize(T)(ref T t) if (!is(T == struct))
793 {
794     t = T.init;
795 }