Sunday, October 17, 2010

Misc progress

There were some irregularities in the FLAIR output vs my output. The first is that I realized I had misunderstood what FLAIR calls modules. When I had seen FLAIR stuff before, it had been all windows junk and every line corresponded to one function. However, the test file I worked with was a .a file. Since a .a file contains .o files, this made it so each library module was actually a collection of functions in each .o file. I think was done because of the way linking groups object files on desktop / operating systems. However, this assumption is not valid for embedded systems due to the heavy static analysis done. I'll add a policy option to control how these signatures are generated. Additionally, there was a bug resulting in skipping functions that had multiple defined names. Code did something like size = next - current positions. If size is 0, assume undefined symbol and skip. However, this 0 size turns out to because of multiple names at the same file position and I was mentally mixing up the tables for all symbols vs defined symbols.
Regarding GUI progress, I've been trying to learn how to write a custom QAbstractScrollArea. This is the long term solution to disassembly, hex, and other complex scroll area issues. Instead of generating just the current page using hacks, a proper widget will be implemented that can fix things up as needed. Not going so well so far, but at least I know roughly what I need to do. It looks like the options are basically to implement QAbstractScrollArea directly or subclass QTextEditor or similar. I tried messing with subclassing QTextEdit, and it looks like it would become a hackish mess quickly. Signals were changing some of the values I tried to override and who knows how it would stand up to future or past Qt revisions. The other solution seems more manageable and proper way to do things. I'll probably implement a hex viewer first since its simpler to toy with than using the full uvudec engine.

Saturday, October 9, 2010

Object architecture plugin-able

The object engine is now pluginable. This was the last major planned core architecture enhancement for the semester. Object plugins allow say, to create a plugin for Microchip's C18 compiler if it used some format (I think it did, but I don't remember for sure). However, this did result in one hack that I haven't figured out if is actually a good idea or really is a hack. There is now a function getPrimaryExecutableAddressSpace() that returns the area where the main program is expected to reside. It seems this will work in most cases for the general "just disassemble the binary" case, so maybe this isn't an issue. Or maybe I should iterate over all executable segments? The issue with the latter is that often these aren't desirable to print. For example, on Linux x86 with ELF files, those aren't desirable. However, one of the engine enhancements was grouping object file format, architecture, operating system (future placeholder), and debugger (future placeholder) into a UVDRuntime class. This makes it so that if enough information is given, it is possible to combine attributes to resolve these special cases.
There are still several parts of the code that need to be improved and will be hammered out more as I need to support a more advanced architecture like x86. The specific semantics of how to read data from within a memory section need to be refined. For example, should the encapsulated data object actually do the mappings for the virtual address space? For example, say the address space actually starts at 0x08400000. Should the first valid address be 0 or 0x08400000 on that data object? One issue with the latter is that if an architecture does not provide byte level addressing, a lot of functions may not make sense. It seems most if not all architectures base their addressing on bytes, not words, so this may not be an issue. Since the number of memory spaces should be fairly small with perhaps up to a thousand on a large Windows program with many DLL dependencies, it may not hurt to provide objects that can map the addresses in both ways as a convenience since it looks like each mapper object only consumes about 24 bytes (virtual func table, target data, start target address, end target address, start read address, end read address) which is relatively small.
Now that the core engine enhancements are done, I will work on the library recognition enhancements. There are several large action items. First, make the BFD output matches FLAIR output exactly. There are some advanced reference features and my lack of understanding of the symbol references that are getting in the way of that. Second, decouple FLIRT code from bfd code. This will mean that if someone can write an object module that finds relocations, it can generate pat files. Finally, implement library recognition in the analysis engine. This is a lower priority since the generated .pat and .sig files can be used in other programs, so are not useless by themselves.
I'm also still trying to figure out GUI issues. The short term solution seems to be to make the scroll bar based on address only and only render the area currently shown. An example software that does this is the Metasm GUI.
Advantages:
  • Much faster render time
  • It will be easier to figure out scroll bar position. Changing data analysis does not mean we'd have to recalculate our position. For a large program, and most are probably large enough, the user probably wouldn't notice this difference anyway due to the huge number of elements at each position
Disadvantages
  • We must perform our own logic when an address is clicked on instead of using tags. This was probably going to happen eventually anyway in case we jumped address spaces
  • To make scrolling be line (which seems more intuitive to me), we will have to buffer some of the nearby rendering and feed it line by line
  • Some advanced level text editor/viewer features may be lost (copy text over multiple pages, etc)

Sunday, October 3, 2010

Plugin away at architecture enhancements

Okay so I wrote this at 1:30 AM or something and I'm really tired, but its hopefully at least somewhat coherent brain dump.
With the new semester comes many new goals. One of the major goals was to get a plugin system up and running.
The first part of this was to modularize the architecture instead of assuming UVD Disassembler config file based architecture which is obviously not gong to work in every case. I spent most of a day or two a few weekends ago and this is now done. First, I separated the code out so that it worked as a dynamically assigned UVDArchitecture object. The second part was to make it a plugin. Although I don't like the way load paths and such are treated (security issues for starters), I have at least a basic system running. Example output:
[mcmaster@gespenst bin]$ ./uvudec.dynamic --help
uvudec version 0.3.0
libuvudec version 0.3.0
Copyright 2009-2010 John McMaster
Portions copyright GNU (MD5 implementation)

Usage: ./uvudec.dynamic
Args:
--debug-general (debug.flag.general): set given debug flag
--debug-flirt (debug.flag.flirt): set given debug flag
--help (action.help): print this message and exit
--version (action.version): print version and exit
...
--plugin (plugin.name): load given library name as plugin
--plugin-path (plugin.path.append): append dir to plugin search path
--plugin-path-prepend (plugin.path.prepend): prepend dir to plugin search path
--input (target.file): source file for data
--output (output.file): output program (default: stdout)

Loaded plugins (1 / 1):

Plugin uvdasm:
--config-language (config.language): default config interpreter language (plugins may require specific)
python: use Python (default)
--config-language-interface (config.language_interface): how to access a specifc interpreter (options as availible)
exec: execute interpreter, parse results
API: use binary API to interpreter (default)
--arch-file (arch.file): architecture/CPU module file
Which reminds me, the GNU hash thing doesn't need to be in there and should be removed since its GPL contamination. Plugins can be also added easily by third parties as the recursive make simply tries to execute all makefiles one level down from the $ROOT/plugin dir. So, also you have to do is copy an example plugin and you can try it out without editing any of the core makefiles. Maybe I'll do something similar to Linux style kernel modules where you latch onto an installed build system. There is an issue with current code installing nicely and having modules compile the same because installed headers will all be prefixed with uvd/, but for dev they currently aren't. I could put all headers in a uvd dir. I was thinking of simply sym linking uvd dir to main code dir. This wouldn't work on native Windows builds, but I don't know if I will ever support that anyway. Probably Cygwin or mingw at best. I should look at some projects like libxml2 that I know do this and see how their build system handles it.
The other major area that needs to be improved, and more so than architecture, is object file abstraction. Assuming input files were only raw binaries leads to lazy addressing throughout the code where its assumed all addresses are absolute. This isn't quite true as RAM is in a different address space as code (on some architectures), but since I'm not doing heavy analysis yet, these haven't been well separated. These improvements will be primarily driven by the need to abstract the object file formats and provide the ability to write plugins
I've never written a plugin architeiture before and so I've played around with several design patterns to try to make it work smooth. Ultimately, it seems different components need different interfaces to work well. What its seeming to boil down to though is that analysis events are best broadcasted as events and if people need to listen to them they register to a single callback and filter out the ones they don't want. However, with heavier duty objects such as architecture instantiation, I haven't quite decided on the exact mechanism. There are several constraints influencing this decision. First, ability to register loaders should be accessible for non-plugins as I haven't strictly made binary loaders and such have to be dynamic libraries and such yet. This requirement may change in the future as it would make the code more regular. Second, it is probably a good idea for factory methods to automatically unregister themselves if a plugin is unloaded. Third, for interactive purposes, it is desirable to be able to probe how good of a match a loader is to an input format without actually instantiating the engine, if possible.
To deal with the first issue, there needs to be a non-plugin way to register creation callbacks. So, simply iterating over the loaded plugins and calling a loadObjectFile or such function is not enough. At the very least, there would also need to be an additional list of registered callbacks. So, this seems to favor the latter method exclusively as it simplifies the code. The main downside is that plugins may have to be more aware of architectural changes. I might make it so that the plugin engine registers loader handlers to give an additional option if its not too much effort.
For the second, using the hybrid plugin and registration method would solve this and I might do that. For the last issue, this creates the issue of needing to maintain correlated function sets. Its certainly easier to do this with plugins since they provide coordinated data structures. Otherwise, I would have to create some hash maps during registration to correlate them. And, since there was no identifier returned to know what engine (plugin) it was, it would be difficult to track which engine it was that provided the best loading. These factors seem to indicate that the best long term solution would be to make every architecture and binary format loader its own plugin.
As far as the actual design of the object abstraction itself, I've been looking into several existing object abstraction frameworks. Mostly though I'm going off of binutils since its what I'm most familiar with. They seem to have a bias towards ELF, but do in fact support a lot of architectures and object formats.
On the GUI side. I tried adding clickable address links, but led to performance issues with large documents. I'll have to look into this some more. I'm thinking of making a custom "scroll" bar that shows the address locations and generates the screen only as needed. I know from time to time I like to copy and paste stuff though, and this might make doing that difficult, but possible, over areas larger than one screen. Also, IDA 6 has been released with a new spiffy Qt driven GUI. I probably won't get to try it out at least until February if I go work for MIT Lincoln Labs full time. But I did get to look at some screenshots and it looks nice.

Friday, September 17, 2010

On disassembler graphical user interfaces

There is now a minimal GUI up and running:

The demonstration here is not so much that I've yet made a usable program, but that I learned the basics of Qt and can start moving forward with what it will take to make a usable GUI.
As part of this process, I wanted to review some of the leading disassembler environments out there and what I like and don't like about their interfaces. I might interject a few things that are more feature related, but try to stick to usability.
The biggest open source player is probably Metasm. I had forgotten about their GUI component until someone reminded me of it recently. I had mostly looked at them before because, during the course of this project, they released a decompiler module. I only played with it briefly, but porting it to a plugin might server as a good starting point for me. In any case, this is what I came up with their GUI disassembling objdump:
Hmm it didn't disassemble it. Not a huge deal, it wasn't hard to figure out how to start it disassembling:
And the decompile feature was pretty easy to use as well:
To be fair, they do label it as as "sample application" of the metasm library rather than a full application bundled with metasm. That said, here are my comments:
-I would expect it to disassemble by default. If the user had a truly good reason why it shouldn't, maybe they should use a "open wizard" menu option that gives advanced open options.
-The graph view is decent. I'd like to see thicker lines
-It doesn't seem like the GUI is a main focus. If it wants to be a serious competitor to IDA, a full GUI should be made. But, RE's typically don't like GUI stuff, so I can see why it might not happen
-Needs more keyboard shotcuts
-Couldn't get the comment function to work. For some reason it seems to decompile
-Crashed on me after using it for only a few minutes
-I'd probably prefer tabs to floating UI elements across the desktop. At the very least, I'd prefer them bound to a main window. Some software. such as Xilinx ISE allows either if people really cared.
-The underlying engine seems pretty powerful and with work, a nicer GUI could be made for it. As such, I might consider writing a metasm plugin if it seems I can really leverage some of their features, such as the decompiling. I'd need to see if I can get uvsync and metasmsync plugins though so they can cleanly exchange analysis data and maybe avoid the issue of linking C++ against Ruby.
-I shouldn't need to know Ruby to run it. Since this is intended as a Ruby sample, seems acceptable given their target audience

Here's what Hex-Rays shows for IDA (http://www.hex-rays.com/idapro/pix/idalarge.gif):
Overall, the GUI is decent since it has so many features. I'm not a high IDA user as I don't have a license for it, but there is the older version available. Also, I've just used it on occasion as some of the companies I've done work for have had a license I can use. So, some of my complaints may be inaccurate and I might not just know how to custimize it / where the correct command is. Some of the areas I think I could improve on:
-Price tag: $$$
-Structure definitions. Supposedly you can import C header files instead of using their janky structure defining tool. I couldn't figure it out. My other complaint about structures is that it doesn't seem you can define a structure on the stack
-Debugging: Far behind OllyDbg. WinDbg is okay, but seriously it should support GDB server out of the box. And WTF is the Bochs debugger?
-The IDA Pro book is pretty clear the closest thing to undo in IDA is to close the workspace and open it again
-IDC script: fortunately, IDAPython exists. Admittedly though, IDC was made at a time when such tools at Python weren't (well?) developed. Plus, IDC is still a million times better than OllyScript
-Graph view is overall decent. I see it sometimes will go out of its way to interleave unrelated loop basic blocks together to try, I think, to make consistent loop entry point shapes.

I use OllyDbg quite a bit of Windows RE. Since I'm too lazy to fire it + VMWare up, grabbed a screenshot from http://www.joestewart.org/morphine-dll/:
For a free product, its nice, but I don't really understand why they don't release the source code. Speaking of that, don't they admit to using a GPL'd assembler?!?!? Seems like anyone could clearly request the source code from them. Anyway what I think:
-Overall, debugging interface is way better than IDA
-I like the CPU view especially
-Stack view is good. I find it has a tendency to force ESP view upon you and for some programs its nicer to use EBP view. Showing function args is very nice.
-User interface could be more flexible. I go long stretches without using the binary view below, making it wasted space

I'd use Immunity debugger more since I like Python, but the phone home bit creeps me out. Granted, I do typically operate on the assumption the machines I'm working on are owned, but its the principle I guess. Anyway, here's their screenshot of it (http://www.immunityinc.com/images/adwithtext5.jpg):
I don't know enough about this product to really write about it. I do know they tried to conform to OllyDbg stuff, so their GUI is similar. I think theres is more powerful though with such additions as the graph view shown above. The real kicker for me not using it is that it seems to claim some compatibility with OllyDbg plugins, but I found they mostly just crash.

The last big one I know of is PyDbg. If i recall though, it depends on PaiMei, which is a tutorial in itself to get running. Or maybe the dependency is the other way around. I think PaiMei in turn requires IDA, another discouragement to me. In any case, when someone showed it to me before, I wasn't very experienced in RE and it was over my head. So, I can't make any real observations about it other than I remember it being difficult to setup due to a large number of dependencies. Here's a token screenshot of PaiMei:
I must say, it has a somewhat appealing UI.

So, in summary, here are the features most lacking in other environments I want:
-Undo. Sure, it might be hard to undo a function call during debugging. But, is it really so hard to undo converting between code and data or undo a function rename?
-Good keyboard shortcuts. I'd like to think of what would be my "ideal" shortcut layout given no history, and then think about what I'd like to do to preserve compatibility. At the very least, I'd like a config menu where you can select IDA or such compatibility mode.
-Efficient use of screen area. While I tend to write source code generously spaced, I like data tightly packed. If someone wants to have a sparser UI, they should be able to drag widgets around to make it fit their taste. IDA and Metasm both have some elements of this, but there is still room for improvement.
-Easy to setup/run. I could be able to run some shell script or executable and it should just run. I shouldn't need to know anything about what language its implemented in.
-Multi platform. Many of these are Windows only
-Free base product. I don't mind paying for plugins if I want some extra kick, but the base product should be free.

Those are the main points for now. I'll probably update this more as I figure out what's important.

Tuesday, August 17, 2010

FLIRT progress

Last semester, my goal was to get a working FLIRT implemetnation up and running. However, with it being my most intense semester to date, I didn't get as far as I wanted Most of the work involved code fixes and some crude code fixes to the Red Plait (rpat) pattern generator.
Really though, rpat needed a full rewrite to be truly useful in my project. While I could have maintained in its original C form, I really would rather have had a highly functionalized version using STL data structures. As of commit e719d6ddba27bf1fa4d1ace18d3426a356d19fa5, this is done. Example output:
[mcmaster@gespenst bin]$ ./uvobj2pat.dynamic --input=../obj2pat/main_d.o
5589E583EC18E8........C1E81F84C07426C744240C........C74424085800 2B E01B 004B :011F _Z14initProgConfigv ^0007 _Z21initFLIRTSharedConfigv ^0016 ^0026 ^0032 uv_err_ret_handler ^0039 g_config ^0040 5589E583EC18........FFC1E81F84C07426........5E0100........085800........240406000000C7
5589E55383EC24C745E8FFFFFFFFC745EC00000000C745F000000000C745F4FF FF F3C5 0349 :01A4 _Z6uvmainiPPc ^0024 _Z13UVDGetVersionv ^0037 strcmp ^0045 _Z13UVDGetVersionv ^005B ^0060 printf ^0065 stdout ^006D fflush ^0072 _Z7UVDInitv ^0081 ^0091 ^009D uv_err_ret_handler ^00A7 g_config ^00B8 ^00C8 ^00D4 uv_err_ret_handler ^00DE _Z14initProgConfigv ^00ED ^00FD ^0109 uv_err_ret_handler ^0127 _ZN9UVDConfig9parseMainEiPKPc ^0138 ^0148 ^0154 uv_err_ret_handler ^0173 ^017F printf_debug_level ^0186 g_flirt ^018B _ZN8UVDFLIRT8getFLIRTEPPS_ ^0199 ^019E puts ^01AF g_flirt ^01BB ^01CB ^01D7 uv_err_ret_handler ^01E1 g_uvd ^01ED ^01FD ^0209 uv_err_ret_handler ^0213 g_uvd ^0222 ^0232 ^023E uv_err_ret_handler ^0248 g_uvd ^0261 ^0271 ^027D uv_err_ret_handler ^0290 _ZNKSt6vectorISsSaISsEE5emptyEv ^029B ^02A0 puts ^02A5 _Z7UVDHelpv ^02AD ^02BD ^02C8 uv_err_ret_handler ^02DD ^02E2 puts ^02EF _Z9UVDDeinitv ^02FE ^030E ^031A uv_err_ret_handler ^0324 ^0334 ^033F uv_err_ret_handler 5589E553........45E8FFFFFFFFC745EC00000000C745........00C745F4FFFFFFFFE8FC........C3E82DFEFFFF895C2404890424E8FCFFFFFF........C0........E8........89C3E80C........5C........2404C7042460000000E8FC........00000000890424E8FCFFFFFF........FFC1E81F84C07429........57010000C744........0000C744240406000000C70424........E8FCFFFFFFE99D020000A100........45EC837DEC007529........57010000C744........0000C744240406000000C7........FFFFE8FCFFFFFFE966020000........FFC1E81F84C07429........57010000C744240881000000C744240406000
5589E583E4F083EC20C7442404........C7042405000000E8........8B450C 3C F345 005C :04ED main ^000D ^0019 printf_debug_level ^002B _Z6uvmainiPPc ^0037 ^0043 printf_debug_level 5589E583E4F083EC20C744........0000C70424050000........FFFF8B450C894424........890424E8FCFFFFFF8944241CC74424044B010000C7
---
Which seems consistent with the assembly:
000004ed
:
4ed: 55 push %ebp
4ee: 89 e5 mov %esp,%ebp
4f0: 83 e4 f0 and $0xfffffff0,%esp
4f3: 83 ec 20 sub $0x20,%esp
4f6: c7 44 24 04 3e 01 00 movl $0x13e,0x4(%esp)
4fd: 00
4fe: c7 04 24 05 00 00 00 movl $0x5,(%esp)
505: e8 fc ff ff ff call 506
50a: 8b 45 0c mov 0xc(%ebp),%eax
50d: 89 44 24 04 mov %eax,0x4(%esp)
511: 8b 45 08 mov 0x8(%ebp),%eax
514: 89 04 24 mov %eax,(%esp)
517: e8 fc ff ff ff call 518
51c: 89 44 24 1c mov %eax,0x1c(%esp)
520: c7 44 24 04 4b 01 00 movl $0x14b,0x4(%esp)
527: 00
528: c7 04 24 05 00 00 00 movl $0x5,(%esp)
52f: e8 fc ff ff ff call 530
534: 83 7c 24 1c 00 cmpl $0x0,0x1c(%esp)
539: 79 07 jns 542
53b: b8 01 00 00 00 mov $0x1,%eax
540: eb 05 jmp 547
542: b8 00 00 00 00 mov $0x0,%eax
547: c9 leave
548: c3 ret

However, the correctness of this won't be known until I actually try processing it. This version should be much easier to maintain and be more stable. There are some assorted points to fix, but it was definitely a step in the right direction. Next steps will be to implement pattern generation for libuvudec based disassembling and verify FLAIR's makesig produces the same output and/or is valid for input to pat2sig. One of the challenges I will of course face is how to support all of the architectures. I'm thinking the best way will be for any arch IDA does not support, I'll put a different magic number at the start of the file and add a new architecture field
Part of the reason I got working on this again is someone pointed out to me someone posted the .sig binary format here. I was hoping to do this last semester, but mostly due to time constraints, didn't get to it. With the format out there now, it will be much easier to implement a pat2sig equivalent and the matching loader.
Finally, I made a quick stab at a Qt GUI:
All it does is display the function list. I was trying to figure out how to get the window to resize to fit the whole screen but needs more work. I don't have a lot of GUI experience, so there will be a learning curve to get things decent. Hopefully though, if this project becomes popular, it shouldn't be too hard for someone to design something better and swap it out. The major issue that became readily apparent is the engine is currently designed for one shot analysis. It cannot respond to annotations. Thus, some sort of analysis event system will have to be implemented. This will be important for plugins as well as they may need to respond to various engine events if they want to do more than just respond to keystrokes. In the near future for the GUI, I'd like to:
-Get the window to resize correctly
-Display a dead listing
-Ability to double click on functions and have that location appear in the dead listing
Next:
-Write comments
-Load and save a workspace (I briefly toyed with this)
-Load FLIRT signatures. This may be sooner than later as I'm currently writing uvpat2sig which will mean I'll have at least most of first revision signature engine up.

Sunday, July 25, 2010

New utility program: uvstructoffset

Under util/uvstructoffset/uvstructoffset.py
Here and there I find the need to verify the APIs I create have the correct structure alignment. I would do this manually with something like:
printf("some_member: 0x%.4X\n", offsetof(struct my_struct_t), some_member);
But this can be automated if there was a program to parse the C code. pycparser seemed like the most convenient C parser to use. After some coding, I could transform structures:
#include

struct sig_header_t
{
uint8_t dell1800FP;
short atomicForceMicroscope;
uint16_t fearAndLoathingInLasVegas;
uint8_t oatmeal;
char paperTowels;
uint16_t jointedGlasswares;
uint8_t micrometer;
int orangina;
char accidentWaitingToHappen[16];
uint8_t SATADrive;
uint16_t fastSteeringMirror;
uint32_t hamburgers;
} __attribute__((__packed__));
Into this:
struct sig_header_t
dell1800FP @ 0x0000
atomicForceMicroscope @ 0x0001
fearAndLoathingInLasVegas @ 0x0003
oatmeal @ 0x0005
paperTowels @ 0x0006
jointedGlasswares @ 0x0007
micrometer @ 0x0009
orangina @ 0x000A
accidentWaitingToHappen @ 0x000E
SATADrive @ 0x001E
fastSteeringMirror @ 0x001F
hamburgers @ 0x0021

Formatting doesn't line up on the web, but you get the idea. The C parser only parses the structure and doesn't try to guess the actual offsets. This is done with gcc since this is really the only reliable way to do it based on all of the different data types and such are considered. For decompiler use, config files will have to be referenced for data sizes and alignment.
After working with JSON, I've decided it is the sort of data exchange format I've been looking for for a while. I've never been a fan of XML because I find it overcompicated to parse and work with for general use. If I need any sort of convient data exchange between programs where performance in't an issue but convenience is, I'll probably use it as the format. As required, these can be migrated to higher performance formats. This will include configuration files and structures definitions.
Eventually I will be needing to parse out structures in the decompiled/disassembled files. Since these files don't need to be parsed often and for other data exchange reasons, I will be using the aforementioned Python parser to output a JSON structure definition.

Saturday, July 10, 2010

ELF object dump repaired

As commented in the previous post, the old ELF code was very ugly and a thorn in my side. It is now fixed and running better than other and much easier to maintain. Example object file:
[mcmaster@gespenst bin]$ objdump --syms --reloc analysis/sub_0EC3.elf

analysis/sub_0EC3.elf: file format elf32-i386

SYMBOL TABLE:
00000000 l df *ABS* 00000000 candela_pltl1_rev_3.bin
00000000 l d .text 00000000 .text
00000000 *UND* 00000000 sub_0FCB
00000000 *UND* 00000000 sub_0FE5
00000000 *UND* 00000000 sub_101C
00000000 *UND* 00000000 sub_75E3
00000000 g F .text 00000107 sub_0EC3


RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
0000009e R_386_16 sub_0FCB
000000c0 R_386_16 sub_0FCB
00000103 R_386_16 sub_0FE5
000000a1 R_386_16 sub_101C
000000c3 R_386_16 sub_75E3
I'm still using 386 object file format and still unsure how I want to deal with that in the future. For now it seems like the logical thing so that tools like objdump will play with it. I could in theory generate a patch for binutils, but that would be annoying for decompiler users and a pain to maintain. Maybe I can make a plugin patch for binutils and try to get it into the mainline? It ha some very limited (read: not useful enough) plugin capability. I didn't look into it too much, but from a quick grep only nm and ar support binutils plugins. At first it didn't look like there is any notion of full architecture plugins, but now I'm thinking its just that only that for some odd reason only certain tools allow use of uninstalled plugins. Maybe I'll send an e-mail to their mailing list for advice. clang/llvm is a more modern project and might work better with this stuff. Unfortunately, I haven't yet spent any time to learn it. From a quick look at it, they expose a lot of API stuff that might be good if I wanted to allow my arch files compile executables, but they seem to still depend on binutils for day to day object inspection. clang can do some linking, but I don't know where this functionality comes from. For the short term, I might consider making a very basic binutils skeleton to work with these files, probably in Python. I'm afraid this will feature creep and never get replaced though and is ultimately not the right way to do things.

Edit:
After some more thinking and research, I've decided by and by the far easiest and cleanest way to do this is to simply provide wrapper programs over binutils. The basic idea will be to parse out binutils files and redirect them to files temporarily fixed up to be compatible with binutils. Basically, this will involve temporarily changing the object type to EM_386 since that's what all constants will be based off of. While this will obviously run into a number of issues, it unfortunate seems by far the cleanest solution.