summaryrefslogtreecommitdiff
path: root/README.md
blob: 7ff7ea3f4a5387bfce12d45664c69adc10212482 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# dlsub

A tool for replacing a subset of functions in dynamic libraries.

Let's say you're meddling around with a program that uses
[SDL](https://libsdl.org).  One thing you might want to do is replace an SDL
function (e.g. `SDL_SetWindowTitle`) with your own function so you have control
over it (e.g. you can set your own special window title).

This is possible on most operating systems. On Unix-like systems, you can use
the `LD_LIBRARY_PATH` environment variable to fool an application into using
your own dynamic library instead of the dynamic library it was intending to use.
On Windows, you can create a DLL in the same directory as the executable with
the same name as the one you want to replace. But the issue with this is you
probably only want to replace a few functions, and you still want access to the
original library functions (e.g. the *real* `SDL_SetWindowTitle`). This is where
dlsub comes in.

## Dependencies

You will need:

- [nasm](https://nasm.us)
- a C compiler

To install these on Ubuntu/Debian:

```
sudo apt install nasm tcc
```

On Unix-like systems, the default is to use TCC (for faster preprocessing and
less likelihood of weird syntax messing dlsub up). You can, however, override
this by setting the C_PREPROCESSOR environment variable.

## Figuring out which library file is being used

On Windows, it may just be a DLL file in the same directory as the exe.
Otherwise, you can install [depends.exe](https://www.dependencywalker.com/);
good luck.

On Unix-like systems, if you want to know what specific library files an
executable is using, run:

```bash
ldd <name of executable>
```

## Usage

The standard usage of dlsub is:

```bash
dlsub -I <library include directory> -i <library header file> -l <library file> -o <output name>
```

You can see `dlsub --help` for a list of all options.

You can specify multiple header files if the library has more than one.
Here is an example invocation for replacing SDL:

```bash
dlsub --no-warn -l /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so -I /usr/include/SDL2 -i SDL.h -i SDL_syswm.h -i SDL_vulkan.h -C -DSDL_DISABLE_IMMINTRIN_H -o sdl
```

(the `-DSDL_DISABLE_IMMINTRIN_H` is needed for tcc, and it also speeds up
processing)

You should now get a file called `sdl.c` and another one called `sdl.asm`.
Now let's say you want to replace `SDL_SetWindowTitle`. First, delete the line
in `sdl.asm`:

```
global SDL_SetWindowTitle:function
```

(This deletes the default replacement, i.e. to redirect to the real SDL
function).
Now at the end of sdl.c, add:

```c
DLSUB_EXPORT void SDL_SetWindowTitle(SDL_Window *window, const char *title) {
	REAL_SDL_SetWindowTitle(window, "substitute title");
}
```

The `DLSUB_EXPORT` ensures that the function is exported out to the dynamic
library (on Windows, where that distinction is made).

You can now compile libSDL2-2.0.so.0 on Linux, with:
```
nasm -f elf64 sdl.asm
cc -fPIC -shared sdl.o sdl.c -o libSDL2-2.0.so.0 -I/usr/include/SDL2
```

And run a program that uses SDL like this:
```
LD_LIBRARY_PATH=/directory/where/your/library/file/is ./some_application
```

Note that dlsub *cannot* handle dynamic libraries' objects (e.g.
`extern int foo;`), so if there are any you will have to make your own
substitutes for those.

## What's with the assembly file?

dlsub needs `nasm` in order to work. This is partly because of varargs
functions: In C, there's no way of redirecting one varargs
function to another.

Also, if for whatever reason there's a function in the dynamic library
that's not defined in any header file, it would be impossible to keep it working
without assembly.

## More examples...

### Replacing `XNextEvent` from libX11

```bash
dlsub --no-warn -l /usr/lib/x86_64-linux-gnu/libX11.so.6 -I /usr/include/X11 -i Xlib.h -i Xutil.h -o x11
```

(you can ignore the warnings).

Delete the line from x11.asm:

```
global XNextEvent:function
```

Add to the bottom of x11.c:

```c
/* let's hope nobody needs to use these */
void *_XCreateMutex_fn, *_XFreeMutex_fn, *_XLockMutex_fn,
	*_XUnlockMutex_fn, *_Xglobal_lock;

int XNextEvent(Display *dpy, XEvent *event) {
	int ret = REAL_XNextEvent(dpy, event);
	/* 
	change all key events to pressing "v"
	(may be a different key for non-QWERTY keyboards)
	*/
	if (event->type == KeyPress || event->type == KeyRelease)
		event->xkey.keycode = 55;
	return ret;
}
```

```bash
nasm -f elf64 x11.asm
cc -fPIC -shared x11.c x11.o -o libX11.so.6 -I/usr/include/X11
```

### Replacing `exp` from libm

Here's a silly example. This could cause some... interesting behavior.

Note that because of glibc name-mangling you can only replace *some* libm
functions.

```bash
dlsub --no-warn -l /lib/x86_64-linux-gnu/libm.so.6 -I /usr/include -i math.h -o math
```

Delete from math.asm:
```
global exp:function
```

Add to math.c:
```
double exp(double x) {
	return 2.0 * x;
}
```

```
nasm -f elf64 math.asm
cc -fPIC -shared math.c math.o -o libm.so.6
```