1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/compiler/aot_file/elf_builder.h"
17
18namespace panda::ecmascript {
19void ElfBuilder::AddShStrTabSection()
20{
21    std::map<ElfSecName, std::pair<uint64_t, uint32_t>> &sections =
22        des_[ShStrTableModuleDesIndex].GetSectionsInfo();
23
24    uint32_t size = 1;
25    for (auto &s : sections_) {
26        std::string str = ModuleSectionDes::GetSecName(s);
27        size = size + str.size() + 1;
28    }
29
30    shStrTabPtr_ = std::make_unique<char []>(size);
31    char *dst = shStrTabPtr_.get();
32    dst[0] = 0x0;
33    uint32_t i = 1;
34    for (auto &s: sections_) {
35        std::string str = ModuleSectionDes::GetSecName(s);
36        uint32_t copySize = str.size();
37        if (copySize == 0) {
38            UNREACHABLE();
39        }
40        ASSERT(size >= i);
41        if ((copySize != 0) && ((memcpy_s(dst + i, size - i + 1, str.data(), copySize)) != EOK)) {
42            UNREACHABLE();
43        }
44        dst[i + copySize] = 0x0;
45        i = i + copySize + 1;
46    }
47    if (sections.find(ElfSecName::SHSTRTAB) != sections.end()) {
48        sections.erase(ElfSecName::SHSTRTAB);
49    }
50    sections[ElfSecName::SHSTRTAB] = std::make_pair(reinterpret_cast<uint64_t>(shStrTabPtr_.get()), size);
51    if (enableSecDump_) {
52        DumpSection();
53    }
54}
55
56uint32_t ElfBuilder::AddAsmStubStrTab(std::ofstream &elfFile,
57    const std::vector<std::pair<std::string, uint32_t>> &asmStubELFInfo)
58{
59    uint32_t size = 1;
60    ASSERT(asmStubELFInfo.size() > 0);
61    uint32_t asmStubSymTabNum = asmStubELFInfo.size() - 1;
62    for (size_t idx = 0; idx < asmStubSymTabNum; ++idx) {
63        const std::string &str = asmStubELFInfo[idx].first;
64        size = size + str.size() + 1;
65    }
66
67    std::unique_ptr<char []> asmStubStrTabPtr = std::make_unique<char []>(size);
68    char *dst = asmStubStrTabPtr.get();
69    dst[0] = 0x0;
70    uint32_t i = 1;
71    for (size_t idx = 0; idx < asmStubSymTabNum; ++idx) {
72        const std::string &str = asmStubELFInfo[idx].first;
73        asmStubStrName_.emplace_back(i);
74        uint32_t copySize = str.size();
75        if (copySize == 0) {
76            UNREACHABLE();
77        }
78        if ((copySize != 0) && ((memcpy_s(dst + i, size - i + 1, str.data(), copySize)) != EOK)) {
79            UNREACHABLE();
80        }
81        dst[i + copySize] = 0x0;
82        i = i + copySize + 1;
83    }
84    elfFile.write(reinterpret_cast<char *>(dst), size);
85    return size;
86}
87
88void ElfBuilder::DumpSection() const
89{
90    const std::map<ElfSecName, std::pair<uint64_t, uint32_t>> &sections = GetFullSecInfo();
91    // dump
92    for (auto &s : sections) {
93        ElfSection section = ElfSection(s.first);
94        if (!section.ShouldDumpToAOTFile()) {
95            continue;
96        }
97        LOG_COMPILER(INFO) << "secname :" << std::dec << static_cast<int>(s.first)
98            << " addr:0x" << std::hex << s.second.first << " size:0x" << s.second.second << std::endl;
99    }
100}
101
102ElfBuilder::ElfBuilder(const std::vector<ModuleSectionDes> &des,
103    const std::vector<ElfSecName> &sections): des_(des), sections_(sections)
104{
105    Initialize();
106    AddShStrTabSection();
107    RemoveNotNeedSection();
108}
109
110void ElfBuilder::Initialize()
111{
112    for (size_t i = 0; i < des_.size(); i++) {
113        des_[i].AddArkStackMapSection();
114    }
115    sectionToAlign_ = {
116        {ElfSecName::TEXT, AOTFileInfo::PAGE_ALIGN},
117        {ElfSecName::STRTAB, 1},
118        {ElfSecName::SYMTAB, AOTFileInfo::DATA_SEC_ALIGN},
119        {ElfSecName::SHSTRTAB, AOTFileInfo::DATA_SEC_ALIGN},
120        {ElfSecName::ARK_STACKMAP, AOTFileInfo::DATA_SEC_ALIGN},
121        {ElfSecName::ARK_FUNCENTRY, AOTFileInfo::DATA_SEC_ALIGN},
122        {ElfSecName::ARK_ASMSTUB, AOTFileInfo::DATA_SEC_ALIGN},
123        {ElfSecName::ARK_MODULEINFO, AOTFileInfo::DATA_SEC_ALIGN},
124    };
125
126    sectionToSegment_ = {
127        {ElfSecName::RODATA, ElfSecName::TEXT},
128        {ElfSecName::RODATA_CST4, ElfSecName::TEXT},
129        {ElfSecName::RODATA_CST8, ElfSecName::TEXT},
130        {ElfSecName::RODATA_CST16, ElfSecName::TEXT},
131        {ElfSecName::RODATA_CST32, ElfSecName::TEXT},
132        {ElfSecName::TEXT, ElfSecName::TEXT},
133        {ElfSecName::STRTAB, ElfSecName::DATA},
134        {ElfSecName::SYMTAB, ElfSecName::DATA},
135        {ElfSecName::SHSTRTAB, ElfSecName::DATA},
136        {ElfSecName::ARK_STACKMAP, ElfSecName::DATA},
137        {ElfSecName::ARK_FUNCENTRY, ElfSecName::DATA},
138        {ElfSecName::ARK_ASMSTUB, ElfSecName::TEXT},
139        {ElfSecName::ARK_MODULEINFO, ElfSecName::DATA},
140    };
141
142    segmentToFlag_ = {
143        {ElfSecName::TEXT, llvm::ELF::PF_X | llvm::ELF::PF_R},
144        {ElfSecName::DATA, llvm::ELF::PF_R},
145    };
146
147    SetLastSection();
148}
149
150void ElfBuilder::RemoveNotNeedSection()
151{
152    const std::map<ElfSecName, std::pair<uint64_t, uint32_t>> &sections = GetFullSecInfo();
153    for (size_t i = 0; i < sections_.size();) {
154        if (sections.find(sections_[i]) == sections.end()) {
155            auto it = sections_.begin() + i;
156            sections_.erase(it);
157            continue;
158        }
159        i++;
160    }
161}
162
163ElfBuilder::~ElfBuilder()
164{
165    shStrTabPtr_ = nullptr;
166}
167
168uint32_t ElfBuilder::GetShIndex(ElfSecName section) const
169{
170    std::set<ElfSecName> secSet(sections_.begin(), sections_.end());
171    uint32_t idx = 1;
172    for (ElfSecName sec : secSet) {
173        if (sec == section) {
174            return idx;
175        }
176        idx++;
177    }
178    return 0;
179}
180
181int ElfBuilder::GetSecNum() const
182{
183    return sections_.size() + 1; // add first empty section.
184}
185
186/*
187ELF Header as follow:
188ELF Header:
189  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
190  Class:                             ELF64
191  Data:                              2's complement, little endian
192  Version:                           1 (current)
193  OS/ABI:                            UNIX - System V
194  ABI Version:                       0
195  Type:                              DYN (Shared object file)
196  Machine:                           Advanced Micro Devices X86-64
197  Version:                           0x4000001
198  Entry point address:               0x0
199  Start of program headers:          16384 (bytes into file)
200  Start of section headers:          64 (bytes into file)
201  Flags:                             0x0
202  Size of this header:               64 (bytes)
203  Size of program headers:           56 (bytes)
204  Number of program headers:         2
205  Size of section headers:           64 (bytes)
206  Number of section headers:         7
207  Section header string table index: 3
208*/
209void ElfBuilder::PackELFHeader(llvm::ELF::Elf64_Ehdr &header, uint32_t version, Triple triple)
210{
211    if (memset_s(reinterpret_cast<void *>(&header), sizeof(llvm::ELF::Elf64_Ehdr), 0,
212                 sizeof(llvm::ELF::Elf64_Ehdr)) != EOK) {
213        UNREACHABLE();
214    }
215    header.e_ident[llvm::ELF::EI_MAG0] = llvm::ELF::ElfMagic[llvm::ELF::EI_MAG0];
216    header.e_ident[llvm::ELF::EI_MAG1] = llvm::ELF::ElfMagic[llvm::ELF::EI_MAG1];
217    header.e_ident[llvm::ELF::EI_MAG2] = llvm::ELF::ElfMagic[llvm::ELF::EI_MAG2];
218    header.e_ident[llvm::ELF::EI_MAG3] = llvm::ELF::ElfMagic[llvm::ELF::EI_MAG3];
219    header.e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64;
220    header.e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB;
221    header.e_ident[llvm::ELF::EI_VERSION] = 1;
222
223    header.e_type = llvm::ELF::ET_DYN;
224    switch (triple) {
225        case Triple::TRIPLE_AMD64:
226            header.e_machine = llvm::ELF::EM_X86_64;
227            break;
228        case Triple::TRIPLE_ARM32:
229            header.e_machine = llvm::ELF::EM_ARM;
230            break;
231        case Triple::TRIPLE_AARCH64:
232            header.e_machine = llvm::ELF::EM_AARCH64;
233            break;
234        default:
235            UNREACHABLE();
236            break;
237    }
238    header.e_version = version;
239    // start of section headers
240    header.e_shoff = sizeof(llvm::ELF::Elf64_Ehdr);
241    // size of ehdr
242    header.e_ehsize = sizeof(llvm::ELF::Elf64_Ehdr);
243    // size of section headers
244    header.e_shentsize = sizeof(llvm::ELF::Elf64_Shdr);
245    // number of section headers
246    header.e_shnum = GetSecNum();
247    // section header string table index
248    header.e_shstrndx = static_cast<llvm::ELF::Elf64_Half>(GetShIndex(ElfSecName::SHSTRTAB));
249    // section header stub sec info index
250    header.e_flags = static_cast<llvm::ELF::Elf64_Word>(GetShIndex(ElfSecName::ARK_MODULEINFO));
251    // phr
252    header.e_phentsize = sizeof(llvm::ELF::Elf64_Phdr);
253    header.e_phnum = GetSegmentNum();
254}
255
256int ElfBuilder::GetSegmentNum() const
257{
258    const std::map<ElfSecName, std::pair<uint64_t, uint32_t>> &sections = GetFullSecInfo();
259    std::set<ElfSecName> segments;
260    for (auto &s: sections) {
261        ElfSection section = ElfSection(s.first);
262        if (!section.ShouldDumpToAOTFile()) {
263            continue;
264        }
265        auto it = sectionToSegment_.find(s.first);
266        ASSERT(it != sectionToSegment_.end());
267        ElfSecName name = it->second;
268        segments.insert(name);
269    }
270    return segments.size();
271}
272
273void ElfBuilder::SetLastSection()
274{
275    const std::map<ElfSecName, std::pair<uint64_t, uint32_t>> &sections = GetFullSecInfo();
276    for (auto &s: sections) {
277        ElfSection section = ElfSection(s.first);
278        if (!section.ShouldDumpToAOTFile()) {
279            continue;
280        }
281        auto it = sectionToSegment_.find(s.first);
282        ASSERT(it != sectionToSegment_.end());
283        ElfSecName name = it->second;
284        if (name == ElfSecName::TEXT) {
285            lastCodeSection = std::max(lastCodeSection, s.first);
286        } else {
287            lastDataSection = std::max(lastDataSection, s.first);
288        }
289    }
290}
291
292llvm::ELF::Elf64_Word ElfBuilder::FindShName(std::string name, uintptr_t strTabPtr, int strTabSize)
293{
294    llvm::ELF::Elf64_Word ans = -1;
295    int len = static_cast<int>(name.size());
296    if (strTabSize < len + 1) {
297        return ans;
298    }
299    LOG_ECMA(DEBUG) << "  FindShName name:" << name.c_str() << std::endl;
300    for (int i = 0; i < strTabSize - len + 1; ++i) {
301        char *dst = reinterpret_cast<char *>(strTabPtr) + i;
302        if (name.compare(dst) == 0) {
303            return i;
304        }
305    }
306    return ans;
307}
308
309std::pair<uint64_t, uint32_t> ElfBuilder::FindShStrTab() const
310{
311    const std::map<ElfSecName, std::pair<uint64_t, uint32_t>> &sections = GetFullSecInfo();
312    uint64_t shStrTabAddr = 0;
313    uint32_t shStrTabSize = 0;
314    for (auto &s: sections) {
315        uint32_t curSecSize = des_[ShStrTableModuleDesIndex].GetSecSize(s.first);
316        uint64_t curSecAddr = des_[ShStrTableModuleDesIndex].GetSecAddr(s.first);
317        if (s.first == ElfSecName::SHSTRTAB) {
318            shStrTabSize = curSecSize;
319            shStrTabAddr = curSecAddr;
320            break;
321        }
322    }
323    return std::make_pair(shStrTabAddr, shStrTabSize);
324}
325
326void ElfBuilder::AllocateShdr(std::unique_ptr<llvm::ELF::Elf64_Shdr []> &shdr, const uint32_t &secNum)
327{
328    shdr = std::make_unique<llvm::ELF::Elf64_Shdr []>(secNum);
329    if (memset_s(reinterpret_cast<void *>(&shdr[0]),
330                 sizeof(llvm::ELF::Elf64_Shdr),
331                 0,
332                 sizeof(llvm::ELF::Elf64_Shdr)) != EOK) {
333        UNREACHABLE();
334    }
335}
336
337llvm::ELF::Elf64_Off ElfBuilder::ComputeEndAddrOfShdr(const uint32_t &secNum) const
338{
339    llvm::ELF::Elf64_Off curSecOffset = sizeof(llvm::ELF::Elf64_Ehdr) + secNum * sizeof(llvm::ELF::Elf64_Shdr);
340    curSecOffset = AlignUp(curSecOffset, PageSize()); // not pagesize align will cause performance degradation
341    return curSecOffset;
342}
343
344ElfSecName ElfBuilder::GetSegmentName(const ElfSecName &secName) const
345{
346    auto it = sectionToSegment_.find(secName);
347    ASSERT(it != sectionToSegment_.end());
348    ElfSecName segName = it->second;
349    return segName;
350}
351
352void ElfBuilder::MergeTextSections(std::ofstream &file,
353                                   std::vector<ModuleSectionDes::ModuleRegionInfo> &moduleInfo,
354                                   llvm::ELF::Elf64_Off &curSecOffset)
355{
356    for (size_t i = 0; i < des_.size(); ++i) {
357        ModuleSectionDes &des = des_[i];
358        ModuleSectionDes::ModuleRegionInfo &curInfo = moduleInfo[i];
359        uint32_t curSecSize = des.GetSecSize(ElfSecName::TEXT);
360        uint64_t curSecAddr = des.GetSecAddr(ElfSecName::TEXT);
361        curSecOffset = AlignUp(curSecOffset, AOTFileInfo::PAGE_ALIGN);
362        file.seekp(curSecOffset);
363        auto curModuleSec = des.GetSectionsInfo();
364        uint64_t rodataAddrBeforeText = 0;
365        uint32_t rodataSizeBeforeText = 0;
366        uint64_t rodataAddrAfterText = 0;
367        uint32_t rodataSizeAfterText = 0;
368        std::tie(rodataAddrBeforeText, rodataSizeBeforeText, rodataAddrAfterText, rodataSizeAfterText) =
369            des.GetMergedRODataAddrAndSize(curSecAddr);
370        if (rodataSizeBeforeText != 0) {
371            file.write(reinterpret_cast<char *>(rodataAddrBeforeText), rodataSizeBeforeText);
372            curInfo.rodataSizeBeforeText = rodataSizeBeforeText;
373            curSecOffset += rodataSizeBeforeText;
374            curSecOffset = AlignUp(curSecOffset, AOTFileInfo::TEXT_SEC_ALIGN);
375            file.seekp(curSecOffset);
376        }
377        stubTextOffset_.emplace_back(curSecOffset);
378        file.write(reinterpret_cast<char *>(curSecAddr), curSecSize);
379        curInfo.textSize = curSecSize;
380        curSecOffset += curSecSize;
381        if (rodataSizeAfterText != 0) {
382            curSecOffset = AlignUp(curSecOffset, AOTFileInfo::DATA_SEC_ALIGN);
383            file.seekp(curSecOffset);
384            file.write(reinterpret_cast<char *>(rodataAddrAfterText), rodataSizeAfterText);
385            curInfo.rodataSizeAfterText = rodataSizeAfterText;
386            curSecOffset += rodataSizeAfterText;
387        }
388    }
389}
390
391void ElfBuilder::MergeStrtabSections(std::ofstream &file,
392                                     std::vector<ModuleSectionDes::ModuleRegionInfo> &moduleInfo,
393                                     llvm::ELF::Elf64_Off &curSecOffset)
394{
395    for (size_t i = 0; i < des_.size(); ++i) {
396        ModuleSectionDes &des = des_[i];
397        ModuleSectionDes::ModuleRegionInfo &curInfo = moduleInfo[i];
398        uint32_t curSecSize = des.GetSecSize(ElfSecName::STRTAB);
399        uint64_t curSecAddr = des.GetSecAddr(ElfSecName::STRTAB);
400        curInfo.strtabSize = curSecSize;
401        file.write(reinterpret_cast<char *>(curSecAddr), curSecSize);
402        curSecOffset += curSecSize;
403        if (des.HasAsmStubStrTab()) {
404            uint32_t asmStubStrTabSize = AddAsmStubStrTab(file, des.GetAsmStubELFInfo());
405            curSecOffset += asmStubStrTabSize;
406            curInfo.strtabSize += asmStubStrTabSize;
407        }
408    }
409}
410
411void ElfBuilder::MergeSymtabSections(std::ofstream &file,
412                                     std::vector<ModuleSectionDes::ModuleRegionInfo> &moduleInfo,
413                                     llvm::ELF::Elf64_Off &curSecOffset,
414                                     llvm::ELF::Elf64_Off &asmStubOffset)
415{
416    using Elf64_Sym = llvm::ELF::Elf64_Sym;
417    uint32_t strTabSize = 0;
418    uint32_t textSecIndex = GetShIndex(ElfSecName::ARK_ASMSTUB);
419    for (size_t i = 0; i < des_.size(); ++i) {
420        ModuleSectionDes &des = des_[i];
421        ModuleSectionDes::ModuleRegionInfo &curInfo = moduleInfo[i];
422        uint32_t curSecSize = des.GetSecSize(ElfSecName::SYMTAB);
423        uint64_t curSecAddr = des.GetSecAddr(ElfSecName::SYMTAB);
424        curInfo.symtabSize = curSecSize;
425        file.write(reinterpret_cast<char *>(curSecAddr), curSecSize);
426        curSecOffset += curSecSize;
427        strTabSize += des.GetSecSize(ElfSecName::STRTAB);
428        if (des.HasAsmStubStrTab()) {
429            const std::vector<std::pair<std::string, uint32_t>> &asmStubELFInfo = des.GetAsmStubELFInfo();
430            ASSERT(asmStubELFInfo.size() > 0);
431            uint32_t asmStubSymTabNum = asmStubELFInfo.size() - 1;
432            std::unique_ptr<Elf64_Sym []> syms = std::make_unique<Elf64_Sym []>(asmStubSymTabNum);
433            ASSERT(asmStubStrName_.size() == asmStubSymTabNum);
434            for (size_t idx = 0; idx < asmStubSymTabNum; ++idx) {
435                Elf64_Sym &sym = syms[idx];
436                sym.setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_FUNC);
437                sym.st_shndx = static_cast<uint16_t>(textSecIndex);
438                sym.st_value = asmStubELFInfo[idx].second + asmStubOffset;
439                sym.st_name = asmStubStrName_[idx];
440                sym.st_name += strTabSize;
441                sym.st_other = llvm::ELF::STV_DEFAULT;
442                sym.st_size = asmStubELFInfo[idx + 1].second - asmStubELFInfo[idx].second;
443            }
444            uint32_t asmStubSymTabSize = asmStubSymTabNum * sizeof(llvm::ELF::Elf64_Sym);
445            file.write(reinterpret_cast<char *>(syms.get()), asmStubSymTabSize);
446            curInfo.symtabSize += asmStubSymTabSize;
447            curSecOffset += asmStubSymTabSize;
448        }
449    }
450}
451
452void ElfBuilder::MergeArkStackMapSections(std::ofstream &file,
453                                          std::vector<ModuleSectionDes::ModuleRegionInfo> &moduleInfo,
454                                          llvm::ELF::Elf64_Off &curSecOffset)
455{
456    for (size_t i = 0; i < des_.size(); ++i) {
457        ModuleSectionDes &des = des_[i];
458        ModuleSectionDes::ModuleRegionInfo &curInfo = moduleInfo[i];
459        uint32_t curSecSize = des.GetSecSize(ElfSecName::ARK_STACKMAP);
460        uint64_t curSecAddr = des.GetSecAddr(ElfSecName::ARK_STACKMAP);
461        uint32_t index = des.GetStartIndex();
462        uint32_t cnt = des.GetFuncCount();
463        curInfo.startIndex = index;
464        curInfo.funcCount = cnt;
465        curInfo.stackMapSize = curSecSize;
466        file.write(reinterpret_cast<char *>(curSecAddr), curSecSize);
467        curSecOffset += curSecSize;
468    }
469}
470
471void ElfBuilder::FixSymtab(llvm::ELF::Elf64_Shdr* shdr)
472{
473    using Elf64_Sym = llvm::ELF::Elf64_Sym;
474    ASSERT(stubTextOffset_.size() == des_.size());
475
476    uint32_t secNum = static_cast<uint32_t>(GetSecNum());
477    uint32_t shStrTabIndex = GetShIndex(ElfSecName::SHSTRTAB);
478    uint32_t strTabIndex = GetShIndex(ElfSecName::STRTAB);
479    uint32_t textSecIndex = GetShIndex(ElfSecName::TEXT);
480
481    uint32_t strTabSize = 0;
482    int firstGlobal = -1;
483    uint32_t count = 0;
484
485    for (size_t idx = 0; idx < des_.size(); ++idx) {
486        uint32_t secSize = des_[idx].GetSecSize(ElfSecName::SYMTAB);
487        uint64_t secAddr = des_[idx].GetSecAddr(ElfSecName::SYMTAB);
488        Elf64_Sym *syms = reinterpret_cast<Elf64_Sym*>(secAddr);
489        size_t n = secSize / sizeof(Elf64_Sym);
490        for (size_t i = 0; i < n; ++i) {
491            Elf64_Sym* sy = &syms[i];
492            if (sy->getBinding() == llvm::ELF::STB_GLOBAL && firstGlobal == -1) {
493                firstGlobal = static_cast<int>(count);
494            }
495            if (sy->getType() == llvm::ELF::STT_SECTION) {
496                sy->st_shndx = static_cast<uint16_t>(shStrTabIndex);
497            } else if (sy->getType() == llvm::ELF::STT_FUNC) {
498                sy->st_shndx = static_cast<uint16_t>(textSecIndex);
499                sy->st_value += stubTextOffset_[idx];
500            }
501            if (sy->st_shndx > secNum) {
502                sy->st_shndx = 0;
503            }
504            sy->st_name += strTabSize;
505            count++;
506        }
507        strTabSize += des_[idx].GetSecSize(ElfSecName::STRTAB);
508    }
509    shdr->sh_info = static_cast<uint32_t>(firstGlobal);
510    shdr->sh_link = strTabIndex;
511}
512
513/*
514
515section of aot.an layout as follows:
516There are 7 section headers, starting at offset 0x40:
517
518Section Headers:
519  [Nr] Name              Type             Address           Offset    Size              EntSize          Flags  Link  Info  Align
520  [ 0]                   NULL             0000000000000000  00000000  0000000000000000  0000000000000000           0     0     0
521  [ 1] .text             PROGBITS         0000000000001000  00001000  0000000000000f61  0000000000000000  AX       0     0     16
522  [ 2] .strtab           STRTAB           0000000000002000  00002000  0000000000000187  0000000000000000   A       0     0     1
523  [ 3] .symtab           SYMTAB           0000000000002188  00002188  00000000000001c8  0000000000000018   A       1     0     8
524  [ 4] .shstrtab         STRTAB           0000000000002350  00002350  000000000000003f  0000000000000000   A       0     0     8
525  [ 5] .ark_funcentry    PROGBITS         0000000000002390  00002390  00000000000006c0  0000000000000000   A       0     0     8
526  [ 6] .ark_stackmaps    PROGBITS         0000000000002a50  00002a50  000000000000070e  0000000000000000   A       0     0     8
527
528section of stub.an layout as follows:
529There are 7 section headers, starting at offset 0x40:
530
531  [Nr] Name              Type             Address           Offset    Size              EntSize          Flags  Link  Info  Align
532  [ 0]                   NULL             0000000000000000  00000000  0000000000000000  0000000000000000           0     0     0
533  [ 1] .text             PROGBITS         0000000000001000  00001000  000000000008225e  0000000000000000  AX       0     0     16
534  [ 2] .ark_asmstub      PROGBITS         0000000000083260  00083260  0000000000002dc0  0000000000000000  AX       0     0     8
535  [ 3] .shstrtab         STRTAB           0000000000087000  00087000  000000000000004c  0000000000000000   A       0     0     8
536  [ 4] .ark_funcentry    PROGBITS         0000000000087050  00087050  0000000000023ca0  0000000000000000   A       0     0     8
537  [ 5] .ark_stackmaps    PROGBITS         00000000000aacf0  000aacf0  0000000000011e90  0000000000000000   A       0     0     8
538  [ 6] .ark_moduleinfo   PROGBITS         00000000000bcb80  000bcb80  000000000000003c  0000000000000000   A       0     0     8
539
540Key to Flags:
541  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
542  L (link order), O (extra OS processing required), G (group), T (TLS),
543  C (compressed), x (unknown), o (OS specific), E (exclude),
544  D (mbind), l (large), p (processor specific)
545*/
546void ElfBuilder::PackELFSections(std::ofstream &file)
547{
548    uint32_t moduleNum = des_.size();
549    const std::map<ElfSecName, std::pair<uint64_t, uint32_t>> &sections = GetFullSecInfo();
550    uint32_t secNum = sections.size() + 1; // 1 : section id = 0 is null section
551    std::unique_ptr<llvm::ELF::Elf64_Shdr []> shdr;
552    AllocateShdr(shdr, secNum);
553    std::vector<ModuleSectionDes::ModuleRegionInfo> moduleInfo(moduleNum);
554    llvm::ELF::Elf64_Off curSecOffset = ComputeEndAddrOfShdr(secNum);
555    file.seekp(curSecOffset);
556
557    int i = static_cast<int>(GetShIndex(ElfSecName::TEXT));
558    auto shStrTab = FindShStrTab();
559
560    for (auto const &[secName, secInfo] : sections) {
561        auto &curShdr = shdr[i];
562        ElfSection section = ElfSection(secName);
563        if (!section.ShouldDumpToAOTFile()) {
564            continue;
565        }
566        curShdr.sh_addralign = sectionToAlign_[secName];
567        curSecOffset = AlignUp(curSecOffset, curShdr.sh_addralign);
568        file.seekp(curSecOffset);
569        ElfSecName segName = GetSegmentName(secName);
570        segments_.insert(segName);
571        std::string secNameStr = ModuleSectionDes::GetSecName(secName);
572        // text section address needs 16 bytes alignment
573        if (secName == ElfSecName::TEXT) {
574            curSecOffset = AlignUp(curSecOffset, AOTFileInfo::PAGE_ALIGN);
575            file.seekp(curSecOffset);
576        }
577        llvm::ELF::Elf64_Word shName = FindShName(secNameStr, shStrTab.first, shStrTab.second);
578        ASSERT(shName != static_cast<llvm::ELF::Elf64_Word>(-1));
579        curShdr.sh_name = shName;
580        curShdr.sh_type = section.Type();
581        curShdr.sh_flags = section.Flag();
582        curShdr.sh_addr = curSecOffset;
583        curShdr.sh_offset = static_cast<uint64_t>(curSecOffset);
584        curShdr.sh_info = 0;
585        curShdr.sh_link = static_cast<uint32_t>(section.Link());
586        sectionToFileOffset_[secName] = static_cast<uintptr_t>(file.tellp());
587        switch (secName) {
588            case ElfSecName::ARK_MODULEINFO: {
589                uint32_t curSecSize = sizeof(ModuleSectionDes::ModuleRegionInfo) * moduleInfo.size();
590                file.write(reinterpret_cast<char *>(moduleInfo.data()), curSecSize);
591                curSecOffset += curSecSize;
592                curShdr.sh_size = curSecSize;
593                break;
594            }
595            case ElfSecName::TEXT: {
596                uint32_t curSize = curSecOffset;
597                MergeTextSections(file, moduleInfo, curSecOffset);
598                curShdr.sh_size = curSecOffset - curSize;
599                break;
600            }
601            case ElfSecName::ARK_STACKMAP: {
602                uint32_t curSize = curSecOffset;
603                MergeArkStackMapSections(file, moduleInfo, curSecOffset);
604                curShdr.sh_size = curSecOffset - curSize;
605                break;
606            }
607            case ElfSecName::STRTAB: {
608                uint32_t curSize = curSecOffset;
609                MergeStrtabSections(file, moduleInfo, curSecOffset);
610                curShdr.sh_size = curSecOffset - curSize;
611                break;
612            }
613            case ElfSecName::SYMTAB: {
614                FixSymtab(&curShdr);
615                uint32_t curSize = curSecOffset;
616                uint32_t asmSecIndex = GetShIndex(ElfSecName::ARK_ASMSTUB);
617                MergeSymtabSections(file, moduleInfo, curSecOffset, shdr[asmSecIndex].sh_offset);
618                curShdr.sh_size = curSecOffset - curSize;
619                break;
620            }
621            case ElfSecName::SHSTRTAB:
622            case ElfSecName::ARK_FUNCENTRY:
623            case ElfSecName::ARK_ASMSTUB: {
624                uint32_t curSecSize = des_[FullSecIndex].GetSecSize(secName);
625                uint64_t curSecAddr = des_[FullSecIndex].GetSecAddr(secName);
626                file.write(reinterpret_cast<char *>(curSecAddr), curSecSize);
627                curSecOffset += curSecSize;
628                curShdr.sh_size = curSecSize;
629                break;
630            }
631            default: {
632                LOG_ECMA(FATAL) << "this section should not dump to an file";
633                break;
634            }
635        }
636        if (secName == lastDataSection || secName == lastCodeSection) {
637            curSecOffset = AlignUp(curSecOffset, PageSize());
638            file.seekp(curSecOffset);
639        }
640        curShdr.sh_entsize = static_cast<uint64_t>(section.Entsize());
641        sectionToShdr_[secName] = curShdr;
642        LOG_COMPILER(DEBUG) << "  shdr[i].sh_entsize " << std::hex << curShdr.sh_entsize << std::endl;
643        ++i;
644    }
645    uint32_t secEnd = static_cast<uint32_t>(file.tellp());
646    file.seekp(sizeof(llvm::ELF::Elf64_Ehdr));
647    file.write(reinterpret_cast<char *>(shdr.get()), secNum * sizeof(llvm::ELF::Elf64_Shdr));
648    file.seekp(secEnd);
649}
650
651unsigned ElfBuilder::GetPFlag(ElfSecName segment) const
652{
653    return segmentToFlag_.at(segment);
654}
655
656/*
657segment layout as follows:
658An Elf file
659Entry point 0x0
660There are 2 program headers, starting at offset 16384
661
662Program Headers:
663  Type           Offset             VirtAddr           PhysAddr           FileSiz            MemSiz              Flags  Align
664  LOAD           0x0000000000001000 0x0000000000001000 0x0000000000001000 0x0000000000000f61 0x0000000000001000  R E    0x1000
665  LOAD           0x0000000000002000 0x0000000000002000 0x0000000000002000 0x000000000000115e 0x0000000000002000  R      0x1000
666
667 Section to Segment mapping:
668  Segment Sections...
669   00     .text
670   01     .strtab .symtab .shstrtab .ark_funcentry .ark_stackmaps
671------------------------------------------------------------------------------------------------------------------------------
672Stub Elf file
673Entry point 0x0
674There are 2 program headers, starting at offset 770048
675
676Program Headers:
677  Type           Offset             VirtAddr           PhysAddr           FileSiz            MemSiz              Flags  Align
678  LOAD           0x0000000000001000 0x0000000000001000 0x0000000000001000 0x0000000000085020 0x0000000000086000  R E    0x1000
679  LOAD           0x0000000000087000 0x0000000000087000 0x0000000000087000 0x0000000000035bbc 0x0000000000036000  R      0x1000
680
681 Section to Segment mapping:
682  Segment Sections...
683   00     .text .ark_asmstub
684   01     .shstrtab .ark_funcentry .ark_stackmaps .ark_moduleinfo
685*/
686void ElfBuilder::PackELFSegment(std::ofstream &file)
687{
688    llvm::ELF::Elf64_Off e_phoff = static_cast<uint64_t>(file.tellp());
689    long phoff = (long)offsetof(struct llvm::ELF::Elf64_Ehdr, e_phoff);
690    // write Elf32_Off e_phoff
691    file.seekp(phoff);
692    file.write(reinterpret_cast<char *>(&e_phoff), sizeof(e_phoff));
693    file.seekp(static_cast<long>(e_phoff));
694
695    int segNum = GetSegmentNum();
696    auto phdrs = std::make_unique<llvm::ELF::Elf64_Phdr []>(segNum);
697    std::map<ElfSecName, llvm::ELF::Elf64_Off> segmentToMaxOffset;
698    std::map<ElfSecName, llvm::ELF::Elf64_Off> segmentToMaxAddress;
699    std::set<ElfSecName> segments;
700    // SecName -> addr & size
701    const std::map<ElfSecName, std::pair<uint64_t, uint32_t>> &sections = GetFullSecInfo();
702    llvm::ELF::Elf64_Off offset = e_phoff;
703    for (auto &s: sections) {
704        ElfSection section = ElfSection(s.first);
705        if (!section.ShouldDumpToAOTFile()) {
706            continue;
707        }
708        auto it = sectionToSegment_.find(s.first);
709        ASSERT(it != sectionToSegment_.end());
710        ElfSecName segName = it->second;
711        segments.insert(segName);
712        if (segmentToMaxOffset.find(segName) == segmentToMaxOffset.end()) {
713            segmentToMaxOffset[segName] = 0;
714        }
715        segmentToMaxOffset[segName] =
716            std::max(segmentToMaxOffset[segName], sectionToShdr_[s.first].sh_offset + sectionToShdr_[s.first].sh_size);
717        segmentToMaxAddress[segName] =
718            std::max(segmentToMaxAddress[segName], sectionToShdr_[s.first].sh_addr + sectionToShdr_[s.first].sh_size);
719        offset = std::min(offset, sectionToShdr_[s.first].sh_offset);
720    }
721    int phdrIndex = 0;
722    llvm::ELF::Elf64_Addr addr = offset;
723    for (auto &it: segments) {
724        ElfSecName name = it;
725        phdrs[phdrIndex].p_align = PageSize();
726        phdrs[phdrIndex].p_type = llvm::ELF::PT_LOAD;
727        phdrs[phdrIndex].p_flags = GetPFlag(name);
728        offset = AlignUp(offset, PageSize());
729        phdrs[phdrIndex].p_offset = offset;
730        phdrs[phdrIndex].p_vaddr = addr % phdrs[phdrIndex].p_align == 0 ?
731            addr : (addr / phdrs[phdrIndex].p_align + 1) * phdrs[phdrIndex].p_align;
732        phdrs[phdrIndex].p_paddr = phdrs[phdrIndex].p_vaddr;
733
734        phdrs[phdrIndex].p_filesz = segmentToMaxOffset[name] - phdrs[phdrIndex].p_offset;
735        phdrs[phdrIndex].p_memsz = segmentToMaxAddress[name] - phdrs[phdrIndex].p_vaddr;
736        phdrs[phdrIndex].p_memsz = AlignUp(phdrs[phdrIndex].p_memsz, PageSize());
737        addr = phdrs[phdrIndex].p_vaddr + phdrs[phdrIndex].p_memsz;
738        offset += phdrs[phdrIndex].p_filesz;
739        ++phdrIndex;
740    }
741    file.write(reinterpret_cast<char *>(phdrs.get()), sizeof(llvm::ELF::Elf64_Phdr) * segNum);
742}
743}  // namespace panda::ecmascript
744