One of the major differences you'll find is that, during the final link stage, a number of C library components are statically linked into the library, forming the INIT and FINI symbols among other things. These are specified with DT_INIT
and DT_FINI
entries in the program header; you will need to transform these into static constructor/destructor entries. DT_NEEDED entries will be lost in a transformation into a .o; you will need to re-add them manually.
The PLT generated in the final link stage needs to be either merged with the final output file, or transformed back into ordinary relocations; this is non-trivial, as the PLT is just code. The GOT is also an issue; it's located at a fixed relative offset from the .text segment, and contains pointers to data members. It also, however, contains a pointer to the _DYNAMIC structure, of which there can only be one per library or executable. And you can't change offsets in the GOT, because they're referenced directly from code.
So it's quite difficult to convert a .so to a true .o again; information has been lost in the conversion to PLT/GOTs. A better approach might be to alter the dynamic linker in the C library to support linking a shared library that's already mapped in memory as a static image. That is, you'd convert the .so to a .o simply by converting it to a page-aligned read-only section; then pass this to the dynamic linker to remap with appropriate permissions and perform normal shared library initialization. Then add a static constructor to call into the C library to initialize the shared library. Finally, add appropriate exported symbols to correspond to dynamic symbols in the shared library's .text segment.
One problem with this approach, though, is that static constructors might run before the static constructor that initializes your fake solib. In this case, they must not attempt to call functions from the solib, or you'll probably crash, as the solib is not yet initialized. This could potentially be avoided by making the exported symbols point into a trampoline function that ensures the solib is initialized first (not so easy with data symbols, though!)
You might also find that this previous question might be of some use to you.