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