From f943104a7a766335c813d0251885b4a0530130ed Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 10 Mar 2020 01:57:25 +0100 Subject: [PATCH 1/7] core.internal.elf.dl: Account for Android/Bionic special case --- src/core/internal/elf/dl.d | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/internal/elf/dl.d b/src/core/internal/elf/dl.d index 25e720a4cb..39c366b3b3 100644 --- a/src/core/internal/elf/dl.d +++ b/src/core/internal/elf/dl.d @@ -149,6 +149,12 @@ struct SharedObject private @nogc nothrow: version (linux) +{ + // TODO: replace with a fixed core.sys.linux.config.__USE_GNU + version (CRuntime_Bionic) {} else version = Linux_Use_GNU; +} + +version (Linux_Use_GNU) { const(char)* getprogname() { @@ -156,7 +162,7 @@ version (linux) return program_invocation_name; } } -else +else // Bionic, BSDs { extern(C) const(char)* getprogname(); } From 4d7bcd1d0872b0c45e04d9fedc97558858fe96bc Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 10 Mar 2020 01:59:00 +0100 Subject: [PATCH 2/7] core.internal.elf.dl: Add method SharedObject.getPath() As alternative to get ahold of the ELF file path in case name() doesn't return a full path. --- src/core/internal/elf/dl.d | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/core/internal/elf/dl.d b/src/core/internal/elf/dl.d index 39c366b3b3..53d26ca4d6 100644 --- a/src/core/internal/elf/dl.d +++ b/src/core/internal/elf/dl.d @@ -98,7 +98,7 @@ struct SharedObject return cast(void*) info.dlpi_addr; } - /// Returns the name of (usually: path to) the object. + /// Returns the name of (usually: path to) the object. Null-terminated. const(char)[] name() const { import core.stdc.string : strlen; @@ -112,6 +112,42 @@ struct SharedObject return cstr[0 .. strlen(cstr)]; } + /** + * Tries to fill the specified buffer with the path to the ELF file, + * according to the /proc//maps file. + * + * Returns: The filled slice (null-terminated), or null if an error occurs. + */ + char[] getPath(size_t N)(ref char[N] buffer) const + if (N > 1) + { + import core.stdc.stdio, core.stdc.string, core.sys.posix.unistd; + + char[N + 128] lineBuffer = void; + + snprintf(lineBuffer.ptr, lineBuffer.length, "/proc/%d/maps", getpid()); + auto file = fopen(lineBuffer.ptr, "r"); + if (!file) + return null; + scope(exit) fclose(file); + + const thisBase = cast(ulong) baseAddress(); + ulong startAddress; + + // prevent overflowing `buffer` by specifying the max length in the scanf format string + enum int maxPathLength = N - 1; + enum scanFormat = "%llx-%*llx %*s %*s %*s %*s %" ~ maxPathLength.stringof ~ "s"; + + while (fgets(lineBuffer.ptr, lineBuffer.length, file)) + { + if (sscanf(lineBuffer.ptr, scanFormat.ptr, &startAddress, buffer.ptr) == 2 && + startAddress == thisBase) + return buffer[0 .. strlen(buffer.ptr)]; + } + + return null; + } + /// Iterates over this object's segments. int opApply(scope int delegate(ref const Elf_Phdr) @nogc nothrow dg) const { From e3c8f38141cd148e92bc949fd88a3e50d0054807 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 10 Mar 2020 02:15:24 +0100 Subject: [PATCH 3/7] core.internal.elf.dl: Add convenience factory SharedObject.thisExecutable() --- src/core/internal/elf/dl.d | 8 ++++++++ src/rt/backtrace/elf.d | 18 ++---------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/core/internal/elf/dl.d b/src/core/internal/elf/dl.d index 53d26ca4d6..a20d7adec6 100644 --- a/src/core/internal/elf/dl.d +++ b/src/core/internal/elf/dl.d @@ -60,6 +60,14 @@ struct SharedObjects struct SharedObject { @nogc nothrow: + /// Returns the executable of the current process. + static SharedObject thisExecutable() + { + foreach (object; SharedObjects) + return object; // first object + assert(0); + } + /** * Tries to find the shared object containing the specified address in one of its segments. * Returns: True on success. diff --git a/src/rt/backtrace/elf.d b/src/rt/backtrace/elf.d index 837dde877d..4747f50c8b 100644 --- a/src/rt/backtrace/elf.d +++ b/src/rt/backtrace/elf.d @@ -38,13 +38,7 @@ struct Image static Image openSelf() { - const(char)* selfPath; - foreach (object; SharedObjects) - { - // the first object is the main binary - selfPath = object.name().ptr; - break; - } + const(char)* selfPath = SharedObject.thisExecutable().name().ptr; Image image; if (!ElfFile.open(selfPath, image.file)) @@ -80,14 +74,6 @@ struct Image if (!isDynamicSharedObject) return 0; - size_t base = 0; - foreach (object; SharedObjects) - { - // only take the first address as this will be the main binary - base = cast(size_t) object.baseAddress; - break; - } - - return base; + return cast(size_t) SharedObject.thisExecutable().baseAddress; } } From c3f8b11c7bc28e8ad89c7f9be15c7956ffd6753c Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 10 Mar 2020 02:17:02 +0100 Subject: [PATCH 4/7] core.internal.elf.io: Enable convenience method for iterating over named sections E.g., this allows to conveniently scan for multiple named sections in a single pass. --- src/core/internal/elf/io.d | 72 +++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/src/core/internal/elf/io.d b/src/core/internal/elf/io.d index 48df1dc7c5..8cbeba4c4b 100644 --- a/src/core/internal/elf/io.d +++ b/src/core/internal/elf/io.d @@ -13,6 +13,7 @@ module core.internal.elf.io; version (Posix): +import core.lifetime : move; import core.sys.posix.fcntl; import core.sys.posix.sys.mman; import core.sys.posix.unistd; @@ -134,41 +135,70 @@ template ElfIO(Elf_Ehdr, Elf_Shdr, ubyte ELFCLASS) return true; } + /** + * Returns a struct to iterate over the named sections. + * Examples: + * -------------------- + * foreach (index, name, sectionHeader; elfFile.namedSections) ... + * -------------------- + */ + NamedSections namedSections() const + { + return NamedSections(this); + } + /** * 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; + foreach (index, name, sectionHeader; namedSections) + { + if (name == sectionName) + { + header = move(sectionHeader); + return true; + } + } + return false; } + } - /** - * 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 + /// Enables iterating over an ELF file's (named) sections. + struct NamedSections + { + @nogc nothrow: + private const(ElfFile)* file; + + private this(ref const ElfFile file) { - import core.stdc.string : strlen; + this.file = &file; + } + + /// name: null-terminated + alias Callback = int delegate(size_t index, const(char)[] name, ElfSectionHeader sectionHeader); - const stringSectionHeader = ElfSectionHeader(this, ehdr.e_shstrndx); - const stringSection = ElfSection(this, stringSectionHeader); + /// + int opApply(scope Callback dg) + { + const stringSectionHeader = ElfSectionHeader(*file, file.ehdr.e_shstrndx); + const stringSection = ElfSection(*file, stringSectionHeader); - foreach (i; 0 .. ehdr.e_shnum) + foreach (i; 0 .. file.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; + import core.stdc.string : strlen; + + auto sectionHeader = ElfSectionHeader(*file, i); + auto sectionName = cast(const(char)*) (stringSection.data.ptr + sectionHeader.sh_name); + const nameLen = strlen(sectionName); + + const r = dg(i, sectionName[0 .. nameLen], move(sectionHeader)); + if (r != 0) + return r; } - // not found - return -1; + return 0; } } From cd814dbab533c81b275f4c124c128e4255c229ee Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 10 Mar 2020 03:20:16 +0100 Subject: [PATCH 5/7] Add some core.internal.elf unittests --- src/core/internal/elf/dl.d | 17 +++++++++++++++++ src/core/internal/elf/io.d | 26 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/core/internal/elf/dl.d b/src/core/internal/elf/dl.d index a20d7adec6..7db21a763f 100644 --- a/src/core/internal/elf/dl.d +++ b/src/core/internal/elf/dl.d @@ -210,3 +210,20 @@ else // Bionic, BSDs { extern(C) const(char)* getprogname(); } + +unittest +{ + import core.stdc.stdio; + + char[512] buffer = void; + foreach (object; SharedObjects) + { + const name = object.name(); + assert(name.length); + const path = object.getPath(buffer); + + printf("DSO name: %s\n", name.ptr); + printf(" path: %s\n", path ? path.ptr : ""); + printf(" base: %p\n", object.baseAddress); + } +} diff --git a/src/core/internal/elf/io.d b/src/core/internal/elf/io.d index 8cbeba4c4b..eeb94e8e9f 100644 --- a/src/core/internal/elf/io.d +++ b/src/core/internal/elf/io.d @@ -315,3 +315,29 @@ private struct MMapRegion(T) private ubyte[] mappedRegion; const(T)* data; } + +version (LinuxOrBSD) +unittest +{ + import core.internal.elf.dl, core.stdc.stdio; + + SharedObject exe = SharedObject.thisExecutable(); + + ElfFile file; + bool success = ElfFile.open(exe.name.ptr, file); + assert(success, "cannot open ELF file"); + + foreach (index, name, sectionHeader; file.namedSections) + { + printf("section %3d %-32s", cast(int) index, name.ptr); + if (const offset = sectionHeader.shdr.sh_addr) + { + auto beg = exe.baseAddress + offset; + printf("%p - %p\n", beg, beg + sectionHeader.shdr.sh_size); + } + else + { + printf("not mapped into memory\n"); + } + } +} From e3bc48406e1e196519cc04d630f897468dfaa26f Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 14 Mar 2020 16:07:56 +0100 Subject: [PATCH 6/7] Android: Fix __WORDSIZE for 64-bit targets --- src/core/sys/linux/config.d | 3 --- src/core/sys/posix/config.d | 16 +++++++++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/core/sys/linux/config.d b/src/core/sys/linux/config.d index 96126674b0..03d3e17e16 100644 --- a/src/core/sys/linux/config.d +++ b/src/core/sys/linux/config.d @@ -27,6 +27,3 @@ deprecated("use _DEFAULT_SOURCE") enum __USE_MISC = _DEFAULT_SOURCE; enum __USE_ATFILE = _ATFILE_SOURCE; enum __USE_GNU = _GNU_SOURCE; - -// Available in bionic from API 21 -version (CRuntime_Bionic) enum __WORDSIZE = 32; diff --git a/src/core/sys/posix/config.d b/src/core/sys/posix/config.d index 9ac42a130a..20e711cb72 100644 --- a/src/core/sys/posix/config.d +++ b/src/core/sys/posix/config.d @@ -61,9 +61,9 @@ version (CRuntime_Glibc) enum __USE_REENTRANT = _REENTRANT; version (D_LP64) - enum __WORDSIZE=64; + enum __WORDSIZE = 64; else - enum __WORDSIZE=32; + enum __WORDSIZE = 32; } else version (CRuntime_Musl) { @@ -109,13 +109,19 @@ else version (CRuntime_UClibc) enum __USE_REENTRANT = _REENTRANT; version (D_LP64) - enum __WORDSIZE=64; + enum __WORDSIZE = 64; else - enum __WORDSIZE=32; + enum __WORDSIZE = 32; } else version (CRuntime_Bionic) { - enum __USE_GNU = false; + enum _GNU_SOURCE = false; + enum __USE_GNU = _GNU_SOURCE; + + version (D_LP64) + enum __WORDSIZE = 64; + else + enum __WORDSIZE = 32; } else version (OpenBSD) { From bfca17493662d34754e566247b2cdee7d199083b Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 14 Mar 2020 16:09:08 +0100 Subject: [PATCH 7/7] Android: Fix Elf_Symndx for 64-bit targets --- src/core/sys/linux/link.d | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/core/sys/linux/link.d b/src/core/sys/linux/link.d index e242d2b287..4d7eb1eb7d 100644 --- a/src/core/sys/linux/link.d +++ b/src/core/sys/linux/link.d @@ -31,7 +31,15 @@ import core.sys.linux.dlfcn : Lmid_t; import core.sys.linux.elf; // -version (X86_Any) +version (Android) +{ + alias __WORDSIZE __ELF_NATIVE_CLASS; + version (D_LP64) + alias uint64_t Elf_Symndx; + else + alias uint32_t Elf_Symndx; +} +else version (X86_Any) { // http://sourceware.org/git/?p=glibc.git;a=blob;f=bits/elfclass.h alias __WORDSIZE __ELF_NATIVE_CLASS;