this repo has no description
at fixPythonPipStalling 319 lines 7.8 kB view raw
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}