From 3299f8cd7839503793eb25677389ad6f086459f7 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 5 Oct 2018 02:24:44 +0200 Subject: [PATCH 1/9] Streamline rt.sections_elf_shared with upstream, mark LDC specifics etc. Including some tiny refactorings from my side (mainly wrt. overdue `DSO.tlsRange()`), which I'll propose with some earlier adaptations upstream later. There have been numerous merge conflicts wrt. to this file in the past; upstreaming our OSX adaptations to enable/help with shared Phobos support for DMD might be feasible as well. --- src/rt/sections_elf_shared.d | 242 ++++++++++++++++------------------- 1 file changed, 112 insertions(+), 130 deletions(-) diff --git a/src/rt/sections_elf_shared.d b/src/rt/sections_elf_shared.d index 6e7380f00a..35e4b789d4 100644 --- a/src/rt/sections_elf_shared.d +++ b/src/rt/sections_elf_shared.d @@ -127,6 +127,7 @@ private: } } + void** _slot; ModuleGroup _moduleGroup; Array!(void[]) _gcRanges; static if (SharedELF) @@ -138,7 +139,6 @@ private: { GetTLSAnchor _getTLSAnchor; } - void** _slot; version (Shared) { @@ -146,6 +146,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 +288,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 +331,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 +378,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; } @@ -443,13 +431,17 @@ 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; @@ -460,10 +452,10 @@ extern(C) void _d_dso_registry(void* arg) 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 +466,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 +483,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 +580,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 +638,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 +694,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 +710,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 +718,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,7 +726,7 @@ 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 dl_phdr_info info, ref Array!(DSO*) deps) { // get the entries of the .dynamic section ElfW!"Dyn"[] dyns; @@ -798,12 +777,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 @@ -847,11 +826,13 @@ static if (SharedELF) void scanSegments(in ref dl_phdr_info info, DSO* pdso) not else pdso._tlsMod = 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: @@ -883,65 +864,57 @@ 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 + * result where the output is to be written; dl_phdr_info is an OS 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, ImageHeader* result=null) nothrow @nogc { - static struct DG { const(void)* addr; dl_phdr_info* result; } + version (linux) enum IterateManually = true; + else version (NetBSD) enum IterateManually = true; + else enum IterateManually = false; - extern(C) int callback(dl_phdr_info* info, size_t sz, void* arg) nothrow @nogc + static if (IterateManually) { - auto p = cast(DG*)arg; - if (findSegmentForAddr(*info, p.addr)) + 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 { - if (p.result !is null) *p.result = *info; - return 1; // break; + 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 } - return 0; // continue iteration - } - - 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; } + auto dg = DG(addr, result); - extern(C) int callback(dl_phdr_info* info, size_t sz, void* arg) nothrow @nogc + /* OS 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 (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 + auto header = _dyld_get_image_header_containing_address(addr); + if (result) *result = header; + return !!header; } - 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); + else version (FreeBSD) + { + return !!_rtld_addr_phdr(addr, result); + } + else version (DragonFlyBSD) + { + return !!_rtld_addr_phdr(addr, result); + } + else + static assert(0, "unimplemented"); } /********************************* @@ -1022,10 +995,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 +1015,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 +1062,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 +1102,5 @@ void[] getTLSRange(size_t mod, size_t sz) nothrow @nogc return (__tls_get_addr(&ti)-TLS_DTV_OFFSET)[0 .. sz]; } } -} + +} // !OSX From 9111b310151838a69e7f683eebe39860bda62442 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 11 Oct 2018 00:35:56 +0200 Subject: [PATCH 2/9] Extract ELF section reading from rt.backtrace.elf to core.elf Incl. some minor refactoring (pointer to ref, make `findSectionByName()` an ElfFile method, add `findSectionHeaderByName()` convenience method etc.). --- src/core/elf.d | 255 +++++++++++++++++++++++++++++++++++++ src/rt/backtrace/elf.d | 278 ++++------------------------------------- 2 files changed, 280 insertions(+), 253 deletions(-) create mode 100644 src/core/elf.d diff --git a/src/core/elf.d b/src/core/elf.d new file mode 100644 index 0000000000..d06e51a651 --- /dev/null +++ b/src/core/elf.d @@ -0,0 +1,255 @@ +/** + * This code reads ELF files and sections using memory mapped IO. + * + * 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 + * 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.elf; +version(FreeBSD) import core.sys.freebsd.sys.elf; +version(DragonFlyBSD) import core.sys.dragonflybsd.sys.elf; + +struct ElfFile +{ + static bool open(const(char)* path, out ElfFile file) @nogc nothrow + { + file.fd = .open(path, O_RDONLY); + if (file.fd < 0) + return false; + + // memory map header + file.ehdr = MMapRegion!Elf_Ehdr(file.fd, 0, Elf_Ehdr.sizeof); + return isValidElfHeader(*file.ehdr); + } + + @disable this(this); + + ~this() @nogc nothrow + { + if (fd != -1) close(fd); + } + + int fd = -1; + MMapRegion!Elf_Ehdr ehdr; + + bool findSectionHeaderByName(const(char)[] sectionName, out ElfSectionHeader header) const @nogc nothrow + { + const index = findSectionIndexByName(sectionName); + if (index == -1) + return false; + header = ElfSectionHeader(this, index); + return true; + } + + size_t findSectionIndexByName(const(char)[] sectionName) const @nogc nothrow + { + 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 +{ + this(ref 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(ref const ElfFile file, ref const ElfSectionHeader shdr) @nogc nothrow + { + data = MMapRegion!void( + file.fd, + shdr.sh_offset, + shdr.sh_size, + ); + + length = shdr.sh_size; + } + + @disable this(this); + + const(void)[] get() const @nogc nothrow + { + return data.get()[0 .. length]; + } + + alias get this; + + MMapRegion!void data; + size_t length; +} + +private: + +const(char)[] getSectionName(ref const ElfSection stringSection, size_t nameIndex) @nogc nothrow +{ + 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) @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); + } + + 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/backtrace/elf.d b/src/rt/backtrace/elf.d index fe8faa7f84..958a4fed35 100644 --- a/src/rt/backtrace/elf.d +++ b/src/rt/backtrace/elf.d @@ -17,6 +17,7 @@ else version(DragonFlyBSD) version = linux_or_bsd; version(linux_or_bsd): +import core.elf; import core.sys.posix.fcntl; import core.sys.posix.unistd; @@ -30,9 +31,23 @@ struct Image static Image openSelf() { - Image image; + 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; + } - if (!ElfFile.openSelf(&image.file)) + Image image; + if (!ElfFile.open(selfPath, image.file)) image.file = ElfFile.init; return image; @@ -45,21 +60,16 @@ struct Image const(ubyte)[] getDebugLineSectionData() { - auto stringSectionHeader = ElfSectionHeader(&file, file.ehdr.e_shstrndx); - auto stringSection = ElfSection(&file, &stringSectionHeader); + ElfSectionHeader dbgSectionHeader; + if (!file.findSectionHeaderByName(".debug_line", dbgSectionHeader)) + return null; - 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); - } + // we don't support compressed debug sections + if ((dbgSectionHeader.shdr.sh_flags & SHF_COMPRESSED) != 0) + return null; - return null; + // debug_line section found and loaded + return cast(const(ubyte)[]) ElfSection(file, dbgSectionHeader).get(); } @property size_t baseAddress() @@ -112,123 +122,6 @@ struct Image 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; @@ -251,124 +144,3 @@ version (FreeBSD) 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); - } - - 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"); -} From 444fcf140dd03e5bad11a00f5105b62a6897b855 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 11 Oct 2018 22:12:44 +0200 Subject: [PATCH 3/9] rt.backtrace.elf: Prevent munmap()ping the .debug_line section data before use --- src/rt/backtrace/elf.d | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/rt/backtrace/elf.d b/src/rt/backtrace/elf.d index 958a4fed35..d4bffdbbb4 100644 --- a/src/rt/backtrace/elf.d +++ b/src/rt/backtrace/elf.d @@ -68,8 +68,14 @@ struct Image if ((dbgSectionHeader.shdr.sh_flags & SHF_COMPRESSED) != 0) return null; - // debug_line section found and loaded - return cast(const(ubyte)[]) ElfSection(file, dbgSectionHeader).get(); + 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() From e7431599862214978ba513b4a2ff78f3cec843b2 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 11 Oct 2018 22:44:31 +0200 Subject: [PATCH 4/9] core.elf: Support simple iteration over the currently loaded shared objects --- src/core/elf.d | 37 +++++++++++++++++++++++++++++++++--- src/rt/backtrace/elf.d | 43 ++++++------------------------------------ 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/core/elf.d b/src/core/elf.d index d06e51a651..e8f89f3a38 100644 --- a/src/core/elf.d +++ b/src/core/elf.d @@ -20,9 +20,40 @@ version(linux_or_bsd): import core.sys.posix.fcntl; import core.sys.posix.unistd; -version(linux) import core.sys.linux.elf; -version(FreeBSD) import core.sys.freebsd.sys.elf; -version(DragonFlyBSD) import core.sys.dragonflybsd.sys.elf; +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; +} + +/**** + * Enables iterating over the process' currently loaded shared objects. + */ +struct SharedObjects +{ + alias Callback = int delegate(ref const dl_phdr_info) @nogc nothrow; + + static int opApply(scope Callback dg) @nogc nothrow + { + extern(C) int nativeCallback(dl_phdr_info* info, size_t, void* data) @nogc nothrow + { + auto dg = *cast(Callback*) data; + return dg(*info); + } + + return dl_iterate_phdr(&nativeCallback, &dg); + } +} struct ElfFile { diff --git a/src/rt/backtrace/elf.d b/src/rt/backtrace/elf.d index d4bffdbbb4..166d3206fb 100644 --- a/src/rt/backtrace/elf.d +++ b/src/rt/backtrace/elf.d @@ -18,8 +18,6 @@ else version(DragonFlyBSD) version = linux_or_bsd; version(linux_or_bsd): import core.elf; -import core.sys.posix.fcntl; -import core.sys.posix.unistd; version(linux) import core.sys.linux.elf; version(FreeBSD) import core.sys.freebsd.sys.elf; @@ -80,49 +78,20 @@ struct Image @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 (ref info; 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; + base = info.dlpi_addr; + break; } - dl_iterate_phdr(&dl_iterate_phdr_cb_ngc_tracehandler, &elfAddress); - return elfAddress.begin; + + return base; } } From cb767f3e819f303ef06d7930eb3c39547313e899 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 11 Oct 2018 22:47:28 +0200 Subject: [PATCH 5/9] core.elf: Avoid code duplication --- src/core/elf.d | 87 +++++++++++++------------------------------------- 1 file changed, 23 insertions(+), 64 deletions(-) diff --git a/src/core/elf.d b/src/core/elf.d index e8f89f3a38..cc2bc63ab0 100644 --- a/src/core/elf.d +++ b/src/core/elf.d @@ -41,11 +41,12 @@ else version(DragonFlyBSD) */ struct SharedObjects { - alias Callback = int delegate(ref const dl_phdr_info) @nogc nothrow; +@nogc nothrow: + alias Callback = int delegate(ref const dl_phdr_info); - static int opApply(scope Callback dg) @nogc nothrow + static int opApply(scope Callback dg) { - extern(C) int nativeCallback(dl_phdr_info* info, size_t, void* data) @nogc nothrow + extern(C) int nativeCallback(dl_phdr_info* info, size_t, void* data) { auto dg = *cast(Callback*) data; return dg(*info); @@ -57,7 +58,8 @@ struct SharedObjects struct ElfFile { - static bool open(const(char)* path, out ElfFile file) @nogc nothrow +@nogc nothrow: + static bool open(const(char)* path, out ElfFile file) { file.fd = .open(path, O_RDONLY); if (file.fd < 0) @@ -70,7 +72,7 @@ struct ElfFile @disable this(this); - ~this() @nogc nothrow + ~this() { if (fd != -1) close(fd); } @@ -78,7 +80,7 @@ struct ElfFile int fd = -1; MMapRegion!Elf_Ehdr ehdr; - bool findSectionHeaderByName(const(char)[] sectionName, out ElfSectionHeader header) const @nogc nothrow + bool findSectionHeaderByName(const(char)[] sectionName, out ElfSectionHeader header) const { const index = findSectionIndexByName(sectionName); if (index == -1) @@ -87,7 +89,7 @@ struct ElfFile return true; } - size_t findSectionIndexByName(const(char)[] sectionName) const @nogc nothrow + size_t findSectionIndexByName(const(char)[] sectionName) const { const stringSectionHeader = ElfSectionHeader(this, ehdr.e_shstrndx); const stringSection = ElfSection(this, stringSectionHeader); @@ -107,7 +109,8 @@ struct ElfFile struct ElfSectionHeader { - this(ref const ElfFile file, size_t index) @nogc nothrow +@nogc nothrow: + this(ref const ElfFile file, size_t index) { assert(Elf_Shdr.sizeof == file.ehdr.e_shentsize); shdr = MMapRegion!Elf_Shdr( @@ -125,7 +128,8 @@ struct ElfSectionHeader struct ElfSection { - this(ref const ElfFile file, ref const ElfSectionHeader shdr) @nogc nothrow +@nogc nothrow: + this(ref const ElfFile file, ref const ElfSectionHeader shdr) { data = MMapRegion!void( file.fd, @@ -138,7 +142,7 @@ struct ElfSection @disable this(this); - const(void)[] get() const @nogc nothrow + const(void)[] get() const { return data.get()[0 .. length]; } @@ -149,9 +153,9 @@ struct ElfSection size_t length; } -private: +private @nogc nothrow: -const(char)[] getSectionName(ref const ElfSection stringSection, size_t nameIndex) @nogc nothrow +const(char)[] getSectionName(ref const ElfSection stringSection, size_t nameIndex) { const data = cast(const(ubyte[])) stringSection.get(); @@ -164,7 +168,7 @@ const(char)[] getSectionName(ref const ElfSection stringSection, size_t nameInde return null; } -bool isValidElfHeader(ref const Elf_Ehdr ehdr) @nogc nothrow +bool isValidElfHeader(ref const Elf_Ehdr ehdr) { if (ehdr.e_ident[EI_MAG0] != ELFMAG0) return false; if (ehdr.e_ident[EI_MAG1] != ELFMAG1) return false; @@ -180,10 +184,11 @@ bool isValidElfHeader(ref const Elf_Ehdr ehdr) @nogc nothrow 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) @nogc nothrow + this(int fd, size_t offset, size_t length) { auto pagesize = sysconf(_SC_PAGESIZE); @@ -196,12 +201,12 @@ struct MMapRegion(T) @disable this(this); - ~this() @nogc nothrow + ~this() { if (mptr) munmap(mptr, realLength); } - const(T)* get() const @nogc nothrow + const(T)* get() const { return cast(T*)(mptr + offsetDiff); } @@ -213,64 +218,18 @@ struct MMapRegion(T) 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) +version (D_LP64) { 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) +else { 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) { From 11972b7565f6334fb6cdb7d714b1009f7a3ae6c2 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 11 Oct 2018 23:07:04 +0200 Subject: [PATCH 6/9] core.elf: Add ElfFile convenience ctor taking a file descriptor --- src/core/elf.d | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/core/elf.d b/src/core/elf.d index cc2bc63ab0..8f8a6453fa 100644 --- a/src/core/elf.d +++ b/src/core/elf.d @@ -61,13 +61,18 @@ struct ElfFile @nogc nothrow: static bool open(const(char)* path, out ElfFile file) { - file.fd = .open(path, O_RDONLY); - if (file.fd < 0) - return false; + file = ElfFile(.open(path, O_RDONLY)); + return file.isValid(); + } - // memory map header - file.ehdr = MMapRegion!Elf_Ehdr(file.fd, 0, Elf_Ehdr.sizeof); - return isValidElfHeader(*file.ehdr); + 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); @@ -80,6 +85,11 @@ struct ElfFile 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); From 14e068bf7f89b99e3ca3b9b1518e5b8044d9103b Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 14 Oct 2018 13:07:11 +0200 Subject: [PATCH 7/9] Move some functionality from rt.sections_elf_shared to core.elf --- src/core/elf.d | 110 +++++++++++++++++++++++++++++- src/rt/backtrace/elf.d | 4 +- src/rt/sections_elf_shared.d | 125 ++++++----------------------------- 3 files changed, 129 insertions(+), 110 deletions(-) diff --git a/src/core/elf.d b/src/core/elf.d index 8f8a6453fa..2cf3dc92a2 100644 --- a/src/core/elf.d +++ b/src/core/elf.d @@ -5,7 +5,7 @@ * * Copyright: Copyright Digital Mars 2015 - 2015. * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Yazan Dabain + * Authors: Yazan Dabain, Martin Kinkelin * Source: $(DRUNTIMESRC core/elf.d) */ @@ -42,20 +42,124 @@ else version(DragonFlyBSD) struct SharedObjects { @nogc nothrow: - alias Callback = int delegate(ref const dl_phdr_info); + 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(*info); + return dg(SharedObject(*info)); } return dl_iterate_phdr(&nativeCallback, &dg); } } +version (linux) +{ + import core.sys.linux.errno : program_invocation_name; +} +else +{ + extern(C) const(char)* getprogname() nothrow @nogc; +} + +struct SharedObject +{ +@nogc nothrow: + alias ProgramHeader = const(ElfW!"Phdr"); + + /**** + * 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) + { + ProgramHeader* 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) + { + version (linux) + { + cstr = program_invocation_name; + } + else + { + cstr = getprogname(); + } + } + + import core.stdc.string; + return cstr[0 .. strlen(cstr)]; + } + + /**** + * Iterates over this object's segments. + */ + int opApply(scope int delegate(ref ProgramHeader) @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 ProgramHeader* 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 I/O: + struct ElfFile { @nogc nothrow: diff --git a/src/rt/backtrace/elf.d b/src/rt/backtrace/elf.d index 166d3206fb..8d26803442 100644 --- a/src/rt/backtrace/elf.d +++ b/src/rt/backtrace/elf.d @@ -84,10 +84,10 @@ struct Image return 0; size_t base = 0; - foreach (ref info; SharedObjects) + foreach (object; SharedObjects) { // only take the first address as this will be the main binary - base = info.dlpi_addr; + base = cast(size_t) object.baseAddress(); break; } diff --git a/src/rt/sections_elf_shared.d b/src/rt/sections_elf_shared.d index 35e4b789d4..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; @@ -390,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; @@ -445,7 +446,7 @@ extern(C) void _d_dso_registry(void* arg) 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); @@ -726,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) + 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; } @@ -746,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,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; @@ -824,7 +825,7 @@ 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; version (LDC) { @@ -862,103 +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 an OS struct - * Returns: - * true if found, and *result is filled in - * References: - * http://linux.die.net/man/3/dl_iterate_phdr - */ -bool findImageHeaderForAddr(in void* addr, ImageHeader* result=null) nothrow @nogc +bool findImageHeaderForAddr(in void* addr, out ImageHeader result) nothrow @nogc { - version (linux) enum IterateManually = true; - else version (NetBSD) enum IterateManually = true; - else enum IterateManually = false; - - static if (IterateManually) - { - 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); - - /* OS 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 (OSX) - { - auto header = _dyld_get_image_header_containing_address(addr); - if (result) *result = header; - return !!header; - } - else version (FreeBSD) + version (OSX) { - return !!_rtld_addr_phdr(addr, result); - } - else version (DragonFlyBSD) - { - return !!_rtld_addr_phdr(addr, result); + result = _dyld_get_image_header_containing_address(addr); + return !!result; } else - static assert(0, "unimplemented"); -} - -/********************************* - * 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]) { - 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)]; } /************************** From 40d591ef1c9bd376410d26d27a5aaf0e9dae43f7 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 14 Oct 2018 16:35:00 +0200 Subject: [PATCH 8/9] Slight core.elf refactoring --- src/core/elf.d | 100 ++++++++++++++++++++----------------------------- 1 file changed, 41 insertions(+), 59 deletions(-) diff --git a/src/core/elf.d b/src/core/elf.d index 2cf3dc92a2..76cdb96650 100644 --- a/src/core/elf.d +++ b/src/core/elf.d @@ -1,5 +1,7 @@ /** - * This code reads ELF files and sections using memory mapped IO. + * 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/ * @@ -11,31 +13,35 @@ 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) version = linux_or_bsd; +else version (FreeBSD) version = linux_or_bsd; +else version (DragonFlyBSD) version = linux_or_bsd; -version(linux_or_bsd): +version (linux_or_bsd): import core.sys.posix.fcntl; import core.sys.posix.unistd; -version(linux) +version (linux) { import core.sys.linux.link; import core.sys.linux.elf; } -else version(FreeBSD) +else version (FreeBSD) { import core.sys.freebsd.sys.link_elf; import core.sys.freebsd.sys.elf; } -else version(DragonFlyBSD) +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. */ @@ -56,20 +62,9 @@ struct SharedObjects } } -version (linux) -{ - import core.sys.linux.errno : program_invocation_name; -} -else -{ - extern(C) const(char)* getprogname() nothrow @nogc; -} - struct SharedObject { @nogc nothrow: - alias ProgramHeader = const(ElfW!"Phdr"); - /**** * Finds the shared object containing the specified address in one of its segments. */ @@ -83,7 +78,7 @@ struct SharedObject { foreach (object; SharedObjects) { - ProgramHeader* segment; + const(Elf_Phdr)* segment; if (object.findSegmentForAddress(address, segment)) { result = object; @@ -111,16 +106,7 @@ struct SharedObject // the main executable has an empty name if (cstr[0] == 0) - { - version (linux) - { - cstr = program_invocation_name; - } - else - { - cstr = getprogname(); - } - } + cstr = getprogname(); import core.stdc.string; return cstr[0 .. strlen(cstr)]; @@ -129,7 +115,7 @@ struct SharedObject /**** * Iterates over this object's segments. */ - int opApply(scope int delegate(ref ProgramHeader) @nogc nothrow dg) const + int opApply(scope int delegate(ref const Elf_Phdr) @nogc nothrow dg) const { foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum]) { @@ -140,7 +126,7 @@ struct SharedObject return 0; } - bool findSegmentForAddress(in void* address, out ProgramHeader* result) const + bool findSegmentForAddress(in void* address, out const(Elf_Phdr)* result) const { if (address < baseAddress()) return false; @@ -158,7 +144,9 @@ struct SharedObject } } -// file-based I/O: +// ------------------------------- +// File-based memory-mapped I/O: +// ------------------------------- struct ElfFile { @@ -269,6 +257,19 @@ struct ElfSection 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(); @@ -284,6 +285,13 @@ const(char)[] getSectionName(ref const ElfSection stringSection, size_t nameInde 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; @@ -331,29 +339,3 @@ struct MMapRegion(T) size_t offsetDiff; void* mptr; } - -version (D_LP64) -{ - alias Elf_Ehdr = Elf64_Ehdr; - alias Elf_Shdr = Elf64_Shdr; - enum ELFCLASS = ELFCLASS64; -} -else -{ - alias Elf_Ehdr = Elf32_Ehdr; - alias Elf_Shdr = Elf32_Shdr; - enum ELFCLASS = ELFCLASS32; -} - -version(LittleEndian) -{ - alias ELFDATA = ELFDATA2LSB; -} -else version(BigEndian) -{ - alias ELFDATA = ELFDATA2MSB; -} -else -{ - static assert(0, "unsupported byte order"); -} From 59645532d3508c67f66fba2c35b2733db4e73204 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 14 Oct 2018 16:46:52 +0200 Subject: [PATCH 9/9] Slightly simplify rt.backtrace.elf --- src/rt/backtrace/elf.d | 42 +++++------------------------------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/src/rt/backtrace/elf.d b/src/rt/backtrace/elf.d index 8d26803442..6f0361fea6 100644 --- a/src/rt/backtrace/elf.d +++ b/src/rt/backtrace/elf.d @@ -29,19 +29,12 @@ struct Image static Image openSelf() { - 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) + const(char)* selfPath; + foreach (object; SharedObjects) { - auto selfPath = "/proc/curproc/file".ptr; + // the first object is the main binary + selfPath = object.name().ptr; + break; } Image image; @@ -94,28 +87,3 @@ struct Image return base; } } - -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 - } - - 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; - } -}