Dealing with codebases with little or no documentation and having to add some functionality that is not similar to that already existing can be extremely challenging.
In this particular situation, I needed to inject a custom section to binutils 'ld' ELF generation late in the linking stage. Adding the section itself was easy. The problem was that I couldn't easily figure out how to inject the actual section contents.
Trying to find the relevant code from millions of lines of code (cloc says 5,252,854 total) proved too much work. Trying to follow the complicated data structures and multi-layered abstracted code structure was too painful.
Finally I got tired of trying to figure things out and I took out the trick from the old hacker toolbox:
Make the app crash around the area where things you with your own code should generate have been generated. Add *(int *)1 = 2; to the code, build it, run it with gdb, and then examine the stack trace to find out how you ended up in this place. Then you can find out what kind of data structures need to be in place for the execution to end up where you need it to be.
Finally, find out where such data structures are generated. To speed up things, make it crash again using the same trick as above and see how you end up on the specific code path. Walking backwards, you eventually find what you need.
The code I needed to add?
struct bfd_link_order *link_order;
link_order = bfd_new_link_order (abfd, s);
if (link_order)
{
link_order->type = bfd_data_link_order;
link_order->u.data.contents = (unsigned char *) data;
link_order->u.data.size = s->size;
link_order->size = s->size;
link_order->offset = 0;
}
I know real coders would likely use proper debuggers with breakpoints and similar. But just adding a crash and debugging it felt more rustic. You know, like real #hacking...