this repo has no description
1
2#include <string>
3#include <iostream>
4#include <set>
5#include <string.h>
6#include <unistd.h>
7#include <fcntl.h>
8#include <elf.h>
9#include <errno.h>
10#include <dlfcn.h>
11#include <sys/mman.h>
12#include <sstream>
13#include <stdexcept>
14#include <fstream>
15#include <link.h>
16
17#ifndef PATH_MAX
18# define PATH_MAX 4096
19#endif
20
21// This is a generator of assembly code for Mach-O
22// libraries wrapping ELF libraries.
23
24// TODO: use wrapgen32 to generate 32-bit wrappers.
25
26void parse_elf(const char* elf, std::string& soname_out, std::set<std::string>& functions_out, std::set<std::string>& vars_out);
27void generate_wrapper(std::ofstream& output, const char* soname, const std::set<std::string>& symbols);
28void generate_var_wrappers(std::ofstream& output, std::ofstream& outputHeader, const std::set<std::string>& vars);
29Elf64_Off vaddr_to_offset(const Elf64_Ehdr* ehdr, Elf64_Addr vaddr);
30
31int main(int argc, const char** argv)
32{
33 std::string elfLibrary;
34 std::set<std::string> functions, vars;
35 std::string soname;
36 std::ofstream output;
37
38 if (argc != 4)
39 {
40 std::cerr << "Usage: " << argv[0] << " <library-name> <output-file> <var-access-header>\n";
41 return 1;
42 }
43
44 elfLibrary = argv[1];
45 output.open(argv[2]);
46
47 try
48 {
49 if (!output.is_open())
50 throw std::runtime_error("Cannot open output file");
51
52 if (access(elfLibrary.c_str(), R_OK) == -1)
53 {
54 // Try loading the library and then ask the loader where it found the library.
55 // It is simpler than parsing /etc/ld.so.conf.
56
57 void* handle = dlopen(elfLibrary.c_str(), RTLD_LAZY | RTLD_LOCAL);
58 struct link_map* lm = NULL;
59
60 if (!handle)
61 {
62 std::stringstream ss;
63 ss << "Cannot load " << elfLibrary << ": " << dlerror();
64 throw std::runtime_error(ss.str());
65 }
66
67 if (dlinfo(handle, RTLD_DI_LINKMAP, &lm) == 0)
68 {
69 elfLibrary = lm->l_name;
70 }
71 else
72 {
73 std::stringstream ss;
74 std::cerr << "Cannot locate " << elfLibrary << ": " << dlerror();
75 throw std::runtime_error(ss.str());
76 }
77
78 dlclose(handle);
79 }
80
81 parse_elf(elfLibrary.c_str(), soname, functions, vars);
82 generate_wrapper(output, soname.c_str(), functions);
83
84 if (!vars.empty())
85 {
86 std::ofstream outputHeader(argv[3]);
87 if (!outputHeader.is_open())
88 throw std::runtime_error("Cannot open output macro header file");
89
90 generate_var_wrappers(output, outputHeader, vars);
91 }
92 }
93 catch (const std::exception& e)
94 {
95 std::cerr << e.what() << std::endl;
96 return 1;
97 }
98
99 return 0;
100}
101
102void parse_elf(const char* elf, std::string& soname, std::set<std::string>& symbols, std::set<std::string>& vars)
103{
104 int fd;
105 const Elf64_Ehdr* ehdr;
106 const char* strings = NULL;
107 uint64_t length;
108
109 fd = open(elf, O_RDONLY);
110 if (fd < 0)
111 {
112 std::stringstream ss;
113 ss << "Error opening " << elf << ": " << strerror(errno);
114 throw std::runtime_error(ss.str());
115 }
116
117 length = lseek(fd, 0, SEEK_END);
118
119 ehdr = (Elf64_Ehdr*) mmap(NULL, length, PROT_READ, MAP_SHARED, fd, 0);
120 if (ehdr == MAP_FAILED)
121 {
122 std::stringstream ss;
123 ss << "Cannot mmap ELF file: " << strerror(errno);
124 throw std::runtime_error(ss.str());
125 }
126
127 close(fd);
128
129 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0 || ehdr->e_ident[EI_CLASS] != ELFCLASS64)
130 {
131 std::stringstream ss;
132 ss << elf << " is not a 64-bit ELF";
133 throw std::runtime_error(ss.str());
134 }
135
136 if (ehdr->e_type != ET_DYN)
137 {
138 std::stringstream ss;
139 ss << elf << " is not a dynamic library ELF";
140 throw std::runtime_error(ss.str());
141 }
142
143 if (ehdr->e_machine != EM_X86_64)
144 {
145 std::stringstream ss;
146 ss << elf << " is not an ELF for x86-64";
147 throw std::runtime_error(ss.str());
148 }
149
150 // Now read program headers, find a PT_DYNAMIC segment type
151 for (int i = 0; i < ehdr->e_phnum; i++)
152 {
153 const Elf64_Phdr* phdr;
154
155 phdr = (const Elf64_Phdr*) (((char*) ehdr) + ehdr->e_phoff + (i * ehdr->e_phentsize));
156
157 if (phdr->p_type == PT_DYNAMIC)
158 {
159 Elf64_Xword off_strtab, off_soname;
160
161 off_strtab = off_soname = 0;
162
163 for (int j = 0; j < phdr->p_filesz; j += sizeof(Elf64_Dyn))
164 {
165 const Elf64_Dyn* dyn;
166
167 dyn = (const Elf64_Dyn*) (((char*) ehdr) + phdr->p_offset + j);
168
169 switch (dyn->d_tag)
170 {
171 case DT_STRTAB:
172 off_strtab = vaddr_to_offset(ehdr, dyn->d_un.d_val);
173 break;
174 case DT_SONAME:
175 off_soname = vaddr_to_offset(ehdr, dyn->d_un.d_val);
176 break;
177 case DT_NULL:
178 goto end_dyn;
179 }
180 }
181end_dyn:
182
183 if (off_strtab != 0)
184 {
185 strings = (const char*) ((char*) ehdr) + off_strtab;
186
187 if (off_soname != 0)
188 {
189 // Read DT_SONAME value from the string table
190 soname = strings + off_soname;
191 }
192 }
193
194 break;
195 }
196 }
197
198 if (strings != NULL)
199 {
200 // Load symbol list
201 const Elf64_Shdr* shdr;
202
203 for (int i = 0; i < ehdr->e_shnum; i++)
204 {
205 shdr = (const Elf64_Shdr*) (((char*) ehdr) + ehdr->e_shoff + (i * ehdr->e_shentsize));
206 if (shdr->sh_type == SHT_DYNSYM)
207 {
208 for (int j = 0; j < shdr->sh_size; j += sizeof(Elf64_Sym))
209 {
210 const Elf64_Sym* sym;
211
212 sym = (const Elf64_Sym*) (((char*) ehdr) + shdr->sh_offset + j);
213
214 if (ELF64_ST_TYPE(sym->st_info) != STT_OBJECT && ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
215 continue;
216 if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL)
217 continue;
218 if (sym->st_shndx == SHN_UNDEF || sym->st_value == 0)
219 continue;
220 if (ELF64_ST_VISIBILITY(sym->st_other) != STV_DEFAULT)
221 continue;
222
223 if (ELF64_ST_TYPE(sym->st_info) == STT_FUNC)
224 symbols.insert(strings + sym->st_name);
225 else
226 vars.insert(strings + sym->st_name);
227 }
228
229 break;
230 }
231 }
232 }
233
234 if (soname.empty())
235 {
236 std::cerr << "WARNING: No DT_SONAME in " << elf << ".\n";
237
238 const char* slash = strrchr(elf, '/');
239 if (slash != NULL)
240 soname = slash + 1;
241 else
242 soname = elf;
243 }
244 if (symbols.empty())
245 {
246 std::stringstream ss;
247 ss << "No symbols found in " << elf;
248 throw std::runtime_error(ss.str());
249 }
250
251 munmap((void*) ehdr, length);
252}
253
254void generate_wrapper(std::ofstream& output, const char* soname, const std::set<std::string>& symbols)
255{
256 output << "#include <elfcalls.h>\n"
257 "extern struct elf_calls* _elfcalls;\n\n"
258 "extern const char __elfname[];\n\n";
259
260 output << "static void* lib_handle;\n"
261 "__attribute__((constructor)) static void initializer() {\n"
262 "\tlib_handle = _elfcalls->dlopen_fatal(__elfname);\n"
263 "}\n\n";
264
265 output << "__attribute__((destructor)) static void destructor() {\n"
266 "\t_elfcalls->dlclose_fatal(lib_handle);\n"
267 "}\n\n";
268
269 for (const std::string& sym : symbols)
270 {
271 output << "void* " << sym << "() {\n"
272 "\t__asm__(\".symbol_resolver _" << sym << "\");\n"
273 "\treturn _elfcalls->dlsym_fatal(lib_handle, \"" << sym << "\");\n"
274 "}\n\n";
275 }
276 output << "asm(\".section __TEXT,__elfname\\n"
277 ".private_extern ___elfname\\n"
278 "___elfname: .asciz \\\"" << soname << "\\\"\");\n";
279}
280
281void generate_var_wrappers(std::ofstream& output, std::ofstream& outputHeader, const std::set<std::string>& vars)
282{
283 outputHeader << "#pragma once\n\n";
284 outputHeader << "#ifdef __cplusplus\n"
285 "extern \"C\" {\n"
286 "#endif\n\n";
287
288 for (const std::string& sym : vars)
289 {
290 output << "void* __elf_get_" << sym << "(void) {\n"
291 "\treturn _elfcalls->dlsym_fatal(lib_handle, \"" << sym << "\");\n"
292 "}\n\n";
293
294 outputHeader << "extern __typeof(" << sym << ")* __elf_get_" << sym << "(void);\n"
295 "#define " << sym << " (*__elf_get_" << sym << "())\n\n";
296 }
297
298 outputHeader << "\n\n#ifdef __cplusplus\n"
299 "}\n"
300 "#endif\n\n";
301}
302
303Elf64_Off vaddr_to_offset(const Elf64_Ehdr* ehdr, Elf64_Xword vaddr)
304{
305 for (int i = 0; i < ehdr->e_phnum; i++)
306 {
307 const Elf64_Phdr* phdr;
308 Elf64_Addr vstart, vend;
309
310 phdr = (const Elf64_Phdr*) (((char*) ehdr) + ehdr->e_phoff + (i * ehdr->e_phentsize));
311 vstart = phdr->p_vaddr;
312 vend = phdr->p_vaddr + phdr->p_memsz;
313 if (vstart <= vaddr && vaddr < vend) {
314 return phdr->p_offset + vaddr - vstart;
315 }
316 }
317
318 return 0;
319}