xref: /llvm-project-15.0.7/lld/ELF/Writer.cpp (revision 522b1d14)
1 //===- Writer.cpp ---------------------------------------------------------===//
2 //
3 //                             The LLVM Linker
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "Writer.h"
11 #include "Chunks.h"
12 #include "Driver.h"
13 #include "llvm/ADT/DenseMap.h"
14 
15 using namespace llvm;
16 using namespace llvm::ELF;
17 using namespace llvm::object;
18 
19 using namespace lld;
20 using namespace lld::elf2;
21 
22 static const int PageSize = 4096;
23 
24 template <class ELFT> Writer<ELFT>::Writer(SymbolTable<ELFT> *T) : Symtab(T) {}
25 template <class ELFT> Writer<ELFT>::~Writer() {}
26 
27 // The main function of the writer.
28 template <class ELFT> void Writer<ELFT>::write(StringRef OutputPath) {
29   createSections();
30   assignAddresses();
31   openFile(OutputPath);
32   writeHeader();
33   writeSections();
34   error(Buffer->commit());
35 }
36 
37 void OutputSection::setVA(uint64_t VA) {
38   Header.sh_addr = VA;
39   for (Chunk *C : Chunks)
40     C->setVA(C->getVA() + VA);
41 }
42 
43 void OutputSection::setFileOffset(uint64_t Off) {
44   if (Header.sh_size == 0)
45     return;
46   Header.sh_offset = Off;
47   for (Chunk *C : Chunks)
48     C->setFileOff(C->getFileOff() + Off);
49 }
50 
51 void OutputSection::addChunk(Chunk *C) {
52   Chunks.push_back(C);
53   C->setOutputSection(this);
54   uint64_t Off = Header.sh_size;
55   Off = RoundUpToAlignment(Off, C->getAlign());
56   C->setVA(Off);
57   C->setFileOff(Off);
58   Off += C->getSize();
59   Header.sh_size = Off;
60 }
61 
62 // Create output section objects and add them to OutputSections.
63 template <class ELFT> void Writer<ELFT>::createSections() {
64   SmallDenseMap<StringRef, OutputSection *> Map;
65   for (Chunk *C : Symtab->getChunks()) {
66     OutputSection *&Sec = Map[C->getSectionName()];
67     if (!Sec) {
68       Sec = new (CAlloc.Allocate()) OutputSection(C->getSectionName());
69       OutputSections.push_back(Sec);
70     }
71     Sec->addChunk(C);
72   }
73 }
74 
75 // Visits all sections to assign incremental, non-overlapping RVAs and
76 // file offsets.
77 template <class ELFT> void Writer<ELFT>::assignAddresses() {
78   SizeOfHeaders = RoundUpToAlignment(sizeof(Elf_Ehdr_Impl<ELFT>) +
79                                          sizeof(Elf_Shdr_Impl<ELFT>) *
80                                              OutputSections.size(),
81                                      PageSize);
82   uint64_t VA = 0x1000; // The first page is kept unmapped.
83   uint64_t FileOff = SizeOfHeaders;
84   for (OutputSection *Sec : OutputSections) {
85     Sec->setVA(VA);
86     Sec->setFileOffset(FileOff);
87     VA += RoundUpToAlignment(Sec->getSize(), PageSize);
88     FileOff += RoundUpToAlignment(Sec->getSize(), 8);
89   }
90   SizeOfImage = SizeOfHeaders + RoundUpToAlignment(VA - 0x1000, PageSize);
91   FileSize = SizeOfHeaders + RoundUpToAlignment(FileOff - SizeOfHeaders, 8);
92 }
93 
94 template <class ELFT> void Writer<ELFT>::writeHeader() {
95   uint8_t *Buf = Buffer->getBufferStart();
96   auto *EHdr = reinterpret_cast<Elf_Ehdr_Impl<ELFT> *>(Buf);
97   EHdr->e_ident[EI_MAG0] = 0x7F;
98   EHdr->e_ident[EI_MAG1] = 0x45;
99   EHdr->e_ident[EI_MAG2] = 0x4C;
100   EHdr->e_ident[EI_MAG3] = 0x46;
101   EHdr->e_ident[EI_CLASS] = ELFCLASS64;
102   EHdr->e_ident[EI_DATA] = ELFDATA2LSB;
103   EHdr->e_ident[EI_VERSION] = EV_CURRENT;
104   EHdr->e_ident[EI_OSABI] = ELFOSABI_GNU;
105 
106   EHdr->e_type = ET_EXEC;
107   EHdr->e_machine = EM_X86_64;
108   EHdr->e_version = EV_CURRENT;
109   EHdr->e_entry = 0x401000;
110   EHdr->e_phoff = sizeof(Elf_Ehdr_Impl<ELFT>);
111   EHdr->e_shoff = 0;
112   EHdr->e_ehsize = sizeof(Elf_Ehdr_Impl<ELFT>);
113   EHdr->e_phentsize = sizeof(Elf_Phdr_Impl<ELFT>);
114   EHdr->e_phnum = 1;
115   EHdr->e_shentsize = sizeof(Elf_Shdr_Impl<ELFT>);
116   EHdr->e_shnum = 0;
117   EHdr->e_shstrndx = 0;
118 
119   auto PHdrs = reinterpret_cast<Elf_Phdr_Impl<ELFT> *>(Buf + EHdr->e_phoff);
120   PHdrs->p_type = PT_LOAD;
121   PHdrs->p_flags = PF_R | PF_X;
122   PHdrs->p_offset = 0x0000;
123   PHdrs->p_vaddr = 0x400000;
124   PHdrs->p_paddr = PHdrs->p_vaddr;
125   PHdrs->p_filesz = FileSize;
126   PHdrs->p_memsz = FileSize;
127   PHdrs->p_align = 0x4000;
128 }
129 
130 template <class ELFT> void Writer<ELFT>::openFile(StringRef Path) {
131   std::error_code EC = FileOutputBuffer::create(Path, FileSize, Buffer,
132                                                 FileOutputBuffer::F_executable);
133   error(EC, Twine("failed to open ") + Path);
134 }
135 
136 // Write section contents to a mmap'ed file.
137 template <class ELFT> void Writer<ELFT>::writeSections() {
138   uint8_t *Buf = Buffer->getBufferStart();
139   for (OutputSection *Sec : OutputSections) {
140     for (Chunk *C : Sec->getChunks())
141       C->writeTo(Buf);
142   }
143 }
144 
145 namespace lld {
146 namespace elf2 {
147 template class Writer<ELF32LE>;
148 template class Writer<ELF32BE>;
149 template class Writer<ELF64LE>;
150 template class Writer<ELF64BE>;
151 }
152 }
153