Ответ 1
Я нашел время, чтобы поиграть с этой идеей, и я очень близок к поиску решения для моего собственного вопроса. Вот подробности:
- gcc позволяет вложенные функции, которые имеют доступ к локальным переменным родительской функции и могут получить метки в родительской функции!
- gcc не будет генерировать код для внутренней функции, если он определен, но не указан. Вы можете определить встроенную функцию no-op, которая получает указатель на локальную функцию и вызывает ее в родительской функции. Это заставит генерировать код для внутренней функции, и это будет нулевая стоимость (при более высоких уровнях оптимизации будет отключен встроенный вызов no-op). Возможно, что в более поздней gcc-оптимизации встроенный вызов предотвратит создание кода для внутренней функции, хотя..
- Плохая вещь, я не вижу способа заставить вложенные (внутренние) функции быть глобальными символами. Они всегда локальны, поэтому нет возможности получить адрес с помощью dlsym.
- другие плохие новости, если программа использует такие вложенные функции, valgrind сбой;) Мне удалось проверить мою простую тестовую программу, но я не смог использовать valgrind, чтобы проверить, нет ли нарушений памяти.
Код, который я использовал для проверки, имеет очевидный недостаток: это не "нулевая стоимость", поскольку глобальный указатель на процедуру обработки исключений должен быть установлен во время выполнения функции. Если бы мы могли сделать это время компиляции!
Ну, в конце концов, если кто-то хочет правильно использовать внутреннюю функцию, например, передать указатель на него в вызовы, чтобы они могли вызвать его в случае сброшенных исключений, мы, вероятно, могли бы очень быстро обрабатывать исключительные ситуации, намного, МНОГО быстрее чем setjmp/longjmp...
Я буду продолжать взламывать, может быть, я найду способ (какой-то блок ассемблера кода, чтобы заставить GAS регистрировать функцию как индивидуальную подпрограмму родителя?).
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
typedef void (*catch_routine)(void*);
catch_routine g_r = NULL;
void tostr_internal(char* str, int a)
{
int result = a + 'a';
if (result < 'a' || result > 'z')
{
// handle exception
if(g_r)
{
g_r(&a);
}
else
{
fprintf(stderr, "Exception not caught!");
abort();
}
}
else
{
str[0] = result;
str[1] = '\0';
}
}
char* tostring(int a)
{
__label__ exhandler;
char* string = (char*)malloc(2*sizeof(char));
void personality(void* exid) {
fprintf(stderr, "Number %d is not a character!\n", *(int*)(exid));
free(string);
goto exhandler;
}
g_r = personality;
tostr_internal(string, a);
return string;
exhandler:
return NULL;
}
int main(int a, char** b)
{
int i = 0;
for(i = 0; i < 10000; i++)
{
int trythisbastard = i % 95;
char* result = tostring(trythisbastard);
if (result)
{
fprintf(stderr, "Number %d is %s\n", trythisbastard, result);
free(result);
}
}
return 0;
}