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