144 changes: 92 additions & 52 deletions src/rt/sections_linux.d → src/rt/sections_elf_shared.d
Original file line number Diff line number Diff line change
@@ -1,25 +1,41 @@
/**
* Written in the D programming language.
* This module provides linux-specific support for sections.
* This module provides ELF-specific support for sections with shared libraries.
*
* Copyright: Copyright Martin Nowak 2012-2013.
* License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Martin Nowak
* Source: $(DRUNTIMESRC src/rt/_sections_linux.d)
*/

module rt.sections_linux;
module rt.sections_elf_shared;

version (linux):
version (linux) enum SharedELF = true;
else version (FreeBSD) enum SharedELF = true;
else enum SharedELF = false;
static if (SharedELF):

// debug = PRINTF;
import core.memory;
import core.stdc.stdio;
import core.stdc.stdlib : calloc, exit, free, malloc, EXIT_FAILURE;
import core.stdc.string : strlen;
import core.sys.linux.dlfcn;
import core.sys.linux.elf;
import core.sys.linux.link;
version (linux)
{
import core.sys.linux.dlfcn;
import core.sys.linux.elf;
import core.sys.linux.link;
}
else version (FreeBSD)
{
import core.sys.freebsd.dlfcn;
import core.sys.freebsd.sys.elf;
import core.sys.freebsd.sys.link_elf;
}
else
{
static assert(0, "unimplemented");
}
import core.sys.posix.pthread;
import rt.deh;
import rt.dmain2;
Expand Down Expand Up @@ -88,7 +104,7 @@ private:
{
Array!(void[]) _codeSegments; // array of code segments
Array!(DSO*) _deps; // D libraries needed by this DSO
link_map* _linkMap; // corresponding link_map*
void* _handle; // corresponding handle
}
}

Expand All @@ -98,12 +114,16 @@ private:
__gshared bool _isRuntimeInitialized;


version (FreeBSD) private __gshared void* dummy_ref;

/****
* Gets called on program startup just before GC is initialized.
*/
void initSections()
{
_isRuntimeInitialized = true;
// reference symbol to support weak linkage
version (FreeBSD) dummy_ref = &_d_dso_registry;
}


Expand Down Expand Up @@ -149,7 +169,7 @@ version (Shared)
if (tdso._addCnt)
{
// Increment the dlopen ref for explicitly loaded libraries to pin them.
.dlopen(tdso._pdso._linkMap.l_name, RTLD_LAZY) !is null || assert(0);
.dlopen(linkMapForHandle(tdso._pdso._handle).l_name, RTLD_LAZY) !is null || assert(0);
(*res)[i]._addCnt = 1; // new array takes over the additional ref count
}
}
Expand All @@ -164,7 +184,7 @@ version (Shared)
{
if (tdso._addCnt)
{
auto handle = handleForName(tdso._pdso._linkMap.l_name);
auto handle = tdso._pdso._handle;
handle !is null || assert(0);
.dlclose(handle);
}
Expand All @@ -189,7 +209,7 @@ version (Shared)
{
if (tdso._addCnt == 0) continue;

auto handle = handleForName(tdso._pdso._linkMap.l_name);
auto handle = tdso._pdso._handle;
handle !is null || assert(0);
for (; tdso._addCnt > 0; --tdso._addCnt)
.dlclose(handle);
Expand Down Expand Up @@ -221,6 +241,9 @@ else

private:

// start of linked list for ModuleInfo references
version (FreeBSD) deprecated extern (C) __gshared void* _Dmodule_ref;

version (Shared)
{
/*
Expand Down Expand Up @@ -256,8 +279,8 @@ version (Shared)
* Hash table to map link_map* to corresponding DSO*.
* The hash table is protected by a Mutex.
*/
__gshared pthread_mutex_t _linkMapToDSOMutex;
__gshared HashTab!(void*, DSO*) _linkMapToDSO;
__gshared pthread_mutex_t _handleToDSOMutex;
__gshared HashTab!(void*, DSO*) _handleToDSO;
}
else
{
Expand Down Expand Up @@ -328,11 +351,15 @@ extern(C) void _d_dso_registry(CompilerDSOData* data)
{
// the first loaded DSO is druntime itself
assert(!_loadedDSOs.empty ||
linkMapForAddr(&_d_dso_registry) == linkMapForAddr(data._slot));
/* We need a local symbol (rt_get_bss_start) or the function
* pointer might be a PLT address in the executable.
* data._slot is already local in the shared library
*/
handleForAddr(&rt_get_bss_start) == handleForAddr(data._slot));

getDependencies(info, pdso._deps);
pdso._linkMap = linkMapForAddr(data._slot);
setDSOForLinkMap(pdso, pdso._linkMap);
pdso._handle = handleForAddr(data._slot);
setDSOForHandle(pdso, pdso._handle);

if (!_rtLoading)
{
Expand Down Expand Up @@ -397,9 +424,9 @@ extern(C) void _d_dso_registry(CompilerDSOData* data)
}
}

assert(pdso._linkMap == linkMapForAddr(data._slot));
unsetDSOForLinkMap(pdso, pdso._linkMap);
pdso._linkMap = null;
assert(pdso._handle == handleForAddr(data._slot));
unsetDSOForHandle(pdso, pdso._handle);
pdso._handle = null;
}
else
{
Expand Down Expand Up @@ -475,7 +502,7 @@ version (Shared)
if (handle is null) return null;

// if it's a D library
if (auto pdso = dsoForLinkMap(linkMapForHandle(handle)))
if (auto pdso = dsoForHandle(handle))
incThreadRef(pdso, true);
return handle;
}
Expand All @@ -489,7 +516,7 @@ version (Shared)
scope (exit) _rtLoading = save;

// if it's a D library
if (auto pdso = dsoForLinkMap(linkMapForHandle(handle)))
if (auto pdso = dsoForHandle(handle))
decThreadRef(pdso, true);
return .dlclose(handle) == 0;
}
Expand All @@ -502,13 +529,13 @@ version (Shared)
void initLocks()
{
version (Shared)
!pthread_mutex_init(&_linkMapToDSOMutex, null) || assert(0);
!pthread_mutex_init(&_handleToDSOMutex, null) || assert(0);
}

void finiLocks()
{
version (Shared)
!pthread_mutex_destroy(&_linkMapToDSOMutex) || assert(0);
!pthread_mutex_destroy(&_handleToDSOMutex) || assert(0);
}

void runModuleConstructors(DSO* pdso, bool runTlsCtors)
Expand Down Expand Up @@ -558,30 +585,30 @@ version (Shared)
return map;
}

DSO* dsoForLinkMap(link_map* map)
DSO* dsoForHandle(void* handle)
{
DSO* pdso;
!pthread_mutex_lock(&_linkMapToDSOMutex) || assert(0);
if (auto ppdso = map in _linkMapToDSO)
!pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
if (auto ppdso = handle in _handleToDSO)
pdso = *ppdso;
!pthread_mutex_unlock(&_linkMapToDSOMutex) || assert(0);
!pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
return pdso;
}

void setDSOForLinkMap(DSO* pdso, link_map* map)
void setDSOForHandle(DSO* pdso, void* handle)
{
!pthread_mutex_lock(&_linkMapToDSOMutex) || assert(0);
assert(map !in _linkMapToDSO);
_linkMapToDSO[map] = pdso;
!pthread_mutex_unlock(&_linkMapToDSOMutex) || assert(0);
!pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
assert(handle !in _handleToDSO);
_handleToDSO[handle] = pdso;
!pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
}

void unsetDSOForLinkMap(DSO* pdso, link_map* map)
void unsetDSOForHandle(DSO* pdso, void* handle)
{
!pthread_mutex_lock(&_linkMapToDSOMutex) || assert(0);
assert(_linkMapToDSO[map] == pdso);
_linkMapToDSO.remove(map);
!pthread_mutex_unlock(&_linkMapToDSOMutex) || assert(0);
!pthread_mutex_lock(&_handleToDSOMutex) || assert(0);
assert(_handleToDSO[handle] == pdso);
_handleToDSO.remove(handle);
!pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
}

void getDependencies(in ref dl_phdr_info info, ref Array!(DSO*) deps)
Expand All @@ -603,7 +630,12 @@ version (Shared)
{
if (dyn.d_tag == DT_STRTAB)
{
strtab = cast(const(char)*)dyn.d_un.d_ptr;
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
else
static assert(0, "unimplemented");
break;
}
}
Expand All @@ -620,7 +652,7 @@ version (Shared)
// the runtime linker has already loaded all dependencies
if (handle is null) assert(0);
// if it's a D library
if (auto pdso = dsoForLinkMap(linkMapForHandle(handle)))
if (auto pdso = dsoForHandle(handle))
deps.insertBack(pdso); // append it to the dependencies
}
}
Expand Down Expand Up @@ -680,13 +712,11 @@ void scanSegments(in ref dl_phdr_info info, DSO* pdso)
* References:
* http://linux.die.net/man/3/dl_iterate_phdr
*/
nothrow
bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null)
version (linux) bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc
{
static struct DG { const(void)* addr; dl_phdr_info* result; }

extern(C) nothrow
int callback(dl_phdr_info* info, size_t sz, void* arg)
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))
Expand All @@ -705,13 +735,16 @@ bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null)
*/
return dl_iterate_phdr(&callback, &dg) != 0;
}
else version (FreeBSD) bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc
{
return !!_rtld_addr_phdr(addr, result);
}

/*********************************
* Determine if 'addr' lies within shared object 'info'.
* If so, return true and fill in 'result' with the corresponding ELF program header.
*/
nothrow
bool findSegmentForAddr(in ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null)
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;
Expand All @@ -728,12 +761,21 @@ bool findSegmentForAddr(in ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* re
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;

@property const(char)* progname() nothrow @nogc
{
version (linux) return program_invocation_name;
version (FreeBSD) return getprogname();
}

nothrow
const(char)[] dsoName(const char* dlpi_name)
{
import core.sys.linux.errno;
// the main executable doesn't have a name in its dlpi_name field
const char* p = dlpi_name[0] != 0 ? dlpi_name : program_invocation_name;
const char* p = dlpi_name[0] != 0 ? dlpi_name : progname;
return p[0 .. strlen(p)];
}

Expand Down Expand Up @@ -791,14 +833,12 @@ body
* Returns:
* the dlopen handle for that DSO or null if addr is not within a loaded DSO
*/
version (Shared) link_map* linkMapForAddr(void* addr)
version (Shared) void* handleForAddr(void* addr)
{
Dl_info info = void;
link_map* map;
if (dladdr1(addr, &info, cast(void**)&map, RTLD_DL_LINKMAP) != 0)
return map;
else
return null;
if (dladdr(addr, &info) != 0)
return handleForName(info.dli_fname);
return null;
}

///////////////////////////////////////////////////////////////////////////////
Expand Down
159 changes: 0 additions & 159 deletions src/rt/sections_freebsd.d

This file was deleted.

1 change: 1 addition & 0 deletions test/exceptions/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ DMD:=
DRUNTIME:=
DRUNTIMESO:=
QUIET:=
LINKDL:=

SRC:=src
ROOT:=./obj/$(OS)/$(MODEL)
Expand Down
1 change: 1 addition & 0 deletions test/init_fini/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ DMD:=
DRUNTIME:=
DRUNTIMESO:=
QUIET:=
LINKDL:=

SRC:=src
ROOT:=./obj/$(OS)/$(MODEL)
Expand Down
20 changes: 11 additions & 9 deletions test/shared/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ DMD:=
DRUNTIME:=
DRUNTIMESO:=
QUIET:=
LINKDL:= # -L-ldl
LDL:=$(subst -L,,$(LINKDL)) # -ldl

SRC:=src
ROOT:=./obj/$(OS)/$(MODEL)
Expand Down Expand Up @@ -34,31 +36,31 @@ $(ROOT)/link_linkdep: $(SRC)/link_linkdep.d $(ROOT)/lib.so $(ROOT)/liblinkdep.so
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKFLAGS) -L$(ROOT)/liblinkdep.so -L$(ROOT)/lib.so

$(ROOT)/load_linkdep: $(SRC)/load_linkdep.d $(ROOT)/lib.so $(ROOT)/liblinkdep.so $(DRUNTIMESO)
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKFLAGS) -L-ldl
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKFLAGS) $(LINKDL)

$(ROOT)/link_loaddep: $(SRC)/link_loaddep.d $(ROOT)/lib.so $(ROOT)/libloaddep.so $(DRUNTIMESO)
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKFLAGS) -L$(ROOT)/libloaddep.so

$(ROOT)/load_loaddep: $(SRC)/load_loaddep.d $(ROOT)/lib.so $(ROOT)/libloaddep.so $(DRUNTIMESO)
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKFLAGS) -L-ldl
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKFLAGS) $(LINKDL)

$(ROOT)/load $(ROOT)/finalize: $(ROOT)/%: $(SRC)/%.d $(ROOT)/lib.so $(DRUNTIMESO)
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< -L-ldl
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKDL)

$(ROOT)/load_13414: $(ROOT)/%: $(SRC)/%.d $(ROOT)/lib_13414.so $(DRUNTIMESO)
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< -L-ldl
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKDL)

$(ROOT)/linkD: $(SRC)/linkD.c $(ROOT)/lib.so $(DRUNTIMESO)
$(QUIET)$(CC) $(CFLAGS) -o $@ $< $(ROOT)/lib.so -ldl
$(QUIET)$(CC) $(CFLAGS) -o $@ $< $(ROOT)/lib.so $(LDL) -pthread

$(ROOT)/linkDR: $(SRC)/linkDR.c $(ROOT)/lib.so $(DRUNTIMESO)
$(QUIET)$(CC) $(CFLAGS) -o $@ $< $(DRUNTIMESO) -ldl
$(QUIET)$(CC) $(CFLAGS) -o $@ $< $(DRUNTIMESO) $(LDL) -pthread

$(ROOT)/loadDR: $(SRC)/loadDR.c $(ROOT)/lib.so $(DRUNTIMESO)
$(QUIET)$(CC) $(CFLAGS) -o $@ $< -ldl
$(QUIET)$(CC) $(CFLAGS) -o $@ $< $(LDL) -pthread

$(ROOT)/host: $(SRC)/host.c $(ROOT)/plugin1.so $(ROOT)/plugin2.so
$(QUIET)$(CC) $(CFLAGS) -o $@ $< -ldl
$(QUIET)$(CC) $(CFLAGS) -o $@ $< $(LDL) -pthread

$(ROOT)/liblinkdep.so: $(ROOT)/lib.so
$(ROOT)/liblinkdep.so: DFLAGS+=-L$(ROOT)/lib.so
Expand All @@ -67,7 +69,7 @@ $(ROOT)/plugin1.so $(ROOT)/plugin2.so: $(SRC)/plugin.d $(DRUNTIMESO)
$(QUIET)$(DMD) -fPIC -shared $(DFLAGS) -of$@ $<

$(ROOT)/%.so: $(SRC)/%.d $(DRUNTIMESO)
$(QUIET)$(DMD) -fPIC -shared $(DFLAGS) -of$@ $< -L-ldl
$(QUIET)$(DMD) -fPIC -shared $(DFLAGS) -of$@ $< $(LINKDL)

clean:
rm -rf obj
6 changes: 5 additions & 1 deletion test/shared/src/load.d
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import core.runtime, core.stdc.stdio, core.thread, core.sys.linux.dlfcn;
import core.runtime, core.stdc.stdio, core.thread;

version (linux) import core.sys.linux.dlfcn;
else version (FreeBSD) import core.sys.freebsd.dlfcn;
else static assert(0, "unimplemented");

void loadSym(T)(void* handle, ref T val, const char* mangle)
{
Expand Down
2 changes: 1 addition & 1 deletion test/shared/src/load_13414.d
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import core.runtime, core.atomic, core.sys.linux.dlfcn;
import core.runtime, core.atomic, core.sys.posix.dlfcn;

shared uint tlsDtor, dtor;
void staticDtorHook() { atomicOp!"+="(tlsDtor, 1); }
Expand Down