Debugging ELF Loadable Modules

Debugging ELF modules loaded in memory can be tricky because the location where the module is loaded into memory does not match the addresses in the ELF file. This has long been a problem with debugging uClinux programs and Linux kernel modules; the same solution can be used with NuttX ELF files (and probably also NxFLAT modules). Below is a summary of one way to do this:

1. Get ELF Module Load Address. Put a change in nuttx/binfmt so that you print the address where the ELF text was loaded into memory.

Turning on BINFMT debug (CONFIG_DEBUG_BINFMT=y) should give you this same information. But it will probably also give you more information than you want to see.

Or, perhaps, you could put a printf at the beginning of your main function so that your ELF module can print its own load address? For example, the difference between the address of main() in your object file and the address of main() at run time should give you the load address.

2. Make the ELF Module Wait for You. Put an infinite loop in the main routine of your elf program like this:

  volatile bool waitforme;
  int main (int arc, char **argv)
    while (!waitforme);

Then when you start the ELF program, you will see where it was loaded in memory and the ELF program will be stuck in an infinite loop waiting for waitforme to become true.

3. Start the Debugger. Start the debugger, connect to the GDB server, halt the program. If you debugger is well behaved, your program should be halted in the above infinite loop in main().

4. Load Offset Symbols. Load symbols using the offset where the ELF module was loaded like

  (gdb) add-symbol-file <myprogram> <load-address>

Where <myprogram> is your ELF program with symbols and <load-address> is the address where the program text was loaded. See above for more details. Single step a couple of times and you see the above loop.

5. And Debug. Set waitforme to a non-zero value and you should step out of the infinite loop. And now you are debugging the ELF program loaded into RAM!

An Easier Way? There might be alternative way to step into the ELF module without modifying it with the waitforme loop. You should be able to put a breakpoint on the OS function task_start(). That function should run before your ELF program and should let you single step from the OS code directly into your loaded ELF application with no changes to the ELF application.

When you step into the application main, then you also have the relocated address of main() and you can used that as described in step 1 to compute the load address.