1 /**
2  * Written in the D programming language.
3  * This module provides Darwin 64 bit specific support for sections.
4  *
5  * Copyright: Copyright Digital Mars 2016.
6  * License: Distributed under the
7  *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
8  *    (See accompanying file LICENSE)
9  * Authors: Jacob Carlborg
10  * Source: $(DRUNTIMESRC rt/_sections_darwin_64.d)
11  */
12 module rt.sections_darwin_64;
13 
14 version (OSX)
15     version = Darwin;
16 else version (iOS)
17     version = Darwin;
18 else version (TVOS)
19     version = Darwin;
20 else version (WatchOS)
21     version = Darwin;
22 
23 version (Darwin):
24 version (D_LP64):
25 
26 import core.sys.darwin.mach.dyld;
27 import core.sys.darwin.mach.getsect;
28 import core.sys.posix.pthread;
29 
30 import rt.util.utility : safeAssert;
31 
32 extern (C) size_t malloc_size(const void* ptr) nothrow @nogc;
33 
34 /**
35  * Returns the TLS range of the image containing the specified TLS symbol,
36  * or null if none was found.
37  */
38 void[] getTLSRange(const void* tlsSymbol) nothrow @nogc
39 {
40     foreach (i ; 0 .. _dyld_image_count)
41     {
42         const header = cast(const(mach_header_64)*) _dyld_get_image_header(i);
43         auto tlvInfo = tlvInfo(header);
44 
45         if (tlvInfo.foundTLSRange(tlsSymbol))
46             return tlvInfo.tlv_addr[0 .. tlvInfo.tlv_size];
47     }
48 
49     return null;
50 }
51 
52 /**
53  * Returns `true` if the correct TLS range was found.
54  *
55  * If the given `info` is located in the same image as the given `tlsSymbol`
56  * this will return `true`.
57  *
58  * Params:
59  *  info = the TLV info containing the TLV base address
60  *  tlsSymbol = the TLS symbol to search for
61  *
62  * Returns: `true` if the correct TLS range was found
63  */
64 bool foundTLSRange(const ref dyld_tlv_info info, const void* tlsSymbol) pure nothrow @nogc
65 {
66     return info.tlv_addr <= tlsSymbol &&
67         tlsSymbol < (info.tlv_addr + info.tlv_size);
68 }
69 
70 
71 /// TLV info.
72 struct dyld_tlv_info
73 {
74     /// sizeof(dyld_tlv_info)
75     size_t info_size;
76 
77     /// Base address of TLV storage
78     void* tlv_addr;
79 
80     /// Byte size of TLV storage
81     size_t tlv_size;
82 }
83 
84 /**
85  * Returns the TLV info for the given image.
86  *
87  * Params:
88  *  header = the image to look for the TLV info in
89  *
90  * Returns: the TLV info
91  */
92 dyld_tlv_info tlvInfo(const mach_header_64* header) nothrow @nogc
93 {
94     const key = header.firstTLVKey;
95     auto tlvAddress = key == pthread_key_t.max ? null : pthread_getspecific(key);
96 
97     dyld_tlv_info info = {
98         info_size: dyld_tlv_info.sizeof,
99         tlv_addr: tlvAddress,
100         tlv_size: tlvAddress ? malloc_size(tlvAddress) : 0
101     };
102 
103     return info;
104 }
105 
106 /**
107  * Returns the first TLV key for the given image.
108  *
109  * The TLV key is a key that associates a value of type `dyld_tlv_info` with a
110  * thread. Each thread local variable has an associates TLV key. The TLV keys
111  * are all the same for each image.
112  *
113  * Params:
114  *  header = the image to look for the TLV key in
115  *
116  * Returns: the first TLV key for the given image or `pthread_key_t.max` if no
117  *  key was found.
118  */
119 pthread_key_t firstTLVKey(const mach_header_64* header) pure nothrow @nogc
120 {
121     intptr_t slide = 0;
122     bool slideComputed = false;
123     const size = mach_header_64.sizeof;
124     auto command = cast(const(load_command)*)(cast(ubyte*) header + size);
125 
126     foreach (_; 0 .. header.ncmds)
127     {
128         if (command.cmd == LC_SEGMENT_64)
129         {
130             auto segment = cast(const segment_command_64*) command;
131 
132             if (!slideComputed && segment.filesize != 0)
133             {
134                 slide = cast(uintptr_t) header - segment.vmaddr;
135                 slideComputed = true;
136             }
137 
138             foreach (const ref section; segment.sections)
139             {
140                 if ((section.flags & SECTION_TYPE) != S_THREAD_LOCAL_VARIABLES)
141                     continue;
142 
143                 return section.firstTLVDescriptor(slide).key;
144             }
145         }
146 
147         command = cast(const(load_command)*)(cast(ubyte*) command + command.cmdsize);
148     }
149 
150     return pthread_key_t.max;
151 }
152 
153 /**
154  * Returns the first TLV descriptor of the given section.
155  *
156  * Params:
157  *  section = the section to get the TLV descriptor from
158  *  slide = the slide
159  *
160  * Returns: the TLV descriptor
161  */
162 const(tlv_descriptor)* firstTLVDescriptor(const ref section_64 section, intptr_t slide) pure nothrow @nogc
163 {
164     return cast(const(tlv_descriptor)*)(section.addr + slide);
165 }
166 
167 /**
168  * Returns the sections of the given segment.
169  *
170  * Params:
171  *  segment = the segment to get the sections from
172  *
173  * Returns: the sections.
174  */
175 const(section_64)[] sections(const segment_command_64* segment) pure nothrow @nogc
176 {
177     const size = segment_command_64.sizeof;
178     const firstSection = cast(const(section_64)*)(cast(ubyte*) segment + size);
179     return firstSection[0 .. segment.nsects];
180 }
181 
182 /// Invokes the specified delegate for each (non-empty) data section.
183 void foreachDataSection(in mach_header* header, intptr_t slide,
184                         scope void delegate(void[] sectionData) processor)
185 {
186     foreach (section; [ SECT_DATA, SECT_BSS, SECT_COMMON ])
187     {
188         auto data = getSection(header, slide, SEG_DATA.ptr, section.ptr);
189         if (data !is null)
190             processor(data);
191     }
192 }
193 
194 /// Returns a section's memory range, or null if not found or empty.
195 void[] getSection(in mach_header* header, intptr_t slide,
196                   in char* segmentName, in char* sectionName)
197 {
198     safeAssert(header.magic == MH_MAGIC_64, "Unsupported header.");
199     auto sect = getsectbynamefromheader_64(cast(mach_header_64*) header,
200                                            segmentName, sectionName);
201 
202     if (sect !is null && sect.size > 0)
203         return (cast(void*)sect.addr + slide)[0 .. cast(size_t) sect.size];
204     return null;
205 }