#include "stdafx.h"
#include "Gc/DwarfTable.h"

#ifdef POSIX

namespace code {
	namespace eh {

		// Struct used as a parameter to '_Unwind_Find_FDE'.
		struct dwarf_eh_bases {
			void *tbase;
			void *dbase;
			void *func;
		};

		// Previous version of the _Unwind_Find_FDE function (present in libgcc.so). Put inside a
		// class to use the one-time initialization without additional locks.
		struct PrevFindFDE {
			typedef FDE *(*Ptr)(void *pc, struct dwarf_eh_bases *bases);
			Ptr ptr;

			PrevFindFDE() {
				ptr = (Ptr)dlsym(RTLD_NEXT, "_Unwind_Find_FDE");
				if (!ptr) {
					printf("Failed to initialize exception handling (no _Unwind_Find_FDE found)\n");
					printf("Did you compile Storm using GCC with DWARF2 unwind information?\n");
					exit(250);
				}
			}
		};

		// Previous FDE finding function.
		PrevFindFDE prevFindFDE;

		// Hook into the exception resolution system so that we can provide our generated DWARF
		// frames when required.
		extern "C" SHARED_EXPORT FDE *_Unwind_Find_FDE(void *pc, struct dwarf_eh_bases *bases) {
			// Try asking the standard library first.
			FDE *found = (*prevFindFDE.ptr)(pc, bases);
			if (found)
				return found;

			// If the standard library did not know of the current 'pc', then we try to find it
			// inside the code generated by Storm!
			found = dwarfTable().find(pc);

			if (found && bases) {
				bases->tbase = null;
				bases->dbase = null;
				bases->func = (void *)found->codeStart();
			}

			return found;
		}


		void initHook() {
			// Note: This hook does not do anything. We need to reference this object file from
			// somewhere that is used, otherwise the linker will not include the object file in the
			// final build, and therefore our override will not be visible otherwise. Just calling
			// the function somewhere is therefore enough to initialize the hook.
		}

	}
}

#endif
