1 //===- ExportTrie.cpp -----------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This is a partial implementation of the Mach-O export trie format. It's 10 // essentially a symbol table encoded as a compressed prefix trie, meaning that 11 // the common prefixes of each symbol name are shared for a more compact 12 // representation. The prefixes are stored on the edges of the trie, and one 13 // edge can represent multiple characters. For example, given two exported 14 // symbols _bar and _baz, we will have a trie like this (terminal nodes are 15 // marked with an asterisk): 16 // 17 // +-+-+ 18 // | | // root node 19 // +-+-+ 20 // | 21 // | _ba 22 // | 23 // +-+-+ 24 // | | 25 // +-+-+ 26 // r / \ z 27 // / \ 28 // +-+-+ +-+-+ 29 // | * | | * | 30 // +-+-+ +-+-+ 31 // 32 // More documentation of the format can be found in 33 // llvm/tools/obj2yaml/macho2yaml.cpp. 34 // 35 //===----------------------------------------------------------------------===// 36 37 #include "ExportTrie.h" 38 #include "Symbols.h" 39 40 #include "lld/Common/ErrorHandler.h" 41 #include "lld/Common/Memory.h" 42 #include "llvm/ADT/Optional.h" 43 #include "llvm/BinaryFormat/MachO.h" 44 #include "llvm/Support/LEB128.h" 45 46 using namespace llvm; 47 using namespace llvm::MachO; 48 using namespace lld; 49 using namespace lld::macho; 50 51 namespace { 52 53 struct Edge { 54 Edge(StringRef s, TrieNode *node) : substring(s), child(node) {} 55 56 StringRef substring; 57 struct TrieNode *child; 58 }; 59 60 struct ExportInfo { 61 uint64_t address; 62 uint8_t flags = 0; 63 explicit ExportInfo(const Symbol &sym) : address(sym.getVA()) { 64 if (sym.isWeakDef()) 65 flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; 66 if (sym.isTlv()) 67 flags |= EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL; 68 // TODO: Add proper support for re-exports & stub-and-resolver flags. 69 } 70 }; 71 72 } // namespace 73 74 struct macho::TrieNode { 75 std::vector<Edge> edges; 76 Optional<ExportInfo> info; 77 // Estimated offset from the start of the serialized trie to the current node. 78 // This will converge to the true offset when updateOffset() is run to a 79 // fixpoint. 80 size_t offset = 0; 81 82 // Returns whether the new estimated offset differs from the old one. 83 bool updateOffset(size_t &nextOffset); 84 void writeTo(uint8_t *buf) const; 85 }; 86 87 bool TrieNode::updateOffset(size_t &nextOffset) { 88 // Size of the whole node (including the terminalSize and the outgoing edges.) 89 // In contrast, terminalSize only records the size of the other data in the 90 // node. 91 size_t nodeSize; 92 if (info) { 93 uint32_t terminalSize = 94 getULEB128Size(info->flags) + getULEB128Size(info->address); 95 // Overall node size so far is the uleb128 size of the length of the symbol 96 // info + the symbol info itself. 97 nodeSize = terminalSize + getULEB128Size(terminalSize); 98 } else { 99 nodeSize = 1; // Size of terminalSize (which has a value of 0) 100 } 101 // Compute size of all child edges. 102 ++nodeSize; // Byte for number of children. 103 for (Edge &edge : edges) { 104 nodeSize += edge.substring.size() + 1 // String length. 105 + getULEB128Size(edge.child->offset); // Offset len. 106 } 107 // On input, 'nextOffset' is the new preferred location for this node. 108 bool result = (offset != nextOffset); 109 // Store new location in node object for use by parents. 110 offset = nextOffset; 111 nextOffset += nodeSize; 112 return result; 113 } 114 115 void TrieNode::writeTo(uint8_t *buf) const { 116 buf += offset; 117 if (info) { 118 // TrieNodes with Symbol info: size, flags address 119 uint32_t terminalSize = 120 getULEB128Size(info->flags) + getULEB128Size(info->address); 121 buf += encodeULEB128(terminalSize, buf); 122 buf += encodeULEB128(info->flags, buf); 123 buf += encodeULEB128(info->address, buf); 124 } else { 125 // TrieNode with no Symbol info. 126 *buf++ = 0; // terminalSize 127 } 128 // Add number of children. TODO: Handle case where we have more than 256. 129 assert(edges.size() < 256); 130 *buf++ = edges.size(); 131 // Append each child edge substring and node offset. 132 for (const Edge &edge : edges) { 133 memcpy(buf, edge.substring.data(), edge.substring.size()); 134 buf += edge.substring.size(); 135 *buf++ = '\0'; 136 buf += encodeULEB128(edge.child->offset, buf); 137 } 138 } 139 140 TrieNode *TrieBuilder::makeNode() { 141 auto *node = make<TrieNode>(); 142 nodes.emplace_back(node); 143 return node; 144 } 145 146 static int charAt(const Symbol *sym, size_t pos) { 147 StringRef str = sym->getName(); 148 if (pos >= str.size()) 149 return -1; 150 return str[pos]; 151 } 152 153 // Build the trie by performing a three-way radix quicksort: We start by sorting 154 // the strings by their first characters, then sort the strings with the same 155 // first characters by their second characters, and so on recursively. Each 156 // time the prefixes diverge, we add a node to the trie. 157 // 158 // node: The most recently created node along this path in the trie (i.e. 159 // the furthest from the root.) 160 // lastPos: The prefix length of the most recently created node, i.e. the number 161 // of characters along its path from the root. 162 // pos: The string index we are currently sorting on. Note that each symbol 163 // S contained in vec has the same prefix S[0...pos). 164 void TrieBuilder::sortAndBuild(MutableArrayRef<const Symbol *> vec, 165 TrieNode *node, size_t lastPos, size_t pos) { 166 tailcall: 167 if (vec.empty()) 168 return; 169 170 // Partition items so that items in [0, i) are less than the pivot, 171 // [i, j) are the same as the pivot, and [j, vec.size()) are greater than 172 // the pivot. 173 const Symbol *pivotSymbol = vec[vec.size() / 2]; 174 int pivot = charAt(pivotSymbol, pos); 175 size_t i = 0; 176 size_t j = vec.size(); 177 for (size_t k = 0; k < j;) { 178 int c = charAt(vec[k], pos); 179 if (c < pivot) 180 std::swap(vec[i++], vec[k++]); 181 else if (c > pivot) 182 std::swap(vec[--j], vec[k]); 183 else 184 k++; 185 } 186 187 bool isTerminal = pivot == -1; 188 bool prefixesDiverge = i != 0 || j != vec.size(); 189 if (lastPos != pos && (isTerminal || prefixesDiverge)) { 190 TrieNode *newNode = makeNode(); 191 node->edges.emplace_back(pivotSymbol->getName().slice(lastPos, pos), 192 newNode); 193 node = newNode; 194 lastPos = pos; 195 } 196 197 sortAndBuild(vec.slice(0, i), node, lastPos, pos); 198 sortAndBuild(vec.slice(j), node, lastPos, pos); 199 200 if (isTerminal) { 201 assert(j - i == 1); // no duplicate symbols 202 node->info = ExportInfo(*pivotSymbol); 203 } else { 204 // This is the tail-call-optimized version of the following: 205 // sortAndBuild(vec.slice(i, j - i), node, lastPos, pos + 1); 206 vec = vec.slice(i, j - i); 207 ++pos; 208 goto tailcall; 209 } 210 } 211 212 size_t TrieBuilder::build() { 213 if (exported.empty()) 214 return 0; 215 216 TrieNode *root = makeNode(); 217 sortAndBuild(exported, root, 0, 0); 218 219 // Assign each node in the vector an offset in the trie stream, iterating 220 // until all uleb128 sizes have stabilized. 221 size_t offset; 222 bool more; 223 do { 224 offset = 0; 225 more = false; 226 for (TrieNode *node : nodes) 227 more |= node->updateOffset(offset); 228 } while (more); 229 230 return offset; 231 } 232 233 void TrieBuilder::writeTo(uint8_t *buf) const { 234 for (TrieNode *node : nodes) 235 node->writeTo(buf); 236 } 237 238 namespace { 239 240 // Parse a serialized trie and invoke a callback for each entry. 241 class TrieParser { 242 public: 243 TrieParser(const uint8_t *buf, size_t size, const TrieEntryCallback &callback) 244 : start(buf), end(start + size), callback(callback) {} 245 246 void parse(const uint8_t *buf, const Twine &cumulativeString); 247 248 void parse() { parse(start, ""); } 249 250 const uint8_t *start; 251 const uint8_t *end; 252 const TrieEntryCallback &callback; 253 }; 254 255 } // namespace 256 257 void TrieParser::parse(const uint8_t *buf, const Twine &cumulativeString) { 258 if (buf >= end) 259 fatal("Node offset points outside export section"); 260 261 unsigned ulebSize; 262 uint64_t terminalSize = decodeULEB128(buf, &ulebSize); 263 buf += ulebSize; 264 uint64_t flags = 0; 265 size_t offset; 266 if (terminalSize != 0) { 267 flags = decodeULEB128(buf, &ulebSize); 268 callback(cumulativeString, flags); 269 } 270 buf += terminalSize; 271 uint8_t numEdges = *buf++; 272 for (uint8_t i = 0; i < numEdges; ++i) { 273 const char *cbuf = reinterpret_cast<const char *>(buf); 274 StringRef substring = StringRef(cbuf, strnlen(cbuf, end - buf)); 275 buf += substring.size() + 1; 276 offset = decodeULEB128(buf, &ulebSize); 277 buf += ulebSize; 278 parse(start + offset, cumulativeString + substring); 279 } 280 } 281 282 void macho::parseTrie(const uint8_t *buf, size_t size, 283 const TrieEntryCallback &callback) { 284 if (size == 0) 285 return; 286 287 TrieParser(buf, size, callback).parse(); 288 } 289