Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Commit

Permalink
Merge pull request #1333 from MartinNowak/fixRuntimeLoading
Browse files Browse the repository at this point in the history
fix FBSD shared library issues
  • Loading branch information
DmitryOlshansky committed Aug 17, 2015
2 parents 4bcd672 + 5ea2b7b commit 8663f53
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 24 deletions.
4 changes: 1 addition & 3 deletions posix.mak
Expand Up @@ -196,9 +196,7 @@ ifeq (1,$(BUILD_WAS_SPECIFIED))
unittest : $(UT_MODULES) $(addsuffix /.run,$(ADDITIONAL_TESTS))
@echo done
else
# The unit tests currently fail on FreeBSD in debug mode
#unittest : unittest-debug unittest-release
unittest : unittest-release
unittest : unittest-debug unittest-release
unittest-%:
$(MAKE) -f $(MAKEFILE) unittest OS=$(OS) MODEL=$(MODEL) DMD=$(DMD) BUILD=$*
endif
Expand Down
93 changes: 74 additions & 19 deletions src/rt/sections_elf_shared.d
Expand Up @@ -281,6 +281,12 @@ version (Shared)
*/
__gshared pthread_mutex_t _handleToDSOMutex;
__gshared HashTab!(void*, DSO*) _handleToDSO;

/*
* Section in executable that contains copy relocations.
* Might be null when druntime is dynamically loaded by a C host.
*/
__gshared const(void)[] _copyRelocSection;
}
else
{
Expand Down Expand Up @@ -331,7 +337,8 @@ extern(C) void _d_dso_registry(CompilerDSOData* data)
// no backlink => register
if (*data._slot is null)
{
if (_loadedDSOs.empty) initLocks(); // first DSO
immutable firstDSO = _loadedDSOs.empty;
if (firstDSO) initLocks();

DSO* pdso = cast(DSO*).calloc(1, DSO.sizeof);
assert(typeid(DSO).init().ptr is null);
Expand All @@ -345,20 +352,21 @@ extern(C) void _d_dso_registry(CompilerDSOData* data)

scanSegments(info, pdso);

checkModuleCollisions(info, pdso._moduleGroup.modules);

version (Shared)
{
// the first loaded DSO is druntime itself
assert(!_loadedDSOs.empty ||
/* 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));
auto handle = handleForAddr(data._slot);

if (firstDSO)
{
/// Assert that the first loaded DSO is druntime itself. Use a
/// local druntime symbol (rt_get_bss_start) to get the handle.
assert(handleForAddr(data._slot) == handleForAddr(&rt_get_bss_start));
_copyRelocSection = getCopyRelocSection();
}
checkModuleCollisions(info, pdso._moduleGroup.modules, _copyRelocSection);

getDependencies(info, pdso._deps);
pdso._handle = handleForAddr(data._slot);
pdso._handle = handle;
setDSOForHandle(pdso, pdso._handle);

if (!_rtLoading)
Expand Down Expand Up @@ -586,6 +594,14 @@ nothrow:
return map;
}

link_map* exeLinkMap(link_map* map)
{
assert(map);
while (map.l_prev !is null)
map = map.l_prev;
return map;
}

DSO* dsoForHandle(void* handle)
{
DSO* pdso;
Expand Down Expand Up @@ -786,21 +802,59 @@ extern(C)
void* rt_get_end() @nogc nothrow;
}

nothrow
void checkModuleCollisions(in ref dl_phdr_info info, in immutable(ModuleInfo)*[] modules)
/// get the BSS section of the executable to check for copy relocations
const(void)[] getCopyRelocSection() nothrow
{
auto bss_start = rt_get_bss_start();
auto bss_end = rt_get_end();
immutable bss_size = bss_end - bss_start;

/**
Check whether __bss_start/_end both lie within the executable DSO.same DSO.
When a C host program dynamically loads druntime, i.e. it isn't linked
against, __bss_start/_end might be defined in different DSOs, b/c the
linker creates those symbols only when they are used.
But as there are no copy relocations when dynamically loading a shared
library, we can simply return a null bss range in that case.
*/
if (bss_size <= 0)
return null;

version (linux)
enum ElfW!"Addr" exeBaseAddr = 0;
else version (FreeBSD)
enum ElfW!"Addr" exeBaseAddr = 0;

dl_phdr_info info = void;
findDSOInfoForAddr(bss_start, &info) || assert(0);
if (info.dlpi_addr != exeBaseAddr)
return null;
findDSOInfoForAddr(bss_end - 1, &info) || assert(0);
if (info.dlpi_addr != exeBaseAddr)
return null;

return bss_start[0 .. bss_size];
}

/**
* Check for module collisions. A module in a shared library collides
* with an existing module if it's ModuleInfo is interposed (search
* symbol interposition) by another DSO. Therefor two modules with the
* same name do not collide if their DSOs are in separate symbol resolution
* chains.
*/
void checkModuleCollisions(in ref dl_phdr_info info, in immutable(ModuleInfo)*[] modules,
in void[] copyRelocSection) nothrow
in { assert(modules.length); }
body
{
immutable(ModuleInfo)* conflicting;

auto bss_start = rt_get_bss_start();
immutable bss_size = rt_get_end() - bss_start;
assert(bss_size >= 0);

foreach (m; modules)
{
auto addr = cast(const(void*))m;
if (cast(size_t)(addr - bss_start) < cast(size_t)bss_size)
if (cast(size_t)(addr - copyRelocSection.ptr) < copyRelocSection.length)
{
// Module is in .bss of the exe because it was copy relocated
}
Expand All @@ -824,7 +878,8 @@ body
cast(int)loading.length, loading.ptr,
cast(int)modname.length, modname.ptr,
cast(int)existing.length, existing.ptr);
assert(0);
import core.stdc.stdlib : _Exit;
_Exit(1);
}
}

Expand Down
18 changes: 16 additions & 2 deletions test/shared/Makefile
Expand Up @@ -4,15 +4,21 @@ include ../common.mak

TESTS:=link load linkD linkDR loadDR host finalize
TESTS+=link_linkdep load_linkdep link_loaddep load_loaddep load_13414
TESTS+=link_mod_collision load_mod_collision

.PHONY: all clean
all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS)))

$(ROOT)/loadDR.done: RUN_ARGS:=$(DRUNTIMESO)
$(ROOT)/loadDR.done $(ROOT)/host.done: RUN_ARGS:=$(DRUNTIMESO)

$(ROOT)/%_mod_collision.done: $(ROOT)/%_mod_collision
@echo Testing $*_mod_collision
$(QUIET)($< $(RUN_ARGS) 2>&1 || true) | grep -qF 'already defined'
@touch $@

$(ROOT)/%.done: $(ROOT)/%
@echo Testing $*
$(QUIET)$(ROOT)/$* $(RUN_ARGS)
$(QUIET)$< $(RUN_ARGS)
@touch $@

$(ROOT)/link: $(SRC)/link.d $(ROOT)/lib.so $(DRUNTIMESO)
Expand Down Expand Up @@ -48,6 +54,14 @@ $(ROOT)/loadDR: $(SRC)/loadDR.c $(ROOT)/lib.so $(DRUNTIMESO)
$(ROOT)/host: $(SRC)/host.c $(ROOT)/plugin1.so $(ROOT)/plugin2.so
$(QUIET)$(CC) $(CFLAGS) -o $@ $< $(LDL) -pthread

$(ROOT)/link_mod_collision: $(ROOT)/%: $(SRC)/%.d $(ROOT)/lib.so $(DRUNTIMESO)
# use no-as-needed to enforce linking of unused lib.so
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< -L--no-as-needed -L$(ROOT)/lib.so

$(ROOT)/load_mod_collision: $(ROOT)/%: $(SRC)/%.d $(ROOT)/lib.so $(DRUNTIMESO)
# use export dynamic so that Module in exe can interposes Module in lib.so
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKDL) -L--export-dynamic

$(ROOT)/liblinkdep.so: $(ROOT)/lib.so
$(ROOT)/liblinkdep.so: DFLAGS+=-L$(ROOT)/lib.so

Expand Down
10 changes: 10 additions & 0 deletions test/shared/src/host.c
Expand Up @@ -5,6 +5,12 @@

int main(int argc, char* argv[])
{
#if defined(__FreeBSD__)
// workaround for Bugzilla 14824
void *druntime = dlopen(argv[1], RTLD_LAZY); // load druntime
assert(druntime);
#endif

const size_t pathlen = strrchr(argv[0], '/') - argv[0] + 1;
char *name = malloc(pathlen + sizeof("plugin1.so"));
memcpy(name, argv[0], pathlen);
Expand Down Expand Up @@ -46,5 +52,9 @@ int main(int argc, char* argv[])
assert(dlclose(plugin1) == 0);

free(name);

#if defined(__FreeBSD__)
dlclose(druntime);
#endif
return EXIT_SUCCESS;
}
2 changes: 2 additions & 0 deletions test/shared/src/lib.d
@@ -1,3 +1,5 @@
module lib;

// test EH
void throwException()
{
Expand Down
5 changes: 5 additions & 0 deletions test/shared/src/link_mod_collision.d
@@ -0,0 +1,5 @@
module lib; // module collides with lib.so

void main()
{
}
11 changes: 11 additions & 0 deletions test/shared/src/load_mod_collision.d
@@ -0,0 +1,11 @@
module lib; // module collides with lib.so

import core.runtime, core.stdc.stdio, core.sys.posix.dlfcn;

void main(string[] args)
{
auto name = args[0];
assert(name[$-19 .. $] == "/load_mod_collision");
name = name[0 .. $-18] ~ "lib.so";
auto lib = Runtime.loadLibrary(name);
}

0 comments on commit 8663f53

Please sign in to comment.