Fangrui Song | 6acd300 | 2020-04-02 11:54:05 -0700 | [diff] [blame] | 1 | //===- Writer.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 "Writer.h" |
| 10 | #include "Config.h" |
| 11 | #include "InputFiles.h" |
| 12 | #include "InputSection.h" |
| 13 | #include "OutputSegment.h" |
| 14 | #include "SymbolTable.h" |
| 15 | #include "Symbols.h" |
| 16 | #include "Target.h" |
| 17 | |
| 18 | #include "lld/Common/ErrorHandler.h" |
| 19 | #include "lld/Common/Memory.h" |
| 20 | #include "llvm/BinaryFormat/MachO.h" |
| 21 | #include "llvm/Support/LEB128.h" |
| 22 | #include "llvm/Support/MathExtras.h" |
| 23 | |
| 24 | using namespace llvm; |
| 25 | using namespace llvm::MachO; |
| 26 | using namespace llvm::support; |
| 27 | using namespace lld; |
| 28 | using namespace lld::macho; |
| 29 | |
| 30 | namespace { |
| 31 | class LCLinkEdit; |
| 32 | class LCDyldInfo; |
| 33 | class LCSymtab; |
| 34 | |
| 35 | class LoadCommand { |
| 36 | public: |
| 37 | virtual ~LoadCommand() = default; |
| 38 | virtual uint32_t getSize() const = 0; |
| 39 | virtual void writeTo(uint8_t *buf) const = 0; |
| 40 | }; |
| 41 | |
| 42 | class Writer { |
| 43 | public: |
| 44 | Writer() : buffer(errorHandler().outputBuffer) {} |
| 45 | |
| 46 | void createLoadCommands(); |
| 47 | void assignAddresses(); |
| 48 | |
| 49 | void openFile(); |
| 50 | void writeHeader(); |
| 51 | void writeSections(); |
| 52 | |
| 53 | void run(); |
| 54 | |
| 55 | std::vector<LoadCommand *> loadCommands; |
| 56 | std::unique_ptr<FileOutputBuffer> &buffer; |
| 57 | uint64_t fileSize = 0; |
| 58 | uint64_t sizeofCmds = 0; |
| 59 | LCLinkEdit *linkEditSeg = nullptr; |
| 60 | LCDyldInfo *dyldInfoSeg = nullptr; |
| 61 | LCSymtab *symtabSeg = nullptr; |
| 62 | }; |
| 63 | |
| 64 | class LCPagezero : public LoadCommand { |
| 65 | public: |
| 66 | uint32_t getSize() const override { return sizeof(segment_command_64); } |
| 67 | |
| 68 | void writeTo(uint8_t *buf) const override { |
| 69 | auto *c = reinterpret_cast<segment_command_64 *>(buf); |
| 70 | c->cmd = LC_SEGMENT_64; |
| 71 | c->cmdsize = getSize(); |
| 72 | strcpy(c->segname, "__PAGEZERO"); |
| 73 | c->vmsize = PageSize; |
| 74 | } |
| 75 | }; |
| 76 | |
| 77 | class LCLinkEdit : public LoadCommand { |
| 78 | public: |
| 79 | uint32_t getSize() const override { return sizeof(segment_command_64); } |
| 80 | |
| 81 | void writeTo(uint8_t *buf) const override { |
| 82 | auto *c = reinterpret_cast<segment_command_64 *>(buf); |
| 83 | c->cmd = LC_SEGMENT_64; |
| 84 | c->cmdsize = getSize(); |
| 85 | strcpy(c->segname, "__LINKEDIT"); |
| 86 | c->fileoff = fileOff; |
| 87 | c->filesize = contents.size(); |
| 88 | c->maxprot = VM_PROT_READ | VM_PROT_WRITE; |
| 89 | c->initprot = VM_PROT_READ; |
| 90 | } |
| 91 | |
| 92 | uint64_t getOffset() const { return fileOff + contents.size(); } |
| 93 | |
| 94 | uint64_t fileOff = 0; |
| 95 | SmallVector<char, 128> contents; |
| 96 | }; |
| 97 | |
| 98 | class LCDyldInfo : public LoadCommand { |
| 99 | public: |
| 100 | uint32_t getSize() const override { return sizeof(dyld_info_command); } |
| 101 | |
| 102 | void writeTo(uint8_t *buf) const override { |
| 103 | auto *c = reinterpret_cast<dyld_info_command *>(buf); |
| 104 | c->cmd = LC_DYLD_INFO_ONLY; |
| 105 | c->cmdsize = getSize(); |
| 106 | c->export_off = exportOff; |
| 107 | c->export_size = exportSize; |
| 108 | } |
| 109 | |
| 110 | uint64_t exportOff = 0; |
| 111 | uint64_t exportSize = 0; |
| 112 | }; |
| 113 | |
| 114 | class LCDysymtab : public LoadCommand { |
| 115 | public: |
| 116 | uint32_t getSize() const override { return sizeof(dysymtab_command); } |
| 117 | |
| 118 | void writeTo(uint8_t *buf) const override { |
| 119 | auto *c = reinterpret_cast<dysymtab_command *>(buf); |
| 120 | c->cmd = LC_DYSYMTAB; |
| 121 | c->cmdsize = getSize(); |
| 122 | } |
| 123 | }; |
| 124 | |
| 125 | class LCSegment : public LoadCommand { |
| 126 | public: |
| 127 | LCSegment(StringRef name, OutputSegment *seg) : name(name), seg(seg) {} |
| 128 | |
| 129 | uint32_t getSize() const override { |
| 130 | return sizeof(segment_command_64) + |
| 131 | seg->sections.size() * sizeof(section_64); |
| 132 | } |
| 133 | |
| 134 | void writeTo(uint8_t *buf) const override { |
| 135 | auto *c = reinterpret_cast<segment_command_64 *>(buf); |
| 136 | buf += sizeof(segment_command_64); |
| 137 | |
| 138 | c->cmd = LC_SEGMENT_64; |
| 139 | c->cmdsize = getSize(); |
| 140 | memcpy(c->segname, name.data(), name.size()); |
| 141 | |
| 142 | InputSection *firstSec = seg->sections.front().second[0]; |
| 143 | InputSection *lastSec = seg->sections.back().second.back(); |
| 144 | |
| 145 | // dyld3's MachOLoaded::getSlide() assumes that the __TEXT segment starts |
| 146 | // from the beginning of the file (i.e. the header). |
| 147 | // TODO: replace this logic by creating a synthetic __TEXT,__mach_header |
| 148 | // section instead. |
| 149 | c->fileoff = name == "__TEXT" ? 0 : firstSec->addr - ImageBase; |
| 150 | c->vmaddr = c->fileoff + ImageBase; |
| 151 | c->vmsize = c->filesize = lastSec->addr + lastSec->data.size() - c->vmaddr; |
| 152 | c->maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; |
| 153 | c->initprot = seg->perms; |
| 154 | c->nsects = seg->sections.size(); |
| 155 | |
| 156 | for (auto &p : seg->sections) { |
| 157 | StringRef s = p.first; |
| 158 | std::vector<InputSection *> §ions = p.second; |
| 159 | |
| 160 | auto *sectHdr = reinterpret_cast<section_64 *>(buf); |
| 161 | buf += sizeof(section_64); |
| 162 | |
| 163 | memcpy(sectHdr->sectname, s.data(), s.size()); |
| 164 | memcpy(sectHdr->segname, name.data(), name.size()); |
| 165 | |
| 166 | sectHdr->addr = sections[0]->addr; |
| 167 | sectHdr->offset = sections[0]->addr - ImageBase; |
| 168 | sectHdr->align = sections[0]->align; |
| 169 | uint32_t maxAlign = 0; |
| 170 | for (const InputSection *section : sections) |
| 171 | maxAlign = std::max(maxAlign, section->align); |
| 172 | sectHdr->align = Log2_32(maxAlign); |
| 173 | sectHdr->flags = sections[0]->flags; |
| 174 | sectHdr->size = sections.back()->addr + sections.back()->data.size() - |
| 175 | sections[0]->addr; |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | private: |
| 180 | StringRef name; |
| 181 | OutputSegment *seg; |
| 182 | }; |
| 183 | |
| 184 | class LCMain : public LoadCommand { |
| 185 | uint32_t getSize() const override { return sizeof(entry_point_command); } |
| 186 | |
| 187 | void writeTo(uint8_t *buf) const override { |
| 188 | auto *c = reinterpret_cast<entry_point_command *>(buf); |
| 189 | c->cmd = LC_MAIN; |
| 190 | c->cmdsize = getSize(); |
| 191 | c->entryoff = config->entry->getVA(); |
| 192 | c->stacksize = 0; |
| 193 | } |
| 194 | }; |
| 195 | |
| 196 | class LCSymtab : public LoadCommand { |
| 197 | public: |
| 198 | uint32_t getSize() const override { return sizeof(symtab_command); } |
| 199 | |
| 200 | void writeTo(uint8_t *buf) const override { |
| 201 | auto *c = reinterpret_cast<symtab_command *>(buf); |
| 202 | c->cmd = LC_SYMTAB; |
| 203 | c->cmdsize = getSize(); |
| 204 | } |
| 205 | }; |
| 206 | |
| 207 | class LCLoadDylib : public LoadCommand { |
| 208 | public: |
| 209 | LCLoadDylib(StringRef path) : path(path) {} |
| 210 | |
| 211 | uint32_t getSize() const override { |
| 212 | return alignTo(sizeof(dylib_command) + path.size() + 1, 8); |
| 213 | } |
| 214 | |
| 215 | void writeTo(uint8_t *buf) const override { |
| 216 | auto *c = reinterpret_cast<dylib_command *>(buf); |
| 217 | buf += sizeof(dylib_command); |
| 218 | |
| 219 | c->cmd = LC_LOAD_DYLIB; |
| 220 | c->cmdsize = getSize(); |
| 221 | c->dylib.name = sizeof(dylib_command); |
| 222 | |
| 223 | memcpy(buf, path.data(), path.size()); |
| 224 | buf[path.size()] = '\0'; |
| 225 | } |
| 226 | |
| 227 | private: |
| 228 | StringRef path; |
| 229 | }; |
| 230 | |
| 231 | class LCLoadDylinker : public LoadCommand { |
| 232 | public: |
| 233 | uint32_t getSize() const override { |
| 234 | return alignTo(sizeof(dylinker_command) + path.size() + 1, 8); |
| 235 | } |
| 236 | |
| 237 | void writeTo(uint8_t *buf) const override { |
| 238 | auto *c = reinterpret_cast<dylinker_command *>(buf); |
| 239 | buf += sizeof(dylinker_command); |
| 240 | |
| 241 | c->cmd = LC_LOAD_DYLINKER; |
| 242 | c->cmdsize = getSize(); |
| 243 | c->name = sizeof(dylinker_command); |
| 244 | |
| 245 | memcpy(buf, path.data(), path.size()); |
| 246 | buf[path.size()] = '\0'; |
| 247 | } |
| 248 | |
| 249 | private: |
| 250 | // Recent versions of Darwin won't run any binary that has dyld at a |
| 251 | // different location. |
| 252 | const StringRef path = "/usr/lib/dyld"; |
| 253 | }; |
| 254 | } // namespace |
| 255 | |
| 256 | void Writer::createLoadCommands() { |
| 257 | linkEditSeg = make<LCLinkEdit>(); |
| 258 | dyldInfoSeg = make<LCDyldInfo>(); |
| 259 | symtabSeg = make<LCSymtab>(); |
| 260 | |
| 261 | loadCommands.push_back(linkEditSeg); |
| 262 | loadCommands.push_back(dyldInfoSeg); |
| 263 | loadCommands.push_back(symtabSeg); |
| 264 | loadCommands.push_back(make<LCPagezero>()); |
| 265 | loadCommands.push_back(make<LCLoadDylinker>()); |
| 266 | loadCommands.push_back(make<LCDysymtab>()); |
| 267 | loadCommands.push_back(make<LCMain>()); |
| 268 | // TODO: dyld requires libSystem to be loaded. libSystem is a universal |
| 269 | // binary and we don't have support for that yet, so mock it out here. |
| 270 | loadCommands.push_back(make<LCLoadDylib>("/usr/lib/libSystem.B.dylib")); |
| 271 | |
| 272 | for (OutputSegment *seg : outputSegments) |
| 273 | if (!seg->sections.empty()) |
| 274 | loadCommands.push_back(make<LCSegment>(seg->name, seg)); |
| 275 | } |
| 276 | |
| 277 | void Writer::assignAddresses() { |
| 278 | uint64_t addr = ImageBase + sizeof(mach_header_64); |
| 279 | |
| 280 | uint64_t size = 0; |
| 281 | for (LoadCommand *lc : loadCommands) |
| 282 | size += lc->getSize(); |
| 283 | sizeofCmds = size; |
| 284 | addr += size; |
| 285 | |
| 286 | for (OutputSegment *seg : outputSegments) { |
| 287 | addr = alignTo(addr, PageSize); |
| 288 | |
| 289 | for (auto &p : seg->sections) { |
| 290 | ArrayRef<InputSection *> sections = p.second; |
| 291 | for (InputSection *isec : sections) { |
| 292 | addr = alignTo(addr, isec->align); |
| 293 | isec->addr = addr; |
| 294 | addr += isec->data.size(); |
| 295 | } |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | linkEditSeg->fileOff = addr - ImageBase; |
| 300 | } |
| 301 | |
| 302 | void Writer::openFile() { |
| 303 | Expected<std::unique_ptr<FileOutputBuffer>> bufferOrErr = |
| 304 | FileOutputBuffer::create(config->outputFile, fileSize, |
| 305 | FileOutputBuffer::F_executable); |
| 306 | |
| 307 | if (!bufferOrErr) |
| 308 | error("failed to open " + config->outputFile + ": " + |
| 309 | llvm::toString(bufferOrErr.takeError())); |
| 310 | else |
| 311 | buffer = std::move(*bufferOrErr); |
| 312 | } |
| 313 | |
| 314 | void Writer::writeHeader() { |
| 315 | auto *hdr = reinterpret_cast<mach_header_64 *>(buffer->getBufferStart()); |
| 316 | hdr->magic = MH_MAGIC_64; |
| 317 | hdr->cputype = CPU_TYPE_X86_64; |
| 318 | hdr->cpusubtype = CPU_SUBTYPE_X86_64_ALL | CPU_SUBTYPE_LIB64; |
| 319 | hdr->filetype = MH_EXECUTE; |
| 320 | hdr->ncmds = loadCommands.size(); |
| 321 | hdr->sizeofcmds = sizeofCmds; |
| 322 | hdr->flags = MH_NOUNDEFS | MH_DYLDLINK | MH_TWOLEVEL; |
| 323 | |
| 324 | uint8_t *p = reinterpret_cast<uint8_t *>(hdr + 1); |
| 325 | for (LoadCommand *lc : loadCommands) { |
| 326 | lc->writeTo(p); |
| 327 | p += lc->getSize(); |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | void Writer::writeSections() { |
| 332 | uint8_t *buf = buffer->getBufferStart(); |
| 333 | |
| 334 | for (OutputSegment *seg : outputSegments) |
| 335 | for (auto § : seg->sections) |
| 336 | for (InputSection *isec : sect.second) |
| 337 | isec->writeTo(buf + isec->addr - ImageBase); |
| 338 | |
| 339 | memcpy(buf + linkEditSeg->fileOff, linkEditSeg->contents.data(), |
| 340 | linkEditSeg->contents.size()); |
| 341 | } |
| 342 | |
| 343 | void Writer::run() { |
| 344 | createLoadCommands(); |
| 345 | assignAddresses(); |
| 346 | fileSize = linkEditSeg->fileOff + linkEditSeg->contents.size(); |
| 347 | |
| 348 | openFile(); |
| 349 | if (errorCount()) |
| 350 | return; |
| 351 | |
| 352 | writeHeader(); |
| 353 | writeSections(); |
| 354 | |
| 355 | if (auto e = buffer->commit()) |
| 356 | error("failed to write to the output file: " + toString(std::move(e))); |
| 357 | } |
| 358 | |
| 359 | void macho::writeResult() { Writer().run(); } |