1 //===- OutputSections.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 #include "OutputSections.h" 10 #include "InputChunks.h" 11 #include "InputElement.h" 12 #include "InputFiles.h" 13 #include "OutputSegment.h" 14 #include "WriterUtils.h" 15 #include "lld/Common/ErrorHandler.h" 16 #include "lld/Common/Memory.h" 17 #include "llvm/ADT/Twine.h" 18 #include "llvm/Support/LEB128.h" 19 #include "llvm/Support/Parallel.h" 20 21 #define DEBUG_TYPE "lld" 22 23 using namespace llvm; 24 using namespace llvm::wasm; 25 26 namespace lld { 27 28 // Returns a string, e.g. "FUNCTION(.text)". 29 std::string toString(const wasm::OutputSection &sec) { 30 if (!sec.name.empty()) 31 return (sec.getSectionName() + "(" + sec.name + ")").str(); 32 return std::string(sec.getSectionName()); 33 } 34 35 namespace wasm { 36 static StringRef sectionTypeToString(uint32_t sectionType) { 37 switch (sectionType) { 38 case WASM_SEC_CUSTOM: 39 return "CUSTOM"; 40 case WASM_SEC_TYPE: 41 return "TYPE"; 42 case WASM_SEC_IMPORT: 43 return "IMPORT"; 44 case WASM_SEC_FUNCTION: 45 return "FUNCTION"; 46 case WASM_SEC_TABLE: 47 return "TABLE"; 48 case WASM_SEC_MEMORY: 49 return "MEMORY"; 50 case WASM_SEC_GLOBAL: 51 return "GLOBAL"; 52 case WASM_SEC_TAG: 53 return "TAG"; 54 case WASM_SEC_EXPORT: 55 return "EXPORT"; 56 case WASM_SEC_START: 57 return "START"; 58 case WASM_SEC_ELEM: 59 return "ELEM"; 60 case WASM_SEC_CODE: 61 return "CODE"; 62 case WASM_SEC_DATA: 63 return "DATA"; 64 case WASM_SEC_DATACOUNT: 65 return "DATACOUNT"; 66 default: 67 fatal("invalid section type"); 68 } 69 } 70 71 StringRef OutputSection::getSectionName() const { 72 return sectionTypeToString(type); 73 } 74 75 void OutputSection::createHeader(size_t bodySize) { 76 raw_string_ostream os(header); 77 debugWrite(os.tell(), "section type [" + getSectionName() + "]"); 78 encodeULEB128(type, os); 79 writeUleb128(os, bodySize, "section size"); 80 os.flush(); 81 log("createHeader: " + toString(*this) + " body=" + Twine(bodySize) + 82 " total=" + Twine(getSize())); 83 } 84 85 void CodeSection::finalizeContents() { 86 raw_string_ostream os(codeSectionHeader); 87 writeUleb128(os, functions.size(), "function count"); 88 os.flush(); 89 bodySize = codeSectionHeader.size(); 90 91 for (InputFunction *func : functions) { 92 func->outputSec = this; 93 func->outSecOff = bodySize; 94 func->calculateSize(); 95 // All functions should have a non-empty body at this point 96 assert(func->getSize()); 97 bodySize += func->getSize(); 98 } 99 100 createHeader(bodySize); 101 } 102 103 void CodeSection::writeTo(uint8_t *buf) { 104 log("writing " + toString(*this) + " offset=" + Twine(offset) + 105 " size=" + Twine(getSize())); 106 log(" headersize=" + Twine(header.size())); 107 log(" codeheadersize=" + Twine(codeSectionHeader.size())); 108 buf += offset; 109 110 // Write section header 111 memcpy(buf, header.data(), header.size()); 112 buf += header.size(); 113 114 // Write code section headers 115 memcpy(buf, codeSectionHeader.data(), codeSectionHeader.size()); 116 117 // Write code section bodies 118 for (const InputChunk *chunk : functions) 119 chunk->writeTo(buf); 120 } 121 122 uint32_t CodeSection::getNumRelocations() const { 123 uint32_t count = 0; 124 for (const InputChunk *func : functions) 125 count += func->getNumRelocations(); 126 return count; 127 } 128 129 void CodeSection::writeRelocations(raw_ostream &os) const { 130 for (const InputChunk *c : functions) 131 c->writeRelocations(os); 132 } 133 134 void DataSection::finalizeContents() { 135 raw_string_ostream os(dataSectionHeader); 136 unsigned segmentCount = std::count_if( 137 segments.begin(), segments.end(), 138 [](OutputSegment *segment) { return segment->requiredInBinary(); }); 139 #ifndef NDEBUG 140 unsigned activeCount = std::count_if( 141 segments.begin(), segments.end(), [](OutputSegment *segment) { 142 return (segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE) == 0; 143 }); 144 #endif 145 146 assert((config->sharedMemory || !config->isPic || activeCount <= 1) && 147 "output segments should have been combined by now"); 148 149 writeUleb128(os, segmentCount, "data segment count"); 150 os.flush(); 151 bodySize = dataSectionHeader.size(); 152 153 for (OutputSegment *segment : segments) { 154 if (!segment->requiredInBinary()) 155 continue; 156 raw_string_ostream os(segment->header); 157 writeUleb128(os, segment->initFlags, "init flags"); 158 if (segment->initFlags & WASM_DATA_SEGMENT_HAS_MEMINDEX) 159 writeUleb128(os, 0, "memory index"); 160 if ((segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE) == 0) { 161 WasmInitExpr initExpr; 162 if (config->isPic) { 163 initExpr.Opcode = WASM_OPCODE_GLOBAL_GET; 164 initExpr.Value.Global = WasmSym::memoryBase->getGlobalIndex(); 165 } else { 166 initExpr = intConst(segment->startVA, config->is64.getValueOr(false)); 167 } 168 writeInitExpr(os, initExpr); 169 } 170 writeUleb128(os, segment->size, "segment size"); 171 os.flush(); 172 173 segment->sectionOffset = bodySize; 174 bodySize += segment->header.size() + segment->size; 175 log("Data segment: size=" + Twine(segment->size) + ", startVA=" + 176 Twine::utohexstr(segment->startVA) + ", name=" + segment->name); 177 178 for (InputChunk *inputSeg : segment->inputSegments) { 179 inputSeg->outputSec = this; 180 inputSeg->outSecOff = segment->sectionOffset + segment->header.size() + 181 inputSeg->outputSegmentOffset; 182 } 183 } 184 185 createHeader(bodySize); 186 } 187 188 void DataSection::writeTo(uint8_t *buf) { 189 log("writing " + toString(*this) + " offset=" + Twine(offset) + 190 " size=" + Twine(getSize()) + " body=" + Twine(bodySize)); 191 buf += offset; 192 193 // Write section header 194 memcpy(buf, header.data(), header.size()); 195 buf += header.size(); 196 197 // Write data section headers 198 memcpy(buf, dataSectionHeader.data(), dataSectionHeader.size()); 199 200 for (const OutputSegment *segment : segments) { 201 if (!segment->requiredInBinary()) 202 continue; 203 // Write data segment header 204 uint8_t *segStart = buf + segment->sectionOffset; 205 memcpy(segStart, segment->header.data(), segment->header.size()); 206 207 // Write segment data payload 208 for (const InputChunk *chunk : segment->inputSegments) 209 chunk->writeTo(buf); 210 } 211 } 212 213 uint32_t DataSection::getNumRelocations() const { 214 uint32_t count = 0; 215 for (const OutputSegment *seg : segments) 216 for (const InputChunk *inputSeg : seg->inputSegments) 217 count += inputSeg->getNumRelocations(); 218 return count; 219 } 220 221 void DataSection::writeRelocations(raw_ostream &os) const { 222 for (const OutputSegment *seg : segments) 223 for (const InputChunk *c : seg->inputSegments) 224 c->writeRelocations(os); 225 } 226 227 bool DataSection::isNeeded() const { 228 for (const OutputSegment *seg : segments) 229 if (seg->requiredInBinary()) 230 return true; 231 return false; 232 } 233 234 // Lots of duplication here with OutputSegment::finalizeInputSegments 235 void CustomSection::finalizeInputSections() { 236 SyntheticMergedChunk *mergedSection = nullptr; 237 std::vector<InputChunk *> newSections; 238 239 for (InputChunk *s : inputSections) { 240 s->outputSec = this; 241 MergeInputChunk *ms = dyn_cast<MergeInputChunk>(s); 242 if (!ms) { 243 newSections.push_back(s); 244 continue; 245 } 246 247 if (!mergedSection) { 248 mergedSection = 249 make<SyntheticMergedChunk>(name, 0, WASM_SEG_FLAG_STRINGS); 250 newSections.push_back(mergedSection); 251 mergedSection->outputSec = this; 252 } 253 mergedSection->addMergeChunk(ms); 254 } 255 256 if (!mergedSection) 257 return; 258 259 mergedSection->finalizeContents(); 260 inputSections = newSections; 261 } 262 263 void CustomSection::finalizeContents() { 264 finalizeInputSections(); 265 266 raw_string_ostream os(nameData); 267 encodeULEB128(name.size(), os); 268 os << name; 269 os.flush(); 270 271 for (InputChunk *section : inputSections) { 272 assert(!section->discarded); 273 section->outSecOff = payloadSize; 274 payloadSize += section->getSize(); 275 } 276 277 createHeader(payloadSize + nameData.size()); 278 } 279 280 void CustomSection::writeTo(uint8_t *buf) { 281 log("writing " + toString(*this) + " offset=" + Twine(offset) + 282 " size=" + Twine(getSize()) + " chunks=" + Twine(inputSections.size())); 283 284 assert(offset); 285 buf += offset; 286 287 // Write section header 288 memcpy(buf, header.data(), header.size()); 289 buf += header.size(); 290 memcpy(buf, nameData.data(), nameData.size()); 291 buf += nameData.size(); 292 293 // Write custom sections payload 294 for (const InputChunk *section : inputSections) 295 section->writeTo(buf); 296 } 297 298 uint32_t CustomSection::getNumRelocations() const { 299 uint32_t count = 0; 300 for (const InputChunk *inputSect : inputSections) 301 count += inputSect->getNumRelocations(); 302 return count; 303 } 304 305 void CustomSection::writeRelocations(raw_ostream &os) const { 306 for (const InputChunk *s : inputSections) 307 s->writeRelocations(os); 308 } 309 310 } // namespace wasm 311 } // namespace lld 312