diff --git a/mak/COPY b/mak/COPY index a23b81056e..11610c0f43 100644 --- a/mak/COPY +++ b/mak/COPY @@ -50,6 +50,9 @@ COPY=\ $(IMPDIR)\core\internal\array\operations.d \ $(IMPDIR)\core\internal\array\utils.d \ \ + $(IMPDIR)\core\internal\elf\dl.d \ + $(IMPDIR)\core\internal\elf\io.d \ + \ $(IMPDIR)\core\internal\util\array.d \ \ $(IMPDIR)\core\stdc\assert_.d \ diff --git a/mak/SRCS b/mak/SRCS index dd2aa23a2d..92bbbe941b 100644 --- a/mak/SRCS +++ b/mak/SRCS @@ -48,6 +48,10 @@ SRCS=\ src\core\internal\array\concatenation.d \ src\core\internal\array\operations.d \ src\core\internal\array\utils.d \ + \ + src\core\internal\elf\dl.d \ + src\core\internal\elf\io.d \ + \ src\core\internal\util\array.d \ \ src\core\stdc\assert_.d \ diff --git a/mak/WINDOWS b/mak/WINDOWS index 5a1995e753..c4d2e63993 100644 --- a/mak/WINDOWS +++ b/mak/WINDOWS @@ -200,6 +200,12 @@ $(IMPDIR)\core\internal\array\utils.d : src\core\internal\array\utils.d $(IMPDIR)\core\internal\array\operations.d : src\core\internal\array\operations.d copy $** $@ +$(IMPDIR)\core\internal\elf\dl.d : src\core\internal\elf\dl.d + copy $** $@ + +$(IMPDIR)\core\internal\elf\io.d : src\core\internal\elf\io.d + copy $** $@ + $(IMPDIR)\core\internal\util\array.d : src\core\internal\util\array.d copy $** $@ diff --git a/posix.mak b/posix.mak index 0dd950dcd7..83431d6430 100644 --- a/posix.mak +++ b/posix.mak @@ -159,6 +159,9 @@ $(DOCDIR)/core_gc_%.html : src/core/gc/%.d $(DMD) $(DOCDIR)/core_internal_%.html : src/core/internal/%.d $(DMD) $(DMD) $(DDOCFLAGS) -Df$@ project.ddoc $(DOCFMT) $< +$(DOCDIR)/core_internal_elf_%.html : src/core/internal/elf/%.d $(DMD) + $(DMD) $(DDOCFLAGS) -Df$@ project.ddoc $(DOCFMT) $< + $(DOCDIR)/core_stdc_%.html : src/core/stdc/%.d $(DMD) $(DMD) $(DDOCFLAGS) -Df$@ project.ddoc $(DOCFMT) $< diff --git a/src/core/internal/elf/dl.d b/src/core/internal/elf/dl.d new file mode 100644 index 0000000000..25e720a4cb --- /dev/null +++ b/src/core/internal/elf/dl.d @@ -0,0 +1,162 @@ +/** + * Simplifies working with shared ELF objects of the current process. + * + * Reference: http://www.dwarfstd.org/ + * + * Copyright: Copyright Digital Mars 2015 - 2018. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: Martin Kinkelin + * Source: $(DRUNTIMESRC core/elf/dl.d) + */ + +module core.internal.elf.dl; + +version (linux) +{ + import core.sys.linux.link; + version = LinuxOrBSD; +} +else version (FreeBSD) +{ + import core.sys.freebsd.sys.link_elf; + version = LinuxOrBSD; +} +else version (DragonFlyBSD) +{ + import core.sys.dragonflybsd.sys.link_elf; + version = LinuxOrBSD; +} + +version (LinuxOrBSD): + +alias Elf_Ehdr = ElfW!"Ehdr"; +alias Elf_Phdr = ElfW!"Phdr"; + +/** + * 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); + } +} + +/** + * A loaded shared ELF object/binary, i.e., executable or shared library. + */ +struct SharedObject +{ +@nogc nothrow: + /** + * Tries to find the shared object containing the specified address in one of its segments. + * Returns: True on success. + */ + static bool findForAddress(const scope 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); + } + } + + /// OS-dependent info structure. + dl_phdr_info info; + + /// Returns the base address of the object. + @property void* baseAddress() const + { + return cast(void*) info.dlpi_addr; + } + + /// Returns the name of (usually: path to) the object. + const(char)[] name() const + { + import core.stdc.string : strlen; + + const(char)* cstr = info.dlpi_name; + + // the main executable has an empty name + if (cstr[0] == 0) + cstr = getprogname(); + + 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; + } + + /** + * Tries to find the segment containing the specified address. + * Returns: True on success. + */ + bool findSegmentForAddress(const scope 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; + } +} + +private @nogc nothrow: + +version (linux) +{ + const(char)* getprogname() + { + import core.sys.linux.errno; + return program_invocation_name; + } +} +else +{ + extern(C) const(char)* getprogname(); +} diff --git a/src/core/internal/elf/io.d b/src/core/internal/elf/io.d new file mode 100644 index 0000000000..48df1dc7c5 --- /dev/null +++ b/src/core/internal/elf/io.d @@ -0,0 +1,287 @@ +/** + * Provides (read-only) memory-mapped I/O for ELF files. + * + * Reference: http://www.dwarfstd.org/ + * + * Copyright: Copyright Digital Mars 2015 - 2018. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: Yazan Dabain, Martin Kinkelin + * Source: $(DRUNTIMESRC core/elf/io.d) + */ + +module core.internal.elf.io; + +version (Posix): + +import core.sys.posix.fcntl; +import core.sys.posix.sys.mman; +import core.sys.posix.unistd; + +version (linux) +{ + import core.sys.linux.link; + version = LinuxOrBSD; +} +else version (FreeBSD) +{ + import core.sys.freebsd.sys.link_elf; + version = LinuxOrBSD; +} +else version (DragonFlyBSD) +{ + import core.sys.dragonflybsd.sys.link_elf; + version = LinuxOrBSD; +} + +/** + * File-based memory-mapped I/O (read-only). + * Only supports ELF files with a byte-order matching the target platform's. + * Params: + * Elf_Ehdr = Expected type of the ELF header (Elf{32,64}_Ehdr) + * Elf_Shdr = Expected type of the ELF section header (Elf{32,64}_Shdr) + * ELFCLASS = Expected ELF class (ELFCLASS{32,64}) + */ +template ElfIO(Elf_Ehdr, Elf_Shdr, ubyte ELFCLASS) +{ + /** + * ELF file (with memory-mapped ELF header). + */ + struct ElfFile + { + @nogc nothrow: + /** + * Tries to open the specified file as ELF file matching the ElfIO + * template parameters. + * Returns: True on success. + */ + static bool open(const(char)* path, out ElfFile file) + { + file = ElfFile(.open(path, O_RDONLY)); + return file.isValid(); + } + + /** + * Constructs an instance based on the specified file descriptor. + * Doesn't validate the file header. + * The file is closed when destructing the instance. + */ + this(int fd) + { + this.fd = fd; + if (fd != -1) + { + // memory map header + this.ehdr = MMapRegion!Elf_Ehdr(fd, 0); + } + } + + @disable this(this); + + /// Closes the file. + ~this() + { + if (fd != -1) + close(fd); + } + + private int fd = -1; + /// Memory-mapped ELF header. + MMapRegion!Elf_Ehdr ehdr; + + /// Returns true if the ELF file header matches the ElfIO template parameters. + bool isValid() const + { + enum EI_MAG0 = 0; + enum EI_MAG1 = 1; + enum EI_MAG2 = 2; + enum EI_MAG3 = 3; + enum EI_CLASS = 4; + enum EI_DATA = 5; + + enum ELFMAG0 = 0x7f; + enum ELFMAG1 = 'E'; + enum ELFMAG2 = 'L'; + enum ELFMAG3 = 'F'; + + enum ELFCLASS32 = 1; + enum ELFCLASS64 = 2; + + enum ELFDATA2LSB = 1; + enum ELFDATA2MSB = 2; + + version (LittleEndian) alias ELFDATA = ELFDATA2LSB; + else version (BigEndian) alias ELFDATA = ELFDATA2MSB; + else static assert(0, "unsupported byte order"); + + if (fd == -1) + return false; + + const ident = ehdr.e_ident; + + if (!(ident[EI_MAG0] == ELFMAG0 && + ident[EI_MAG1] == ELFMAG1 && + ident[EI_MAG2] == ELFMAG2 && + ident[EI_MAG3] == ELFMAG3)) + return false; + + if (ident[EI_CLASS] != ELFCLASS) + return false; + + // the file's byte order must match the target's + if (ident[EI_DATA] != ELFDATA) + return false; + + return true; + } + + /** + * Tries to find the header of the section with the specified name. + * Returns: True on success. + */ + bool findSectionHeaderByName(const(char)[] sectionName, out ElfSectionHeader header) const + { + const index = findSectionIndexByName(sectionName); + if (index == -1) + return false; + header = ElfSectionHeader(this, index); + return true; + } + + /** + * Tries to find the index of the section with the specified name. + * Returns: -1 if not found, otherwise 0-based section index. + */ + size_t findSectionIndexByName(const(char)[] sectionName) const + { + import core.stdc.string : strlen; + + const stringSectionHeader = ElfSectionHeader(this, ehdr.e_shstrndx); + const stringSection = ElfSection(this, stringSectionHeader); + + foreach (i; 0 .. ehdr.e_shnum) + { + auto sectionHeader = ElfSectionHeader(this, i); + auto pCurrentName = cast(const(char)*) (stringSection.data.ptr + sectionHeader.sh_name); + auto currentName = pCurrentName[0 .. strlen(pCurrentName)]; + if (sectionName == currentName) + return i; + } + + // not found + return -1; + } + } + + /** + * Memory-mapped ELF section header. + */ + struct ElfSectionHeader + { + @nogc nothrow: + /// Constructs a new instance based on the specified file and section index. + 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 * Elf_Shdr.sizeof + ); + } + + @disable this(this); + + alias shdr this; + /// Memory-mapped section header. + MMapRegion!Elf_Shdr shdr; + } + + /** + * Memory-mapped ELF section data. + */ + struct ElfSection + { + @nogc nothrow: + /// Constructs a new instance based on the specified file and section header. + this(ref const ElfFile file, ref const ElfSectionHeader shdr) + { + mappedRegion = MMapRegion!void(file.fd, shdr.sh_offset, shdr.sh_size); + size = shdr.sh_size; + } + + @disable this(this); + + /// Returns the memory-mapped section data. + /// The data is accessible as long as this ElfSection is alive. + const(void)[] data() const + { + return mappedRegion.data[0 .. size]; + } + + alias data this; + + private: + MMapRegion!void mappedRegion; + size_t size; + } +} + +/// ELF class for 32-bit ELF files. +enum ELFCLASS32 = 1; +/// ELF class for 64-bit ELF files. +enum ELFCLASS64 = 2; + +// convenience aliases for the target platform +version (LinuxOrBSD) +{ + /// Native ELF header type. + alias Elf_Ehdr = ElfW!"Ehdr"; + /// Native ELF section header type. + alias Elf_Shdr = ElfW!"Shdr"; + + /// Native ELF class. + version (D_LP64) alias ELFCLASS = ELFCLASS64; + else alias ELFCLASS = ELFCLASS32; + + /// + alias NativeElfIO = ElfIO!(Elf_Ehdr, Elf_Shdr, ELFCLASS); + + /// Native ELF file. + alias ElfFile = NativeElfIO.ElfFile; + /// Native ELF section header. + alias ElfSectionHeader = NativeElfIO.ElfSectionHeader; + /// Native ELF section. + alias ElfSection = NativeElfIO.ElfSection; +} + +private struct MMapRegion(T) +{ +@nogc nothrow: + this(int fd, size_t offset, size_t length = 1) + { + if (fd == -1) + return; + + const pageSize = sysconf(_SC_PAGESIZE); + const pagedOffset = (offset / pageSize) * pageSize; + const offsetDiff = offset - pagedOffset; + const mappedSize = length * T.sizeof + offsetDiff; + + auto ptr = cast(ubyte*) mmap(null, mappedSize, PROT_READ, MAP_PRIVATE, fd, pagedOffset); + + mappedRegion = ptr[0 .. mappedSize]; + data = cast(T*) (ptr + offsetDiff); + } + + @disable this(this); + + ~this() + { + if (mappedRegion !is null) + munmap(mappedRegion.ptr, mappedRegion.length); + } + + alias data this; + + private ubyte[] mappedRegion; + const(T)* data; +} diff --git a/src/rt/backtrace/dwarf.d b/src/rt/backtrace/dwarf.d index 77feabe2d0..1d45862f55 100644 --- a/src/rt/backtrace/dwarf.d +++ b/src/rt/backtrace/dwarf.d @@ -51,77 +51,80 @@ int traceHandlerOpApplyImpl(const void*[] callstack, scope int delegate(ref size const char** frameList = backtrace_symbols(callstack.ptr, cast(int) callstack.length); scope(exit) free(cast(void*) frameList); - // find address -> file, line mapping using dwarf debug_line - Array!Location locations; auto image = Image.openSelf(); - if (image.isValid) - { - auto debugLineSectionData = image.getDebugLineSectionData(); + int processCallstack(const(ubyte)[] debugLineSectionData) + { + // find address -> file, line mapping using dwarf debug_line + Array!Location locations; if (debugLineSectionData) { - // resolve addresses locations.length = callstack.length; foreach (size_t i; 0 .. callstack.length) locations[i].address = cast(size_t) callstack[i]; resolveAddresses(debugLineSectionData, locations[], image.baseAddress); } - } - int ret = 0; - foreach (size_t i; 0 .. callstack.length) - { - char[1536] buffer = void; - size_t bufferLength = 0; - - void appendToBuffer(Args...)(const(char)* format, Args args) + int ret = 0; + foreach (size_t i; 0 .. callstack.length) { - const count = snprintf(buffer.ptr + bufferLength, buffer.length - bufferLength, format, args); - assert(count >= 0); - bufferLength += count; - if (bufferLength >= buffer.length) - bufferLength = buffer.length - 1; - } + char[1536] buffer = void; + size_t bufferLength = 0; - if (locations.length > 0 && locations[i].line != -1) - { - appendToBuffer("%.*s:%d ", cast(int) locations[i].file.length, locations[i].file.ptr, locations[i].line); - } - else - { - buffer[0 .. 5] = "??:? "; - bufferLength = 5; - } + void appendToBuffer(Args...)(const(char)* format, Args args) + { + const count = snprintf(buffer.ptr + bufferLength, buffer.length - bufferLength, format, args); + assert(count >= 0); + bufferLength += count; + if (bufferLength >= buffer.length) + bufferLength = buffer.length - 1; + } - char[1024] symbolBuffer = void; - auto symbol = getDemangledSymbol(frameList[i][0 .. strlen(frameList[i])], symbolBuffer); - if (symbol.length > 0) - appendToBuffer("%.*s ", cast(int) symbol.length, symbol.ptr); + if (locations.length > 0 && locations[i].line != -1) + { + appendToBuffer("%.*s:%d ", cast(int) locations[i].file.length, locations[i].file.ptr, locations[i].line); + } + else + { + buffer[0 .. 5] = "??:? "; + bufferLength = 5; + } - const addressLength = 20; - const maxBufferLength = buffer.length - addressLength; - if (bufferLength > maxBufferLength) - { - buffer[maxBufferLength-4 .. maxBufferLength] = "... "; - bufferLength = maxBufferLength; + char[1024] symbolBuffer = void; + auto symbol = getDemangledSymbol(frameList[i][0 .. strlen(frameList[i])], symbolBuffer); + if (symbol.length > 0) + appendToBuffer("%.*s ", cast(int) symbol.length, symbol.ptr); + + const addressLength = 20; + const maxBufferLength = buffer.length - addressLength; + if (bufferLength > maxBufferLength) + { + buffer[maxBufferLength-4 .. maxBufferLength] = "... "; + bufferLength = maxBufferLength; + } + static if (size_t.sizeof == 8) + appendToBuffer("[0x%llx]", callstack[i]); + else + appendToBuffer("[0x%x]", callstack[i]); + + auto output = buffer[0 .. bufferLength]; + auto pos = i; + ret = dg(pos, output); + if (ret || symbol == "_Dmain") break; } - static if (size_t.sizeof == 8) - appendToBuffer("[0x%llx]", callstack[i]); - else - appendToBuffer("[0x%x]", callstack[i]); - auto output = buffer[0 .. bufferLength]; - auto pos = i; - ret = dg(pos, output); - if (ret || symbol == "_Dmain") break; + return ret; } - return ret; + + return image.isValid + ? image.processDebugLineSectionData(&processCallstack) + : processCallstack(null); } private: -// the lifetime of the Location data is the lifetime of the mmapped ElfSection +// the lifetime of the Location data is bound to the lifetime of debugLineSectionData void resolveAddresses(const(ubyte)[] debugLineSectionData, Location[] locations, size_t baseAddress) @nogc nothrow { debug(DwarfDebugMachine) import core.stdc.stdio; diff --git a/src/rt/backtrace/elf.d b/src/rt/backtrace/elf.d index c16a204c36..837dde877d 100644 --- a/src/rt/backtrace/elf.d +++ b/src/rt/backtrace/elf.d @@ -3,7 +3,7 @@ * * Reference: http://www.dwarfstd.org/ * - * Copyright: Copyright Digital Mars 2015 - 2015. + * Copyright: Copyright Digital Mars 2015 - 2018. * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Yazan Dabain * Source: $(DRUNTIMESRC rt/backtrace/elf.d) @@ -11,18 +11,26 @@ module rt.backtrace.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): +version (linux) +{ + import core.sys.linux.elf; + version = LinuxOrBSD; +} +else version (FreeBSD) +{ + import core.sys.freebsd.sys.elf; + version = LinuxOrBSD; +} +else version (DragonFlyBSD) +{ + import core.sys.dragonflybsd.sys.elf; + version = LinuxOrBSD; +} -import core.sys.posix.fcntl; -import core.sys.posix.unistd; +version (LinuxOrBSD): -version (linux) import core.sys.linux.elf; -version (FreeBSD) import core.sys.freebsd.sys.elf; -version (DragonFlyBSD) import core.sys.dragonflybsd.sys.elf; +import core.internal.elf.dl; +import core.internal.elf.io; struct Image { @@ -30,9 +38,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; @@ -43,341 +58,36 @@ struct Image return file != ElfFile.init; } - const(ubyte)[] getDebugLineSectionData() + T processDebugLineSectionData(T)(scope T delegate(const(ubyte)[]) processor) { - auto stringSectionHeader = ElfSectionHeader(&file, file.ehdr.e_shstrndx); - auto stringSection = ElfSection(&file, &stringSectionHeader); + ElfSectionHeader dbgSectionHeader; + ElfSection dbgSection; - auto dbgSectionIndex = findSectionByName(&file, &stringSection, ".debug_line"); - if (dbgSectionIndex != -1) + if (file.findSectionHeaderByName(".debug_line", dbgSectionHeader)) { - 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); + if (!(dbgSectionHeader.shdr.sh_flags & SHF_COMPRESSED)) + dbgSection = ElfSection(file, dbgSectionHeader); } - return null; + return processor(cast(const(ubyte)[]) dbgSection.data()); } @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; + base = cast(size_t) object.baseAddress; + break; } - 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 - ); + return base; } - - @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 - } - - 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); - } - - 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) -{ - version (D_X32) - { - alias Elf_Ehdr = Elf32_Ehdr; - alias Elf_Shdr = Elf32_Shdr; - enum ELFCLASS = ELFCLASS32; - } - else - { - 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/macho.d b/src/rt/backtrace/macho.d index 3d4573b1cc..dfa67b47a2 100644 --- a/src/rt/backtrace/macho.d +++ b/src/rt/backtrace/macho.d @@ -61,11 +61,11 @@ struct Image return self !is null; } - const(ubyte)[] getDebugLineSectionData() + T processDebugLineSectionData(T)(scope T delegate(const(ubyte)[]) processor) { c_ulong size; auto data = getsectiondata(self, "__DWARF", "__debug_line", &size); - return data[0 .. size]; + return processor(data[0 .. size]); } @property size_t baseAddress() diff --git a/src/rt/sections_elf_shared.d b/src/rt/sections_elf_shared.d index 767d1a682c..832b9e2170 100644 --- a/src/rt/sections_elf_shared.d +++ b/src/rt/sections_elf_shared.d @@ -20,6 +20,7 @@ else enum SharedELF = false; static if (SharedELF): // debug = PRINTF; +import core.internal.elf.dl; import core.memory; import core.stdc.config; import core.stdc.stdio; @@ -413,17 +414,17 @@ extern(C) void _d_dso_registry(CompilerDSOData* data) pdso._moduleGroup = ModuleGroup(toRange(data._minfo_beg, data._minfo_end)); - dl_phdr_info info = void; - const headerFound = findDSOInfoForAddr(data._slot, &info); - safeAssert(headerFound, "Failed to find image header."); + SharedObject object = void; + const objectFound = SharedObject.findForAddress(data._slot, object); + safeAssert(objectFound, "Failed to find shared ELF object."); - scanSegments(info, pdso); + scanSegments(object, pdso); version (Shared) { auto handle = handleForAddr(data._slot); - getDependencies(info, pdso._deps); + getDependencies(object, pdso._deps); pdso._handle = handle; setDSOForHandle(pdso, pdso._handle); @@ -696,15 +697,15 @@ version (Shared) !pthread_mutex_unlock(&_handleToDSOMutex) || assert(0); } - void getDependencies(const scope ref dl_phdr_info info, ref Array!(DSO*) deps) + void getDependencies(const scope 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 & ~(size_t.sizeof - 1))); + auto p = cast(ElfW!"Dyn"*)(object.baseAddress + (phdr.p_vaddr & ~(size_t.sizeof - 1))); dyns = p[0 .. phdr.p_memsz / ElfW!"Dyn".sizeof]; break; } @@ -716,15 +717,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; @@ -764,21 +765,21 @@ version (Shared) * Scan segments in Linux dl_phdr_info struct and store * the TLS and writeable data segments in *pdso. */ -void scanSegments(const scope ref dl_phdr_info info, DSO* pdso) nothrow @nogc +void scanSegments(const scope 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 & ~(size_t.sizeof - 1))); + auto beg = object.baseAddress + (phdr.p_vaddr & ~(size_t.sizeof - 1)); 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 & ~(size_t.sizeof - 1))); + auto beg = object.baseAddress + (phdr.p_vaddr & ~(size_t.sizeof - 1)); pdso._codeSegments.insertBack(beg[0 .. phdr.p_memsz]); } break; @@ -790,7 +791,7 @@ void scanSegments(const scope ref dl_phdr_info info, DSO* pdso) nothrow @nogc // 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; break; @@ -800,97 +801,6 @@ void scanSegments(const scope ref dl_phdr_info info, DSO* pdso) nothrow @nogc } } -/************************** - * 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 findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) 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 (FreeBSD) - { - return !!_rtld_addr_phdr(addr, result); - } - else version (DragonFlyBSD) - { - return !!_rtld_addr_phdr(addr, 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. - */ -bool findSegmentForAddr(const scope ref dl_phdr_info info, const scope 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 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 (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 (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)]; -} - /************************** * Input: * addr an internal address of a DSO