1#pragma once
2
3#include "utils.h"
4
5class 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
198std::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