1 | #pragma once |
2 | |
3 | #include <elf.h> |
4 | #include <glob.h> |
5 | |
6 | #include <filesystem> |
7 | #include <fstream> |
8 | #include <map> |
9 | #include <memory> |
10 | #include <optional> |
11 | #include <queue> |
12 | #include <set> |
13 | #include <tuple> |
14 | |
15 | #include "utils.h" |
16 | |
17 | class ELFBinary { |
18 | public: |
19 | ELFBinary(const std::filesystem::path path); |
20 | ELFBinary(const ELFBinary&) = default; |
21 | |
22 | const std::string filename() const { return path_.filename().string(); } |
23 | const Elf64_Addr GetSymbolAddr(const size_t symbol_index); |
24 | const std::vector<std::string> neededs() const { return neededs_; } |
25 | const std::vector<Elf64_Sym>& symtabs() const { return symtabs_; } |
26 | const std::optional<std::filesystem::path> runpath() const { return runpath_; } |
27 | const std::optional<std::filesystem::path> rpath() const { return rpath_; } |
28 | const bool has_tls() const { return has_tls_; } |
29 | const Elf64_Phdr file_tls() const { return file_tls_; } |
30 | const Elf64_Addr base_addr() const { return base_addr_; } |
31 | const Elf64_Addr end_addr() const { return end_addr_; } |
32 | const std::filesystem::path path() const { return path_; } |
33 | const std::vector<Elf64_Rela> relas() const { return relas_; } |
34 | const std::vector<Elf64_Rela> pltrelas() const { return pltrelas_; } |
35 | const Elf64_Xword init() const { return init_; } |
36 | const Elf64_Xword fini() const { return fini_; } |
37 | const Elf64_Xword init_arraysz() const { return init_arraysz_; } |
38 | const Elf64_Xword init_array() const { return init_array_; } |
39 | const Elf64_Xword fini_arraysz() const { return fini_arraysz_; } |
40 | const Elf64_Xword fini_array() const { return fini_array_; } |
41 | const char* strtab() const { return strtab_; } |
42 | const Elf64_Ehdr ehdr() const { return ehdr_; } |
43 | |
44 | Elf64_Addr Load(Elf64_Addr base_addr, std::shared_ptr<std::ofstream> map_file); |
45 | void ParseDynamic(); |
46 | |
47 | private: |
48 | // To generate copy constructor, we cannot make member variables const. |
49 | std::filesystem::path path_; |
50 | char* file_base_addr_; |
51 | Elf64_Addr base_addr_ = 0; |
52 | Elf64_Addr end_addr_ = 0; |
53 | Elf64_Ehdr ehdr_; |
54 | Elf64_Rela* rela_ = nullptr; |
55 | Elf64_Rela* jmprel_ = nullptr; |
56 | std::vector<Elf64_Rela> relas_; |
57 | std::vector<Elf64_Rela> pltrelas_; |
58 | Elf64_Xword relasz_ = 0; |
59 | Elf64_Xword relacount_ = 0; // What's this? |
60 | Elf64_Xword relaent_ = 0; |
61 | Elf64_Xword pltrelsz_ = 0; |
62 | Elf64_Xword pltrel_ = 0; |
63 | Elf64_Xword pltrelent_ = 0; |
64 | Elf64_Xword init_ = 0; |
65 | Elf64_Xword fini_ = 0; |
66 | Elf64_Xword init_arraysz_ = 0; |
67 | Elf64_Xword init_array_ = 0; |
68 | Elf64_Xword fini_arraysz_ = 0; |
69 | Elf64_Xword fini_array_ = 0; |
70 | Elf64_Sym* symtab_ = nullptr; |
71 | std::vector<Elf64_Sym> symtabs_; |
72 | Elf64_Xword syment_ = 0; |
73 | std::vector<Elf64_Phdr> file_phdrs_; |
74 | Elf64_Phdr file_dynamic_ = {.p_filesz = 0}; |
75 | bool has_tls_ = false; |
76 | Elf64_Phdr file_tls_; |
77 | std::vector<std::string> neededs_; |
78 | std::optional<std::filesystem::path> runpath_ = std::nullopt; |
79 | std::optional<std::filesystem::path> rpath_ = std::nullopt; |
80 | char* strtab_ = nullptr; |
81 | Elf64_Xword strsz_ = 0; |
82 | }; |
83 | |
84 | class DynLoader { |
85 | public: |
86 | DynLoader(const std::filesystem::path& main_path, const std::vector<std::string>& args, const std::vector<std::string>& envs); |
87 | // The main function |
88 | void Run(); |
89 | |
90 | void LoadDependingLibs(const std::filesystem::path& root_path); |
91 | void Relocate(); |
92 | std::optional<std::pair<size_t, size_t>> SearchSym(const std::string& name, bool skip_main); |
93 | std::vector<ELFBinary> binaries_; |
94 | |
95 | private: |
96 | std::filesystem::path main_path_; |
97 | std::shared_ptr<std::ofstream> map_file_; |
98 | const std::vector<std::string> args_; |
99 | const std::vector<std::string> envs_; |
100 | Elf64_Addr next_base_addr_; |
101 | std::set<std::string> loaded_; |
102 | std::map<std::filesystem::path, bool> relocated_; |
103 | |
104 | void Execute(std::vector<std::string> args, std::vector<std::string> envs); |
105 | void __attribute__((noinline)) ExecuteCore(uint64_t* stack, size_t stack_num, uint64_t entry); |
106 | Elf64_Addr TLSSymOffset(const std::string& name); |
107 | }; |
108 | |
109 | void InitializeDynLoader(const std::filesystem::path& main_path, const std::vector<std::string>& envs, |
110 | const std::vector<std::string>& argv); |
111 | |
112 | std::shared_ptr<DynLoader> GetDynLoader(); |
113 | |
114 | std::filesystem::path FindLibrary(std::string library_name, std::optional<std::filesystem::path> runpath, |
115 | std::optional<std::filesystem::path> rpath); |
116 | |