1 | #pragma once |
2 | |
3 | #include "utils.h" |
4 | |
5 | class ExecLoader { |
6 | public: |
7 | ExecLoader(const std::string& filename, char* head, const size_t size) |
8 | : head_(head), size_(size), filename_(filename) { |
9 | ehdr_ = reinterpret_cast<Elf64_Ehdr*>(head); |
10 | for (uint16_t i = 0; i < ehdr()->e_phnum; i++) { |
11 | phdrs_.emplace_back(reinterpret_cast<Elf64_Phdr*>( |
12 | head_ + ehdr()->e_phoff + i * ehdr()->e_phentsize)); |
13 | } |
14 | for (auto ph : phdrs()) { |
15 | LOG(INFO) << LOG_BITS(ph->p_type); |
16 | if (ph->p_type == PT_DYNAMIC) { |
17 | CHECK(ph_dynamic_ == NULL); |
18 | ph_dynamic_ = ph; |
19 | } |
20 | } |
21 | } |
22 | |
23 | void Show() { |
24 | LOG(INFO) << "Ehdr:" << LOG_BITS(ehdr()->e_entry) << LOG_BITS(size_) |
25 | << LOG_BITS(head_); |
26 | for (auto p : phdrs()) { |
27 | LOG(INFO) << "Phdr:" << LOG_BITS(p->p_vaddr) |
28 | << LOG_BITS(p->p_offset) << LOG_BITS(p->p_filesz); |
29 | } |
30 | } |
31 | |
32 | void Load() { |
33 | LOG(INFO) << "Load start" ; |
34 | for (auto ph : phdrs()) { |
35 | if (ph->p_type != PT_LOAD) { |
36 | continue; |
37 | } |
38 | LOG(INFO) << LOG_BITS(reinterpret_cast<void*>(ph->p_vaddr)) |
39 | << LOG_BITS(ph->p_memsz); |
40 | void* mmap_start = |
41 | reinterpret_cast<void*>((ph->p_vaddr) & (~(0xfff))); |
42 | void* mmap_end = reinterpret_cast<void*>( |
43 | ((ph->p_vaddr + ph->p_memsz) + 0xfff) & (~(0xfff))); |
44 | size_t mmap_size = reinterpret_cast<size_t>(mmap_end) - |
45 | reinterpret_cast<size_t>(mmap_start); |
46 | char* p = reinterpret_cast<char*>( |
47 | mmap(mmap_start, mmap_size, PROT_READ | PROT_WRITE | PROT_EXEC, |
48 | MAP_SHARED | MAP_ANONYMOUS, -1, 0)); |
49 | LOG(INFO) << "mmap: " << LOG_KEY(filename()) << LOG_BITS(p) |
50 | << LOG_BITS(ph->p_vaddr); |
51 | CHECK_EQ(mmap_start, p); |
52 | CHECK_LE(reinterpret_cast<Elf64_Addr>(mmap_start), ph->p_vaddr); |
53 | CHECK_LT(ph->p_vaddr + ph->p_memsz, |
54 | reinterpret_cast<Elf64_Addr>(mmap_end)); |
55 | LOG(INFO) << LOG_BITS(p) << LOG_BITS(head() + ph->p_offset) |
56 | << LOG_BITS(ph->p_filesz); |
57 | memcpy(reinterpret_cast<void*>(ph->p_vaddr), head() + ph->p_offset, |
58 | ph->p_filesz); |
59 | memories_.emplace_back(std::make_pair(p, ph->p_memsz)); |
60 | } |
61 | LOG(INFO) << "Load end" ; |
62 | } |
63 | |
64 | void Unload() { |
65 | for (auto p : memories_) { |
66 | munmap(p.first, p.second); |
67 | } |
68 | } |
69 | |
70 | void Relocate() {} |
71 | |
72 | // To assign variables of stack, stack_num and entry to %rdi, %rsi and %rdx |
73 | // I use the calling convention. For details, see A.2.1 Calling Conventions |
74 | // in https://refspecs.linuxfoundation.org/elf/x86_64-abi-0.99.pdf. Of |
75 | // course, compiler must not inline this function. |
76 | void __attribute__((noinline)) |
77 | ExecuteCore(uint64_t* stack, size_t stack_num, uint64_t entry) { |
78 | for (size_t i = 0; i < stack_num; i++) { |
79 | asm volatile("pushq %0" ::"m" (*(stack + i))); |
80 | } |
81 | |
82 | asm volatile("jmp *%0" ::"r" (entry)); |
83 | } |
84 | |
85 | void Execute(std::vector<std::string> envs) { |
86 | unsigned long at_random = getauxval(AT_RANDOM); |
87 | unsigned long at_pagesz = getauxval(AT_PAGESZ); |
88 | CHECK_NE(at_random, 0UL); |
89 | LOG(INFO) << LOG_BITS(at_random) << LOG_BITS(at_pagesz); |
90 | |
91 | // Some commented out auxiliary values because they are not appropriate |
92 | // as loading programs. These values are for sloader itself. |
93 | std::vector<unsigned long> aux_types{ |
94 | AT_IGNORE, |
95 | // AT_EXECFD, |
96 | // AT_PHDR, |
97 | AT_PHENT, |
98 | // AT_PHNUM, |
99 | AT_PAGESZ, |
100 | // AT_BASE, |
101 | AT_FLAGS, |
102 | // AT_ENTRY, |
103 | AT_NOTELF, AT_UID, AT_EUID, AT_GID, AT_EGID, AT_CLKTCK, AT_PLATFORM, |
104 | AT_HWCAP, AT_FPUCW, AT_DCACHEBSIZE, AT_ICACHEBSIZE, AT_UCACHEBSIZE, |
105 | AT_IGNOREPPC, AT_SECURE, AT_BASE_PLATFORM, AT_RANDOM, AT_HWCAP2, |
106 | // AT_EXECFN, |
107 | AT_SYSINFO, AT_SYSINFO_EHDR, AT_L1I_CACHESHAPE, AT_L1D_CACHESHAPE, |
108 | AT_L2_CACHESHAPE, AT_L3_CACHESHAPE, AT_L1I_CACHESIZE, |
109 | AT_L1I_CACHEGEOMETRY, AT_L1D_CACHESIZE, AT_L1D_CACHEGEOMETRY, |
110 | AT_L2_CACHESIZE, AT_L2_CACHEGEOMETRY, AT_L3_CACHESIZE, |
111 | AT_L3_CACHEGEOMETRY, AT_MINSIGSTKSZ}; |
112 | |
113 | std::vector<std::pair<unsigned long, unsigned long>> aux_tvs; |
114 | for (size_t i = 0; i < aux_types.size(); i++) { |
115 | unsigned long v = getauxval(aux_types[i]); |
116 | if (v != 0) { |
117 | aux_tvs.emplace_back(std::make_pair(aux_types[i], v)); |
118 | LOG(INFO) << LOG_BITS(aux_types[i]) << LOG_BITS(v); |
119 | } |
120 | } |
121 | |
122 | // See http://articles.manugarg.com/aboutelfauxiliaryvectors.html for |
123 | // the stack layout padding. |
124 | // |
125 | // 4 words padding |
126 | // 0 |
127 | // AT_NULL |
128 | // auxs |
129 | // NULL |
130 | // envs |
131 | // argv[argc] (must be null) |
132 | // argv[0] = filename |
133 | // argc |
134 | size_t stack_index = 0; |
135 | size_t stack_num = 4 + 2 + 2 * aux_tvs.size() + 1 + envs.size() + 3; |
136 | size_t stack_size = sizeof(uint64_t) * stack_num; |
137 | unsigned long* stack = reinterpret_cast<uint64_t*>(malloc(stack_size)); |
138 | memset(stack, 0, stack_size); |
139 | |
140 | // 4 words padding |
141 | stack_index += 4; |
142 | |
143 | // First two elements are 0 and AT_NULL. |
144 | stack_index += 2; |
145 | |
146 | // auxs |
147 | for (size_t i = 0; i < aux_tvs.size(); i++) { |
148 | *(stack + stack_index) = aux_tvs[i].second; |
149 | stack_index++; |
150 | *(stack + stack_index) = aux_tvs[i].first; |
151 | stack_index++; |
152 | } |
153 | |
154 | // End of environment variables |
155 | stack_index++; |
156 | |
157 | // Environment variables |
158 | for (size_t i = 0; i < envs.size(); i++) { |
159 | *(stack + stack_index) = |
160 | reinterpret_cast<uint64_t>(envs[i].c_str()); |
161 | stack_index++; |
162 | } |
163 | |
164 | // argv[argc] |
165 | stack_index++; |
166 | |
167 | // argv[0] |
168 | *(stack + stack_index) = reinterpret_cast<uint64_t>(filename().c_str()); |
169 | stack_index++; |
170 | |
171 | // argc |
172 | *(stack + stack_index) = 1; |
173 | stack_index++; |
174 | |
175 | CHECK_EQ(stack_index, stack_num); |
176 | |
177 | ExecuteCore(stack, stack_num, ehdr()->e_entry); |
178 | |
179 | free(stack); |
180 | LOG(INFO) << "Execute end" ; |
181 | } |
182 | std::string filename() { return filename_; } |
183 | Elf64_Ehdr* ehdr() { return ehdr_; } |
184 | char* head() { return head_; } |
185 | std::vector<Elf64_Phdr*> phdrs() { return phdrs_; } |
186 | |
187 | private: |
188 | char* head_ = nullptr; |
189 | size_t size_; |
190 | std::string filename_ = "" ; |
191 | Elf64_Ehdr* ehdr_ = nullptr; |
192 | std::vector<Elf64_Phdr*> phdrs_; |
193 | Elf64_Phdr* ph_dynamic_ = nullptr; |
194 | std::vector<Elf64_Dyn*> dyns_; |
195 | std::vector<std::pair<char*, uint32_t>> memories_; |
196 | }; |
197 | |
198 | std::unique_ptr<ExecLoader> MakeExecLoader( |
199 | const std::filesystem::path& filepath, const std::string& argv0) { |
200 | int fd = open(filepath.c_str(), O_RDONLY); |
201 | CHECK(fd >= 0); |
202 | |
203 | size_t size = lseek(fd, 0, SEEK_END); |
204 | CHECK_GT(size, 8UL + 16UL); |
205 | |
206 | size_t mapped_size = (size + 0xfff) & ~0xfff; |
207 | |
208 | char* p = (char*)mmap(NULL, mapped_size, PROT_READ | PROT_WRITE | PROT_EXEC, |
209 | MAP_PRIVATE, fd, 0); |
210 | CHECK(p != MAP_FAILED); |
211 | |
212 | return std::make_unique<ExecLoader>(argv0, p, mapped_size); |
213 | } |
214 | |