xref: /netbsd-src/external/gpl3/gcc/dist/libphobos/libdruntime/gcc/sections/macho.d (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 // MACHO-specific support for sections.
2 // Copyright (C) 2021-2022 Free Software Foundation, Inc.
3 
4 // GCC is free software; you can redistribute it and/or modify it under
5 // the terms of the GNU General Public License as published by the Free
6 // Software Foundation; either version 3, or (at your option) any later
7 // version.
8 
9 // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
10 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 // for more details.
13 
14 // Under Section 7 of GPL version 3, you are granted additional
15 // permissions described in the GCC Runtime Library Exception, version
16 // 3.1, as published by the Free Software Foundation.
17 
18 // You should have received a copy of the GNU General Public License and
19 // a copy of the GCC Runtime Library Exception along with this program;
20 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
21 // <http://www.gnu.org/licenses/>.
22 
23 module gcc.sections.macho;
24 
25 version (OSX):
26 
27 import core.memory;
28 import core.stdc.stdlib;
29 import core.sys.darwin.dlfcn;
30 import core.sys.darwin.mach.dyld;
31 import core.sys.darwin.mach.getsect;
32 import core.sys.posix.pthread;
33 import rt.minfo;
34 import core.internal.container.array;
35 import core.internal.container.hashtab;
36 import gcc.sections.common;
37 
38 version (GNU_EMUTLS)
39     import gcc.emutls;
40 
41 alias DSO SectionGroup;
42 struct DSO
43 {
opApplyDSO44     static int opApply(scope int delegate(ref DSO) dg)
45     {
46         foreach (dso; _loadedDSOs)
47         {
48             if (auto res = dg(*dso))
49                 return res;
50         }
51         return 0;
52     }
53 
opApplyReverseDSO54     static int opApplyReverse(scope int delegate(ref DSO) dg)
55     {
56         foreach_reverse (dso; _loadedDSOs)
57         {
58             if (auto res = dg(*dso))
59                 return res;
60         }
61         return 0;
62     }
63 
immutableDSO64     @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
65     {
66         return _moduleGroup.modules;
67     }
68 
69     @property ref inout(ModuleGroup) moduleGroup() inout return nothrow @nogc
70     {
71         return _moduleGroup;
72     }
73 
74     @property inout(void[])[] gcRanges() inout nothrow @nogc
75     {
76         return _gcRanges[];
77     }
78 
79 private:
80 
81     invariant()
82     {
83         safeAssert(_moduleGroup.modules.length > 0, "No modules for DSO.");
84     }
85 
86     void** _slot;
87     ModuleGroup _moduleGroup;
88     Array!(void[]) _gcRanges;
89 
versionDSO90     version (Shared)
91     {
92         Array!(void[]) _codeSegments; // array of code segments
93         Array!(DSO*) _deps; // D libraries needed by this DSO
94         void* _handle; // corresponding handle
95     }
96 }
97 
98 /****
99  * Boolean flag set to true while the runtime is initialized.
100  */
101 __gshared bool _isRuntimeInitialized;
102 
103 /****
104  * Gets called on program startup just before GC is initialized.
105  */
initSections()106 void initSections() nothrow @nogc
107 {
108     _isRuntimeInitialized = true;
109 }
110 
111 /***
112  * Gets called on program shutdown just after GC is terminated.
113  */
finiSections()114 void finiSections() nothrow @nogc
115 {
116     _isRuntimeInitialized = false;
117 }
118 
119 alias ScanDG = void delegate(void* pbeg, void* pend) nothrow;
120 
version(Shared)121 version (Shared)
122 {
123     import gcc.sections : pinLoadedLibraries, unpinLoadedLibraries,
124            inheritLoadedLibraries, cleanupLoadedLibraries;
125 
126     /***
127      * Called once per thread; returns array of thread local storage ranges
128      */
129     Array!(ThreadDSO)* initTLSRanges() @nogc nothrow
130     {
131         return &_loadedDSOs();
132     }
133 
134     void finiTLSRanges(Array!(ThreadDSO)* tdsos) @nogc nothrow
135     {
136         // Nothing to do here. tdsos used to point to the _loadedDSOs instance
137         // in the dying thread's TLS segment and as such is not valid anymore.
138         // The memory for the array contents was already reclaimed in
139         // cleanupLoadedLibraries().
140     }
141 
142     void scanTLSRanges(Array!(ThreadDSO)* tdsos, scope ScanDG dg) nothrow
143     {
144         version (GNU_EMUTLS)
145             _d_emutls_scan(dg);
146         else
147             static assert(0, "Native TLS unimplemented");
148     }
149 
150     // interface for core.thread to inherit loaded libraries
151     pragma(mangle, gcc.sections.pinLoadedLibraries.mangleof)
152     void* pinLoadedLibraries() nothrow @nogc
153     {
154         auto res = cast(Array!(ThreadDSO)*)calloc(1, Array!(ThreadDSO).sizeof);
155         res.length = _loadedDSOs.length;
156         foreach (i, ref tdso; _loadedDSOs)
157         {
158             (*res)[i] = tdso;
159             if (tdso._addCnt)
160             {
161                 // Increment the dlopen ref for explicitly loaded libraries to pin them.
162                 const success = .dlopen(nameForDSO(tdso._pdso), RTLD_LAZY) !is null;
163                 safeAssert(success, "Failed to increment dlopen ref.");
164                 (*res)[i]._addCnt = 1; // new array takes over the additional ref count
165             }
166         }
167         return res;
168     }
169 
170     pragma(mangle, gcc.sections.unpinLoadedLibraries.mangleof)
171     void unpinLoadedLibraries(void* p) nothrow @nogc
172     {
173         auto pary = cast(Array!(ThreadDSO)*)p;
174         // In case something failed we need to undo the pinning.
175         foreach (ref tdso; *pary)
176         {
177             if (tdso._addCnt)
178             {
179                 auto handle = tdso._pdso._handle;
180                 safeAssert(handle !is null, "Invalid library handle.");
181                 .dlclose(handle);
182             }
183         }
184         pary.reset();
185         .free(pary);
186     }
187 
188     // Called before TLS ctors are ran, copy over the loaded libraries
189     // of the parent thread.
190     pragma(mangle, gcc.sections.inheritLoadedLibraries.mangleof)
191     void inheritLoadedLibraries(void* p) nothrow @nogc
192     {
193         safeAssert(_loadedDSOs.empty, "DSOs have already been registered for this thread.");
194         _loadedDSOs.swap(*cast(Array!(ThreadDSO)*)p);
195         .free(p);
196     }
197 
198     // Called after all TLS dtors ran, decrements all remaining dlopen refs.
199     pragma(mangle, gcc.sections.cleanupLoadedLibraries.mangleof)
200     void cleanupLoadedLibraries() nothrow @nogc
201     {
202         foreach (ref tdso; _loadedDSOs)
203         {
204             if (tdso._addCnt == 0) continue;
205 
206             auto handle = tdso._pdso._handle;
207             safeAssert(handle !is null, "Invalid DSO handle.");
208             for (; tdso._addCnt > 0; --tdso._addCnt)
209                 .dlclose(handle);
210         }
211 
212         // Free the memory for the array contents.
213         _loadedDSOs.reset();
214     }
215 }
216 else
217 {
218     /***
219      * Called once per thread; returns array of thread local storage ranges
220      */
221     Array!(void[])* initTLSRanges() nothrow @nogc
222     {
223         return null;
224     }
225 
226     void finiTLSRanges(Array!(void[])* rngs) nothrow @nogc
227     {
228     }
229 
230     void scanTLSRanges(Array!(void[])* rngs, scope ScanDG dg) nothrow
231     {
232         version (GNU_EMUTLS)
233             _d_emutls_scan(dg);
234         else
235             static assert(0, "Native TLS unimplemented");
236     }
237 }
238 
239 private:
240 
version(Shared)241 version (Shared)
242 {
243     /*
244      * Array of thread local DSO metadata for all libraries loaded and
245      * initialized in this thread.
246      *
247      * Note:
248      *     A newly spawned thread will inherit these libraries.
249      * Note:
250      *     We use an array here to preserve the order of
251      *     initialization.  If that became a performance issue, we
252      *     could use a hash table and enumerate the DSOs during
253      *     loading so that the hash table values could be sorted when
254      *     necessary.
255      */
256     struct ThreadDSO
257     {
258         DSO* _pdso;
259         static if (_pdso.sizeof == 8) uint _refCnt, _addCnt;
260         else static if (_pdso.sizeof == 4) ushort _refCnt, _addCnt;
261         else static assert(0, "unimplemented");
262         alias _pdso this;
263     }
264 
265     @property ref Array!(ThreadDSO) _loadedDSOs() @nogc nothrow
266     {
267         static Array!(ThreadDSO) x;
268         return x;
269     }
270 
271     /*
272      * Set to true during rt_loadLibrary/rt_unloadLibrary calls.
273      */
274     bool _rtLoading;
275 
276     /*
277      * Hash table to map the native handle (as returned by dlopen)
278      * to the corresponding DSO*, protected by a mutex.
279      */
280     __gshared pthread_mutex_t _handleToDSOMutex;
281     @property ref HashTab!(void*, DSO*) _handleToDSO() @nogc nothrow
282     {
283         __gshared HashTab!(void*, DSO*) x;
284         return x;
285     }
286 }
287 else
288 {
289     /*
290      * Static DSOs loaded by the runtime linker. This includes the
291      * executable. These can't be unloaded.
292      */
293     @property ref Array!(DSO*) _loadedDSOs() @nogc nothrow
294     {
295         __gshared Array!(DSO*) x;
296         return x;
297     }
298 
299     enum _rtLoading = false;
300 }
301 
302 ///////////////////////////////////////////////////////////////////////////////
303 // Compiler to runtime interface.
304 ///////////////////////////////////////////////////////////////////////////////
305 
306 struct MachHeader
307 {
308     const(mach_header)* header; // the mach header of the image
309     intptr_t slide;             // virtural memory address slide amount
310 }
311 
312 /****
313  * This data structure is generated by the compiler, and then passed to
314  * _d_dso_registry().
315  */
316 struct CompilerDSOData
317 {
318     size_t _version;                                       // currently 1
319     void** _slot;                                          // can be used to store runtime data
320     immutable(object.ModuleInfo*)* _minfo_beg, _minfo_end; // array of modules in this object file
321 }
322 
toRange(T)323 T[] toRange(T)(T* beg, T* end) { return beg[0 .. end - beg]; }
324 
325 /* For each shared library and executable, the compiler generates code that
326  * sets up CompilerDSOData and calls _d_dso_registry().
327  * A pointer to that code is inserted into both the .ctors and .dtors
328  * segment so it gets called by the loader on startup and shutdown.
329  */
_d_dso_registry(CompilerDSOData * data)330 extern(C) void _d_dso_registry(CompilerDSOData* data)
331 {
332     // only one supported currently
333     safeAssert(data._version >= 1, "Incompatible compiler-generated DSO data version.");
334 
335     // no backlink => register
336     if (*data._slot is null)
337     {
338         immutable firstDSO = _loadedDSOs.empty;
339         if (firstDSO) initLocks();
340 
341         DSO* pdso = cast(DSO*).calloc(1, DSO.sizeof);
342         assert(typeid(DSO).initializer().ptr is null);
343         pdso._slot = data._slot;
344         *data._slot = pdso; // store backlink in library record
345 
346         pdso._moduleGroup = ModuleGroup(toRange(data._minfo_beg, data._minfo_end));
347 
348         MachHeader header = void;
349         const headerFound = findImageHeaderForAddr(data._slot, header);
350         safeAssert(headerFound, "Failed to find image header.");
351 
352         scanSegments(header, pdso);
353 
354         version (Shared)
355         {
356             auto handle = handleForAddr(data._slot);
357 
358             getDependencies(header, pdso._deps);
359             pdso._handle = handle;
360             setDSOForHandle(pdso, pdso._handle);
361 
362             if (!_rtLoading)
363             {
364                 /* This DSO was not loaded by rt_loadLibrary which
365                  * happens for all dependencies of an executable or
366                  * the first dlopen call from a C program.
367                  * In this case we add the DSO to the _loadedDSOs of this
368                  * thread with a refCnt of 1 and call the TlsCtors.
369                  */
370                 immutable ushort refCnt = 1, addCnt = 0;
371                 _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt));
372             }
373         }
374         else
375         {
376             foreach (p; _loadedDSOs)
377                 safeAssert(p !is pdso, "DSO already registered.");
378             _loadedDSOs.insertBack(pdso);
379         }
380 
381         // don't initialize modules before rt_init was called
382         if (_isRuntimeInitialized)
383         {
384             registerGCRanges(pdso);
385             // rt_loadLibrary will run tls ctors, so do this only for dlopen
386             immutable runTlsCtors = !_rtLoading;
387             runModuleConstructors(pdso, runTlsCtors);
388         }
389     }
390     // has backlink => unregister
391     else
392     {
393         DSO* pdso = cast(DSO*)*data._slot;
394         *data._slot = null;
395 
396         // don't finalizes modules after rt_term was called (see Bugzilla 11378)
397         if (_isRuntimeInitialized)
398         {
399             // rt_unloadLibrary already ran tls dtors, so do this only for dlclose
400             immutable runTlsDtors = !_rtLoading;
401             runModuleDestructors(pdso, runTlsDtors);
402             unregisterGCRanges(pdso);
403             // run finalizers after module dtors (same order as in rt_term)
404             version (Shared) runFinalizers(pdso);
405         }
406 
407         version (Shared)
408         {
409             if (!_rtLoading)
410             {
411                 /* This DSO was not unloaded by rt_unloadLibrary so we
412                  * have to remove it from _loadedDSOs here.
413                  */
414                 foreach (i, ref tdso; _loadedDSOs)
415                 {
416                     if (tdso._pdso == pdso)
417                     {
418                         _loadedDSOs.remove(i);
419                         break;
420                     }
421                 }
422             }
423 
424             unsetDSOForHandle(pdso, pdso._handle);
425         }
426         else
427         {
428             // static DSOs are unloaded in reverse order
429             safeAssert(pdso == _loadedDSOs.back, "DSO being unregistered isn't current last one.");
430             _loadedDSOs.popBack();
431         }
432 
433         freeDSO(pdso);
434 
435         // last DSO being unloaded => shutdown registry
436         if (_loadedDSOs.empty)
437         {
438             version (GNU_EMUTLS)
439                 _d_emutls_destroy();
440             version (Shared)
441             {
442                 safeAssert(_handleToDSO.empty, "_handleToDSO not in sync with _loadedDSOs.");
443                 _handleToDSO.reset();
444             }
445             finiLocks();
446         }
447     }
448 }
449 
450 ///////////////////////////////////////////////////////////////////////////////
451 // dynamic loading
452 ///////////////////////////////////////////////////////////////////////////////
453 
454 // Shared D libraries are only supported when linking against a shared druntime library.
455 
version(Shared)456 version (Shared)
457 {
458     ThreadDSO* findThreadDSO(DSO* pdso) nothrow @nogc
459     {
460         foreach (ref tdata; _loadedDSOs)
461             if (tdata._pdso == pdso) return &tdata;
462         return null;
463     }
464 
465     void incThreadRef(DSO* pdso, bool incAdd)
466     {
467         if (auto tdata = findThreadDSO(pdso)) // already initialized
468         {
469             if (incAdd && ++tdata._addCnt > 1) return;
470             ++tdata._refCnt;
471         }
472         else
473         {
474             foreach (dep; pdso._deps)
475                 incThreadRef(dep, false);
476             immutable ushort refCnt = 1, addCnt = incAdd ? 1 : 0;
477             _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt));
478             pdso._moduleGroup.runTlsCtors();
479         }
480     }
481 
482     void decThreadRef(DSO* pdso, bool decAdd)
483     {
484         auto tdata = findThreadDSO(pdso);
485         safeAssert(tdata !is null, "Failed to find thread DSO.");
486         safeAssert(!decAdd || tdata._addCnt > 0, "Mismatching rt_unloadLibrary call.");
487 
488         if (decAdd && --tdata._addCnt > 0) return;
489         if (--tdata._refCnt > 0) return;
490 
491         pdso._moduleGroup.runTlsDtors();
492         foreach (i, ref td; _loadedDSOs)
493             if (td._pdso == pdso) _loadedDSOs.remove(i);
494         foreach (dep; pdso._deps)
495             decThreadRef(dep, false);
496     }
497 
498     extern(C) void* rt_loadLibrary(const char* name)
499     {
500         immutable save = _rtLoading;
501         _rtLoading = true;
502         scope (exit) _rtLoading = save;
503 
504         auto handle = .dlopen(name, RTLD_LAZY);
505         if (handle is null) return null;
506 
507         // if it's a D library
508         if (auto pdso = dsoForHandle(handle))
509             incThreadRef(pdso, true);
510         return handle;
511     }
512 
513     extern(C) int rt_unloadLibrary(void* handle)
514     {
515         if (handle is null) return false;
516 
517         immutable save = _rtLoading;
518         _rtLoading = true;
519         scope (exit) _rtLoading = save;
520 
521         // if it's a D library
522         if (auto pdso = dsoForHandle(handle))
523             decThreadRef(pdso, true);
524         return .dlclose(handle) == 0;
525     }
526 }
527 
528 ///////////////////////////////////////////////////////////////////////////////
529 // helper functions
530 ///////////////////////////////////////////////////////////////////////////////
531 
initLocks()532 void initLocks() nothrow @nogc
533 {
534     version (Shared)
535         !pthread_mutex_init(&_handleToDSOMutex, null) || assert(0);
536 }
537 
finiLocks()538 void finiLocks() nothrow @nogc
539 {
540     version (Shared)
541         !pthread_mutex_destroy(&_handleToDSOMutex) || assert(0);
542 }
543 
runModuleConstructors(DSO * pdso,bool runTlsCtors)544 void runModuleConstructors(DSO* pdso, bool runTlsCtors)
545 {
546     pdso._moduleGroup.sortCtors();
547     pdso._moduleGroup.runCtors();
548     if (runTlsCtors) pdso._moduleGroup.runTlsCtors();
549 }
550 
runModuleDestructors(DSO * pdso,bool runTlsDtors)551 void runModuleDestructors(DSO* pdso, bool runTlsDtors)
552 {
553     if (runTlsDtors) pdso._moduleGroup.runTlsDtors();
554     pdso._moduleGroup.runDtors();
555 }
556 
registerGCRanges(DSO * pdso)557 void registerGCRanges(DSO* pdso) nothrow @nogc
558 {
559     foreach (rng; pdso._gcRanges)
560         GC.addRange(rng.ptr, rng.length);
561 }
562 
unregisterGCRanges(DSO * pdso)563 void unregisterGCRanges(DSO* pdso) nothrow @nogc
564 {
565     foreach (rng; pdso._gcRanges)
566         GC.removeRange(rng.ptr);
567 }
568 
version(Shared)569 version (Shared) void runFinalizers(DSO* pdso)
570 {
571     foreach (seg; pdso._codeSegments)
572         GC.runFinalizers(seg);
573 }
574 
freeDSO(DSO * pdso)575 void freeDSO(DSO* pdso) nothrow @nogc
576 {
577     pdso._gcRanges.reset();
578     version (Shared)
579     {
580         pdso._codeSegments.reset();
581         pdso._deps.reset();
582         pdso._handle = null;
583     }
584     .free(pdso);
585 }
586 
version(Shared)587 version (Shared)
588 {
589 @nogc nothrow:
590     const(char)* nameForDSO(in DSO* pdso)
591     {
592         Dl_info info = void;
593         const success = dladdr(pdso._slot, &info) != 0;
594         safeAssert(success, "Failed to get DSO info.");
595         return info.dli_fname;
596     }
597 
598     DSO* dsoForHandle(void* handle)
599     {
600         DSO* pdso;
601         !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
602         if (auto ppdso = handle in _handleToDSO)
603             pdso = *ppdso;
604         !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
605         return pdso;
606     }
607 
608     void setDSOForHandle(DSO* pdso, void* handle)
609     {
610         !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
611         safeAssert(handle !in _handleToDSO, "DSO already registered.");
612         _handleToDSO[handle] = pdso;
613         !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
614     }
615 
616     void unsetDSOForHandle(DSO* pdso, void* handle)
617     {
618         !pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
619         safeAssert(_handleToDSO[handle] == pdso, "Handle doesn't match registered DSO.");
620         _handleToDSO.remove(handle);
621         !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
622     }
623 
624     void getDependencies(in MachHeader info, ref Array!(DSO*) deps)
625     {
626         // FIXME: Not implemented yet.
627     }
628 
629     void* handleForName(const char* name)
630     {
631         auto handle = .dlopen(name, RTLD_NOLOAD | RTLD_LAZY);
632         if (handle !is null) .dlclose(handle); // drop reference count
633         return handle;
634     }
635 }
636 
637 ///////////////////////////////////////////////////////////////////////////////
638 // Mach-O program header iteration
639 ///////////////////////////////////////////////////////////////////////////////
640 
641 /************
642  * Scan segments in the image header and store
643  * the writeable data segments in *pdso.
644  */
645 
scanSegments(in MachHeader info,DSO * pdso)646 void scanSegments(in MachHeader info, DSO* pdso)
647 {
648     foreach (e; dataSegs)
649     {
650         auto sect = getSection(info.header, info.slide, e.seg.ptr, e.sect.ptr);
651         if (sect != null)
652             pdso._gcRanges.insertBack((cast(void*)sect.ptr)[0 .. sect.length]);
653     }
654 
655     version (Shared)
656     {
657         void[] text = getSection(info.header, info.slide, "__TEXT", "__text");
658         if (!text)
659             assert(0, "Failed to get text section.");
660         pdso._codeSegments.insertBack(text);
661     }
662 }
663 
664 /**************************
665  * Input:
666  *      result where the output is to be written
667  * Returns:
668  *      true if found, and *result is filled in
669  */
670 
findImageHeaderForAddr(in void * addr,out MachHeader result)671 bool findImageHeaderForAddr(in void* addr, out MachHeader result)
672 {
673     Dl_info info;
674     if (dladdr(addr, &info) == 0)
675         return false;
676 
677     foreach (i; 0 .. _dyld_image_count())
678     {
679         if (info.dli_fbase == _dyld_get_image_header(i))
680         {
681             result.header = cast(const(mach_header)*)info.dli_fbase;
682             result.slide = _dyld_get_image_vmaddr_slide(i);
683             return true;
684         }
685     }
686     return false;
687 }
688 
689 /**************************
690  * Input:
691  *      addr  an internal address of a DSO
692  * Returns:
693  *      the dlopen handle for that DSO or null if addr is not within a loaded DSO
694  */
version(Shared)695 version (Shared) void* handleForAddr(void* addr) nothrow @nogc
696 {
697     Dl_info info = void;
698     if (dladdr(addr, &info) != 0)
699         return handleForName(info.dli_fname);
700     return null;
701 }
702 
703 struct SegRef
704 {
705     string seg;
706     string sect;
707 }
708 
709 static immutable SegRef[] dataSegs = [{SEG_DATA, SECT_DATA},
710                                       {SEG_DATA, SECT_BSS},
711                                       {SEG_DATA, SECT_COMMON}];
712 
713 /**
714  * Returns the section for the named section in the named segment
715  * for the mach_header pointer passed, or null if not found.
716  */
getSection(in mach_header * header,intptr_t slide,in char * segmentName,in char * sectionName)717 ubyte[] getSection(in mach_header* header, intptr_t slide,
718                    in char* segmentName, in char* sectionName)
719 {
720     version (D_LP64)
721     {
722         assert(header.magic == MH_MAGIC_64);
723         auto sect = getsectbynamefromheader_64(cast(mach_header_64*)header,
724                                             segmentName,
725                                             sectionName);
726     }
727     else
728     {
729         assert(header.magic == MH_MAGIC);
730         auto sect = getsectbynamefromheader(header,
731                                             segmentName,
732                                             sectionName);
733     }
734 
735     if (sect !is null && sect.size > 0)
736         return (cast(ubyte*)sect.addr + slide)[0 .. cast(size_t)sect.size];
737     return null;
738 }
739