1/*
2 * Copyright (c) 2024 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#include "ecmascript/compiler/aot_file/elf_checker.h"
16#include <fstream>
17#include <type_traits>
18
19namespace panda::ecmascript {
20/* Check Aligned */
21template <typename AlignType> static bool CheckAlign(void* alignPtr, size_t offset = 0)
22{
23    if (ALLOW_UNALIGNED != 0) {
24        return true;
25    }
26    size_t ptrPos = reinterpret_cast<size_t>(alignPtr) + offset;
27    if ((ptrPos & (alignof(AlignType) - 1)) == 0) {
28        return true;
29    } else {
30        return false;
31    }
32}
33
34/* Swap LSB and MSB */
35template <typename IntType> static void ByteSwap(IntType& x)
36{
37    static constexpr unsigned int iByteMuskOff0 = 0xffu;
38    static constexpr unsigned int iByteMuskOff1 = iByteMuskOff0 << 8;
39    static constexpr unsigned int iByteMuskOff2 = iByteMuskOff1 << 8;
40    static constexpr unsigned int iByteMuskOff3 = iByteMuskOff2 << 8;
41    static constexpr unsigned long long llByteMuskOff0 = 0xffuLL;
42    static constexpr unsigned long long llByteMuskOff1 = llByteMuskOff0 << 8;
43    static constexpr unsigned long long llByteMuskOff2 = llByteMuskOff1 << 8;
44    static constexpr unsigned long long llByteMuskOff3 = llByteMuskOff2 << 8;
45    static constexpr unsigned long long llByteMuskOff4 = llByteMuskOff3 << 8;
46    static constexpr unsigned long long llByteMuskOff5 = llByteMuskOff4 << 8;
47    static constexpr unsigned long long llByteMuskOff6 = llByteMuskOff5 << 8;
48    static constexpr unsigned long long llByteMuskOff7 = llByteMuskOff6 << 8;
49    static constexpr unsigned int move1 = 8; // 8: move 8 bits
50    static constexpr unsigned int move2 = move1 + 8 * 2; // 8: skip one pair
51    static constexpr unsigned int move3 = move2 + 8 * 2; // 8: skip another pair
52    static constexpr unsigned int move4 = move3 + 8 * 2; // 8: skip another pair
53    switch (sizeof(IntType)) {
54        case sizeof(int8_t):
55            return;
56        case sizeof(int16_t):
57            x = IntType((((x) >> move1) & iByteMuskOff0) |
58                (((x) & iByteMuskOff0) << move1)); // 8: position to change between LSB and MSB
59            return;
60        case sizeof(int32_t):
61            x = ((((x) & iByteMuskOff3) >> move2) | (((x) & iByteMuskOff2) >> move1) |
62                (((x) & iByteMuskOff1) << move1) | (((x) & iByteMuskOff0) << move2));
63            return;
64        case sizeof(int64_t):
65            x = ((((x) & llByteMuskOff7) >> move4) | (((x) & llByteMuskOff6) >> move3) |
66                (((x) & llByteMuskOff5) >> move2) | (((x) & llByteMuskOff4) >> move1) |
67                (((x) & llByteMuskOff3) << move1) | (((x) & llByteMuskOff2) << move2) |
68                (((x) & llByteMuskOff1) << move3) | (((x) & llByteMuskOff0) << move4));
69            return;
70        default:
71            return;
72    }
73}
74
75/* This function requires copying memory. Avoid using it if MemMap is avalible. */
76ElfChecker::ElfChecker(const void* data, int len) : elfLen_(len), elfErrorCode_(0), fromMmap_(false)
77{
78    if (len <= 0 || data == nullptr) {
79        elfData_ = nullptr;
80        return;
81    }
82    elfData_ = new char[len];
83    if (memcpy_s(elfData_, len, data, len) != 0) {
84        delete[] elfData_;
85        elfData_ = nullptr;
86        return;
87    }
88}
89
90ElfChecker::ElfChecker(const std::string& path) : elfErrorCode_(0), fromMmap_(false)
91{
92    if (!panda::ecmascript::FileExist(path.c_str())) {
93        elfData_ = nullptr;
94        elfLen_ = 0;
95        return;
96    }
97    std::ifstream file(path, std::ios::binary | std::ios::ate);
98    if (file.is_open() == false) {
99        elfData_ = nullptr;
100        elfLen_ = 0;
101        return;
102    }
103    elfLen_ = static_cast<size_t>(file.tellg());
104    file.seekg(0, std::ios::beg);
105    if (elfLen_ <= 0) {
106        elfData_ = nullptr;
107        return;
108    }
109    elfData_ = new char[elfLen_];
110    file.read(elfData_, elfLen_);
111    return;
112}
113
114ElfChecker::ElfChecker(const MemMap& mem) : elfErrorCode_(0), fromMmap_(true)
115{
116    elfData_ = reinterpret_cast<char*>(mem.GetMem());
117    elfLen_ = mem.GetSize();
118    if (elfLen_ == 0) {
119        elfData_ = nullptr;
120    }
121}
122
123bool ElfChecker::CheckValidElf()
124{
125    if (elfData_ == nullptr || elfLen_ <= 0) {
126        return false;
127    }
128    Elf* elf = ElfMemory(elfData_, elfLen_);
129    if (elf == nullptr || elf->kind == ELF_KIND_NONE || CheckIfError() == false) {
130        ElfRelease(elf);
131        return false;
132    }
133    int release_num = ElfRelease(elf);
134    if (release_num != 0 || CheckIfError() == false) {
135        return false;
136    }
137    return true;
138}
139
140int ElfChecker::CheckIfError() const
141{
142    if (elfErrorCode_ != ELF_ERR_NOERROR) {
143        return false;
144    }
145    return true;
146}
147
148void ElfChecker::SetErrorCode(int value)
149{
150    if (value >= 0 && value < ELF_ERR_NUM) {
151        elfErrorCode_ = value;
152    } else {
153        elfErrorCode_ = ELF_ERR_UNKNOWN_ERROR;
154    }
155}
156
157ElfChecker::Elf* ElfChecker::ElfAllocate(
158    void* mapAddress, int64_t offset, size_t maxsize, ElfCommand cmd, Elf* parent, ElfKind kind, size_t extra)
159{
160    size_t alloc_size = sizeof(Elf) + extra;
161    Elf* result = reinterpret_cast<Elf*>(new char[alloc_size]());
162    if (result == nullptr) {
163        SetErrorCode(ELF_ERR_NOMEM);
164    } else {
165        result->kind = kind;
166        result->refCount = 1;
167        result->cmd = cmd;
168        result->startOffset = offset;
169        result->maxSize = maxsize;
170        result->mapAddress = mapAddress;
171        result->parent = parent;
172    }
173
174    return result;
175}
176
177ElfChecker::ElfKind ElfChecker::GetElfKind(void* buf) const
178{
179    /* Could be an ELF file.  */
180    int eclass = (int)(reinterpret_cast<unsigned char*>(buf))[llvm::ELF::EI_CLASS];
181    int data = (int)(reinterpret_cast<unsigned char*>(buf))[llvm::ELF::EI_DATA];
182    int version = (int)(reinterpret_cast<unsigned char*>(buf))[llvm::ELF::EI_VERSION];
183
184    if (eclass > llvm::ELF::ELFCLASSNONE && eclass <= llvm::ELF::ELFCLASS64 && data > llvm::ELF::ELFDATANONE &&
185        data <= llvm::ELF::ELFDATA2MSB && version == llvm::ELF::EV_CURRENT) {
186        return ELF_KIND_ELF;
187    }
188
189    /* We do not know this file type.  */
190    return ELF_KIND_NONE;
191}
192
193/* Get the number of sections from the ELF header.  */
194template <typename FromElfHeader, typename FromElfSectionHeader, typename SizeType>
195size_t ElfChecker::GetSectionHeaderNum(
196    FromElfHeader* ehdr, size_t maxsize, unsigned char* eIdent, void* mapAddress, int64_t offset)
197{
198    size_t result = ehdr->e_shnum;
199    if (SECUREC_UNLIKELY(result == 0) && ehdr->e_shoff != 0) {
200        if (SECUREC_UNLIKELY(ehdr->e_shoff >= maxsize) ||
201            SECUREC_UNLIKELY(maxsize - ehdr->e_shoff < sizeof(FromElfSectionHeader))) {
202            return 0;
203        }
204        SizeType size;
205        FromElfSectionHeader* dest =
206            reinterpret_cast<FromElfSectionHeader*>(reinterpret_cast<char*>(mapAddress) + ehdr->e_shoff + offset);
207        if (eIdent[llvm::ELF::EI_DATA] == ELF_BYTEORDER && CheckAlign<FromElfSectionHeader>(dest) == true) {
208            /* We can directly access the memory.  */
209            size = dest->sh_size;
210        } else {
211            char* memSrc =
212                reinterpret_cast<char*>(mapAddress) + ehdr->e_shoff + offset + offsetof(FromElfSectionHeader, sh_size);
213            if (memcpy_s(&size, sizeof(size), memSrc, sizeof(SizeType)) != 0) {
214                SetErrorCode(ELF_ERR_NOMEM);
215                return 0;
216            }
217
218            if (eIdent[llvm::ELF::EI_DATA] != ELF_BYTEORDER) {
219                ByteSwap(size);
220            }
221            if (size > ~((llvm::ELF::Elf64_Word)0)) {
222                /* Invalid value, it is too large.  */
223                SetErrorCode(ELF_ERR_INVALID_ELF);
224                return 0;
225            }
226        }
227        result = size;
228    }
229
230    /* If the section headers were truncated, pretend none were there.
231     */
232    if (ehdr->e_shoff > maxsize || maxsize - ehdr->e_shoff < sizeof(FromElfSectionHeader) * result) {
233        result = 0;
234    }
235    return result;
236}
237
238/* Get Section Header Number */
239size_t ElfChecker::GetShnum(void* mapAddress, unsigned char* eIdent, int64_t offset, size_t maxsize)
240{
241    union {
242        llvm::ELF::Elf32_Ehdr* e32;
243        llvm::ELF::Elf64_Ehdr* e64;
244        void* p;
245    } ehdrPtr;
246    union {
247        llvm::ELF::Elf32_Ehdr e32;
248        llvm::ELF::Elf64_Ehdr e64;
249    } ehdrMem;
250    bool is32 = eIdent[llvm::ELF::EI_CLASS] == llvm::ELF::ELFCLASS32;
251
252    if ((is32 && maxsize < sizeof(llvm::ELF::Elf32_Ehdr)) || (!is32 && maxsize < sizeof(llvm::ELF::Elf64_Ehdr))) {
253        /* Not long enough */
254        SetErrorCode(ELF_ERR_INVALID_ELF);
255        return 0;
256    }
257    /* Make the ELF header available.  */
258    bool isAligned = (is32 ? CheckAlign<llvm::ELF::Elf32_Ehdr>(eIdent) : CheckAlign<llvm::ELF::Elf64_Ehdr>(eIdent));
259    if (eIdent[llvm::ELF::EI_DATA] == ELF_BYTEORDER && isAligned) {
260        ehdrPtr.p = eIdent;
261    } else {
262        ehdrPtr.p = &ehdrMem;
263        if (is32) {
264            if (ALLOW_UNALIGNED) {
265                ehdrMem.e32.e_shnum = reinterpret_cast<llvm::ELF::Elf32_Ehdr*>(eIdent)->e_shnum;
266                ehdrMem.e32.e_shoff = reinterpret_cast<llvm::ELF::Elf32_Ehdr*>(eIdent)->e_shoff;
267            } else {
268                if (memcpy_s(&ehdrMem, sizeof(ehdrMem), eIdent, sizeof(llvm::ELF::Elf32_Ehdr)) != 0) {
269                    SetErrorCode(ELF_ERR_NOMEM);
270                    return 0;
271                };
272            }
273
274            if (eIdent[llvm::ELF::EI_DATA] != ELF_BYTEORDER) {
275                ByteSwap(ehdrMem.e32.e_shnum);
276                ByteSwap(ehdrMem.e32.e_shoff);
277            }
278        } else {
279            if (ALLOW_UNALIGNED) {
280                ehdrMem.e64.e_shnum = reinterpret_cast<llvm::ELF::Elf64_Ehdr*>(eIdent)->e_shnum;
281                ehdrMem.e64.e_shoff = reinterpret_cast<llvm::ELF::Elf64_Ehdr*>(eIdent)->e_shoff;
282            } else {
283                if (memcpy_s(&ehdrMem, sizeof(ehdrMem), eIdent, sizeof(llvm::ELF::Elf64_Ehdr)) != 0) {
284                    SetErrorCode(ELF_ERR_NOMEM);
285                    return 0;
286                }
287            };
288
289            if (eIdent[llvm::ELF::EI_DATA] != ELF_BYTEORDER) {
290                ByteSwap(ehdrMem.e64.e_shnum);
291                ByteSwap(ehdrMem.e64.e_shoff);
292            }
293        }
294    }
295    if (is32) {
296        return GetSectionHeaderNum<llvm::ELF::Elf32_Ehdr, llvm::ELF::Elf32_Shdr, llvm::ELF::Elf32_Word>(
297            ehdrPtr.e32, maxsize, eIdent, mapAddress, offset);
298    } else {
299        return GetSectionHeaderNum<llvm::ELF::Elf64_Ehdr, llvm::ELF::Elf64_Shdr, llvm::ELF::Elf64_Xword>(
300            ehdrPtr.e64, maxsize, eIdent, mapAddress, offset);
301    }
302}
303
304/* Get Details from mapAddress,  */
305template <typename ElfEhdr, typename ElfShdr, typename ElfItemField>
306ElfChecker::Elf* ElfChecker::GetElfItem(ElfItemField& elfItemField, void* mapAddress, unsigned char* eIdent,
307    int64_t offset, size_t maxSize, ElfCommand cmd, size_t scnCnt, Elf* elf)
308{
309    /* This pointer might not be directly usable if the alignment is
310       not sufficient for the architecture.  */
311    ElfEhdr* ehdr = reinterpret_cast<ElfEhdr*>(reinterpret_cast<char*>(mapAddress) + offset);
312    if (eIdent[llvm::ELF::EI_DATA] == ELF_BYTEORDER && CheckAlign<ElfEhdr>(ehdr)) {
313        /* We can use the mmapped memory.  */
314        elfItemField.ehdr = ehdr;
315    } else {
316        if (memcpy_s(&elfItemField.elfHeaderMem, sizeof(ElfEhdr), eIdent, sizeof(ElfEhdr)) != 0) {
317            SetErrorCode(ELF_ERR_NOMEM);
318            return nullptr;
319        }
320        elfItemField.ehdr = reinterpret_cast<ElfEhdr*>(&elfItemField.elfHeaderMem);
321        if (eIdent[llvm::ELF::EI_DATA] != ELF_BYTEORDER) {
322            ByteSwap(elfItemField.elfHeaderMem.e_type);
323            ByteSwap(elfItemField.elfHeaderMem.e_machine);
324            ByteSwap(elfItemField.elfHeaderMem.e_version);
325            ByteSwap(elfItemField.elfHeaderMem.e_entry);
326            ByteSwap(elfItemField.elfHeaderMem.e_phoff);
327            ByteSwap(elfItemField.elfHeaderMem.e_shoff);
328            ByteSwap(elfItemField.elfHeaderMem.e_flags);
329            ByteSwap(elfItemField.elfHeaderMem.e_ehsize);
330            ByteSwap(elfItemField.elfHeaderMem.e_phentsize);
331            ByteSwap(elfItemField.elfHeaderMem.e_phnum);
332            ByteSwap(elfItemField.elfHeaderMem.e_shentsize);
333            ByteSwap(elfItemField.elfHeaderMem.e_shnum);
334            ByteSwap(elfItemField.elfHeaderMem.e_shstrndx);
335        }
336    }
337    auto elfSHOff = elfItemField.ehdr->e_shoff;
338    if (eIdent[llvm::ELF::EI_DATA] == ELF_BYTEORDER && cmd != ELF_CMD_READ_MMAP &&
339        CheckAlign<ElfShdr>(ehdr, elfSHOff)) {
340        if (SECUREC_UNLIKELY(scnCnt > 0 && elfSHOff >= maxSize) ||
341            SECUREC_UNLIKELY(maxSize - elfSHOff < scnCnt * sizeof(ElfShdr))) {
342            delete[] reinterpret_cast<char*>(elf);
343            elf = nullptr;
344            SetErrorCode(ELF_ERR_INVALID_ELF);
345            return nullptr;
346        }
347        if (scnCnt > 0) {
348            elfItemField.shdr = reinterpret_cast<ElfShdr*>(reinterpret_cast<char*>(ehdr) + elfSHOff);
349        }
350        for (size_t cnt = 0; cnt < scnCnt; ++cnt) {
351            elfItemField.sections.data()[cnt].index = cnt;
352            elfItemField.sections.data()[cnt].elf = elf;
353            if (std::is_same<ElfShdr, llvm::ELF::Elf32_Shdr>::value) {
354                elfItemField.sections.data()[cnt].shdr.e32 =
355                    reinterpret_cast<llvm::ELF::Elf32_Shdr*>(&elfItemField.shdr[cnt]);
356            } else {
357                elfItemField.sections.data()[cnt].shdr.e64 =
358                    reinterpret_cast<llvm::ELF::Elf64_Shdr*>(&elfItemField.shdr[cnt]);
359            }
360
361            if (SECUREC_LIKELY(elfItemField.shdr[cnt].sh_offset < maxSize) &&
362                SECUREC_LIKELY(elfItemField.shdr[cnt].sh_size <= maxSize - elfItemField.shdr[cnt].sh_offset)) {
363                elfItemField.sections.data()[cnt].rawDataBase = elfItemField.sections.data()[cnt].dataBase =
364                    (reinterpret_cast<char*>(mapAddress) + offset + elfItemField.shdr[cnt].sh_offset);
365            }
366            elfItemField.sections.data()[cnt].list = &elfItemField.sections;
367
368            /* If this is a section with an extended index add a
369               reference in the section which uses the extended
370               index.  */
371            if (elfItemField.shdr[cnt].sh_type == llvm::ELF::SHT_SYMTAB_SHNDX &&
372                elfItemField.shdr[cnt].sh_link < scnCnt) {
373                elfItemField.sections.data()[elfItemField.shdr[cnt].sh_link]
374                    .extendSectionHeaderIndex = static_cast<int>(cnt);
375            }
376            /* Set the own extendSectionHeaderIndex field in case it has not yet
377               been set.  */
378            if (elfItemField.sections.data()[cnt].extendSectionHeaderIndex == 0) {
379                elfItemField.sections.data()[cnt].extendSectionHeaderIndex = -1;
380            }
381        }
382    } else {
383        for (size_t cnt = 0; cnt < scnCnt; ++cnt) {
384            elfItemField.sections.data()[cnt].index = cnt;
385            elfItemField.sections.data()[cnt].elf = elf;
386            elfItemField.sections.data()[cnt].list = &elfItemField.sections;
387        }
388    }
389
390    /* So far only one block with sections.  */
391    elfItemField.sectionLast = &elfItemField.sections;
392    return elf;
393}
394
395/* Create descriptor for ELF file in memory.  */
396ElfChecker::Elf* ElfChecker::FileReadElf(
397    void* mapAddress, unsigned char* eIdent, int64_t offset, size_t maxSize, ElfCommand cmd, Elf* parent)
398{
399    /* Verify the binary is of the class we can handle.  */
400    if (SECUREC_UNLIKELY((eIdent[llvm::ELF::EI_CLASS] != llvm::ELF::ELFCLASS32 &&
401                          eIdent[llvm::ELF::EI_CLASS] != llvm::ELF::ELFCLASS64) ||
402                         (eIdent[llvm::ELF::EI_DATA] != llvm::ELF::ELFDATA2LSB &&
403                          eIdent[llvm::ELF::EI_DATA] != llvm::ELF::ELFDATA2MSB))) {
404        /* Cannot handle this.  */
405        SetErrorCode(ELF_ERR_INVALID_ELF);
406        return nullptr;
407    }
408
409    size_t scncnt = GetShnum(mapAddress, eIdent, offset, maxSize);
410    if (CheckIfError() == false) {
411        return nullptr;
412    }
413
414    /* Check for too many sections.  */
415    if (eIdent[llvm::ELF::EI_CLASS] == llvm::ELF::ELFCLASS32) {
416        if (scncnt > SIZE_MAX / (sizeof(ElfSectionBase) + sizeof(llvm::ELF::Elf32_Shdr))) {
417            SetErrorCode(ELF_ERR_INVALID_ELF);
418            return nullptr;
419        }
420    } else if (scncnt > SIZE_MAX / (sizeof(ElfSectionBase) + sizeof(llvm::ELF::Elf64_Shdr))) {
421        SetErrorCode(ELF_ERR_INVALID_ELF);
422        return nullptr;
423    }
424
425    /* We can now allocate the memory.  Even if there are no section
426       headers, we allocate space for a zeroth section in case we need it
427       later.  */
428    const size_t scnMax = (scncnt ? scncnt : (cmd == ELF_CMD_RDWR || cmd == ELF_CMD_RDWR_MMAP) ? 1 : 0);
429    Elf* elf = ElfAllocate(mapAddress, offset, maxSize, cmd, parent, ELF_KIND_ELF, scnMax * sizeof(ElfSectionBase));
430    if (elf == nullptr) {
431        return nullptr;
432    }
433
434    ASSERT((unsigned int)scncnt == scncnt);
435    ASSERT(offsetof(Elf, state.elf32.sections) == offsetof(Elf, state.elf64.sections));
436    elf->state.elf32.sections.cnt = scncnt;
437    elf->state.elf32.sections.max = scnMax;
438
439    /* Make the class easily available.  */
440    elf->binaryClass = eIdent[llvm::ELF::EI_CLASS];
441
442    if (eIdent[llvm::ELF::EI_CLASS] == llvm::ELF::ELFCLASS32) {
443        return GetElfItem<llvm::ELF::Elf32_Ehdr, llvm::ELF::Elf32_Shdr, decltype(elf->state.elf32)>(
444            elf->state.elf32, mapAddress, eIdent, offset, maxSize, cmd, scncnt, elf);
445    } else {
446        return GetElfItem<llvm::ELF::Elf64_Ehdr, llvm::ELF::Elf64_Shdr, decltype(elf->state.elf64)>(
447            elf->state.elf64, mapAddress, eIdent, offset, maxSize, cmd, scncnt, elf);
448    }
449}
450
451ElfChecker::Elf* ElfChecker::ReadMmapedFile(
452    void* mapAddress, int64_t offset, size_t maxsize, ElfCommand cmd, Elf* parent)
453{
454    unsigned char* eIdent = reinterpret_cast<unsigned char*>(mapAddress) + offset;
455    /* See what kind of object we have here.  */
456    ElfKind kind = GetElfKind(eIdent);
457    if (kind == ELF_KIND_ELF) {
458        return FileReadElf(mapAddress, eIdent, offset, maxsize, cmd, parent);
459    } else {
460        return ElfAllocate(mapAddress, offset, maxsize, cmd, parent, ELF_KIND_NONE, 0);
461    }
462}
463
464ElfChecker::Elf* ElfChecker::ElfMemory(char* image, size_t size)
465{
466    if (image == nullptr) {
467        SetErrorCode(ELF_ERR_INVALID_OPERAND);
468        return nullptr;
469    }
470    return ReadMmapedFile(image, 0, size, ELF_CMD_READ, nullptr);
471}
472
473int ElfChecker::ElfRelease(Elf* elf) const
474{
475    if (elf == nullptr) {
476        return 0;
477    }
478    if (elf->refCount != 0) {
479        --elf->refCount;
480        if (elf->refCount != 0) {
481            int result = elf->refCount;
482            return result;
483        }
484    }
485    Elf* parent = elf->parent;
486    delete[] reinterpret_cast<char*>(elf);
487    elf = nullptr;
488    return (parent != nullptr && parent->refCount == 0 ? ElfRelease(parent) : 0);
489}
490
491ElfChecker::~ElfChecker()
492{
493    if (fromMmap_ == false && elfData_ != nullptr) {
494        delete[] elfData_;
495        elfData_ = nullptr;
496    }
497}
498} // namespace panda::ecmascript
499