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 StringRef OutputSection::getSectionName() const { 37 return sectionTypeToString(type); 38 } 39 40 void OutputSection::createHeader(size_t bodySize) { 41 raw_string_ostream os(header); 42 debugWrite(os.tell(), "section type [" + getSectionName() + "]"); 43 encodeULEB128(type, os); 44 writeUleb128(os, bodySize, "section size"); 45 os.flush(); 46 log("createHeader: " + toString(*this) + " body=" + Twine(bodySize) + 47 " total=" + Twine(getSize())); 48 } 49 50 void CodeSection::finalizeContents() { 51 raw_string_ostream os(codeSectionHeader); 52 writeUleb128(os, functions.size(), "function count"); 53 os.flush(); 54 bodySize = codeSectionHeader.size(); 55 56 for (InputFunction *func : functions) { 57 func->outputSec = this; 58 func->outSecOff = bodySize; 59 func->calculateSize(); 60 // All functions should have a non-empty body at this point 61 assert(func->getSize()); 62 bodySize += func->getSize(); 63 } 64 65 createHeader(bodySize); 66 } 67 68 void CodeSection::writeTo(uint8_t *buf) { 69 log("writing " + toString(*this) + " offset=" + Twine(offset) + 70 " size=" + Twine(getSize())); 71 log(" headersize=" + Twine(header.size())); 72 log(" codeheadersize=" + Twine(codeSectionHeader.size())); 73 buf += offset; 74 75 // Write section header 76 memcpy(buf, header.data(), header.size()); 77 buf += header.size(); 78 79 // Write code section headers 80 memcpy(buf, codeSectionHeader.data(), codeSectionHeader.size()); 81 82 // Write code section bodies 83 for (const InputChunk *chunk : functions) 84 chunk->writeTo(buf); 85 } 86 87 uint32_t CodeSection::getNumRelocations() const { 88 uint32_t count = 0; 89 for (const InputChunk *func : functions) 90 count += func->getNumRelocations(); 91 return count; 92 } 93 94 void CodeSection::writeRelocations(raw_ostream &os) const { 95 for (const InputChunk *c : functions) 96 c->writeRelocations(os); 97 } 98 99 void DataSection::finalizeContents() { 100 raw_string_ostream os(dataSectionHeader); 101 unsigned segmentCount = std::count_if( 102 segments.begin(), segments.end(), 103 [](OutputSegment *segment) { return segment->requiredInBinary(); }); 104 #ifndef NDEBUG 105 unsigned activeCount = std::count_if( 106 segments.begin(), segments.end(), [](OutputSegment *segment) { 107 return (segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE) == 0; 108 }); 109 #endif 110 111 assert((config->sharedMemory || !config->isPic || config->extendedConst || 112 activeCount <= 1) && 113 "output segments should have been combined by now"); 114 115 writeUleb128(os, segmentCount, "data segment count"); 116 os.flush(); 117 bodySize = dataSectionHeader.size(); 118 bool is64 = config->is64.getValueOr(false); 119 120 for (OutputSegment *segment : segments) { 121 if (!segment->requiredInBinary()) 122 continue; 123 raw_string_ostream os(segment->header); 124 writeUleb128(os, segment->initFlags, "init flags"); 125 if (segment->initFlags & WASM_DATA_SEGMENT_HAS_MEMINDEX) 126 writeUleb128(os, 0, "memory index"); 127 if ((segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE) == 0) { 128 if (config->isPic && config->extendedConst) { 129 writeU8(os, WASM_OPCODE_GLOBAL_GET, "global get"); 130 writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), 131 "literal (global index)"); 132 if (segment->startVA) { 133 writePtrConst(os, segment->startVA, is64, "offset"); 134 writeU8(os, is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD, "add"); 135 } 136 writeU8(os, WASM_OPCODE_END, "opcode:end"); 137 } else { 138 WasmInitExpr initExpr; 139 initExpr.Extended = false; 140 if (config->isPic) { 141 assert(segment->startVA == 0); 142 initExpr.Inst.Opcode = WASM_OPCODE_GLOBAL_GET; 143 initExpr.Inst.Value.Global = WasmSym::memoryBase->getGlobalIndex(); 144 } else { 145 initExpr = intConst(segment->startVA, is64); 146 } 147 writeInitExpr(os, initExpr); 148 } 149 } 150 writeUleb128(os, segment->size, "segment size"); 151 os.flush(); 152 153 segment->sectionOffset = bodySize; 154 bodySize += segment->header.size() + segment->size; 155 log("Data segment: size=" + Twine(segment->size) + ", startVA=" + 156 Twine::utohexstr(segment->startVA) + ", name=" + segment->name); 157 158 for (InputChunk *inputSeg : segment->inputSegments) { 159 inputSeg->outputSec = this; 160 inputSeg->outSecOff = segment->sectionOffset + segment->header.size() + 161 inputSeg->outputSegmentOffset; 162 } 163 } 164 165 createHeader(bodySize); 166 } 167 168 void DataSection::writeTo(uint8_t *buf) { 169 log("writing " + toString(*this) + " offset=" + Twine(offset) + 170 " size=" + Twine(getSize()) + " body=" + Twine(bodySize)); 171 buf += offset; 172 173 // Write section header 174 memcpy(buf, header.data(), header.size()); 175 buf += header.size(); 176 177 // Write data section headers 178 memcpy(buf, dataSectionHeader.data(), dataSectionHeader.size()); 179 180 for (const OutputSegment *segment : segments) { 181 if (!segment->requiredInBinary()) 182 continue; 183 // Write data segment header 184 uint8_t *segStart = buf + segment->sectionOffset; 185 memcpy(segStart, segment->header.data(), segment->header.size()); 186 187 // Write segment data payload 188 for (const InputChunk *chunk : segment->inputSegments) 189 chunk->writeTo(buf); 190 } 191 } 192 193 uint32_t DataSection::getNumRelocations() const { 194 uint32_t count = 0; 195 for (const OutputSegment *seg : segments) 196 for (const InputChunk *inputSeg : seg->inputSegments) 197 count += inputSeg->getNumRelocations(); 198 return count; 199 } 200 201 void DataSection::writeRelocations(raw_ostream &os) const { 202 for (const OutputSegment *seg : segments) 203 for (const InputChunk *c : seg->inputSegments) 204 c->writeRelocations(os); 205 } 206 207 bool DataSection::isNeeded() const { 208 for (const OutputSegment *seg : segments) 209 if (seg->requiredInBinary()) 210 return true; 211 return false; 212 } 213 214 // Lots of duplication here with OutputSegment::finalizeInputSegments 215 void CustomSection::finalizeInputSections() { 216 SyntheticMergedChunk *mergedSection = nullptr; 217 std::vector<InputChunk *> newSections; 218 219 for (InputChunk *s : inputSections) { 220 s->outputSec = this; 221 MergeInputChunk *ms = dyn_cast<MergeInputChunk>(s); 222 if (!ms) { 223 newSections.push_back(s); 224 continue; 225 } 226 227 if (!mergedSection) { 228 mergedSection = 229 make<SyntheticMergedChunk>(name, 0, WASM_SEG_FLAG_STRINGS); 230 newSections.push_back(mergedSection); 231 mergedSection->outputSec = this; 232 } 233 mergedSection->addMergeChunk(ms); 234 } 235 236 if (!mergedSection) 237 return; 238 239 mergedSection->finalizeContents(); 240 inputSections = newSections; 241 } 242 243 void CustomSection::finalizeContents() { 244 finalizeInputSections(); 245 246 raw_string_ostream os(nameData); 247 encodeULEB128(name.size(), os); 248 os << name; 249 os.flush(); 250 251 for (InputChunk *section : inputSections) { 252 assert(!section->discarded); 253 section->outSecOff = payloadSize; 254 payloadSize += section->getSize(); 255 } 256 257 createHeader(payloadSize + nameData.size()); 258 } 259 260 void CustomSection::writeTo(uint8_t *buf) { 261 log("writing " + toString(*this) + " offset=" + Twine(offset) + 262 " size=" + Twine(getSize()) + " chunks=" + Twine(inputSections.size())); 263 264 assert(offset); 265 buf += offset; 266 267 // Write section header 268 memcpy(buf, header.data(), header.size()); 269 buf += header.size(); 270 memcpy(buf, nameData.data(), nameData.size()); 271 buf += nameData.size(); 272 273 // Write custom sections payload 274 for (const InputChunk *section : inputSections) 275 section->writeTo(buf); 276 } 277 278 uint32_t CustomSection::getNumRelocations() const { 279 uint32_t count = 0; 280 for (const InputChunk *inputSect : inputSections) 281 count += inputSect->getNumRelocations(); 282 return count; 283 } 284 285 void CustomSection::writeRelocations(raw_ostream &os) const { 286 for (const InputChunk *s : inputSections) 287 s->writeRelocations(os); 288 } 289 290 } // namespace wasm 291 } // namespace lld 292