Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.
/ druntime Public archive
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 155 additions & 68 deletions src/rt/sections_elf_shared.d
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ else version (NetBSD) enum SharedELF = true;
else version (DragonFlyBSD) enum SharedELF = true;
else version (CRuntime_UClibc) enum SharedELF = true;
else enum SharedELF = false;
static if (SharedELF):

version (OSX) enum SharedDarwin = true;
else enum SharedDarwin = false;

static if (SharedELF): // TODO: SharedDarwin too

// debug = PRINTF;
import core.memory;
Expand All @@ -36,6 +40,15 @@ else version (FreeBSD)
import core.sys.freebsd.sys.elf;
import core.sys.freebsd.sys.link_elf;
}
else version (OSX)
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure I understand when you're using SharedDarwin and when you're using OSX.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I noticed the inconsistency too. It's not my code, and as you know, I have no interest in & knowledge of the different Mac OS flavours (desktop, iOS, watchOS, whatever).

Copy link
Contributor

Choose a reason for hiding this comment

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

It is your code, you added SharedDarwin.

{
import core.sys.osx.dlfcn;
import core.sys.osx.mach.dyld;
import core.sys.osx.mach.getsect;

extern(C) intptr_t _dyld_get_image_slide(const mach_header*) nothrow @nogc;
extern(C) mach_header* _dyld_get_image_header_containing_address(const void *addr) nothrow @nogc;
}
else version (NetBSD)
{
import core.sys.netbsd.dlfcn;
Expand Down Expand Up @@ -94,6 +107,7 @@ struct DSO

@property immutable(FuncTable)[] ehTables() const nothrow @nogc
{
// TODO: possibly required for OSX
return null;
}

Expand All @@ -108,13 +122,24 @@ private:
{
assert(_moduleGroup.modules.length);
version (CRuntime_UClibc) {} else
assert(_tlsMod || !_tlsSize);
static if (SharedELF)
{
assert(_tlsMod || !_tlsSize);
}
}

void** _slot;
ModuleGroup _moduleGroup;
Array!(void[]) _gcRanges;
size_t _tlsMod;
size_t _tlsSize;
static if (SharedELF)
{
size_t _tlsMod;
size_t _tlsSize;
}
else static if (SharedDarwin)
{
GetTLSAnchor _getTLSAnchor;
}

version (Shared)
{
Expand All @@ -126,7 +151,15 @@ private:
// get the TLS range for the executing thread
void[] tlsRange() const nothrow @nogc
{
return getTLSRange(_tlsMod, _tlsSize);
static if (SharedELF)
{
return getTLSRange(_tlsMod, _tlsSize);
}
else static if (SharedDarwin)
{
return getTLSRange(_getTLSAnchor());
}
else static assert(0, "unimplemented");
}
}

Expand Down Expand Up @@ -198,7 +231,7 @@ version (Shared)
if (tdso._addCnt)
{
// Increment the dlopen ref for explicitly loaded libraries to pin them.
.dlopen(linkMapForHandle(tdso._pdso._handle).l_name, RTLD_LAZY) !is null || assert(0);
.dlopen(nameForDSO(tdso._pdso), RTLD_LAZY) !is null || assert(0);
(*res)[i]._addCnt = 1; // new array takes over the additional ref count
}
}
Expand Down Expand Up @@ -319,17 +352,20 @@ version (Shared)
bool _rtLoading;

/*
* Hash table to map link_map* to corresponding DSO*.
* The hash table is protected by a Mutex.
* Hash table to map the native handle (as returned by dlopen)
* to the corresponding DSO*, protected by a mutex.
*/
__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;
static if (SharedELF)
{
/*
* 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 All @@ -352,6 +388,12 @@ else
// Compiler to runtime interface.
///////////////////////////////////////////////////////////////////////////////

version (OSX)
private alias ImageHeader = mach_header*;
Copy link
Contributor

@jacob-carlborg jacob-carlborg Oct 6, 2018

Choose a reason for hiding this comment

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

Will this work for both 32bit and 64bit? There's a mach_header_64 type as well. I guess it will work as long as no fields are accessed.

Copy link
Contributor Author

@kinke kinke Oct 10, 2018

Choose a reason for hiding this comment

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

It does work for LDC, apparently for the reason you mentioned (used for _dyld_get_image_header_containing_address(), _dyld_get_image_slide() and LDC-specific hacky (pointer-casting) getSection(); the latter is available in the sections_osx_*.d files here and would need to be moved.]

else
private alias ImageHeader = dl_phdr_info;

extern(C) alias GetTLSAnchor = void* function() nothrow @nogc;

/*
* This data structure is generated by the compiler, and then passed to
Expand All @@ -362,6 +404,7 @@ struct CompilerDSOData
size_t _version; // currently 1
void** _slot; // can be used to store runtime data
immutable(object.ModuleInfo*)* _minfo_beg, _minfo_end; // array of modules in this object file
static if (SharedDarwin) GetTLSAnchor _getTLSAnchor;
}

T[] toRange(T)(T* beg, T* end) { return beg[0 .. end - beg]; }
Expand All @@ -384,20 +427,23 @@ extern(C) void _d_dso_registry(CompilerDSOData* data)

DSO* pdso = cast(DSO*).calloc(1, DSO.sizeof);
assert(typeid(DSO).initializer().ptr is null);
pdso._slot = data._slot;
*data._slot = pdso; // store backlink in library record

pdso._moduleGroup = ModuleGroup(toRange(data._minfo_beg, data._minfo_end));

dl_phdr_info info = void;
findDSOInfoForAddr(data._slot, &info) || assert(0);
static if (SharedDarwin) pdso._getTLSAnchor = data._getTLSAnchor;

scanSegments(info, pdso);
ImageHeader header = void;
findImageHeaderForAddr(data._slot, &header) || assert(0);

scanSegments(header, pdso);

version (Shared)
{
auto handle = handleForAddr(data._slot);

getDependencies(info, pdso._deps);
getDependencies(header, pdso._deps);
pdso._handle = handle;
setDSOForHandle(pdso, pdso._handle);

Expand Down Expand Up @@ -627,21 +673,13 @@ void freeDSO(DSO* pdso) nothrow @nogc
version (Shared)
{
@nogc nothrow:
link_map* linkMapForHandle(void* handle)
const(char)* nameForDSO(in DSO* pdso)
{
link_map* map;
dlinfo(handle, RTLD_DI_LINKMAP, &map) == 0 || assert(0);
return map;
Dl_info info = void;
dladdr(pdso._slot, &info) || assert(0);
return info.dli_fname;
}

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 All @@ -668,7 +706,7 @@ version (Shared)
!pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
}

void getDependencies(in ref dl_phdr_info info, ref Array!(DSO*) deps)
static if (SharedELF) void getDependencies(in ref dl_phdr_info info, ref Array!(DSO*) deps)
{
// get the entries of the .dynamic section
ElfW!"Dyn"[] dyns;
Expand Down Expand Up @@ -719,6 +757,10 @@ version (Shared)
deps.insertBack(pdso); // append it to the dependencies
}
}
else static if (SharedDarwin) void getDependencies(in ImageHeader info, ref Array!(DSO*) deps)
{
// FIXME: Not implemented yet.
}

void* handleForName(const char* name)
{
Expand All @@ -733,10 +775,10 @@ version (Shared)
///////////////////////////////////////////////////////////////////////////////

/************
* Scan segments in Linux dl_phdr_info struct and store
* Scan segments in the image header and store
* the TLS and writeable data segments in *pdso.
*/
void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc
static if (SharedELF) void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc
{
foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum])
{
Expand Down Expand Up @@ -771,6 +813,27 @@ void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc
}
}
}
else static if (SharedDarwin) void scanSegments(mach_header* info, DSO* pdso)
{
import rt.mach_utils;

immutable slide = _dyld_get_image_slide(info);
foreach (e; dataSections)
{
auto sect = getSection(info, slide, e.seg, e.sect);
if (sect != null)
pdso._gcRanges.insertBack((cast(void*)sect.ptr)[0 .. sect.length]);
}

version (Shared)
{
auto text = getSection(info, slide, "__TEXT", "__text");
if (!text) {
assert(0, "Failed to get text section.");
}
pdso._codeSegments.insertBack(cast(void[])text);
}
}

/**************************
* Input:
Expand All @@ -780,7 +843,7 @@ void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc
* References:
* http://linux.die.net/man/3/dl_iterate_phdr
*/
bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc
bool findImageHeaderForAddr(in void* addr, ImageHeader* result=null) nothrow @nogc
{
version (linux) enum IterateManually = true;
else version (NetBSD) enum IterateManually = true;
Expand Down Expand Up @@ -809,6 +872,12 @@ bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc
*/
return dl_iterate_phdr(&callback, &dg) != 0;
}
else version (OSX)
{
auto header = _dyld_get_image_header_containing_address(addr);
if (result) *result = header;
return !!header;
Copy link
Member

@ibuclaw ibuclaw Dec 7, 2020

Choose a reason for hiding this comment

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

Without knowing this existed, I independently came up with this implementation for findDSOInfoForAddr:

foreach (i; 0 .. _dyld_image_count())
{
    const header = _dyld_get_image_header(i);
    const slide = _dyld_get_image_vmaddr_slide(i);
    const section = getSection(header, slide, SEG_DATA, SECT_DATA);

    // less than base address of section means quick reject
    if (!section.length || addr < section.ptr)
       continue;

    if (addr < section.ptr + section.length)
    {
        result.header = header;
        result.slide = slide;
        return true;
    }
}
return false;

Probably could align ourselves up, mixing what you have here with _dyld_get_image_vmaddr_slide for getting the slide value. So you don't have to rely on a private function later.

I think:

const header = _dyld_get_image_header_containing_address(addr);
if (header is null)
    return false;
foreach (i; 0 .. _dyld_image_count())
{
    if (header == _dyld_get_image_header(i))
    {
        result.header = header;
        result.slide = _dyld_get_image_vmaddr_slide(i);
        return true;
    }
}
return false;

Copy link
Member

@ibuclaw ibuclaw Dec 7, 2020

Choose a reason for hiding this comment

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

On darwin9, and older versions, will have to use dladdr() instead (though realistically even supporting darwin8 (OSX 10.4) is a bit of a stretch).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

The slide can be calculated like this [1]. Also note this [2]:

"The following functions allow you to iterate through all loaded images. This is not a thread safe operation. Another thread can add or remove an image during the iteration.

Many uses of these routines can be replace by a call to dladdr() which will return the mach_header and name of an image, given an address in the image. dladdr() is thread safe."

[1] https://github.com/ldc-developers/druntime/blob/a261a84e4c6b208c65ae39da0ae844ae3dd06744/src/rt/sections_darwin_64.d#L134

[2] https://opensource.apple.com/source/dyld/dyld-750.6/include/mach-o/dyld.h.auto.html

Copy link
Member

Choose a reason for hiding this comment

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

@jacob-carlborg good to know, though you're still using _dyld_image_count and get_image_header

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, IIRC I couldn't get that to work with dladdr.

}
else version (FreeBSD)
{
return !!_rtld_addr_phdr(addr, result);
Expand All @@ -825,7 +894,7 @@ bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc
* Determine if 'addr' lies within shared object 'info'.
* If so, return true and fill in 'result' with the corresponding ELF program header.
*/
bool findSegmentForAddr(in ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null) nothrow @nogc
static if (SharedELF) 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 @@ -845,13 +914,15 @@ bool findSegmentForAddr(in ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* re
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 (OSX) 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 (OSX) return getprogname();
version (DragonFlyBSD) return getprogname();
version (NetBSD) return getprogname();
}
Expand Down Expand Up @@ -894,42 +965,58 @@ struct tls_index
size_t ti_offset;
}

extern(C) void* __tls_get_addr(tls_index* ti) nothrow @nogc;
version (OSX)
{
extern(C) void _d_dyld_getTLSRange(void*, void**, size_t*) nothrow @nogc;

/* The dynamic thread vector (DTV) pointers may point 0x8000 past the start of
* each TLS block. This is at least true for PowerPC and Mips platforms.
* See: https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/powerpc/dl-tls.h;h=f7cf6f96ebfb505abfd2f02be0ad0e833107c0cd;hb=HEAD#l34
* https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/mips/dl-tls.h;h=93a6dc050cb144b9f68b96fb3199c60f5b1fcd18;hb=HEAD#l32
*/
version (X86)
enum TLS_DTV_OFFSET = 0x0;
else version (X86_64)
enum TLS_DTV_OFFSET = 0x0;
else version (ARM)
enum TLS_DTV_OFFSET = 0x0;
else version (AArch64)
enum TLS_DTV_OFFSET = 0x0;
else version (SPARC)
enum TLS_DTV_OFFSET = 0x0;
else version (SPARC64)
enum TLS_DTV_OFFSET = 0x0;
else version (PPC)
enum TLS_DTV_OFFSET = 0x8000;
else version (PPC64)
enum TLS_DTV_OFFSET = 0x8000;
else version (MIPS32)
enum TLS_DTV_OFFSET = 0x8000;
else version (MIPS64)
enum TLS_DTV_OFFSET = 0x8000;
void[] getTLSRange(void *tlsSymbol) nothrow @nogc
{
void* start = null;
size_t size = 0;
_d_dyld_getTLSRange(tlsSymbol, &start, &size);
assert(start && size, "Could not determine TLS range.");
Copy link
Contributor

Choose a reason for hiding this comment

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

This check will be removed if asserts are removed, is that intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Most likely not => #2324.

return start[0 .. size];
}
}
else
static assert( false, "Platform not supported." );

void[] getTLSRange(size_t mod, size_t sz) nothrow @nogc
{
if (mod == 0)
return null;
extern(C) void* __tls_get_addr(tls_index* ti) nothrow @nogc;

// base offset
auto ti = tls_index(mod, 0);
return (__tls_get_addr(&ti)-TLS_DTV_OFFSET)[0 .. sz];
/* The dynamic thread vector (DTV) pointers may point 0x8000 past the start of
* each TLS block. This is at least true for PowerPC and Mips platforms.
* See: https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/powerpc/dl-tls.h;h=f7cf6f96ebfb505abfd2f02be0ad0e833107c0cd;hb=HEAD#l34
* https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/mips/dl-tls.h;h=93a6dc050cb144b9f68b96fb3199c60f5b1fcd18;hb=HEAD#l32
*/
version (X86)
enum TLS_DTV_OFFSET = 0x0;
else version (X86_64)
enum TLS_DTV_OFFSET = 0x0;
else version (ARM)
enum TLS_DTV_OFFSET = 0x0;
else version (AArch64)
enum TLS_DTV_OFFSET = 0x0;
else version (SPARC)
enum TLS_DTV_OFFSET = 0x0;
else version (SPARC64)
enum TLS_DTV_OFFSET = 0x0;
else version (PPC)
enum TLS_DTV_OFFSET = 0x8000;
else version (PPC64)
enum TLS_DTV_OFFSET = 0x8000;
else version (MIPS32)
enum TLS_DTV_OFFSET = 0x8000;
else version (MIPS64)
enum TLS_DTV_OFFSET = 0x8000;
else
static assert( false, "Platform not supported." );

void[] getTLSRange(size_t mod, size_t sz) nothrow @nogc
{
if (mod == 0)
return null;

// base offset
auto ti = tls_index(mod, 0);
return (__tls_get_addr(&ti)-TLS_DTV_OFFSET)[0 .. sz];
}
}