Note: Please include the mailing list when replying to discussions, as someone else may well want to see the discussion, and may be better placed to answer.
Like I’ve tried to explain, there is a generic piece of code that understands how to load code in general (the class RuntimeDyld and related bits), and then specific implementations that derive from a base class to do the specific relocation and exception handling for that particular hardware and file-format - for each supported processor architecture and file-format, there needs to be a specific class that implements some functions (processRelocationRef is one of those). Technically, it looks like it’s using a “pImpl” pattern, but the basic principle is the same either way - generic code handles the generic case, a derived class that understands how to deal with the specifics is used to actually perform relocations in that particular case.
Exception handling is also target-specific, so in x86-64 and i386, how exception information is stored and used is different (I don’t know the exact details in this case as COFF is the file-format used on Windows, and it’s been at least 8 or 10 years since I did any programming at all on a Windows machine, I know that i386 on Linux uses an exception table, and x86-64 on linux essentially has debug information [DWARF tables]). The exception information is used to determine how to unwind the stack and destroy objects on the way back to the “catch” for that particular exception. There is code required both to load the exception tables into memory, and to interpret/use those tables - but I’m not overly familiar with how that works for JIT’d code. [Actually, looking at the code for x86-64, it looks like it’s mainly SEH (Structured Exception Handling) that is dealt with - the overall concept still applies, but SEH is a Windows concept for handling exceptions, which includes hardware exceptions such as integer division by zero and memory access exceptions - regular C++ exceptions are dealt with separately, and that is what uses what I described for Linux earlier in this paragraph].
As to WHY different architectures use different relocations and exception handling tables, that’s an ABI design issue - a convention that is based on the needs and requirements for each architecture, and a bunch of compromises between simplicity (a very simple table is easy to construct), space (simple table takes up more space than a more complex table construction - like a zip file or a text file - the zip file is more complicated to read, but takes up a lot less space) and code complexity (save space in table, more complex code most likely). Either way, for a given platform (OS, Processor, file format), there is a given ABI for handling exceptions. The loader needs to load the table in the correct way into the correct part of memory, and when an exception is thrown, the table(s) need to be understood and acted upon to find the way back to the relevant place where the exception is caught.
The fact that the classes are declared in different files is similar to my simple animal example, where you’d have a animal.h for the base class, a cat.h, dog.h and fish.h for the actual implementations. Obviously, the specific implementations for the RuntimeDyld belongs in “Target” because they are dependent on the actual target (which is the combination of fileformat, OS and processor architecture).