Skip to content
This repository was archived by the owner on Feb 8, 2024. It is now read-only.
71 changes: 69 additions & 2 deletions src/core/internal/elf/dl.d
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -98,7 +106,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;
Expand All @@ -112,6 +120,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/<PID>/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
{
Expand Down Expand Up @@ -149,14 +193,37 @@ 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()
{
import core.sys.linux.errno;
return program_invocation_name;
}
}
else
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);
}
}
Copy link
Member Author

@kinke kinke Mar 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E.g.:

DSO name: runtime/druntime-test-runner-shared
    path: /home/martin/build-ldc/runtime/druntime-test-runner-shared
    base: 0x5589831e6000
DSO name: linux-vdso.so.1
    path: [vdso]
    base: 0x7ffdeb9f7000
DSO name: /home/martin/build-ldc/lib/libdruntime-ldc-unittest-shared.so.91
    path: /home/martin/build-ldc/lib/libdruntime-ldc-unittest-shared.so.2.0.91
    base: 0x7ff21166b000
DSO name: /lib/x86_64-linux-gnu/libgcc_s.so.1
    path: /lib/x86_64-linux-gnu/libgcc_s.so.1
    base: 0x7ff211453000
DSO name: /lib/x86_64-linux-gnu/libc.so.6
    path: /lib/x86_64-linux-gnu/libc-2.27.so
    base: 0x7ff211062000
DSO name: /lib64/ld-linux-x86-64.so.2
    path: /lib/x86_64-linux-gnu/ld-2.27.so
    base: 0x7ff211b53000
DSO name: /lib/x86_64-linux-gnu/libm.so.6
    path: /lib/x86_64-linux-gnu/libm-2.27.so
    base: 0x7ff210cc4000
DSO name: /lib/x86_64-linux-gnu/libpthread.so.0
    path: /lib/x86_64-linux-gnu/libpthread-2.27.so
    base: 0x7ff210aa5000
DSO name: /lib/x86_64-linux-gnu/librt.so.1
    path: /lib/x86_64-linux-gnu/librt-2.27.so
    base: 0x7ff21089d000
DSO name: /lib/x86_64-linux-gnu/libdl.so.2
    path: /lib/x86_64-linux-gnu/libdl-2.27.so
    base: 0x7ff210699000

98 changes: 77 additions & 21 deletions src/core/internal/elf/io.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

const stringSectionHeader = ElfSectionHeader(this, ehdr.e_shstrndx);
const stringSection = ElfSection(this, stringSectionHeader);
/// name: null-terminated
alias Callback = int delegate(size_t index, const(char)[] name, ElfSectionHeader sectionHeader);

foreach (i; 0 .. ehdr.e_shnum)
///
int opApply(scope Callback dg)
{
const stringSectionHeader = ElfSectionHeader(*file, file.ehdr.e_shstrndx);
const stringSection = ElfSection(*file, stringSectionHeader);

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;
}
}

Expand Down Expand Up @@ -285,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");
}
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E.g.:

section   0                                 not mapped into memory
section   1 .interp                         0x55a0f1d27270 - 0x55a0f1d2728c
section   2 .note.ABI-tag                   0x55a0f1d2728c - 0x55a0f1d272ac
section   3 .note.gnu.build-id              0x55a0f1d272ac - 0x55a0f1d272d0
section   4 .gnu.hash                       0x55a0f1d272d0 - 0x55a0f1d273b0
section   5 .dynsym                         0x55a0f1d273b0 - 0x55a0f1d278d8
section   6 .dynstr                         0x55a0f1d278d8 - 0x55a0f1d280da
section   7 .gnu.version                    0x55a0f1d280da - 0x55a0f1d28148
section   8 .gnu.version_r                  0x55a0f1d28148 - 0x55a0f1d281a8
section   9 .rela.dyn                       0x55a0f1d281a8 - 0x55a0f1d28358
section  10 .rela.plt                       0x55a0f1d28358 - 0x55a0f1d284f0
section  11 .init                           0x55a0f1d284f0 - 0x55a0f1d28507
section  12 .plt                            0x55a0f1d28510 - 0x55a0f1d28630
section  13 .plt.got                        0x55a0f1d28630 - 0x55a0f1d28638
section  14 .text                           0x55a0f1d28640 - 0x55a0f1d28d72
section  15 .fini                           0x55a0f1d28d74 - 0x55a0f1d28d7d
section  16 .rodata                         0x55a0f1d28d80 - 0x55a0f1d28df0
section  17 .eh_frame_hdr                   0x55a0f1d28df0 - 0x55a0f1d28e9c
section  18 .eh_frame                       0x55a0f1d28ea0 - 0x55a0f1d291d8
section  19 .gcc_except_table               0x55a0f1d291d8 - 0x55a0f1d291f8
section  20 .tbss                           0x55a0f1f29cd8 - 0x55a0f1f29ce8
section  21 .init_array                     0x55a0f1f29cd8 - 0x55a0f1f29ce8
section  22 .fini_array                     0x55a0f1f29ce8 - 0x55a0f1f29cf8
section  23 .dynamic                        0x55a0f1f29cf8 - 0x55a0f1f29f28
section  24 .got                            0x55a0f1f29f28 - 0x55a0f1f2a000
section  25 .data                           0x55a0f1f2a000 - 0x55a0f1f2a058
section  26 __minfo                         0x55a0f1f2a058 - 0x55a0f1f2a060
section  27 .bss                            0x55a0f1f2a060 - 0x55a0f1f2a070
section  28 .comment                        not mapped into memory
section  29 .symtab                         not mapped into memory
section  30 .strtab                         not mapped into memory
section  31 .shstrtab                       not mapped into memory

3 changes: 0 additions & 3 deletions src/core/sys/linux/config.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;
10 changes: 9 additions & 1 deletion src/core/sys/linux/link.d
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,15 @@ import core.sys.linux.dlfcn : Lmid_t;
import core.sys.linux.elf;

// <bits/elfclass.h>
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;
Expand Down
16 changes: 11 additions & 5 deletions src/core/sys/posix/config.d
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand Down
18 changes: 2 additions & 16 deletions src/rt/backtrace/elf.d
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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;
}
}
Loading