Skip to content

Commit 43f181e

Browse files
committed
Add dynamic loading (dlopen(), etc.) to stdlib
Adds an implementation of dynamic loading for E9Tool call instrumentation. This makes it possible to load and call functions in external shared objects/libraries, which has many applications. There are lots of caveats, please see the E9Tool user guide for more information.
1 parent 2f4179a commit 43f181e

12 files changed

Lines changed: 537 additions & 17 deletions

File tree

doc/e9tool-user-guide.md

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ to be used directly.
2828
* [2.2.2 Call Action Options](#s222)
2929
* [2.2.3 Call Action Standard Library](#s223)
3030
* [2.2.4 Call Action Initialization](#s224)
31+
* [2.2.4 Call Action Dynamic Loading](#s225)
3132
- [2.3 Plugin Actions](#s23)
3233

3334
---
@@ -942,7 +943,7 @@ stack manually inside the instrumentation code.
942943
#### <a id="s223">2.2.3 Call Action Standard Library</a>
943944

944945
The main limitation of call actions is that the instrumentation
945-
cannot use dynamically linked libraries, including `glibc`.
946+
cannot use dynamically linked libraries directly, including `glibc`.
946947
This is because the instrumentation binary is directly injected
947948
into the rewritten binary rather than dynamically/statically linked.
948949

@@ -957,21 +958,8 @@ However, only a subset of libc is implemented, so it is WYSIWYG.
957958
Many common libc functions, including file I/O and memory
958959
allocation, have been implemented.
959960

960-
It is not advisable to call the "real" `glibc` functions from call
961-
instrumentation, namely:
962-
963-
* `glibc` functions use the System V ABI which is not compatible with
964-
the clean call ABI.
965-
Specifically, the clean ABI does not align the stack nor save/restore
966-
floating point registers for performance reasons.
967-
* Many `glibc` functions are not reentrant and access/modify global state
968-
such as `errno`.
969-
Thus, calling `glibc` functions directly can break transparency and/or cause
970-
problems such as deadlocks.
971-
972-
Unlike `glibc`,
973-
the parallel libc is designed to be compatible with the clean ABI and
974-
handle problems such as deadlocks gracefully.
961+
Unlike `glibc` the parallel libc is designed to be compatible with the clean
962+
ABI and handle problems, such as deadlocks, more gracefully.
975963

976964
---
977965
#### <a id="s224">2.2.4 Call Action Initialization</a>
@@ -1004,6 +992,61 @@ zero/`NULL` for patched shared objects.
1004992
In the example above, the initialization function searches for an
1005993
environment variable `MAX`, and sets the `max` counter accordingly.
1006994

995+
---
996+
#### <a id="s225">2.2.5 Call Action Dynamic Loading</a>
997+
998+
The parallel libc also provides an implementation of the standard dynamic linker
999+
functions `dlopen()`, `dlsym()`, and `dlclose()`.
1000+
These can be used to dynamically load shared objects at runtime, or access
1001+
existing shared libraries that are already dynamically linked into the original
1002+
program.
1003+
To enable, simply define the `LIBDL` macro before including `stdlib.c`.
1004+
1005+
#define LIBDL
1006+
#include "stdlib.c"
1007+
1008+
The `dlinit(dynamic)` function must also be called in the `init()` routine,
1009+
where `dynamic` is the fourth argument to the `init()` function:
1010+
1011+
void init(int argc, char **argv, char **envp, void *dynamic)
1012+
{
1013+
int result = dlinit(dynamic);
1014+
...
1015+
}
1016+
1017+
Once initialized, the `dlopen()`, `dlsym()`, and `dlclose()` functions can be
1018+
used similarly to the standard `libdl` counterparts.
1019+
1020+
Note that function pointers returned by `dlsym()` **should not be called
1021+
directly** unless you know what you are doing.
1022+
This is because most libraries are compiled with the System V ABI, which is
1023+
incompatible with the clean call ABI used by the instrumentation.
1024+
To avoid ABI incompatibility, the external library code should be called using
1025+
a special wrapper function `dlcall()`:
1026+
1027+
intptr_t dlcall(void *func, arg1, arg2, ...);
1028+
1029+
The `dlcall()` function will:
1030+
1031+
* Align/restore the stack pointer to 16bytes, as required by the System V ABI.
1032+
* Save/restore the extended register state, including `%xmm0`, etc.
1033+
* Save/restore the glibc version of `errno`.
1034+
1035+
Be aware that the dynamic loading API has several caveats:
1036+
1037+
* The `dlopen()`, `dlsym()`, and `dlclose()` are wrappers for the glibc
1038+
versions of these functions (`__libc_dlopen`, etc.).
1039+
The glibc versions do not officially exist, so this functionality may change
1040+
at any time.
1041+
Also the glibc versions lack some features, such as `RTLD_NEXT`, that are
1042+
available with the standard libdl versions.
1043+
* Since glibc is required, the original binary must be dynamically linked.
1044+
* Many external library functions are not designed to be reentrant, and this
1045+
may cause deadlocks if a signal occurs when the signal handler is also
1046+
instrumented.
1047+
* The `dlcall()` function supports a maximum of 16 arguments.
1048+
* The `dlcall()` function is relatively slow, so ought to be used sparingly.
1049+
10071050
---
10081051
### <a id="s23">2.3 Plugin Actions</a>
10091052

0 commit comments

Comments
 (0)