diff --git a/CHANGES b/CHANGES index 22f86c1..bbe9c61 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,13 @@ Next version +- GPR#52: -base option consistently applied to mingw64, cygwin64 and msvc64. + Default is no longer specified, making msvc64 consistent with the other two + ports (David Allsopp) +- GPR#52: 64-bit ports can now always process RELOC_REL32* jumps by generating + trampolines for cases where the relocations are more than 32GiB away. New + functionality passed as flexdll_relocate_v2, which allows mixing executables + and DLLs compiled with and without the new features. If either executable or + DLL lacks the flexdll_relocate_v2 support, then it falls back to the previous + error message if a RELOC_REL32 is more than 2GiB away. (David Allsopp) - GPR#127: Recognise hyphens in option names in the COFF .drectve section. Fixes #126 (Reza Barazesh) - GPR#136: Fix parallel access crashes and misbehavior (David Allsopp, Jan Midtgaard, Antonin Décimo) - GPR#140: Fixes #29. Support relocation kind 0003 (IMAGE_REL_AMD64_ADDR32NB) and diff --git a/appveyor_build.sh b/appveyor_build.sh index 003ae31..1592217 100755 --- a/appveyor_build.sh +++ b/appveyor_build.sh @@ -24,24 +24,32 @@ function configure_ocaml { sed -i -e 's/@iflexdir@/-I"$(ROOTDIR)\/flexdll"/' Makefile.config.in fi + if [ "$1" = "full" ] ; then + DISABLE=() + else + DISABLE=(--disable-debugger \ + --disable-ocamldoc \ + --disable-systhreads \ + --disable-str-lib \ + --disable-unix-lib \ + --disable-bigarray-lib \ + $GRAPHICS_DISABLE \ + $OCAMLTEST_DISABLE \ + --disable-debug-runtime) + fi ./configure --build=x86_64-pc-cygwin --host=$OCAML_TARGET \ - --prefix=$OCAMLROOT \ - --disable-debugger \ - --disable-ocamldoc \ - --disable-systhreads \ - --disable-str-lib \ - --disable-unix-lib \ - --disable-bigarray-lib \ - $GRAPHICS_DISABLE \ - $OCAMLTEST_DISABLE \ - --disable-debug-runtime + --prefix=$OCAMLROOT "${DISABLE[@]}" else # "Classic" configuration cp config/m-nt.h $HEADER_DIR/m.h cp config/s-nt.h $HEADER_DIR/s.h - sed -e "s|PREFIX=.*|PREFIX=$OCAMLROOT|" \ - -e 's/\(OTHERLIBRARIES\|WITH_DEBUGGER\|WITH_OCAMLDOC\|DEBUGGER\|EXTRALIBS\|WITH_OCAMLBUILD\|CAMLP4\)=.*/\1=/' \ + if [ "$1" = "full" ] ; then + DISABLE=() + else + DISABLE=(-e 's/\(OTHERLIBRARIES\|WITH_OCAMLDOC\|WITH_DEBUGGER\|DEBUGGER\|EXTRALIBS\|WITH_OCAMLBUILD\|CAMLP4\)=.*/\1=/') + fi + sed -e "s|PREFIX=.*|PREFIX=$OCAMLROOT|" "${DISABLE[@]}" \ config/Makefile.$OCAML_PORT > $CONFIG_DIR/Makefile #run "Content of config/Makefile" cat $CONFIG_DIR/Makefile fi @@ -196,14 +204,26 @@ done popd if [ "$SKIP_OCAML_TEST" = no ] ; then - configure_ocaml + configure_ocaml full + + if [[ -e otherlibs/win32unix/Makefile ]]; then + unix_dir='win32' + else + unix_dir='' + fi + # This tortures the ocamldoc compilation as it means that dllcamlstr, + # dllunix and ocamlrun will all be more than 2GiB apart. + sed -i -e "s/^LDOPTS=.*/\0 -ldopt -base -ldopt 0x210000000/" "otherlibs/${unix_dir}unix/Makefile" + sed -i -e "/^include \.\.\/Makefile/aLDOPTS=-ldopt -base -ldopt 0x310000000" otherlibs/str/Makefile cd flexdll git remote add local $(echo "$APPVEYOR_BUILD_FOLDER"| cygpath -f -) -f --tags run "git checkout $APPVEYOR_REPO_COMMIT" git checkout merge cd .. + mv $OCAMLROOT $OCAMLROOT-Disabled run "make world" $MAKEOCAML flexdll world + mv $OCAMLROOT-Disabled $OCAMLROOT fi if [ "$ARTEFACTS" = 'yes' ] ; then diff --git a/cmdline.ml b/cmdline.ml index 1d23053..50766ba 100644 --- a/cmdline.ml +++ b/cmdline.ml @@ -44,7 +44,7 @@ let implib = ref false let deffile = ref None let stack_reserve = ref None let no_rel_relocs = ref false -let base_addr = ref "0x10000" +let base_addr = ref None let usage_msg = Printf.sprintf @@ -86,7 +86,7 @@ let specs = [ "-norelrelocs", Arg.Set no_rel_relocs, " Ensure that no relative relocation is generated"; - "-base", Arg.String (fun s -> base_addr := s), + "-base", Arg.String (fun s -> base_addr := Some s), " Specify base address (Win64 only)"; "-pthread", Arg.Unit (fun () -> extra_args := "-pthread" :: !extra_args), diff --git a/flexdll.c b/flexdll.c index edada97..ac5d24c 100644 --- a/flexdll.c +++ b/flexdll.c @@ -42,7 +42,9 @@ typedef unsigned long uintnat; typedef struct { UINT_PTR kind; char *name; UINT_PTR *addr; } reloc_entry; typedef struct { char *first; char *last; DWORD old; } nonwr; typedef struct { nonwr *nonwr; reloc_entry entries[]; } reloctbl; -typedef struct { void *addr; char *name; } dynsymbol; +typedef struct { void *addr; char *name; } symtbl_entry; +typedef struct { void *addr; char *name; void *trampoline; } dynsymbol; +typedef struct { UINT_PTR size; symtbl_entry entries[]; } raw_symtbl; typedef struct { UINT_PTR size; dynsymbol entries[]; } symtbl; typedef struct dlunit { void *handle; @@ -51,7 +53,7 @@ typedef struct dlunit { int count; struct dlunit *next,*prev; } dlunit; -typedef void *resolver(void*, const char*); +typedef dynsymbol *resolver(void*, const char*); static HANDLE units_mutex = INVALID_HANDLE_VALUE; @@ -259,7 +261,7 @@ static void cannot_resolve_msg(char *name, err_t *err) { err->message[l+n] = 0; } -static void relocate(resolver f, void *data, reloctbl *tbl, err_t *err) { +static void relocate(resolver f, void *data, reloctbl *tbl, void **jmptbl, err_t *err) { reloc_entry *ptr; INT_PTR s; DWORD prev_protect; @@ -267,6 +269,10 @@ static void relocate(resolver f, void *data, reloctbl *tbl, err_t *err) { SYSTEM_INFO si; char *page_start, *page_end; char *prev_page_start = (char*)1, *prev_page_end = (char*)1; + dynsymbol *sym; + int rel_offset; + char *reloc_type; + MEMORY_BASIC_INFORMATION info; if (!tbl) return; @@ -278,12 +284,13 @@ static void relocate(resolver f, void *data, reloctbl *tbl, err_t *err) { for (ptr = tbl->entries; ptr->kind; ptr++) { if (ptr->kind & RELOC_DONE) continue; - s = (UINT_PTR) f(data,ptr->name); - if (!s) { + sym = f(data, ptr->name); + if (!sym) { err->code = 2; cannot_resolve_msg(ptr->name, err); goto restore; } + s = (UINT_PTR)sym->addr; /* Set up page protection to allow the relocation. We will undo the change on the next relocation if it falls in a different @@ -320,81 +327,83 @@ static void relocate(resolver f, void *data, reloctbl *tbl, err_t *err) { switch (ptr->kind & 0xff) { case RELOC_ABS: - *(ptr->addr) += s; + rel_offset = -1; break; case RELOC_REL32: - s -= (INT_PTR)(ptr -> addr) + 4; - s += *((INT32*) ptr -> addr); - if (s != (INT32) s) { - sprintf(err->message, "flexdll error: cannot relocate %s RELOC_REL32, target is too far: %p %p", ptr->name, (void *)((UINT_PTR) s), (void *) ((UINT_PTR)(INT32) s)); - err->code = 3; - goto restore; - } - *((UINT32*) ptr->addr) = (INT32) s; + rel_offset = 4; + reloc_type = "REL32"; break; case RELOC_REL32_1: - s -= (INT_PTR)(ptr -> addr) + 5; - s += *((INT32*) ptr -> addr); - if (s != (INT32) s) { - sprintf(err->message, "flexdll error: cannot relocate RELOC_REL32_1, target is too far: %p %p",(void *)((UINT_PTR) s), (void *) ((UINT_PTR)(INT32) s)); - err->code = 3; - goto restore; - } - *((UINT32*) ptr->addr) = (INT32) s; + rel_offset = 5; + reloc_type = "REL32_1"; break; case RELOC_REL32_2: - s -= (INT_PTR)(ptr -> addr) + 6; - s += *((INT32*) ptr -> addr); - if (s != (INT32) s) { - sprintf(err->message, "flexdll error: cannot relocate RELOC_REL32_2, target is too far: %p %p",(void *)((UINT_PTR) s), (void *) ((UINT_PTR)(INT32) s)); - err->code = 3; - goto restore; - } - *((UINT32*) ptr->addr) = (INT32) s; + rel_offset = 6; + reloc_type = "REL32_2"; break; case RELOC_REL32_3: - s -= (INT_PTR)(ptr -> addr) + 7; - s += *((INT32*) ptr -> addr); - if (s != (INT32) s) { - sprintf(err->message, "flexdll error: cannot relocate RELOC_REL32_3, target is too far: %p %p",(void *)((UINT_PTR) s), (void *) ((UINT_PTR)(INT32) s)); - err->code = 3; - goto restore; - } - *((UINT32*) ptr->addr) = (INT32) s; + rel_offset = 7; + reloc_type = "REL32_3"; break; case RELOC_REL32_4: - s -= (INT_PTR)(ptr -> addr) + 8; - s += *((INT32*) ptr -> addr); - if (s != (INT32) s) { - sprintf(err->message, "flexdll error: cannot relocate RELOC_REL32_4, target is too far: %p %p",(void *)((UINT_PTR) s), (void *) ((UINT_PTR)(INT32) s)); - err->code = 3; - goto restore; - } - *((UINT32*) ptr->addr) = (INT32) s; + rel_offset = 8; + reloc_type = "REL32_4"; break; case RELOC_REL32_5: - s -= (INT_PTR)(ptr -> addr) + 9; - s += *((INT32*) ptr -> addr); - if (s != (INT32) s) { - sprintf(err->message, "flexdll error: cannot relocate RELOC_REL32_5, target is too far: %p %p",(void *)((UINT_PTR) s), (void *) ((UINT_PTR)(INT32) s)); - err->code = 3; - goto restore; - } - *((UINT32*) ptr->addr) = (INT32) s; + rel_offset = 9; + reloc_type = "REL32_5"; break; case RELOC_32NB: - s += *((INT32*) ptr -> addr); - if (s != (INT32) s) { - sprintf(err->message, "flexdll error: cannot relocate %s RELOC_32NB, target is too far: %p %p", ptr->name, (void *)((UINT_PTR) s), (void *) ((UINT_PTR)(INT32) s)); - err->code = 3; - goto restore; - } - *((UINT32*) ptr->addr) = (INT32) s; + rel_offset = 0; + reloc_type = "32NB"; break; default: fprintf(stderr, "flexdll: unknown relocation kind"); exit(2); } + + if (rel_offset < 0) { + *(ptr->addr) += s; + } else { + if (rel_offset) + s -= (INT_PTR)(ptr -> addr) + rel_offset; + s += *((INT32*) ptr -> addr); +retry: + if (s != (INT32) s) { + if (!jmptbl) { + sprintf(err->message, "flexdll error: cannot relocate %s RELOC_%s, target is too far: %p %p", + ptr->name, reloc_type, (void *)((UINT_PTR) s), (void *) ((UINT_PTR)(INT32) s)); + err->code = 3; + return; + } + if (!sym->trampoline) { + void* trampoline; + /* trampolines cannot be created for data */ + if (VirtualQuery(sym->addr, &info, sizeof(info)) && !(info.Protect & 0xf0)) { + sprintf(err->message, "flexdll error: cannot relocate %s RELOC_%s, target is too far, and not executable: %p %p", + ptr->name, reloc_type, (void *)((UINT_PTR) s), (void *) ((UINT_PTR)(INT32) s)); + err->code = 3; + return; + } + trampoline = sym->trampoline = *jmptbl; + /* rex.W jmpq $0x0(%rip) */ + *((__int64*)trampoline) = 0x25ff48; + /* Place the actual symbol immediately after the instruction */ + *((UINT_PTR*)((char*)trampoline + 7)) = (UINT_PTR)sym->addr; + /* Pad with nop */ + *(((char*)trampoline + 15)) = 0x90; + *((UINT_PTR*)jmptbl) += 16; + } + s = (UINT_PTR)(sym->trampoline); + s -= (INT_PTR)(ptr->addr) + rel_offset; + s += *((INT32*)ptr->addr); + } + if (s != (INT32)s) { + sym->trampoline = NULL; + goto retry; + } + *((UINT32*) ptr->addr) = (INT32)s; + } ptr->kind |= RELOC_DONE; } restore: @@ -407,8 +416,10 @@ static void relocate(resolver f, void *data, reloctbl *tbl, err_t *err) { } } -static void relocate_master(resolver f, void *data, reloctbl **ptr, err_t *err) { - while (0 == err->code && *ptr) relocate(f,data,*ptr++,err); +static void relocate_master(resolver f, void *data, reloctbl **ptr, void *jmptbl, err_t *err) { + void **pjmptbl = jmptbl ? &jmptbl : NULL; + while (0 == err->code && *ptr) + relocate(f, data, *ptr++, pjmptbl, err); } /* Symbol tables */ @@ -432,7 +443,7 @@ static int compare_dynsymbol(const void *s1, const void *s2) { return strcmp(((dynsymbol*) s1) -> name, ((dynsymbol*) s2) -> name); } -static void *find_symbol(symtbl *tbl, const char *name) { +static dynsymbol *find_symbol(symtbl *tbl, const char *name) { static dynsymbol s; dynsymbol *sym; @@ -442,14 +453,14 @@ static void *find_symbol(symtbl *tbl, const char *name) { sym = bsearch(&s,&tbl->entries,tbl->size, sizeof(dynsymbol),&compare_dynsymbol); - return (NULL == sym ? NULL : sym -> addr); + return sym; } /* API */ -extern symtbl static_symtable; +extern raw_symtbl static_symtable; static dlunit *units = NULL; static dlunit main_unit; @@ -467,13 +478,40 @@ static void unlink_unit(dlunit *unit) { if (unit->next) unit->next->prev=unit->prev; } -static void *find_symbol_global(void *data, const char *name) { - void *sym; +static symtbl *augment_symtbl(raw_symtbl *raw_symtbl) { + symtbl *result; + dynsymbol *ptr; + symtbl_entry *src; + int i; + result = (symtbl*)malloc(raw_symtbl->size * sizeof(dynsymbol) + sizeof(UINT_PTR)); + ptr = result->entries; + src = raw_symtbl->entries; + result->size = raw_symtbl->size; + i = (int)result->size; + while (i-- > 0) { + ptr->addr = src->addr; + ptr->name = (src++)->name; + (ptr++)->trampoline = NULL; + } + return result; +} + +static symtbl *get_static_symtable(void) { + static symtbl *table = NULL; + + if (table) + return table; + else + return (table = augment_symtbl(&static_symtable)); +} + +static dynsymbol *find_symbol_global(void *data, const char *name) { + dynsymbol *sym; dlunit *unit; (void)data; /* data is unused */ if (!name) return NULL; - sym = find_symbol(&static_symtable, name); + sym = find_symbol(get_static_symtable(), name); if (sym) return sym; for (unit = units; unit; unit = unit->next) { @@ -488,17 +526,41 @@ static void *find_symbol_global(void *data, const char *name) { return NULL; } -int flexdll_relocate(void *tbl) { +int flexdll_relocate_v2(void *tbl, void *jmptbl) { err_t * err; err = get_tls_error(TLS_ERROR_RESET); if(err == NULL) return 0; if (!tbl) { printf("No master relocation table\n"); return 0; } - relocate_master(find_symbol_global, NULL, tbl, err); + relocate_master(find_symbol_global, NULL, tbl, jmptbl, err); if (err->code) return 0; return 1; } +int flexdll_relocate(void *tbl) {return flexdll_relocate_v2(tbl, NULL);} + +void set_env_ptr(char* name, void* ptr) { + char env[256]; + +#if defined(CYGWIN) || __STDC_SECURE_LIB__ >= 200411L + sprintf(env, "%p", ptr); +#endif + +#ifdef CYGWIN + setenv(name, env, 1); +#elif __STDC_SECURE_LIB__ >= 200411L + _putenv_s(name, env); +#else + { + char* s; + sprintf(env, "%s=%p", name, relocate); + s = malloc(strlen(env) + 1); + strcpy(s, env); + putenv(s); + } +#endif +} + #ifdef CYGWIN void *flexdll_dlopen(const char *file, int mode) { #else @@ -506,32 +568,15 @@ void *flexdll_wdlopen(const wchar_t *file, int mode) { #endif void *handle; dlunit *unit; - char flexdll_relocate_env[256]; int exec = (mode & FLEXDLL_RTLD_NOEXEC ? 0 : 1); - void* relocate = (exec ? &flexdll_relocate : 0); err_t * err; err = get_tls_error(TLS_ERROR_RESET); if(err == NULL) return NULL; if (!file) return &main_unit; -#ifdef CYGWIN - sprintf(flexdll_relocate_env,"%p",relocate); - setenv("FLEXDLL_RELOCATE", flexdll_relocate_env, 1); -#else -#if __STDC_SECURE_LIB__ >= 200411L - sprintf(flexdll_relocate_env,"%p",relocate); - _putenv_s("FLEXDLL_RELOCATE", flexdll_relocate_env); -#else - { - char* s; - sprintf(flexdll_relocate_env,"FLEXDLL_RELOCATE=%p",relocate); - s = malloc(strlen(flexdll_relocate_env) + 1); - strcpy(s, flexdll_relocate_env); - putenv(s); - } -#endif /* __STDC_SECURE_LIB__ >= 200411L*/ -#endif /* CYGWIN */ + set_env_ptr("FLEXDLL_RELOCATE", (exec ? &flexdll_relocate : 0)); + set_env_ptr("FLEXDLL_RELOCATE_V2", (exec ? &flexdll_relocate_v2 : 0)); again: if (units_mutex == INVALID_HANDLE_VALUE) { @@ -558,7 +603,7 @@ void *flexdll_wdlopen(const wchar_t *file, int mode) { else { unit = malloc(sizeof(dlunit)); unit->handle = handle; - unit->symtbl = ll_dlsym(handle, "symtbl"); + unit->symtbl = augment_symtbl(ll_dlsym(handle, "symtbl")); unit->count = 1; unit->global = 0; push_unit(unit); @@ -568,7 +613,7 @@ void *flexdll_wdlopen(const wchar_t *file, int mode) { if (exec) { /* Relocation has already been done if the flexdll's DLL entry point is used */ - flexdll_relocate(ll_dlsym(handle, "reloctbl")); + flexdll_relocate_v2(ll_dlsym(handle, "reloctbl"), ll_dlsym(handle, "jmptbl")); if (err->code) { flexdll_dlclose(unit); ReleaseMutex(units_mutex); return NULL; } } @@ -611,12 +656,12 @@ void flexdll_dlclose(void *u) { if (NULL == u || u == &main_unit) return; ll_dlclose(unit->handle); unit->count--; - if (unit->count == 0) { unlink_unit(unit); free(unit); } + if (unit->count == 0) { unlink_unit(unit); free(unit->symtbl); free(unit); } } void *flexdll_dlsym(void *u, const char *name) { - void *res; + dynsymbol *res; err_t * err; err = get_tls_error(TLS_ERROR_NOP); if (err == NULL) return NULL; @@ -626,10 +671,10 @@ void *flexdll_dlsym(void *u, const char *name) { return NULL; } if (u == &main_unit) res = find_symbol_global(NULL,name); - else if (NULL == u) res = find_symbol(&static_symtable,name); + else if (NULL == u) res = find_symbol(get_static_symtable(),name); else res = find_symbol(((dlunit*)u)->symtbl,name); ReleaseMutex(units_mutex); - return res; + return (res ? res->addr : NULL); } char *flexdll_dlerror(void) { @@ -648,9 +693,9 @@ char *flexdll_dlerror(void) { void flexdll_dump_exports(void *u) { dlunit *unit = u; - if (NULL == u) { dump_symtbl(&static_symtable); } + if (NULL == u) { dump_symtbl(get_static_symtable()); } else if (u == &main_unit) { - dump_symtbl(&static_symtable); + dump_symtbl(get_static_symtable()); for (unit = units; unit; unit = unit->next) if (unit->global) { dump_symtbl(unit->symtbl); } } diff --git a/flexdll_initer.c b/flexdll_initer.c index 5f23dc6..4d57c0b 100644 --- a/flexdll_initer.c +++ b/flexdll_initer.c @@ -19,16 +19,37 @@ #include typedef int func(void*); +typedef int func_v2(void*,void*); extern int reloctbl; +#if defined(_WIN64) || defined(__CYGWIN64__) +extern int jmptbl; +#endif static int flexdll_init() { func *sym = 0; - char *s = getenv("FLEXDLL_RELOCATE"); - if (!s) { fprintf(stderr, "Cannot find FLEXDLL_RELOCATE\n"); return FALSE; } - sscanf(s,"%p",&sym); - /* sym = 0 means "loaded not for execution" */ - if (!sym || sym(&reloctbl)) return TRUE; +#if defined(_WIN64) || defined(__CYGWIN64) + func_v2 *sym_v2 = 0; +#endif + /* If the supplied symbol is NULL, treat as "loaded not for execution" */ +#if defined(_WIN64) || defined(__CYGWIN64) + char *s = getenv("FLEXDLL_RELOCATE_V2"); + if (!s) { +#else + char *s; +#endif + s = getenv("FLEXDLL_RELOCATE"); + if (!s) { fprintf(stderr, "Cannot find FLEXDLL_RELOCATE\n"); return FALSE; } + /* The executable image doesn't support the V2 interface, so RELOC_REL32 + may fail. */ + sscanf(s, "%p", &sym); + if (!sym || sym(&reloctbl)) return TRUE; +#if defined(_WIN64) || defined(__CYGWIN64) + } else { + sscanf(s, "%p", &sym_v2); + if (!sym_v2 || sym_v2(&reloctbl, &jmptbl)) return TRUE; + } +#endif return FALSE; } diff --git a/reloc.ml b/reloc.ml index 9b3c8ca..74fb613 100644 --- a/reloc.ml +++ b/reloc.ml @@ -410,7 +410,7 @@ module StrSet = Set.Make(String) by their name). It also lists segments that are normally write-protected and that must be de-protected to enable the patching process. *) -let add_reloc_table obj obj_name p = +let add_reloc_table obj obj_name p trampolines = let sname = Symbol.gen_sym () in (* symbol pointing to the reloc table *) let sect = Section.create ".reltbl" 0xc0300040l in let data = Buffer.create 1024 in @@ -432,33 +432,33 @@ let add_reloc_table obj obj_name p = rtype - https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#x64-processors *) - let kind = match !machine, rel.rtype with + let kind, may_trampoline = match !machine, rel.rtype with | `x86, 0x06 (* IMAGE_REL_I386_DIR32 *) | `x64, 0x01 (* IMAGE_REL_AMD64_ADDR64 *) -> - 0x0002 (* absolute, native size (32/64) *) + 0x0002, false (* absolute, native size (32/64) *) | `x86, 0x07 (* IMAGE_REL_I386_DIR32NB *) | `x64, 0x03 (* IMAGE_REL_AMD64_ADDR32NB *) -> - 0x0007 (* 32nb *) + 0x0007, true (* 32nb *) | `x64, 0x04 (* IMAGE_REL_AMD64_REL32 *) | `x86, 0x14 (* IMAGE_REL_I386_REL32 *) when not !no_rel_relocs -> - 0x0001 (* rel32 *) + 0x0001, true (* rel32 *) | `x64, 0x05 (* IMAGE_REL_AMD64_REL32_1 *) when not !no_rel_relocs-> - 0x0004 (* rel32_1 *) + 0x0004, true (* rel32_1 *) | `x64, 0x06 (* IMAGE_REL_AMD64_REL32_2 *) when not !no_rel_relocs-> - 0x0005 (* rel32_2 *) + 0x0005, true (* rel32_2 *) | `x64, 0x07 (* IMAGE_REL_AMD64_REL32_3 *) when not !no_rel_relocs-> - 0x0008 (* rel32_3 *) + 0x0008, true (* rel32_3 *) | `x64, 0x08 (* IMAGE_REL_AMD64_REL32_4 *) when not !no_rel_relocs-> - 0x0003 (* rel32_4 *) + 0x0003, true (* rel32_4 *) | `x64, 0x09 (* IMAGE_REL_AMD64_REL32_5 *) when not !no_rel_relocs-> - 0x0006 (* rel32_5 *) + 0x0006, true (* rel32_5 *) | (`x86 | `x64), (0x0a (* IMAGE_REL_{I386|AMD64}_SECTION *) | 0x0b (* IMAGE_REL_{I386|AMD64}_SECREL*) ) -> - 0x0100 (* debug relocs: ignore *) + 0x0100, false (* debug relocs: ignore *) | _, k -> let msg = @@ -469,6 +469,7 @@ let add_reloc_table obj obj_name p = (* Printf.eprintf "%s\n%!" msg; 0x0001 *) in + if may_trampoline then trampolines := StrSet.add rel.symbol.sym_name !trampolines; int_to_buf data kind; (* name *) @@ -605,6 +606,12 @@ let add_master_reloc_table obj names symname = sect.data <- `String (Buffer.to_bytes data); obj.sections <- sect :: obj.sections +let add_master_jmp_table obj names symname = + let trampolines = StrSet.cardinal names in + let sect = Section.create ".mjmptbl" 0xe0500020l in + obj.symbols <- (Symbol.export symname sect 0l) :: obj.symbols; + sect.data <- `Uninit (trampolines * 16); + obj.sections <- sect :: obj.sections let collect_dllexports obj = @@ -851,7 +858,7 @@ let build_dll link_exe output_file files exts extra_args = List.iter (fun fn -> collect_file (find_file fn)) exts; if main_pgm then add_def (usym "static_symtable") - else add_def (usym "reloctbl"); + else (add_def (usym "reloctbl"); if !machine = `x64 then add_def (usym "jmptbl")); if !machine = `x64 then add_def "__ImageBase" else add_def "___ImageBase"; @@ -909,6 +916,7 @@ let build_dll link_exe output_file files exts extra_args = let libobjects = Hashtbl.create 16 in let reloctbls = ref [] in + let trampolines = ref StrSet.empty in let exported = ref StrSet.empty in List.iter (fun s -> exported := StrSet.add (usym s) !exported) !defexports; @@ -929,7 +937,7 @@ let build_dll link_exe output_file files exts extra_args = Printf.printf "** Imported symbols for %s:\n%!" name; StrSet.iter print_endline imps ); - let sym = add_reloc_table obj name (fun s -> StrSet.mem s.sym_name imps) in + let sym = add_reloc_table obj name (fun s -> StrSet.mem s.sym_name imps) trampolines in reloctbls := sym :: !reloctbls in @@ -1035,7 +1043,11 @@ let build_dll link_exe output_file files exts extra_args = add_export_table obj (if !noexport then [] else StrSet.elements !exported) (usym (if main_pgm then "static_symtable" else "symtbl")); - if not main_pgm then add_master_reloc_table obj !reloctbls (usym "reloctbl"); + if not main_pgm then begin + add_master_reloc_table obj !reloctbls (usym "reloctbl"); + if !machine = `x64 then + add_master_jmp_table obj !trampolines (usym "jmptbl"); + end; if !errors then exit 2; @@ -1116,7 +1128,9 @@ let build_dll link_exe output_file files exts extra_args = in let extra_args = - if !machine = `x64 then (Printf.sprintf "/base:%s " !base_addr) ^ extra_args else extra_args + match !machine, !base_addr with + | `x64, Some base_addr -> Printf.sprintf "/base:%s %s" base_addr extra_args + | _ -> extra_args in let extra_args = @@ -1134,10 +1148,11 @@ let build_dll link_exe output_file files exts extra_args = with the Windows 7 SDK in 64-bit mode. *) Printf.sprintf - "link /nologo %s%s%s%s%s /implib:%s /out:%s /subsystem:%s %s %s %s" + "link /nologo %s%s%s%s%s%s /implib:%s /out:%s /subsystem:%s %s %s %s" (if !verbose >= 2 then "/verbose " else "") (if link_exe = `EXE then "" else "/dll ") (if main_pgm then "" else "/export:symtbl /export:reloctbl ") + (if main_pgm || !machine = `x86 then "" else "/export:jmptbl ") (if main_pgm then "" else if !noentry then "/noentry " else let s = match !machine with @@ -1158,9 +1173,16 @@ let build_dll link_exe output_file files exts extra_args = else let def_file, oc = open_temp_file "flexlink" ".def" in Printf.fprintf oc "EXPORTS\n reloctbl\n symtbl\n"; + if !machine = `x64 then + Printf.fprintf oc " jmptbl\n"; close_out oc; Filename.quote def_file in + let extra_args = + match !machine, !base_addr with + | `x64, Some base_addr -> Printf.sprintf "-Xlinker --image-base -Xlinker %s %s" base_addr extra_args + | _ -> extra_args + in Printf.sprintf "%s %s%s -L. %s %s -o %s %s %s %s %s" !gcc @@ -1179,9 +1201,16 @@ let build_dll link_exe output_file files exts extra_args = else let def_file, oc = open_temp_file "flexlink" ".def" in Printf.fprintf oc "EXPORTS\n reloctbl\n symtbl\n"; + if !machine = `x64 then + Printf.fprintf oc " jmptbl\n"; close_out oc; Filename.quote def_file in + let extra_args = + match !machine, !base_addr with + | `x64, Some base_addr -> Printf.sprintf "-Xlinker --image-base -Xlinker %s %s" base_addr extra_args + | _ -> extra_args + in Printf.sprintf "%s -m%s %s%s -L. %s %s -o %s %s %s %s %s %s" !gcc