diff --git a/src/core/elf.d b/src/core/elf.d new file mode 100644 index 0000000000..76cdb96650 --- /dev/null +++ b/src/core/elf.d @@ -0,0 +1,341 @@ +/** + * This code simplifies working with ELF binaries, e.g., iterating + * over loaded shared objects & their segments as well as reading + * headers and sections from ELF files. + * + * Reference: http://www.dwarfstd.org/ + * + * Copyright: Copyright Digital Mars 2015 - 2015. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: Yazan Dabain, Martin Kinkelin + * Source: $(DRUNTIMESRC core/elf.d) + */ + +module core.elf; + +version (linux) version = linux_or_bsd; +else version (FreeBSD) version = linux_or_bsd; +else version (DragonFlyBSD) version = linux_or_bsd; + +version (linux_or_bsd): + +import core.sys.posix.fcntl; +import core.sys.posix.unistd; + +version (linux) +{ + import core.sys.linux.link; + import core.sys.linux.elf; +} +else version (FreeBSD) +{ + import core.sys.freebsd.sys.link_elf; + import core.sys.freebsd.sys.elf; +} +else version (DragonFlyBSD) +{ + import core.sys.dragonflybsd.sys.link_elf; + import core.sys.dragonflybsd.sys.elf; +} + +alias Elf_Phdr = ElfW!"Phdr"; +alias Elf_Ehdr = ElfW!"Ehdr"; +alias Elf_Shdr = ElfW!"Shdr"; + +/**** + * Enables iterating over the process' currently loaded shared objects. + */ +struct SharedObjects +{ +@nogc nothrow: + alias Callback = int delegate(SharedObject); + + static int opApply(scope Callback dg) + { + extern(C) int nativeCallback(dl_phdr_info* info, size_t, void* data) + { + auto dg = *cast(Callback*) data; + return dg(SharedObject(*info)); + } + + return dl_iterate_phdr(&nativeCallback, &dg); + } +} + +struct SharedObject +{ +@nogc nothrow: + /**** + * Finds the shared object containing the specified address in one of its segments. + */ + static bool findForAddress(in void* address, out SharedObject result) + { + version (linux) enum IterateManually = true; + else version (NetBSD) enum IterateManually = true; + else enum IterateManually = false; + + static if (IterateManually) + { + foreach (object; SharedObjects) + { + const(Elf_Phdr)* segment; + if (object.findSegmentForAddress(address, segment)) + { + result = object; + return true; + } + } + return false; + } + else + { + return !!_rtld_addr_phdr(address, &result.info); + } + } + + dl_phdr_info info; + + void* baseAddress() const + { + return cast(void*) info.dlpi_addr; + } + + const(char)[] name() const + { + const(char)* cstr = info.dlpi_name; + + // the main executable has an empty name + if (cstr[0] == 0) + cstr = getprogname(); + + import core.stdc.string; + return cstr[0 .. strlen(cstr)]; + } + + /**** + * Iterates over this object's segments. + */ + int opApply(scope int delegate(ref const Elf_Phdr) @nogc nothrow dg) const + { + foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum]) + { + const r = dg(phdr); + if (r != 0) + return r; + } + return 0; + } + + bool findSegmentForAddress(in void* address, out const(Elf_Phdr)* result) const + { + if (address < baseAddress()) + return false; + + foreach (ref phdr; this) + { + const begin = baseAddress() + phdr.p_vaddr; + if (cast(size_t)(address - begin) < phdr.p_memsz) + { + result = &phdr; + return true; + } + } + return false; + } +} + +// ------------------------------- +// File-based memory-mapped I/O: +// ------------------------------- + +struct ElfFile +{ +@nogc nothrow: + static bool open(const(char)* path, out ElfFile file) + { + file = ElfFile(.open(path, O_RDONLY)); + return file.isValid(); + } + + this(int fd) + { + this.fd = fd; + if (fd != -1) + { + // memory map header + this.ehdr = MMapRegion!Elf_Ehdr(fd, 0, Elf_Ehdr.sizeof); + } + } + + @disable this(this); + + ~this() + { + if (fd != -1) close(fd); + } + + int fd = -1; + MMapRegion!Elf_Ehdr ehdr; + + bool isValid() const + { + return fd != -1 && isValidElfHeader(*ehdr); + } + + bool findSectionHeaderByName(const(char)[] sectionName, out ElfSectionHeader header) const + { + const index = findSectionIndexByName(sectionName); + if (index == -1) + return false; + header = ElfSectionHeader(this, index); + return true; + } + + size_t findSectionIndexByName(const(char)[] sectionName) const + { + const stringSectionHeader = ElfSectionHeader(this, ehdr.e_shstrndx); + const stringSection = ElfSection(this, stringSectionHeader); + + foreach (i; 0 .. ehdr.e_shnum) + { + auto sectionHeader = ElfSectionHeader(this, i); + auto currentName = getSectionName(stringSection, sectionHeader.sh_name); + if (sectionName == currentName) + return i; + } + + // not found + return -1; + } +} + +struct ElfSectionHeader +{ +@nogc nothrow: + this(ref const ElfFile file, size_t index) + { + assert(Elf_Shdr.sizeof == file.ehdr.e_shentsize); + shdr = MMapRegion!Elf_Shdr( + file.fd, + file.ehdr.e_shoff + index * file.ehdr.e_shentsize, + file.ehdr.e_shentsize + ); + } + + @disable this(this); + + alias shdr this; + MMapRegion!Elf_Shdr shdr; +} + +struct ElfSection +{ +@nogc nothrow: + this(ref const ElfFile file, ref const ElfSectionHeader shdr) + { + data = MMapRegion!void( + file.fd, + shdr.sh_offset, + shdr.sh_size, + ); + + length = shdr.sh_size; + } + + @disable this(this); + + const(void)[] get() const + { + return data.get()[0 .. length]; + } + + alias get this; + + MMapRegion!void data; + size_t length; +} + +private @nogc nothrow: + +version (linux) +{ + const(char)* getprogname() + { + import core.sys.linux.errno; + return program_invocation_name; + } +} +else +{ + extern(C) const(char)* getprogname(); +} + +const(char)[] getSectionName(ref const ElfSection stringSection, size_t nameIndex) +{ + const data = cast(const(ubyte[])) stringSection.get(); + + foreach (i; nameIndex .. data.length) + { + if (data[i] == 0) + return cast(const(char)[]) data[nameIndex .. i]; + } + + return null; +} + +bool isValidElfHeader(ref const Elf_Ehdr ehdr) +{ + version (D_LP64) alias ELFCLASS = ELFCLASS64; + else alias ELFCLASS = ELFCLASS32; + + version (LittleEndian) alias ELFDATA = ELFDATA2LSB; + else version (BigEndian) alias ELFDATA = ELFDATA2MSB; + else static assert(0, "unsupported byte order"); + + if (ehdr.e_ident[EI_MAG0] != ELFMAG0) return false; + if (ehdr.e_ident[EI_MAG1] != ELFMAG1) return false; + if (ehdr.e_ident[EI_MAG2] != ELFMAG2) return false; + if (ehdr.e_ident[EI_MAG3] != ELFMAG3) return false; + + // elf class and data encoding should match target's config + if (ehdr.e_ident[EI_CLASS] != ELFCLASS) return false; + if (ehdr.e_ident[EI_DATA] != ELFDATA ) return false; + + return true; +} + +struct MMapRegion(T) +{ +@nogc nothrow: + import core.sys.posix.sys.mman; + import core.sys.posix.unistd; + + this(int fd, size_t offset, size_t length) + { + auto pagesize = sysconf(_SC_PAGESIZE); + + auto realOffset = (offset / pagesize) * pagesize; + offsetDiff = offset - realOffset; + realLength = length + offsetDiff; + + mptr = mmap(null, realLength, PROT_READ, MAP_PRIVATE, fd, realOffset); + } + + @disable this(this); + + ~this() + { + if (mptr) munmap(mptr, realLength); + } + + const(T)* get() const + { + return cast(T*)(mptr + offsetDiff); + } + + alias get this; + + size_t realLength; + size_t offsetDiff; + void* mptr; +} diff --git a/src/rt/backtrace/elf.d b/src/rt/backtrace/elf.d index fe8faa7f84..6f0361fea6 100644 --- a/src/rt/backtrace/elf.d +++ b/src/rt/backtrace/elf.d @@ -17,8 +17,7 @@ else version(DragonFlyBSD) version = linux_or_bsd; version(linux_or_bsd): -import core.sys.posix.fcntl; -import core.sys.posix.unistd; +import core.elf; version(linux) import core.sys.linux.elf; version(FreeBSD) import core.sys.freebsd.sys.elf; @@ -30,9 +29,16 @@ struct Image static Image openSelf() { - Image image; + const(char)* selfPath; + foreach (object; SharedObjects) + { + // the first object is the main binary + selfPath = object.name().ptr; + break; + } - if (!ElfFile.openSelf(&image.file)) + Image image; + if (!ElfFile.open(selfPath, image.file)) image.file = ElfFile.init; return image; @@ -45,330 +51,39 @@ struct Image const(ubyte)[] getDebugLineSectionData() { - auto stringSectionHeader = ElfSectionHeader(&file, file.ehdr.e_shstrndx); - auto stringSection = ElfSection(&file, &stringSectionHeader); - - auto dbgSectionIndex = findSectionByName(&file, &stringSection, ".debug_line"); - if (dbgSectionIndex != -1) - { - auto dbgSectionHeader = ElfSectionHeader(&file, dbgSectionIndex); - // we don't support compressed debug sections - if ((dbgSectionHeader.shdr.sh_flags & SHF_COMPRESSED) != 0) - return null; - // debug_line section found and loaded - return ElfSection(&file, &dbgSectionHeader); - } - - return null; + ElfSectionHeader dbgSectionHeader; + if (!file.findSectionHeaderByName(".debug_line", dbgSectionHeader)) + return null; + + // we don't support compressed debug sections + if ((dbgSectionHeader.shdr.sh_flags & SHF_COMPRESSED) != 0) + return null; + + auto dbgSection = ElfSection(file, dbgSectionHeader); + const sectionData = cast(const(ubyte)[]) dbgSection.get(); + // do not munmap() the section data to be returned + import core.stdc.string; + ElfSection initialSection; + memcpy(&dbgSection, &initialSection, ElfSection.sizeof); + + return sectionData; } @property size_t baseAddress() { - version(linux) - { - import core.sys.linux.link; - import core.sys.linux.elf; - } - else version(FreeBSD) - { - import core.sys.freebsd.sys.link_elf; - import core.sys.freebsd.sys.elf; - } - else version(DragonFlyBSD) - { - import core.sys.dragonflybsd.sys.link_elf; - import core.sys.dragonflybsd.sys.elf; - } - - static struct ElfAddress - { - size_t begin; - bool set; - } - ElfAddress elfAddress; - // the DWARF addresses for DSOs are relative const isDynamicSharedObject = (file.ehdr.e_type == ET_DYN); if (!isDynamicSharedObject) return 0; - extern(C) int dl_iterate_phdr_cb_ngc_tracehandler(dl_phdr_info* info, size_t, void* elfObj) @nogc + size_t base = 0; + foreach (object; SharedObjects) { - auto obj = cast(ElfAddress*) elfObj; // only take the first address as this will be the main binary - if (obj.set) - return 0; - - obj.set = true; - - // use the base address of the object file - obj.begin = info.dlpi_addr; - return 0; - } - dl_iterate_phdr(&dl_iterate_phdr_cb_ngc_tracehandler, &elfAddress); - return elfAddress.begin; - } -} - -private: - -struct ElfFile -{ - static bool openSelf(ElfFile* file) @nogc nothrow - { - version (linux) - { - auto selfPath = "/proc/self/exe".ptr; - } - else version (FreeBSD) - { - char[1024] selfPathBuffer = void; - auto selfPath = getFreeBSDExePath(selfPathBuffer[]); - if (selfPath is null) return false; - } - else version (DragonFlyBSD) - { - auto selfPath = "/proc/curproc/file".ptr; - } - - file.fd = open(selfPath, O_RDONLY); - if (file.fd >= 0) - { - // memory map header - file.ehdr = MMapRegion!Elf_Ehdr(file.fd, 0, Elf_Ehdr.sizeof); - if (file.ehdr.isValidElfHeader()) - return true; - else - return false; - } - else - return false; - } - - @disable this(this); - - ~this() @nogc nothrow - { - if (fd != -1) close(fd); - } - - int fd = -1; - MMapRegion!Elf_Ehdr ehdr; -} - -struct ElfSectionHeader -{ - this(const(ElfFile)* file, size_t index) @nogc nothrow - { - assert(Elf_Shdr.sizeof == file.ehdr.e_shentsize); - shdr = MMapRegion!Elf_Shdr( - file.fd, - file.ehdr.e_shoff + index * file.ehdr.e_shentsize, - file.ehdr.e_shentsize - ); - } - - @disable this(this); - - alias shdr this; - MMapRegion!Elf_Shdr shdr; -} - -struct ElfSection -{ - this(ElfFile* file, ElfSectionHeader* shdr) @nogc nothrow - { - data = MMapRegion!ubyte( - file.fd, - shdr.sh_offset, - shdr.sh_size, - ); - - length = shdr.sh_size; - } - - @disable this(this); - - const(ubyte)[] get() @nogc nothrow - { - return data.get()[0 .. length]; - } - - alias get this; - - MMapRegion!ubyte data; - size_t length; -} - -const(char)[] getSectionName(const(ElfFile)* file, ElfSection* stringSection, size_t nameIndex) @nogc nothrow -{ - const(ubyte)[] data = stringSection.get(); - - foreach (i; nameIndex .. data.length) - { - if (data[i] == 0) - return cast(const(char)[])data[nameIndex .. i]; - } - - return null; -} - -size_t findSectionByName(const(ElfFile)* file, ElfSection* stringSection, const(char)[] sectionName) @nogc nothrow -{ - foreach (s; 0 .. file.ehdr.e_shnum) - { - auto sectionHeader = ElfSectionHeader(file, s); - auto currentName = getSectionName(file, stringSection, sectionHeader.sh_name); - if (sectionName == currentName) - return s; // TODO: attempt to move ElfSectionHeader instead of returning index - } - - // not found - return -1; -} - -private: - -version (FreeBSD) -{ - extern (C) int sysctl(const int* name, uint namelen, void* oldp, size_t* oldlenp, const void* newp, size_t newlen) @nogc nothrow; - const(char)* getFreeBSDExePath(char[] buffer) @nogc nothrow - { - enum - { - CTL_KERN = 1, - KERN_PROC = 14, - KERN_PROC_PATHNAME = 12 + base = cast(size_t) object.baseAddress(); + break; } - int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1]; - size_t len = buffer.length; - - auto result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0); // get the length of the path - if (result != 0) return null; - if (len + 1 > buffer.length) return null; - buffer[len] = 0; - return buffer.ptr; - } -} - -bool isValidElfHeader(const(Elf_Ehdr)* ehdr) @nogc nothrow -{ - if (ehdr.e_ident[EI_MAG0] != ELFMAG0) return false; - if (ehdr.e_ident[EI_MAG1] != ELFMAG1) return false; - if (ehdr.e_ident[EI_MAG2] != ELFMAG2) return false; - if (ehdr.e_ident[EI_MAG3] != ELFMAG3) return false; - - // elf class and data encoding should match target's config - if (ehdr.e_ident[EI_CLASS] != ELFCLASS) return false; - if (ehdr.e_ident[EI_DATA] != ELFDATA ) return false; - - return true; -} - -struct MMapRegion(T) -{ - import core.sys.posix.sys.mman; - import core.sys.posix.unistd; - - this(int fd, size_t offset, size_t length) @nogc nothrow - { - auto pagesize = sysconf(_SC_PAGESIZE); - - auto realOffset = (offset / pagesize) * pagesize; - offsetDiff = offset - realOffset; - realLength = length + offsetDiff; - - mptr = mmap(null, realLength, PROT_READ, MAP_PRIVATE, fd, realOffset); - } - - @disable this(this); - - ~this() @nogc nothrow - { - if (mptr) munmap(mptr, realLength); - } - - const(T)* get() const @nogc nothrow - { - return cast(T*)(mptr + offsetDiff); + return base; } - - alias get this; - - size_t realLength; - size_t offsetDiff; - void* mptr; -} - -version(X86) -{ - alias Elf_Ehdr = Elf32_Ehdr; - alias Elf_Shdr = Elf32_Shdr; - enum ELFCLASS = ELFCLASS32; -} -else version(X86_64) -{ - alias Elf_Ehdr = Elf64_Ehdr; - alias Elf_Shdr = Elf64_Shdr; - enum ELFCLASS = ELFCLASS64; -} -else version(ARM) -{ - alias Elf_Ehdr = Elf32_Ehdr; - alias Elf_Shdr = Elf32_Shdr; - enum ELFCLASS = ELFCLASS32; -} -else version(AArch64) -{ - alias Elf_Ehdr = Elf64_Ehdr; - alias Elf_Shdr = Elf64_Shdr; - enum ELFCLASS = ELFCLASS64; -} -else version(PPC) -{ - alias Elf_Ehdr = Elf32_Ehdr; - alias Elf_Shdr = Elf32_Shdr; - enum ELFCLASS = ELFCLASS32; -} -else version(PPC64) -{ - alias Elf_Ehdr = Elf64_Ehdr; - alias Elf_Shdr = Elf64_Shdr; - enum ELFCLASS = ELFCLASS64; -} -else version(MIPS) -{ - alias Elf_Ehdr = Elf32_Ehdr; - alias Elf_Shdr = Elf32_Shdr; - enum ELFCLASS = ELFCLASS32; -} -else version(MIPS64) -{ - alias Elf_Ehdr = Elf64_Ehdr; - alias Elf_Shdr = Elf64_Shdr; - enum ELFCLASS = ELFCLASS64; -} -else version(SystemZ) -{ - alias Elf_Ehdr = Elf64_Ehdr; - alias Elf_Shdr = Elf64_Shdr; - enum ELFCLASS = ELFCLASS64; -} -else -{ - static assert(0, "unsupported architecture"); -} - -version(LittleEndian) -{ - alias ELFDATA = ELFDATA2LSB; -} -else version(BigEndian) -{ - alias ELFDATA = ELFDATA2MSB; -} -else -{ - static assert(0, "unsupported byte order"); } diff --git a/src/rt/sections_elf_shared.d b/src/rt/sections_elf_shared.d index 6e7380f00a..668e227901 100644 --- a/src/rt/sections_elf_shared.d +++ b/src/rt/sections_elf_shared.d @@ -24,6 +24,7 @@ else enum SharedDarwin = false; static if (SharedELF || SharedDarwin): // debug = PRINTF; +import core.elf; import core.memory; import core.stdc.stdio; import core.stdc.stdlib : calloc, exit, free, malloc, EXIT_FAILURE; @@ -127,6 +128,7 @@ private: } } + void** _slot; ModuleGroup _moduleGroup; Array!(void[]) _gcRanges; static if (SharedELF) @@ -138,7 +140,6 @@ private: { GetTLSAnchor _getTLSAnchor; } - void** _slot; version (Shared) { @@ -146,6 +147,20 @@ private: Array!(DSO*) _deps; // D libraries needed by this DSO void* _handle; // corresponding handle } + + // get the TLS range for the executing thread + void[] tlsRange() const nothrow @nogc + { + static if (SharedELF) + { + return getTLSRange(_tlsMod, _tlsSize); + } + else static if (SharedDarwin) + { + return getTLSRange(_getTLSAnchor()); + } + else static assert(0, "unimplemented"); + } } /**** @@ -274,21 +289,16 @@ version (Shared) else { /*** - * Returns array of thread local storage ranges, lazily allocating it if - * necessary. + * Called once per thread; returns array of thread local storage ranges */ Array!(void[])* initTLSRanges() nothrow @nogc { - if (!_tlsRanges) - _tlsRanges = cast(Array!(void[])*)calloc(1, Array!(void[]).sizeof); - _tlsRanges || assert(0, "Could not allocate TLS range storage"); - return _tlsRanges; + return &_tlsRanges; } void finiTLSRanges(Array!(void[])* rngs) nothrow @nogc { rngs.reset(); - .free(rngs); } void scanTLSRanges(Array!(void[])* rngs, scope ScanDG dg) nothrow @@ -322,37 +332,16 @@ version (Shared) */ struct ThreadDSO { - static if (_pdso.sizeof == 8) alias CntType = uint; - else static if (_pdso.sizeof == 4) alias CntType = ushort; - else static assert(0, "unimplemented"); - - this(DSO* pdso, CntType refCnt, CntType addCnt) - { - _pdso = pdso; - _refCnt = refCnt; - _addCnt = addCnt; - updateTLSRange(); - } - DSO* _pdso; - alias _pdso this; - + static if (_pdso.sizeof == 8) uint _refCnt, _addCnt; + else static if (_pdso.sizeof == 4) ushort _refCnt, _addCnt; + else static assert(0, "unimplemented"); void[] _tlsRange; - CntType _refCnt; - CntType _addCnt; - + alias _pdso this; // update the _tlsRange for the executing thread void updateTLSRange() nothrow @nogc { - static if (SharedELF) - { - _tlsRange = getTLSRange(_pdso._tlsMod, _pdso._tlsSize); - } - else static if (SharedDarwin) - { - _tlsRange = getTLSRange(_pdso._getTLSAnchor()); - } - else static assert(0, "unimplemented"); + _tlsRange = _pdso.tlsRange(); } } Array!(ThreadDSO) _loadedDSOs; @@ -390,7 +379,7 @@ else * Thread local array that contains TLS memory ranges for each * library initialized in this thread. */ - Array!(void[])* _tlsRanges; + Array!(void[]) _tlsRanges; enum _rtLoading = false; } @@ -402,7 +391,7 @@ else version (OSX) private alias ImageHeader = mach_header*; else - private alias ImageHeader = dl_phdr_info; + private alias ImageHeader = SharedObject; extern(C) alias GetTLSAnchor = void* function() nothrow @nogc; @@ -443,27 +432,31 @@ extern(C) void _d_dso_registry(void* arg) pdso._slot = data._slot; *data._slot = pdso; // store backlink in library record - auto minfoBeg = data._minfo_beg; - while (minfoBeg < data._minfo_end && !*minfoBeg) ++minfoBeg; - auto minfoEnd = minfoBeg; - while (minfoEnd < data._minfo_end && *minfoEnd) ++minfoEnd; - pdso._moduleGroup = ModuleGroup(toRange(minfoBeg, minfoEnd)); + version (LDC) + { + auto minfoBeg = data._minfo_beg; + while (minfoBeg < data._minfo_end && !*minfoBeg) ++minfoBeg; + auto minfoEnd = minfoBeg; + while (minfoEnd < data._minfo_end && *minfoEnd) ++minfoEnd; + pdso._moduleGroup = ModuleGroup(toRange(minfoBeg, minfoEnd)); + } + else + pdso._moduleGroup = ModuleGroup(toRange(data._minfo_beg, data._minfo_end)); - version (DigitalMars) pdso._ehTables = toRange(data._deh_beg, data._deh_end); static if (SharedDarwin) pdso._getTLSAnchor = data._getTLSAnchor; ImageHeader header = void; - findImageHeaderForAddr(data._slot, &header) || assert(0); + findImageHeaderForAddr(data._slot, header) || assert(0); scanSegments(header, pdso); version (Shared) { auto handle = handleForAddr(data._slot); - pdso._handle = handle; - setDSOForHandle(pdso, pdso._handle); getDependencies(header, pdso._deps); + pdso._handle = handle; + setDSOForHandle(pdso, pdso._handle); if (!_rtLoading) { @@ -474,7 +467,7 @@ extern(C) void _d_dso_registry(void* arg) * thread with a refCnt of 1 and call the TlsCtors. */ immutable ushort refCnt = 1, addCnt = 0; - _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt)); + _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt, pdso.tlsRange())); } } else @@ -491,11 +484,7 @@ extern(C) void _d_dso_registry(void* arg) } foreach (p; _loadedDSOs) assert(p !is pdso); _loadedDSOs.insertBack(pdso); - version (OSX) - auto tlsRange = getTLSRange(data._getTLSAnchor()); - else - auto tlsRange = getTLSRange(pdso._tlsMod, pdso._tlsSize); - initTLSRanges().insertBack(tlsRange); + _tlsRanges.insertBack(pdso.tlsRange()); } // don't initialize modules before rt_init was called (see Bugzilla 11378) @@ -592,7 +581,7 @@ version (Shared) foreach (dep; pdso._deps) incThreadRef(dep, false); immutable ushort refCnt = 1, addCnt = incAdd ? 1 : 0; - _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt)); + _loadedDSOs.insertBack(ThreadDSO(pdso, refCnt, addCnt, pdso.tlsRange())); pdso._moduleGroup.runTlsCtors(); } } @@ -650,17 +639,13 @@ version (Shared) void initLocks() nothrow @nogc { version (Shared) - { !pthread_mutex_init(&_handleToDSOMutex, null) || assert(0); - } } void finiLocks() nothrow @nogc { version (Shared) - { !pthread_mutex_destroy(&_handleToDSOMutex) || assert(0); - } } void runModuleConstructors(DSO* pdso, bool runTlsCtors) @@ -710,18 +695,13 @@ version (Shared) { @nogc nothrow: const(char)* nameForDSO(in DSO* pdso) - { - return nameForAddr(pdso._slot); - } - - const(char)* nameForAddr(in void* addr) { Dl_info info = void; - dladdr(addr, &info) || assert(0); + dladdr(pdso._slot, &info) || assert(0); return info.dli_fname; } - DSO* dsoForHandle(void* handle) nothrow @nogc + DSO* dsoForHandle(void* handle) { DSO* pdso; !pthread_mutex_lock(&_handleToDSOMutex) || assert(0); @@ -731,7 +711,7 @@ version (Shared) return pdso; } - void setDSOForHandle(DSO* pdso, void* handle) nothrow @nogc + void setDSOForHandle(DSO* pdso, void* handle) { !pthread_mutex_lock(&_handleToDSOMutex) || assert(0); assert(handle !in _handleToDSO); @@ -739,7 +719,7 @@ version (Shared) !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0); } - void unsetDSOForHandle(DSO* pdso, void* handle) nothrow @nogc + void unsetDSOForHandle(DSO* pdso, void* handle) { !pthread_mutex_lock(&_handleToDSOMutex) || assert(0); assert(_handleToDSO[handle] == pdso); @@ -747,15 +727,15 @@ version (Shared) !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0); } - static if(SharedELF) void getDependencies(in ref dl_phdr_info info, ref Array!(DSO*) deps) nothrow @nogc + static if (SharedELF) void getDependencies(in ref SharedObject object, ref Array!(DSO*) deps) { // get the entries of the .dynamic section ElfW!"Dyn"[] dyns; - foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum]) + foreach (ref phdr; object) { if (phdr.p_type == PT_DYNAMIC) { - auto p = cast(ElfW!"Dyn"*)(info.dlpi_addr + phdr.p_vaddr); + auto p = cast(ElfW!"Dyn"*)(object.baseAddress() + phdr.p_vaddr); dyns = p[0 .. phdr.p_memsz / ElfW!"Dyn".sizeof]; break; } @@ -767,15 +747,15 @@ version (Shared) if (dyn.d_tag == DT_STRTAB) { version (CRuntime_Musl) - strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate + strtab = cast(const(char)*)(object.baseAddress() + dyn.d_un.d_ptr); // relocate else version (linux) strtab = cast(const(char)*)dyn.d_un.d_ptr; else version (FreeBSD) - strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate + strtab = cast(const(char)*)(object.baseAddress() + dyn.d_un.d_ptr); // relocate else version (NetBSD) - strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate + strtab = cast(const(char)*)(object.baseAddress() + dyn.d_un.d_ptr); // relocate else version (DragonFlyBSD) - strtab = cast(const(char)*)(info.dlpi_addr + dyn.d_un.d_ptr); // relocate + strtab = cast(const(char)*)(object.baseAddress() + dyn.d_un.d_ptr); // relocate else static assert(0, "unimplemented"); break; @@ -798,12 +778,12 @@ version (Shared) deps.insertBack(pdso); // append it to the dependencies } } - else static if(SharedDarwin) void getDependencies(in ImageHeader info, ref Array!(DSO*) deps) + else static if (SharedDarwin) void getDependencies(in ImageHeader info, ref Array!(DSO*) deps) { // FIXME: Not implemented yet. } - void* handleForName(const char* name) nothrow @nogc + void* handleForName(const char* name) { auto handle = .dlopen(name, RTLD_NOLOAD | RTLD_LAZY); if (handle !is null) .dlclose(handle); // drop reference count @@ -819,21 +799,21 @@ version (Shared) * Scan segments in the image header and store * the TLS and writeable data segments in *pdso. */ -static if (SharedELF) void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc +static if (SharedELF) void scanSegments(in ref SharedObject object, DSO* pdso) nothrow @nogc { - foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum]) + foreach (ref phdr; object) { switch (phdr.p_type) { case PT_LOAD: if (phdr.p_flags & PF_W) // writeable data segment { - auto beg = cast(void*)(info.dlpi_addr + phdr.p_vaddr); + auto beg = object.baseAddress() + phdr.p_vaddr; pdso._gcRanges.insertBack(beg[0 .. phdr.p_memsz]); } version (Shared) if (phdr.p_flags & PF_X) // code segment { - auto beg = cast(void*)(info.dlpi_addr + phdr.p_vaddr); + auto beg = object.baseAddress() + phdr.p_vaddr; pdso._codeSegments.insertBack(beg[0 .. phdr.p_memsz]); } break; @@ -845,13 +825,15 @@ static if (SharedELF) void scanSegments(in ref dl_phdr_info info, DSO* pdso) not // uClibc doesn't provide a 'dlpi_tls_modid' definition } else - pdso._tlsMod = info.dlpi_tls_modid; + pdso._tlsMod = object.info.dlpi_tls_modid; pdso._tlsSize = phdr.p_memsz; - - // align to multiple of size_t to avoid misaligned scanning - // (size is subtracted from TCB address to get base of TLS) - immutable mask = size_t.sizeof - 1; - pdso._tlsSize = (pdso._tlsSize + mask) & ~mask; + version (LDC) + { + // align to multiple of size_t to avoid misaligned scanning + // (size is subtracted from TCB address to get base of TLS) + immutable mask = size_t.sizeof - 1; + pdso._tlsSize = (pdso._tlsSize + mask) & ~mask; + } break; default: @@ -881,111 +863,17 @@ else static if (SharedDarwin) void scanSegments(mach_header* info, DSO* pdso) } } -/************************** - * Input: - * result where the output is to be written; dl_phdr_info is a Linux struct - * Returns: - * true if found, and *result is filled in - * References: - * http://linux.die.net/man/3/dl_iterate_phdr - */ -version (linux) bool findImageHeaderForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc +bool findImageHeaderForAddr(in void* addr, out ImageHeader result) nothrow @nogc { - static struct DG { const(void)* addr; dl_phdr_info* result; } - - extern(C) int callback(dl_phdr_info* info, size_t sz, void* arg) nothrow @nogc + version (OSX) { - auto p = cast(DG*)arg; - if (findSegmentForAddr(*info, p.addr)) - { - if (p.result !is null) *p.result = *info; - return 1; // break; - } - return 0; // continue iteration + result = _dyld_get_image_header_containing_address(addr); + return !!result; } - - auto dg = DG(addr, result); - - /* Linux function that walks through the list of an application's shared objects and - * calls 'callback' once for each object, until either all shared objects - * have been processed or 'callback' returns a nonzero value. - */ - return dl_iterate_phdr(&callback, &dg) != 0; -} -else version (FreeBSD) bool findImageHeaderForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc -{ - return !!_rtld_addr_phdr(addr, result); -} -else version (OSX) bool findImageHeaderForAddr(in void* addr, mach_header** result=null) nothrow @nogc -{ - auto header = _dyld_get_image_header_containing_address(addr); - if (result) *result = header; - return !!header; -} -else version (NetBSD) bool findImageHeaderForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc -{ - static struct DG { const(void)* addr; dl_phdr_info* result; } - - extern(C) int callback(dl_phdr_info* info, size_t sz, void* arg) nothrow @nogc - { - auto p = cast(DG*)arg; - if (findSegmentForAddr(*info, p.addr)) - { - if (p.result !is null) *p.result = *info; - return 1; // break; - } - return 0; // continue iteration - } - auto dg = DG(addr, result); - return dl_iterate_phdr(&callback, &dg) != 0; -} -else version (DragonFlyBSD) bool findImageHeaderForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc -{ - return !!_rtld_addr_phdr(addr, result); -} - -/********************************* - * Determine if 'addr' lies within shared object 'info'. - * If so, return true and fill in 'result' with the corresponding ELF program header. - */ -static if (SharedELF) bool findSegmentForAddr(in ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null) nothrow @nogc -{ - if (addr < cast(void*)info.dlpi_addr) // less than base address of object means quick reject - return false; - - foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum]) + else { - auto beg = cast(void*)(info.dlpi_addr + phdr.p_vaddr); - if (cast(size_t)(addr - beg) < phdr.p_memsz) - { - if (result !is null) *result = phdr; - return true; - } + return SharedObject.findForAddress(addr, result); } - return false; -} - -version (linux) import core.sys.linux.errno : program_invocation_name; -// should be in core.sys.freebsd.stdlib -version (FreeBSD) extern(C) const(char)* getprogname() nothrow @nogc; -version (OSX) extern(C) const(char)* getprogname() nothrow @nogc; -version (DragonFlyBSD) extern(C) const(char)* getprogname() nothrow @nogc; -version (NetBSD) extern(C) const(char)* getprogname() nothrow @nogc; - -@property const(char)* progname() nothrow @nogc -{ - version (linux) return program_invocation_name; - version (FreeBSD) return getprogname(); - version (OSX) return getprogname(); - version (DragonFlyBSD) return getprogname(); - version (NetBSD) return getprogname(); -} - -const(char)[] dsoName(const char* dlpi_name) nothrow @nogc -{ - // the main executable doesn't have a name in its dlpi_name field - const char* p = dlpi_name[0] != 0 ? dlpi_name : progname; - return p[0 .. strlen(p)]; } /************************** @@ -1022,10 +910,14 @@ struct tls_index version (OSX) { extern(C) void _d_dyld_getTLSRange(void*, void**, size_t*) nothrow @nogc; - private align(16) ubyte dummyTlsSymbol = 42; - // By initalizing dummyTlsSymbol with something non-zero and aligning - // to 16-bytes, section __thread_data will be aligned as a workaround - // for https://github.com/ldc-developers/ldc/issues/1252 + + version (LDC) + { + private align(16) ubyte dummyTlsSymbol = 42; + // By initializing dummyTlsSymbol with something non-zero and aligning + // to 16-bytes, section __thread_data will be aligned as a workaround + // for https://github.com/ldc-developers/ldc/issues/1252 + } void[] getTLSRange(void *tlsSymbol) nothrow @nogc { @@ -1038,14 +930,15 @@ version (OSX) } else { -version(LDC) + +version (LDC) { - version(PPC) + version (PPC) { extern(C) void* __tls_get_addr_opt(tls_index* ti) nothrow @nogc; alias __tls_get_addr = __tls_get_addr_opt; } - else version(PPC64) + else version (PPC64) { extern(C) void* __tls_get_addr_opt(tls_index* ti) nothrow @nogc; alias __tls_get_addr = __tls_get_addr_opt; @@ -1084,12 +977,15 @@ else version(MIPS64) else static assert( false, "Platform not supported." ); -// We do not want to depend on __tls_get_addr for non-Shared builds to support -// linking against a static C runtime. -version (X86) version = X86_Any; -version (X86_64) version = X86_Any; -version (Shared) {} else version (linux) version (X86_Any) - version = Static_Linux_X86_Any; +version (LDC) +{ + // We do not want to depend on __tls_get_addr for non-Shared builds to support + // linking against a static C runtime. + version (X86) version = X86_Any; + version (X86_64) version = X86_Any; + version (Shared) {} else version (linux) version (X86_Any) + version = Static_Linux_X86_Any; +} void[] getTLSRange(size_t mod, size_t sz) nothrow @nogc { @@ -1121,4 +1017,5 @@ void[] getTLSRange(size_t mod, size_t sz) nothrow @nogc return (__tls_get_addr(&ti)-TLS_DTV_OFFSET)[0 .. sz]; } } -} + +} // !OSX