@@ 78,3 78,71 @@ map .rodata and .data sections correspondingly. Not that hard.
### Data relocations
+Things get more complicated if we have pointers inside data, like in
+following:
+
+```c
+const char *strings[] = {"foo", "bar", NULL};
+```
+
+We can find array relative to current intruction, sure, but what is
+inside this array? Right, addresses that we don't know at compile
+time. So dynamic loader have to populate this array with addresses of
+strings in .rodata at run-time. This is called data relocation. Can we
+avoid it?
+
+Yes, we can. We can store offsets instead of pointers:
+
+```
+const char *strtab = "\0foo\0bar\0";
+const long strings[] = [1, 5, -1];
+```
+
+And whenever we need string, we use "strtab + strings[i]" instead. A
+bit more verbose and requires some build automation to generate array
+of offsets, but now everything is back into .rodata and no relocations
+are necessary.
+
+### Procedure linkage table
+
+Now suppose we are trying to dynamically load shared object that have
+some third-party library, like libgdbm or libexpat, compiled in. And
+let's even assume that this third-party does not need data
+relocations. But we can be almost sure that it will call some
+functions from standard C library, like read(2) or malloc(3), and does
+it directly, since it is the most natural and efficient way for static
+linking.
+
+In ideal world all libraries would be minimalistic and only work with
+data provided by input buffers, but in our worlds that is not the
+case. Actually, I am not sure if it is even possible to design
+equivalent of "libcurl" in minimalistic way.
+=> https://nullprogram.com/blog/2018/06/10
+
+Problem is that we don't know addresses of functions in standard
+library either, so loaded shared object includes array of addresses
+for standard library functions together with directives about which
+functions and in which order should be populated by dynamic loader.
+And answer "which" is answered by function name. And to make it
+possible, main executable must maintain mapping from every function
+name in standard library to its address. And we might include every
+function in other libraries linked into main executable too, since
+price is already paid. This is quite involved process, and now length
+of function name actually affects size of the executable.
+
+The only alternative to this process is to compile standard library
+into shared object itself, but that would mean that executable will
+have multiple copies of standard library functions in memory at same
+time. Plus, now we need somehow to ensure that main application and
+shared object have compatible implementations of malloc(3) compiled
+in. Sounds like even bigger mess.
+
+## Conclusion
+
+When I started writing this post, I wanted to make a point that with a
+bit of coding discipline we can drastically reduce complexity of
+dynamic loading. While doing research I realized that it is not true.
+
+While interface of dlopen(3) could probably made more minimalistic,
+dynamically loading code that uses functions from standard library is
+fundamentally hard.