Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support relocation kind 0003 and extend IMAGE_REL_ types #141

Merged
merged 5 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGES
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
Next version
- 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
IMAGE_REL_I386_DIR32NB. Extend relative types to IMAGE_REL_AMD64_REL32_5. (Jonah Beckford)

Version 0.43
- GPR#108: Add -lgcc_s to Cygwin's link libraries, upstreaming a patch from the
Expand Down
40 changes: 36 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,11 +371,43 @@ Note that you can define and use the `_imp__XXX` symbols by hand, you
don't have to use the `__declspec(dllimport)` notation (this is useful
if you use a compiler that doesn't support this notation).

Anyway, there is no compelling reason for adopting this style. A very
small advantage might be that there will be fewer relocations at runtime
and that more code pages can be shared amongst several instances of the
same DLL used by different processes.
There *are* compelling reasons to adopt this style.

A very small advantage might be that there will be fewer relocations at
runtime and that more code pages can be shared amongst several instances
of the same DLL used by different processes.

A big advantage on x86_64 systems is to avoid relocation errors like:

```text
Fatal error: cannot load shared library plug1
Reason: flexdll error: cannot relocate XXX
RELOC_REL32, target is too far: FFFFFFFF2EDEC956 000000002EDEC956
```

Without `__declspec(dllimport)` your symbol (ex. `extern XXX`) generates
the following pseudo-assembly:

```asm
CALL XXX ; this is a 32-bit relative address
```

These 32-bit relative addresses are restricted to 2GiB jumps, and there is
no guarantee that your main programs and all its DLLs will reside in the same
2GiB block of virtual memory. The more DLLs you have, and the bigger they
are, the more likely you will encounter a `RELOC_REL32` error.

flexdll will verify every 32-bit relative address `CALL` during
`flexdll_dlopen` to ensure they are within 2GiB.
However, the indirection introduced by `__declspec(dllimport) extern` makes
the pseudo-assembly instead:

```asm
void *_imp__XXX = &XXX;
CALL [_imp__XXX] ; this is a 64-bit indirect address
```

which works well with flexdll.

## Advanced topic: static constructors and the entry point

Expand Down
49 changes: 42 additions & 7 deletions flexdll.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,17 @@
typedef long intnat;
typedef unsigned long uintnat;

/* RELOC_ constants except RELOC_DONE have ordinal values based on when
they were introduced to the code base. These ordinal values are
persisted in .obj files so do not re-use any existing ordinals. */
#define RELOC_REL32 0x0001
#define RELOC_ABS 0x0002
#define RELOC_REL32_4 0x0003
#define RELOC_REL32_1 0x0004
#define RELOC_REL32_2 0x0005
#define RELOC_REL32_3 0x0008
#define RELOC_REL32_4 0x0003
#define RELOC_REL32_5 0x0006
#define RELOC_32NB 0x0007
#define RELOC_DONE 0x0100

typedef struct { UINT_PTR kind; char *name; UINT_PTR *addr; } reloc_entry;
Expand Down Expand Up @@ -326,6 +332,36 @@ static void relocate(resolver f, void *data, reloctbl *tbl, err_t *err) {
}
*((UINT32*) ptr->addr) = (INT32) s;
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;
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;
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;
break;
case RELOC_REL32_4:
s -= (INT_PTR)(ptr -> addr) + 8;
s += *((INT32*) ptr -> addr);
Expand All @@ -336,21 +372,20 @@ static void relocate(resolver f, void *data, reloctbl *tbl, err_t *err) {
}
*((UINT32*) ptr->addr) = (INT32) s;
break;
case RELOC_REL32_1:
s -= (INT_PTR)(ptr -> addr) + 5;
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_1, target is too far: %p %p",(void *)((UINT_PTR) s), (void *) ((UINT_PTR)(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;
break;
case RELOC_REL32_2:
s -= (INT_PTR)(ptr -> addr) + 6;
case RELOC_32NB:
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));
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;
}
Expand Down
23 changes: 19 additions & 4 deletions reloc.ml
Original file line number Diff line number Diff line change
Expand Up @@ -427,19 +427,34 @@ let add_reloc_table obj obj_name p =
let syms = ref [] in
let reloc secsym min max rel =
if p rel.symbol then (
(* kind *)
(* kind = f(machine,rtype)
- a RELOC_ constant in flexdll.c

rtype
- https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#x64-processors *)
let kind = match !machine, rel.rtype with
| `x86, 0x06 (* IMAGE_REL_I386_DIR32 *)
| `x64, 0x01 (* IMAGE_REL_AMD64_ADDR64 *) ->
0x0002 (* absolute, native size (32/64) *)

| `x86, 0x07 (* IMAGE_REL_I386_DIR32NB *)
| `x64, 0x03 (* IMAGE_REL_AMD64_ADDR32NB *) ->
0x0007 (* 32nb *)

| `x64, 0x04 (* IMAGE_REL_AMD64_REL32 *)
| `x86, 0x14 (* IMAGE_REL_I386_REL32 *) when not !no_rel_relocs ->
0x0001 (* rel32 *)

| `x64, 0x05 when not !no_rel_relocs -> 0x0004 (* rel32_1 *)
| `x64, 0x08 when not !no_rel_relocs-> 0x0003 (* rel32_4 *)
| `x64, 0x06 when not !no_rel_relocs-> 0x0005 (* rel32_2 *)
| `x64, 0x05 (* IMAGE_REL_AMD64_REL32_1 *) when not !no_rel_relocs->
0x0004 (* rel32_1 *)
| `x64, 0x06 (* IMAGE_REL_AMD64_REL32_2 *) when not !no_rel_relocs->
0x0005 (* rel32_2 *)
| `x64, 0x07 (* IMAGE_REL_AMD64_REL32_3 *) when not !no_rel_relocs->
0x0008 (* rel32_3 *)
| `x64, 0x08 (* IMAGE_REL_AMD64_REL32_4 *) when not !no_rel_relocs->
0x0003 (* rel32_4 *)
| `x64, 0x09 (* IMAGE_REL_AMD64_REL32_5 *) when not !no_rel_relocs->
0x0006 (* rel32_5 *)

| (`x86 | `x64), (0x0a (* IMAGE_REL_{I386|AMD64}_SECTION *) |
0x0b (* IMAGE_REL_{I386|AMD64}_SECREL*) ) ->
Expand Down