Simple PIC

This project demonstrates turning a COFF capability into PIC without using a loader.

Project Files

Notes

This project demonstrates how to turn a COFF capability (written to PICO conventions) into PIC without a loader.

Use the ./link program to turn the test.x86.o or test.x64.o demonstration programs into PIC:

./link /path/to/simplepic/loader.spec demo/test.x64.o out.bin

The above is possible because Crystal Palace has several tools to dynamically rewrite a PICO to make it work as PIC. These features often rely on helper functions. This project implements its PIC helper functions in bin/services.x64.o. And, we make this services code part of our PICO capability by merging it via loader.spec.

Entry Point at Position 0

Crystal Palace uses go() as the entry point for its PIC and PICO programs. make pic +gofirst in loader.spec moves our go() function to position 0 for us (if it's not already there).

Resolving Win32 APIs

One of the problems PIC programs must solve is resolving Win32 APIs. Crystal Palace's PIC dynamic function resolution rewrites a compiled program to delegate MODULE$Function calls to a user-specified resolver.

In this project, we use dfr "resolve" "ror13" "KERNEL32, NTDLL" to setup a resolver for any Win32 APIs from KERNEL32 or NTDLL. The arguments to the resolve function are the ror13 hashes of the requested module and function.

One of the downsides of the ror13 resolver is that we can't load any missing libraries into our process. To help with this, we setup a default resolver to handle other Win32 API resolutions. This command is dfr "resolve_ext" "strings. By giving the default resolver string arguments, we have the option to load a missing library if we need to. Also, we can safely use KERNEL32 and NTDLL functions in this default resolver--because they're resolved by a module-specific resolver we declared earlier.

findModuleByHash and findModuleByFunc are from the Tradecraft Garden Library. It's included into our program with mergelib "../libtcg/libtcg.x64.zip".

Fixing x86 pointers

One of the plagues of writing position-independent code is coping with x86's direct addressing model. Instructions which work with data expect to act on full pointers. This is a problem, because we don't know the full pointers to anything until runtime.

This project uses fixptrs "_caller" to help with this problem. When fixptrs is setup, Crystal Palace will rewrite data references to call our _caller function to help construct a full pointer on the spot. This little boost makes x86 PIC look like x64 PIC. And, as a bonus, fixptrs gives us "string constants" from x86 PIC too.

Global variables

fixbss "getBSS" rewrites any references to the .bss section (unintialized global variables) to call getBSS() and adds the .bss offset to our helper's return value. This restores uninitialized global variables in PIC.

The getBSS function in services.c looks for a read/write data cave to act as home for the BSS section. getBSS searches for a suitable slack space between a mapped read/write section's real size and its page-rounded up virtual size in memory. Here, we look for a large enough data cave in the .data section of either the current program's loaded module or Kernel32. One downside of this techique is it's not friendly to cohabitating with other PIC using the same technique.

What about all of the extra stuff?

Our services module defines a lot of helper functions to help make COFFs written to PICO conventions work as x86 and x64 PIC. While this is a lot of extra cruft, we use +optimize with make pic to enable link-time optimization in this program. With link-time optimization enabled, any helpers not needed by our capability are discarded at link-time.

License

This project is licensed under the BSD License.