OK I don't have an off the shelf complete solution for you. My Renesas single core controller must still be in the post but I can describe the approach I take.
I have one error table per module which is essentially a static data array of structs:-
static sError ErrorTable[] =
{
{ ERR1, "Error desription string with {0} inserts", 1, 0, 0, 0, 0 },
{ ERR2, "Another error with {0}{1}{2}", 3, 5, 7, 0, 0 },
};
In my case some of the parameters are actually addreses of functions to call to collect the error information.
This does mean managing an error list but only per module.
For each thread I then place a CErrorHandler object on the stack at or near the start of the thread which remains in existance while the thread runs.
At each point where an error can be raised the code being built is being built into some Module we'll call M1 and therefore needs to access the M1 error table. I do this a little bit differenly but essentially all you need is ErrorTable to be a module scope global variable and always called ErrorTable, the MACRO or function call at the point of raising the error can then just refer to &ErrorTable and it will always get the M1 error table.
When a RaiseError is hit at runtime the code is running on thread T1 so it looks up the CErrorHandler for thread T1, passing the &ErrorTable and the error number being raised. The rest is largely up to what kinds of errors you want and how you want to handle them.
The 'looking up the CErrorHandler for thread T1' bit can be simple or tricky depeneding on how threading works in your OS and whether you have Thread Local Storage. I have a nifty and hopefully portable solution to this called 'Flyers' which can be found in an old article of mine
here
Only a rough outline but I hope some of that was helpful.