├── Makefile ├── cpp.cc ├── cpp.h ├── func.c ├── objfcn.c ├── objfcn.h ├── test_objfcn.c └── test_objfcn_cpp.cc /Makefile: -------------------------------------------------------------------------------- 1 | CLANG := clang 2 | CFLAGS := -g -O -Wall -MMD 3 | AARCH64_CXX := aarch64-linux-gnu-g++ 4 | 5 | TEST_BINARIES := test_objfcn_64 test_objfcn_32 test_objfcn_cpp_64 6 | TEST_TARGET_OBJS := \ 7 | func_64_pie.o \ 8 | func_64_pic.o \ 9 | func_32_nopic.o \ 10 | func_64.so \ 11 | cpp_64.so 12 | 13 | ifdef ARM 14 | TEST_BINARIES += test_objfcn_arm32 15 | TEST_TARGET_OBJS += func_arm32_nopic.o 16 | endif 17 | 18 | ifdef AARCH64 19 | TEST_BINARIES += test_objfcn_cpp_aarch64 20 | TEST_TARGET_OBJS += cpp_aarch64.so 21 | endif 22 | 23 | all: test 24 | 25 | test: $(TEST_BINARIES) $(TEST_TARGET_OBJS) 26 | ./test_objfcn_64 func_64_pie.o 27 | ./test_objfcn_64 func_64_pic.o 28 | ./test_objfcn_32 func_32_nopic.o 29 | ./test_objfcn_64 func_64.so 30 | ./test_objfcn_cpp_64 cpp_64.so 31 | ifdef ARM 32 | qemu-arm -L /usr/arm-linux-gnueabi ./test_objfcn_arm32 func_arm32_nopic.o 33 | endif 34 | 35 | test_objfcn_64: test_objfcn.c objfcn.c func.c 36 | $(CC) $(CFLAGS) -rdynamic -o $@ test_objfcn.c objfcn.c -ldl 37 | 38 | test_objfcn_32: test_objfcn.c objfcn.c func.c 39 | $(CC) $(CFLAGS) -m32 -rdynamic -o $@ test_objfcn.c objfcn.c -ldl 40 | 41 | test_objfcn_arm32: test_objfcn.c objfcn.c func.c 42 | $(CLANG) -target arm-linux-gnueabi $(CFLAGS) -rdynamic -o $@ test_objfcn.c objfcn.c -ldl 43 | 44 | test_objfcn_cpp_64: test_objfcn_cpp.cc objfcn.c 45 | $(CXX) $(CFLAGS) -rdynamic -o $@ test_objfcn_cpp.cc objfcn.c -ldl 46 | 47 | test_objfcn_cpp_aarch64: test_objfcn_cpp.cc objfcn.c 48 | $(AARCH64_CXX) $(CFLAGS) -rdynamic -o $@ test_objfcn_cpp.cc objfcn.c -ldl 49 | 50 | func_64_pic.o: func.c 51 | $(CC) -fPIC -c -o $@ $< 52 | 53 | func_64.so: func_64_pic.o 54 | $(CC) -fPIC -shared -o $@ $< 55 | 56 | cpp_64.so: cpp.cc 57 | $(CXX) -fPIC -shared -o $@ $< 58 | 59 | func_64_pie.o: func.c 60 | $(CC) -fPIE -c -o $@ $< 61 | 62 | func_32_nopic.o: func.c 63 | $(CC) -m32 -fno-PIC -c -o $@ $< 64 | 65 | func_arm32_nopic.o: func.c 66 | $(CLANG) -target arm-linux-gnueabi -fno-PIC -c -o $@ $< 67 | 68 | cpp_aarch64.so: cpp.cc 69 | $(AARCH64_CXX) -fPIC -shared -o $@ $< 70 | 71 | -include *.d 72 | 73 | clean: 74 | rm -f $(TEST_BINARIES) *.o *.so 75 | -------------------------------------------------------------------------------- /cpp.cc: -------------------------------------------------------------------------------- 1 | #include "cpp.h" 2 | 3 | #include 4 | 5 | namespace { 6 | thread_local int g_tls_var = 19; 7 | thread_local int g_tls_var2 = 120; 8 | thread_local int g_tls_bss; 9 | thread_local int g_tls_bss2; 10 | } 11 | 12 | __attribute__((constructor)) 13 | static void init() { 14 | ++g_tls_bss; 15 | } 16 | 17 | extern "C" { 18 | int func(int x) { 19 | g_tls_var++; 20 | g_tls_bss -= 3; 21 | g_tls_bss2 += 3; 22 | return x + g_tls_var + g_tls_var2 + g_tls_bss + g_tls_bss2; 23 | } 24 | } 25 | 26 | class BaseImpl : public Base { 27 | public: 28 | int vf() override { 29 | return 1234; 30 | } 31 | }; 32 | 33 | Base* MakeBase() { 34 | return new BaseImpl(); 35 | } 36 | -------------------------------------------------------------------------------- /cpp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class Base { 4 | public: 5 | virtual ~Base() {} 6 | virtual int vf() = 0; 7 | }; 8 | 9 | Base* MakeBase(); 10 | -------------------------------------------------------------------------------- /func.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int g_counter = 0; 4 | int g_value = 3; 5 | const int g_const = 42; 6 | 7 | int dummy(void) { 8 | return -3; 9 | } 10 | 11 | int func_in_main(void); 12 | 13 | int func(int x) { 14 | g_counter++; 15 | g_value = x; 16 | return x + g_counter + g_value + g_const + func_in_main(); 17 | } 18 | -------------------------------------------------------------------------------- /objfcn.c: -------------------------------------------------------------------------------- 1 | #ifndef _GNU_SOURCE 2 | # define _GNU_SOURCE 3 | #endif 4 | #include "objfcn.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #define OBJFCN_LOG 1 20 | 21 | #define OBJFCN_TLS_SIZE 8192 22 | 23 | #if OBJFCN_LOG 24 | # define LOGF(...) if (1) fprintf(stderr, __VA_ARGS__) 25 | #else 26 | # define LOGF(...) if (0) fprintf(stderr, __VA_ARGS__) 27 | #endif 28 | 29 | #define OBJFCN_SPLIT_ALLOC 0 30 | 31 | #if defined(__x86_64__) 32 | # define R_64 R_X86_64_64 33 | # define R_PC32 R_X86_64_PC32 34 | # define R_PLT32 R_X86_64_PLT32 35 | # define R_RELATIVE R_X86_64_RELATIVE 36 | # define R_GLOB_DAT R_X86_64_GLOB_DAT 37 | # define R_JUMP_SLOT R_X86_64_JUMP_SLOT 38 | # define DYN_SUPPORTED 1 39 | #elif defined(__i386__) 40 | # define R_32 R_386_32 41 | # define R_PC32 R_386_PC32 42 | #elif defined(__arm__) 43 | #elif defined(__aarch64__) 44 | # define R_64 R_AARCH64_ABS64 45 | # define R_RELATIVE R_AARCH64_RELATIVE 46 | # define R_GLOB_DAT R_AARCH64_GLOB_DAT 47 | # define R_JUMP_SLOT R_AARCH64_JUMP_SLOT 48 | # define DYN_SUPPORTED 1 49 | #else 50 | # error "Unsupported architecture" 51 | #endif 52 | 53 | #if __SIZEOF_POINTER__ == 8 54 | # define Elf_Addr Elf64_Addr 55 | # define Elf_Ehdr Elf64_Ehdr 56 | # define Elf_Phdr Elf64_Phdr 57 | # define Elf_Shdr Elf64_Shdr 58 | # define Elf_Sym Elf64_Sym 59 | # define Elf_Rel Elf64_Rela 60 | # define Elf_Rela Elf64_Rela 61 | # define Elf_Dyn Elf64_Dyn 62 | # define Elf_Versym Elf64_Versym 63 | # define Elf_Verneed Elf64_Verneed 64 | # define Elf_Vernaux Elf64_Vernaux 65 | # define ELFW_ST_BIND(v) ELF64_ST_BIND(v) 66 | # define ELFW_ST_TYPE(v) ELF64_ST_TYPE(v) 67 | # define ELFW_R_SYM(v) ELF64_R_SYM(v) 68 | # define ELFW_R_TYPE(v) ELF64_R_TYPE(v) 69 | #else 70 | # define Elf_Addr Elf32_Addr 71 | # define Elf_Ehdr Elf32_Ehdr 72 | # define Elf_Phdr Elf32_Phdr 73 | # define Elf_Shdr Elf32_Shdr 74 | # define Elf_Sym Elf32_Sym 75 | # define Elf_Rel Elf32_Rel 76 | # define Elf_Rela Elf32_Rela 77 | # define Elf_Dyn Elf32_Dyn 78 | # define Elf_Versym Elf32_Versym 79 | # define Elf_Verneed Elf32_Verneed 80 | # define Elf_Vernaux Elf32_Vernaux 81 | # define ELFW_ST_BIND(v) ELF32_ST_BIND(v) 82 | # define ELFW_ST_TYPE(v) ELF32_ST_TYPE(v) 83 | # define ELFW_R_SYM(v) ELF32_R_SYM(v) 84 | # define ELFW_R_TYPE(v) ELF32_R_TYPE(v) 85 | #endif 86 | 87 | __thread char g_objfcn_tls[OBJFCN_TLS_SIZE]; 88 | 89 | typedef struct { 90 | char* name; 91 | char* addr; 92 | } symbol; 93 | 94 | typedef struct { 95 | uint32_t nbuckets; 96 | uint32_t nchain; 97 | uint8_t tail[1]; 98 | } Elf_Hash; 99 | 100 | static uint32_t* elf_hash_buckets(Elf_Hash* hash) { 101 | return (uint32_t*)(hash->tail); 102 | } 103 | 104 | static uint32_t* elf_hash_chains(Elf_Hash* hash) { 105 | return (uint32_t*)(&elf_hash_buckets(hash)[hash->nbuckets]); 106 | } 107 | 108 | static uint32_t elf_hash_calc(const char* p) { 109 | uint32_t h = 0, g; 110 | while (*p) { 111 | h = (h << 4) + (unsigned char)*p++; 112 | g = h & 0xf0000000; 113 | h ^= g; 114 | h ^= g >> 24; 115 | } 116 | return h; 117 | } 118 | 119 | typedef struct { 120 | uint32_t nbuckets; 121 | uint32_t symndx; 122 | uint32_t maskwords; 123 | uint32_t shift2; 124 | uint8_t tail[1]; 125 | } Elf_GnuHash; 126 | 127 | static Elf_Addr* gnu_hash_bloom_filter(Elf_GnuHash* hash) { 128 | return (Elf_Addr*)(hash->tail); 129 | } 130 | 131 | static uint32_t* gnu_hash_buckets(Elf_GnuHash* hash) { 132 | return (uint32_t*)(&gnu_hash_bloom_filter(hash)[hash->maskwords]); 133 | } 134 | 135 | static uint32_t* gnu_hash_hashvals(Elf_GnuHash* hash) { 136 | return (uint32_t*)(&gnu_hash_buckets(hash)[hash->nbuckets]); 137 | } 138 | 139 | static uint32_t gnu_hash_calc(const char* p) { 140 | uint32_t h = 5381; 141 | for (; *p; p++) { 142 | h = h * 33 + (unsigned char)*p; 143 | } 144 | return h; 145 | } 146 | 147 | typedef struct { 148 | symbol* symbols; 149 | int num_symbols; 150 | char* code; 151 | size_t code_size; 152 | size_t code_used; 153 | 154 | int is_dyn; 155 | char* base; 156 | const char* strtab; 157 | Elf_Sym* symtab; 158 | Elf_Hash* elf_hash; 159 | Elf_GnuHash* gnu_hash; 160 | Elf_Versym* versym; 161 | Elf_Verneed* verneed; 162 | const char** verstrs; // indexed by versym 163 | } obj_handle; 164 | 165 | static char obj_error[256]; 166 | 167 | static uintptr_t align_down(uintptr_t v, size_t align) { 168 | return v & ~(align - 1); 169 | } 170 | 171 | static uintptr_t align_up(uintptr_t v, size_t align) { 172 | return align_down(v + align - 1, align); 173 | } 174 | 175 | #if OBJFCN_SPLIT_ALLOC 176 | 177 | static char* alloc_code(obj_handle* obj, size_t size) { 178 | char* r = obj->code + obj->code_used; 179 | obj->code_used += size; 180 | return r; 181 | } 182 | 183 | static void align_code(obj_handle* obj, size_t align) { 184 | obj->code_used = align_up(obj->code_used, align); 185 | } 186 | 187 | #else 188 | 189 | static char* code; 190 | static size_t code_used; 191 | 192 | static char* alloc_code(obj_handle* obj, size_t size) { 193 | char* r = code + code_used; 194 | code_used += size; 195 | return r; 196 | } 197 | 198 | static void align_code(obj_handle* obj, size_t align) { 199 | code_used = align_up(code_used, align); 200 | } 201 | 202 | static void init(void) { 203 | code = (char*)mmap(NULL, 1024 * 1024 * 1024, 204 | PROT_READ | PROT_WRITE | PROT_EXEC, 205 | MAP_PRIVATE | MAP_ANONYMOUS, 206 | -1, 0); 207 | if (code == MAP_FAILED) { 208 | sprintf(obj_error, "mmap failed"); 209 | } 210 | } 211 | 212 | #endif 213 | 214 | static char* read_file(const char* filename) { 215 | FILE* fp = NULL; 216 | char* bin = NULL; 217 | long filesize = 0; 218 | fp = fopen(filename, "rb"); 219 | if (fp == NULL) { 220 | sprintf(obj_error, "failed to open %s: %s", filename, strerror(errno)); 221 | return NULL; 222 | } 223 | 224 | if (fseek(fp, 0, SEEK_END) != 0) { 225 | sprintf(obj_error, "fseek failed: %s", strerror(errno)); 226 | goto error; 227 | } 228 | 229 | filesize = ftell(fp); 230 | if (filesize < 0) { 231 | sprintf(obj_error, "ftell failed: %s", strerror(errno)); 232 | goto error; 233 | } 234 | 235 | if (fseek(fp, 0, SEEK_SET) != 0) { 236 | sprintf(obj_error, "fseek failed: %s", strerror(errno)); 237 | goto error; 238 | } 239 | 240 | bin = (char*)malloc(filesize); 241 | if (fread(bin, 1, filesize, fp) != (size_t)filesize) { 242 | sprintf(obj_error, "fread failed: %s", strerror(errno)); 243 | goto error; 244 | } 245 | 246 | fclose(fp); 247 | return bin; 248 | 249 | error: 250 | free(bin); 251 | fclose(fp); 252 | return NULL; 253 | } 254 | 255 | static int should_load(Elf_Shdr* shdr) { 256 | #ifdef SHT_ARM_EXIDX 257 | return shdr->sh_flags & SHF_ALLOC && shdr->sh_type != SHT_ARM_EXIDX; 258 | #else 259 | return shdr->sh_flags & SHF_ALLOC; 260 | #endif 261 | } 262 | 263 | static size_t relocate(obj_handle* obj, 264 | const char* bin, 265 | Elf_Sym* symtab, 266 | const char* strtab, 267 | char** addrs, 268 | int code_size_only) { 269 | size_t code_size = 0; 270 | Elf_Ehdr* ehdr = (Elf_Ehdr*)bin; 271 | Elf_Shdr* shdrs = (Elf_Shdr*)(bin + ehdr->e_shoff); 272 | for (int i = 0; i < ehdr->e_shnum; i++) { 273 | Elf_Shdr* shdr = &shdrs[i]; 274 | int has_addend = shdr->sh_type == SHT_RELA; 275 | size_t relsize = has_addend ? sizeof(Elf_Rela) : sizeof(Elf_Rel); 276 | int relnum = shdr->sh_size / relsize; 277 | char* target_base = addrs[shdr->sh_info]; 278 | 279 | if ((shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA) || 280 | !should_load(&shdrs[shdr->sh_info])) { 281 | continue; 282 | } 283 | 284 | for (int j = 0; j < relnum; j++) { 285 | Elf_Rela* rel = (Elf_Rela*)(bin + shdr->sh_offset + relsize * j); 286 | char* target = target_base + rel->r_offset; 287 | Elf_Sym* sym = &symtab[ELFW_R_SYM(rel->r_info)]; 288 | int addend = has_addend ? rel->r_addend : 0; 289 | char* sym_addr = NULL; 290 | 291 | if (!code_size_only) { 292 | switch (ELFW_ST_TYPE(sym->st_info)) { 293 | case STT_SECTION: 294 | sym_addr = addrs[sym->st_shndx]; 295 | break; 296 | 297 | case STT_FUNC: 298 | case STT_OBJECT: 299 | sym_addr = (char*)sym->st_value; 300 | break; 301 | 302 | case STT_NOTYPE: 303 | if (sym->st_shndx == SHN_UNDEF) { 304 | sym_addr = (char*)dlsym(RTLD_DEFAULT, strtab + sym->st_name); 305 | if (sym_addr == NULL) { 306 | sprintf(obj_error, "failed to resolve %s", 307 | strtab + sym->st_name); 308 | return (size_t)-1; 309 | } 310 | } else { 311 | sym_addr = addrs[sym->st_shndx]; 312 | } 313 | break; 314 | 315 | default: 316 | sprintf(obj_error, "unsupported relocation sym %d", 317 | ELFW_ST_TYPE(sym->st_info)); 318 | return (size_t)-1; 319 | } 320 | //fprintf(stderr, "%d %s target=%p sym_addr=%p addend=%d\n", 321 | // j, strtab + sym->st_name, target, sym_addr, addend); 322 | } 323 | 324 | switch (ELFW_R_TYPE(rel->r_info)) { 325 | #ifdef R_32 326 | case R_32: 327 | if (!code_size_only) 328 | *(uint32_t*)target += (uint32_t)sym_addr + addend; 329 | break; 330 | #endif 331 | 332 | #ifdef R_64 333 | case R_64: 334 | if (!code_size_only) 335 | *(uint64_t*)target += (uint64_t)sym_addr + addend; 336 | break; 337 | #endif 338 | 339 | #ifdef R_PC32 340 | case R_PC32: 341 | if (!code_size_only) 342 | *(uint32_t*)target += (sym_addr - target) + addend; 343 | break; 344 | #endif 345 | 346 | #ifdef R_PLT32 347 | case R_PLT32: 348 | if (code_size_only) { 349 | code_size += 6 + 8; 350 | } else { 351 | #if defined(__x86_64__) 352 | void* dest = sym_addr; 353 | sym_addr = alloc_code(obj, 6 + 8); 354 | sym_addr[0] = 0xff; 355 | sym_addr[1] = 0x25; 356 | *(uint32_t*)(sym_addr + 2) = 0; 357 | *(uint64_t*)(sym_addr + 6) = (uint64_t)dest; 358 | #endif 359 | *(uint32_t*)target += (sym_addr - target) + addend; 360 | } 361 | break; 362 | #endif 363 | 364 | #if defined(__x86_64__) 365 | case R_X86_64_REX_GOTPCRELX: 366 | if (code_size_only) { 367 | code_size += 8; 368 | } else { 369 | void* dest = sym_addr; 370 | sym_addr = alloc_code(obj, 8); 371 | *(uint64_t*)(sym_addr) = (uint64_t)dest; 372 | *(uint32_t*)target += (sym_addr - target) + addend; 373 | } 374 | break; 375 | #endif 376 | 377 | #if defined(__arm__) 378 | case R_ARM_CALL: 379 | if (code_size_only) { 380 | code_size += 8; 381 | } else { 382 | void* dest = sym_addr; 383 | sym_addr = alloc_code(obj, 8); 384 | // ldr pc, [pc, #-4] 385 | *(uint32_t*)sym_addr = 0xe51ff004; 386 | *(uint32_t*)(sym_addr + 4) = (uint32_t)dest; 387 | int32_t v = ((sym_addr - target) + addend - 8) >> 2; 388 | if (v >= (1 << 23) || v < -(1 << 23)) { 389 | sprintf(obj_error, "Relocation out of range: %x", v); 390 | return (size_t)-1; 391 | } 392 | *(uint32_t*)target = 393 | (((uint8_t*)target)[3] << 24U) | (0xffffff & v); 394 | } 395 | break; 396 | 397 | case R_ARM_ABS32: 398 | if (!code_size_only) 399 | *(uint32_t*)target = (uint32_t)sym_addr + addend; 400 | break; 401 | #endif 402 | 403 | default: 404 | sprintf(obj_error, "Unknown reloc: %ld", 405 | (long)ELFW_R_TYPE(rel->r_info)); 406 | return (size_t)-1; 407 | } 408 | } 409 | } 410 | return code_size; 411 | } 412 | 413 | static int is_defined(Elf_Sym* sym) { 414 | int bind = ELFW_ST_BIND(sym->st_info); 415 | return ((bind == STB_GLOBAL || bind == STB_WEAK) && 416 | sym->st_shndx != SHN_UNDEF); 417 | } 418 | 419 | static void* objsym_dyn_elf_hash(obj_handle* obj, const char* symbol) { 420 | assert(obj->elf_hash); 421 | Elf_Hash* elf_hash = obj->elf_hash; 422 | 423 | uint32_t h = elf_hash_calc(symbol); 424 | uint32_t n = elf_hash_buckets(elf_hash)[h % elf_hash->nbuckets]; 425 | for (; n; n = elf_hash_chains(elf_hash)[n]) { 426 | Elf_Sym* sym = &obj->symtab[n]; 427 | 428 | if (!strcmp(symbol, obj->strtab + sym->st_name) && is_defined(sym)) { 429 | return obj->base + sym->st_value; 430 | } 431 | } 432 | return NULL; 433 | } 434 | 435 | static void* objsym_dyn_gnu_hash(obj_handle* obj, const char* symbol) { 436 | assert(obj->gnu_hash); 437 | Elf_GnuHash* gnu_hash = obj->gnu_hash; 438 | 439 | uint32_t h = gnu_hash_calc(symbol); 440 | // TODO(hamaji): Use the bloom filter. 441 | int n = gnu_hash_buckets(gnu_hash)[h % gnu_hash->nbuckets]; 442 | // fprintf(stderr, "lookup n=%d mask=%x\n", n, gnu_hash->maskwords); 443 | if (n == 0) return NULL; 444 | const uint32_t* hv = &gnu_hash_hashvals(gnu_hash)[n - gnu_hash->symndx]; 445 | for (Elf_Sym* sym = &obj->symtab[n];; ++sym) { 446 | uint32_t h2 = *hv++; 447 | if ((h & ~1) == (h2 & ~1) && 448 | !strcmp(symbol, obj->strtab + sym->st_name) && 449 | is_defined(sym)) { 450 | return obj->base + sym->st_value; 451 | } 452 | if (h2 & 1) break; 453 | } 454 | return NULL; 455 | } 456 | 457 | static void* objsym_dyn(obj_handle* obj, const char* symbol) { 458 | assert(obj->symtab); 459 | if (obj->gnu_hash) { 460 | return objsym_dyn_gnu_hash(obj, symbol); 461 | } else { 462 | return objsym_dyn_elf_hash(obj, symbol); 463 | } 464 | } 465 | 466 | #if DYN_SUPPORTED 467 | 468 | static void undefined() { 469 | LOGF("undefined function called\n"); 470 | abort(); 471 | } 472 | 473 | #endif 474 | 475 | #if defined(__aarch64__) 476 | 477 | typedef struct TlsDesc { 478 | ptrdiff_t (*entry)(struct TlsDesc*); 479 | void* arg; 480 | } TlsDesc; 481 | 482 | static ptrdiff_t return_tls(struct TlsDesc* desc) { 483 | return (ptrdiff_t)desc->arg; 484 | } 485 | 486 | #endif 487 | 488 | static void relocate_dyn(const char* reloc_type, obj_handle* obj, 489 | Elf_Rel* rel, int relsz) { 490 | size_t i; 491 | for (i = 0; i < relsz / sizeof(*rel); rel++, i++) { 492 | LOGF("rel offset=%x\n", (int)rel->r_offset); 493 | void** addr = (void**)(obj->base + rel->r_offset); 494 | int type = ELFW_R_TYPE(rel->r_info); 495 | int sym_idx = ELFW_R_SYM(rel->r_info); 496 | Elf_Sym* sym = obj->symtab + sym_idx; 497 | const char* sname = obj->strtab + sym->st_name; 498 | void* val = 0; 499 | 500 | val = objsym_dyn(obj, sname); 501 | if (!val) { 502 | const char* verstr = 0; 503 | if (obj->versym && obj->verneed) { 504 | int ver = obj->versym[sym_idx]; 505 | verstr = obj->verstrs[ver]; 506 | } 507 | if (verstr) { 508 | val = dlvsym(RTLD_DEFAULT, sname, verstr); 509 | } else { 510 | val = dlsym(RTLD_DEFAULT, sname); 511 | } 512 | } 513 | 514 | LOGF("%s: %p %s(%p) %d => %p\n", 515 | reloc_type, (void*)addr, sname, sym, type, val); 516 | 517 | switch (type) { 518 | #if 0 519 | case R_386_32: { 520 | *addr += (int)val; 521 | } 522 | case R_386_COPY: { 523 | if (val) { 524 | *addr = *(int*)val; 525 | } else { 526 | fprintf(stderr, "undefined: %s\n", sname); 527 | abort(); 528 | } 529 | } 530 | case R_386_GLOB_DAT: { 531 | break; 532 | } 533 | case R_386_JMP_SLOT: { 534 | if (val) { 535 | *addr = (int)val; 536 | } else { 537 | *addr = (int)&undefined; 538 | } 539 | break; 540 | } 541 | #endif 542 | 543 | #if DYN_SUPPORTED 544 | case R_GLOB_DAT: 545 | case R_JUMP_SLOT: { 546 | if (val) { 547 | *addr = val; 548 | } else { 549 | *addr = (void*)&undefined; 550 | } 551 | break; 552 | } 553 | 554 | case R_RELATIVE: { 555 | *addr = (void*)(*(char**)addr + (intptr_t)obj->base); 556 | break; 557 | } 558 | 559 | case R_64: { 560 | #if __SIZEOF_POINTER__ == 8 561 | *addr = (void*)((char*)val + rel->r_addend); 562 | #else 563 | *addr = val; 564 | #endif 565 | break; 566 | } 567 | #endif 568 | 569 | case R_X86_64_DTPMOD64: { 570 | // TODO(hamaji): Retrive the right module ID. 571 | *addr = (void*)1; 572 | break; 573 | } 574 | 575 | case R_X86_64_DTPOFF64: { 576 | break; 577 | } 578 | 579 | #if defined(__aarch64__) 580 | case R_AARCH64_TLSDESC: { 581 | TlsDesc* desc = (TlsDesc*)addr; 582 | desc->entry = &return_tls; 583 | desc->arg = (void*)(g_objfcn_tls + sym->st_value + rel->r_addend - (intptr_t)__builtin_thread_pointer()); 584 | break; 585 | } 586 | #endif 587 | 588 | default: 589 | LOGF("Unsupported reloc: %d\n", type); 590 | abort(); 591 | break; 592 | 593 | } 594 | 595 | } 596 | } 597 | 598 | static void parse_version(obj_handle* obj, int verneed_num) { 599 | int num_verstrs = 2; 600 | obj->verstrs = (const char**)calloc(num_verstrs, sizeof(char*)); 601 | 602 | Elf_Verneed* vn = obj->verneed; 603 | for (int i = 0; i < verneed_num; ++i) { 604 | LOGF("VERNEED: ver=%d cnt=%d file=%s aux=%d next=%d\n", 605 | vn->vn_version, vn->vn_cnt, obj->strtab + vn->vn_file, 606 | vn->vn_aux, vn->vn_next); 607 | Elf_Vernaux* vna = (Elf_Vernaux*)((char*)vn + vn->vn_aux); 608 | for (int j = 0; j < vn->vn_cnt; ++j) { 609 | LOGF(" VERNAUX: hash=%d flags=%d other=%d name=%s next=%d\n", 610 | vna->vna_hash, vna->vna_flags, vna->vna_other, 611 | obj->strtab + vna->vna_name, vna->vna_next); 612 | 613 | int ver = vna->vna_other; 614 | if (num_verstrs <= ver) { 615 | num_verstrs = ver + 1; 616 | obj->verstrs = (const char**)realloc(obj->verstrs, 617 | sizeof(char*) * num_verstrs); 618 | } 619 | obj->verstrs[ver] = obj->strtab + vna->vna_name; 620 | 621 | vna = (Elf_Vernaux*)((char*)vna + vna->vna_next); 622 | } 623 | vn = (Elf_Verneed*)((char*)vn + vn->vn_next); 624 | } 625 | } 626 | 627 | static int load_object_dyn(obj_handle* obj, const char* bin, 628 | const char* filename) { 629 | Elf_Ehdr* ehdr = (Elf_Ehdr*)bin; 630 | Elf_Phdr* phdrs = (Elf_Phdr*)(bin + ehdr->e_phoff); 631 | 632 | size_t max_addr = 0; 633 | for (int i = 0; i < ehdr->e_phnum; i++) { 634 | Elf_Phdr* phdr = &phdrs[i]; 635 | if (phdr->p_type != PT_LOAD) continue; 636 | size_t end_addr = align_up(phdr->p_vaddr + phdr->p_memsz, 4096); 637 | if (max_addr < end_addr) { 638 | max_addr = end_addr; 639 | } 640 | } 641 | 642 | char* code = alloc_code(obj, max_addr); 643 | align_code(obj, 4096); 644 | 645 | obj->base = code; 646 | obj->code_size = max_addr; 647 | obj->is_dyn = 1; 648 | 649 | for (int i = 0; i < ehdr->e_phnum; i++) { 650 | Elf_Phdr* phdr = &phdrs[i]; 651 | if (phdr->p_type != PT_LOAD) continue; 652 | memcpy(code + phdr->p_vaddr, bin + phdr->p_offset, phdr->p_filesz); 653 | } 654 | 655 | for (int i = 0; i < ehdr->e_phnum; i++) { 656 | Elf_Phdr* phdr = &phdrs[i]; 657 | if (phdr->p_type != PT_TLS) continue; 658 | assert(phdr->p_memsz <= OBJFCN_TLS_SIZE); 659 | memcpy(g_objfcn_tls, bin + phdr->p_offset, phdr->p_filesz); 660 | } 661 | 662 | for (int i = 0; i < ehdr->e_phnum; i++) { 663 | Elf_Phdr* phdr = &phdrs[i]; 664 | if (phdr->p_type != PT_DYNAMIC) continue; 665 | 666 | Elf_Dyn* dyns = (Elf_Dyn*)(code + phdr->p_vaddr); 667 | for (Elf_Dyn* dyn = dyns; dyn->d_tag; dyn++) { 668 | if (dyn->d_tag == DT_STRTAB) { 669 | obj->strtab = code + dyn->d_un.d_ptr; 670 | } 671 | } 672 | 673 | Elf_Rel* rel = NULL; 674 | int relsz = 0, pltrelsz = 0; 675 | void** init_array = NULL; 676 | int init_arraysz = 0; 677 | int verneed_num = 0; 678 | for (Elf_Dyn* dyn = dyns; dyn->d_tag; dyn++) { 679 | switch (dyn->d_tag) { 680 | case DT_NEEDED: { 681 | const char* name = obj->strtab + dyn->d_un.d_ptr; 682 | void* handle = dlopen(name, RTLD_GLOBAL); 683 | LOGF("DT_NEEDED %s %p\n", name, handle); 684 | (void)handle; 685 | break; 686 | } 687 | 688 | case DT_SYMTAB: 689 | obj->symtab = (Elf_Sym*)(code + dyn->d_un.d_ptr); 690 | break; 691 | 692 | case DT_HASH: 693 | obj->elf_hash = (Elf_Hash*)(code + dyn->d_un.d_ptr); 694 | break; 695 | 696 | case DT_GNU_HASH: 697 | obj->gnu_hash = (Elf_GnuHash*)(code + dyn->d_un.d_ptr); 698 | break; 699 | 700 | case DT_RELENT: 701 | case DT_PLTREL: { 702 | int pltrel = dyn->d_un.d_val; 703 | assert(pltrel == DT_RELA); 704 | break; 705 | } 706 | 707 | case DT_RELA: { 708 | rel = (Elf_Rel*)(code + dyn->d_un.d_ptr); 709 | LOGF("rel: %p\n", rel); 710 | break; 711 | } 712 | case DT_RELASZ: { 713 | relsz = dyn->d_un.d_val; 714 | LOGF("relsz: %d\n", relsz); 715 | break; 716 | } 717 | case DT_PLTRELSZ: { 718 | pltrelsz = dyn->d_un.d_val; 719 | LOGF("pltrelsz: %d\n", pltrelsz); 720 | break; 721 | } 722 | 723 | case DT_INIT_ARRAY: { 724 | init_array = (void**)(code + dyn->d_un.d_ptr); 725 | break; 726 | } 727 | case DT_INIT_ARRAYSZ: { 728 | init_arraysz = dyn->d_un.d_val; 729 | break; 730 | } 731 | 732 | case DT_VERSYM: { 733 | obj->versym = (Elf_Versym*)(code + dyn->d_un.d_ptr); 734 | break; 735 | } 736 | 737 | case DT_VERNEED: { 738 | obj->verneed = (Elf_Verneed*)(code + dyn->d_un.d_ptr); 739 | break; 740 | } 741 | 742 | case DT_VERNEEDNUM: { 743 | verneed_num = dyn->d_un.d_val; 744 | break; 745 | } 746 | 747 | } 748 | } 749 | 750 | parse_version(obj, verneed_num); 751 | 752 | assert(rel); 753 | relocate_dyn("rel", obj, rel, relsz); 754 | relocate_dyn("pltrel", obj, rel + relsz / sizeof(*rel), pltrelsz); 755 | 756 | #if defined(__arm__) || defined(__aarch64__) 757 | __builtin___clear_cache(obj->base, obj->base + obj->code_size); 758 | #endif 759 | 760 | if (init_array) { 761 | for (size_t i = 0; i < init_arraysz / sizeof(void*); i++) { 762 | LOGF("calling init_array: %p\n", init_array[i]); 763 | ((void(*)())(init_array[i]))(); 764 | } 765 | } 766 | } 767 | return 1; 768 | } 769 | 770 | static int load_object(obj_handle* obj, const char* bin, const char* filename) { 771 | Elf_Ehdr* ehdr = (Elf_Ehdr*)bin; 772 | Elf_Shdr* shdrs = (Elf_Shdr*)(bin + ehdr->e_shoff); 773 | //Elf_Shdr* shstrtab = &shdrs[ehdr->e_shstrndx]; 774 | Elf_Sym* symtab = NULL; 775 | int symnum = 0; 776 | int strtab_index = -1; 777 | const char* strtab = NULL; 778 | char* addrs[ehdr->e_shnum]; 779 | 780 | for (int i = 0; i < ehdr->e_shnum; i++) { 781 | Elf_Shdr* shdr = &shdrs[i]; 782 | if (shdr->sh_type == SHT_SYMTAB) { 783 | symtab = (Elf_Sym*)(bin + shdr->sh_offset); 784 | symnum = shdr->sh_size / sizeof(Elf_Sym); 785 | strtab_index = shdr->sh_link; 786 | } 787 | } 788 | 789 | for (int i = 0; i < ehdr->e_shnum; i++) { 790 | Elf_Shdr* shdr = &shdrs[i]; 791 | if (shdr->sh_type == SHT_STRTAB && i == strtab_index) { 792 | strtab = bin + shdr->sh_offset; 793 | } 794 | } 795 | 796 | #if OBJFCN_SPLIT_ALLOC 797 | { 798 | size_t expected_code_size = 0; 799 | for (int i = 0; i < ehdr->e_shnum; i++) { 800 | Elf_Shdr* shdr = &shdrs[i]; 801 | if (should_load(shdr)) { 802 | expected_code_size += shdr->sh_size; 803 | expected_code_size = align_up(expected_code_size, 16); 804 | } 805 | } 806 | size_t reloc_code_size = relocate(obj, bin, symtab, strtab, addrs, 807 | 1 /* code_size_only */); 808 | if (reloc_code_size == (size_t)-1) { 809 | return 0; 810 | } 811 | expected_code_size += reloc_code_size; 812 | 813 | obj->code_size = align_up(expected_code_size, 4096); 814 | obj->code = (char*)mmap(NULL, obj->code_size, 815 | PROT_READ | PROT_WRITE | PROT_EXEC, 816 | MAP_PRIVATE | MAP_ANONYMOUS, 817 | -1, 0); 818 | if (obj->code == MAP_FAILED) { 819 | sprintf(obj_error, "mmap failed: %s", strerror(errno)); 820 | return 0; 821 | } 822 | 823 | #if 0 824 | fprintf(stderr, "%p-%p (+%zx) %s\n", 825 | obj->code, obj->code + obj->code_size, 826 | expected_code_size, filename); 827 | #endif 828 | #if OBJFCN_LOG 829 | { 830 | char buf[256]; 831 | FILE* log_fp; 832 | sprintf(buf, "/tmp/objfcn.%d.log", getpid()); 833 | log_fp = fopen(buf, "ab"); 834 | fprintf(log_fp, "objopen %p-%p (+%zx) %s\n", 835 | obj->code, obj->code + obj->code_size, 836 | expected_code_size, filename); 837 | fclose(log_fp); 838 | } 839 | #endif 840 | } 841 | 842 | #endif 843 | 844 | memset(addrs, 0, sizeof(addrs)); 845 | for (int i = 0; i < ehdr->e_shnum; i++) { 846 | Elf_Shdr* shdr = &shdrs[i]; 847 | if (should_load(shdr)) { 848 | addrs[i] = alloc_code(obj, shdr->sh_size); 849 | align_code(obj, 16); 850 | if (shdr->sh_type != SHT_NOBITS) { 851 | memcpy(addrs[i], bin + shdr->sh_offset, shdr->sh_size); 852 | } 853 | } 854 | } 855 | 856 | for (int i = 0; i < symnum; i++) { 857 | Elf_Sym* sym = &symtab[i]; 858 | if (ELFW_ST_TYPE(sym->st_info) == STT_OBJECT || 859 | ELFW_ST_TYPE(sym->st_info) == STT_FUNC) { 860 | obj->num_symbols++; 861 | } 862 | } 863 | obj->symbols = (symbol*)malloc(sizeof(symbol) * obj->num_symbols); 864 | for (int i = 0, ns = 0; i < symnum; i++) { 865 | Elf_Sym* sym = &symtab[i]; 866 | if (ELFW_ST_TYPE(sym->st_info) == STT_OBJECT || 867 | ELFW_ST_TYPE(sym->st_info) == STT_FUNC) { 868 | const char* name = strtab + sym->st_name; 869 | char* addr = addrs[sym->st_shndx] + sym->st_value; 870 | // Write back so we can use the address later. 871 | sym->st_value = (Elf_Addr)addr; 872 | //fprintf(stderr, "%s => %p\n", name, addr); 873 | obj->symbols[ns].name = strdup(name); 874 | obj->symbols[ns].addr = addr; 875 | ns++; 876 | } 877 | } 878 | 879 | if (relocate(obj, bin, symtab, strtab, addrs, 0 /* code_size_only */) == 880 | (size_t)-1) { 881 | return 0; 882 | } 883 | 884 | #if defined(__arm__) || defined(__aarch64__) 885 | __builtin___clear_cache(obj->code, obj->code + obj->code_size); 886 | #endif 887 | 888 | return 1; 889 | } 890 | 891 | void* objopen(const char* filename, int flags) { 892 | char* bin = NULL; 893 | obj_handle* obj = NULL; 894 | Elf_Ehdr* ehdr = NULL; 895 | 896 | #if !OBJFCN_SPLIT_ALLOC 897 | if (!code) { 898 | init(); 899 | } 900 | if (code == MAP_FAILED) { 901 | return NULL; 902 | } 903 | #endif 904 | 905 | bin = read_file(filename); 906 | if (bin == NULL) { 907 | return NULL; 908 | } 909 | 910 | obj = (obj_handle*)malloc(sizeof(obj_handle)); 911 | if (obj == NULL) { 912 | sprintf(obj_error, "malloc failed"); 913 | free(bin); 914 | return NULL; 915 | } 916 | memset(obj, 0, sizeof(*obj)); 917 | 918 | ehdr = (Elf_Ehdr*)bin; 919 | if (memcmp(ehdr->e_ident, ELFMAG, 4)) { 920 | sprintf(obj_error, "%s is not ELF", filename); 921 | free(bin); 922 | free(obj); 923 | return NULL; 924 | } 925 | 926 | // TODO: more validation. 927 | 928 | if (ehdr->e_type == ET_DYN) { 929 | if (load_object_dyn(obj, bin, filename)) { 930 | free(bin); 931 | return obj; 932 | } 933 | } else { 934 | if (load_object(obj, bin, filename)) { 935 | free(bin); 936 | return obj; 937 | } 938 | } 939 | 940 | free(bin); 941 | objclose(obj); 942 | return NULL; 943 | } 944 | 945 | int objclose(void* handle) { 946 | obj_handle* obj = (obj_handle*)handle; 947 | if (obj->code) { 948 | #if OBJFCN_LOG 949 | char buf[256]; 950 | FILE* log_fp; 951 | sprintf(buf, "/tmp/objfcn.%d.log", getpid()); 952 | log_fp = fopen(buf, "ab"); 953 | fprintf(log_fp, "objclose %p-%p\n", 954 | obj->code, obj->code + obj->code_size); 955 | fclose(log_fp); 956 | #endif 957 | munmap(obj->code, obj->code_size); 958 | } 959 | for (int i = 0; i < obj->num_symbols; i++) { 960 | free(obj->symbols[i].name); 961 | } 962 | free(obj->verstrs); 963 | free(obj); 964 | return 0; 965 | } 966 | 967 | void* objsym(void* handle, const char* symbol) { 968 | obj_handle* obj = (obj_handle*)handle; 969 | if (obj->is_dyn) { 970 | return objsym_dyn(obj, symbol); 971 | } 972 | 973 | for (int i = 0; i < obj->num_symbols; i++) { 974 | if (!strcmp(obj->symbols[i].name, symbol)) { 975 | return obj->symbols[i].addr; 976 | } 977 | } 978 | return NULL; 979 | } 980 | 981 | char* objerror(void) { 982 | return obj_error; 983 | } 984 | -------------------------------------------------------------------------------- /objfcn.h: -------------------------------------------------------------------------------- 1 | #ifndef RUBY_OBJFCN_H 2 | #define RUBY_OBJFCN_H 1 3 | 4 | void* objopen(const char* filename, int flags); 5 | 6 | int objclose(void* handle); 7 | 8 | void* objsym(void* handle, const char* symbol); 9 | 10 | char* objerror(void); 11 | 12 | #endif /* RUBY_OBJFCN_H */ 13 | -------------------------------------------------------------------------------- /test_objfcn.c: -------------------------------------------------------------------------------- 1 | #include "objfcn.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "func.c" 7 | 8 | typedef int (*func_t)(int); 9 | 10 | int failed = 0; 11 | 12 | void check(int expected, int actual) { 13 | if (expected != actual) { 14 | fprintf(stderr, "expected %d but comes %d\n", expected, actual); 15 | failed++; 16 | } 17 | } 18 | 19 | int func_in_main(void) { 20 | return 99; 21 | } 22 | 23 | int main(int argc, char* argv[]) { 24 | if (argc <= 1) { 25 | fprintf(stderr, "object file not specified\n"); 26 | return 1; 27 | } 28 | void* handle = objopen(argv[1], 0); 29 | if (handle == NULL) { 30 | fprintf(stderr, "objopen failed: %s\n", objerror()); 31 | return 1; 32 | } 33 | func_t fp = (func_t)objsym(handle, "func"); 34 | if (fp == NULL) { 35 | fprintf(stderr, "objsym failed\n"); 36 | return 1; 37 | } 38 | check(func(-1), fp(-1)); 39 | check(func(-1), fp(-1)); 40 | objclose(handle); 41 | return failed; 42 | } 43 | -------------------------------------------------------------------------------- /test_objfcn_cpp.cc: -------------------------------------------------------------------------------- 1 | #include "objfcn.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "cpp.h" 9 | 10 | typedef int (*func_t)(int); 11 | 12 | int failed = 0; 13 | 14 | void check(int expected, int actual) { 15 | if (expected != actual) { 16 | fprintf(stderr, "expected %d but comes %d\n", expected, actual); 17 | failed++; 18 | } 19 | } 20 | 21 | int func_in_main(void) { 22 | return 99; 23 | } 24 | 25 | int main(int argc, char* argv[]) { 26 | if (argc <= 1) { 27 | fprintf(stderr, "object file not specified\n"); 28 | return 1; 29 | } 30 | void* handle = objopen(argv[1], 0); 31 | if (handle == NULL) { 32 | fprintf(stderr, "objopen failed: %s\n", objerror()); 33 | return 1; 34 | } 35 | func_t fp = (func_t)objsym(handle, "func"); 36 | if (fp == NULL) { 37 | fprintf(stderr, "objsym failed\n"); 38 | return 1; 39 | } 40 | check(140, fp(-1)); 41 | check(141, fp(-1)); 42 | 43 | Base* (*make_base)() = (Base*(*)())objsym(handle, "_Z8MakeBasev"); 44 | Base* b = make_base(); 45 | b->vf(); 46 | 47 | objclose(handle); 48 | 49 | std::string s("done"); 50 | fprintf(stderr, "%s failed=%d\n", s.c_str(), failed); 51 | 52 | return failed; 53 | } 54 | --------------------------------------------------------------------------------