Kscrash: Rastreos de pila incorrectos para excepciones de C++ en marcos integrados

Creado en 20 feb. 2017  ·  12Comentarios  ·  Fuente: kstenerud/KSCrash

Parece que la función __cxa_throw en KSCrashMonitor_CPPException.c no se llama cuando se lanza una excepción de C++ en un marco integrado, por ejemplo, CrashLib en la aplicación Crash-Tester.

No he sido capaz de llegar a una solución a esto. Para mí, parece que cualquier biblioteca incrustada siempre usará __cxa_throw de libc++, y no la implementación en KSCrash.

Además: cuando se genera una excepción de C++ no controlada en un marco integrado, KSCrash fallará durante la generación de informes, ya que stackCursor->advanceCursor será igual a NULL en KSCrashReport.c:writeBacktrace().

Editar: se agregó información sobre el bloqueo de KSCrash durante la presentación de informes.

Comentario más útil

Después de una mayor investigación, parece que mi comentario anterior era incorrecto:

Creo que este problema debería cambiarse de nombre a algo así como _"Faltan rastros de pila para las excepciones de C++ lanzadas desde imágenes no vinculadas estáticamente con KSCrash"_.

En el caso de que KSCrash esté vinculado a la aplicación principal como una biblioteca estática:

  • MyApplication ( main.o , libKSCrash.a )

    • libDynamicLibrary.dylib ( lib.o )

    • /usr/lib/libc++abi.dylib

Cualquier excepción lanzada desde MyApplication ingresará a libKSCrash.a::__cxa_throw . Además, dado que libKSCrash.a declara __cxa_throw como weak , vincular MyApplication no falla si main.o tiene su propio __cxa_throw anular. Hasta ahora tan bueno. Sin embargo, como se ha observado en este informe de error, cualquier excepción lanzada desde libDynamicLibrary.dylib no activará libKSCrash.a::__cxa_throw y terminará en /usr/lib/libc++abi.dylib::__cxa_throw .

En el caso de que KSCrash esté vinculado a la aplicación principal como una biblioteca dinámica:

  • MyApplication ( main.o )

    • libKSCrash.dylib

    • /usr/lib/libc++abi.dylib

    • libDynamicLibrary.dylib ( lib.o )

    • /usr/lib/libc++abi.dylib

Cualquier excepción lanzada desde MyApplication entrará en libKSCrash.dylib::__cxa_throw , pero solo si:

  1. __cxa_throw se exporta desde libKSCrash.dylib
  2. __cxa_throw no está marcado como weak

Si __cxa_throw está marcado como weak ( 0000000000002e90 (__TEXT,__text) weak external ___cxa_throw ), entonces, al vincular libKSCrash.dylib el vinculador parece mirar /usr/lib/libc++abi.dylib , encuentra un no -débil __cxa_throw , y concluye que libKSCrash.dylib::__cxa_throw debe ignorarse. Durante la búsqueda de símbolos en tiempo de ejecución en el momento del lanzamiento desde MyApplication , se ignora libKSCrash.dylib::__cxa_throw .

Esto me lleva a creer que el atributo weak debe agregarse condicionalmente solo cuando se crea KSCrash como una biblioteca estática.

Al igual que en el primer caso de uso, cualquier excepción lanzada desde libDynamicLibrary.dylib no activará libKSCrash.dylib::__cxa_throw y terminará en /usr/lib/libc++abi.dylib::__cxa_throw .

Este problema podría solucionarse potencialmente con algunos parches de tiempo de ejecución de bajo nivel de imágenes cargadas con referencias no definidas a __cxa_throw . Usando la aplicación de prueba a la que se hace referencia en el artículo, verifiqué que la técnica también funciona por __cxa_throw .

cooked_throw

Todos 12 comentarios

¿Ha habido suerte con esto? Estoy tratando de integrar KSCrash en mi aplicación, pero me quedé atascado debido al marco integrado.
Por favor, avíseme si tiene algún trabajo para esto.

No hubo suerte, y no creo que sea posible, al menos no en nuestro caso de uso. Este es mi entendimiento de la situación:

La forma en que KSCrash registra el seguimiento de la pila es mediante la creación de su propia implementación de __cxa_throw, que permite el acceso a la pila antes de que libc++ la desenrolle. Esto funciona siempre que KSCrash esté vinculado estáticamente con el código C++, ya que el enlazador encontrará y usará __cxa_throw desde KSCrash en lugar de la implementación en libc++. Pero en el caso de un marco incrustado que se vincule a libc++, se usará la implementación __cxa_throw de libc++, ya que el marco no conoce el código en KSCrash. Tal vez haya una solución alternativa si tiene control sobre el marco integrado, pero esto no funcionará en nuestro caso de uso.

Por favor, avíseme si encuentra una solución.

Tengo control sobre el marco incrustado. Pero no estoy seguro de cómo solucionarlo.
También probé PLCrashReporter, el mismo problema.

¿Significa esto que, idealmente, KSCrash debería construirse como una biblioteca estática y vincularse a la aplicación, para al menos detectar las excepciones de C++ lanzadas en el código de la aplicación?

Pero eso aún dejaría cualquier excepción lanzada en cualquier biblioteca dinámica no detectada por KSCrash si entiendo su informe de error correctamente @pdrtrifork ? Si controlamos las bibliotecas dinámicas incrustadas, ¿podríamos vincular una biblioteca estática a cada una de ellas con solo ese código auxiliar __cxa_throw, que simplemente se remitirá a un controlador KSCrash compartido?

Ja, así que al investigar esto terminé aquí http://stackoverflow.com/questions/36846628/conditionally-overriding-cxa-throw , que fue abierto por nuestro propio @kstenerud 😄

De todos modos, PR #219 debería al menos evitar fallas durante el informe de fallas.

@kstenerud Creo que este problema debería cambiarse de nombre a algo así como _"Faltan rastros de pila para las excepciones de C++ lanzadas desde imágenes no vinculadas estáticamente con KSCrash"_. Por lo que sé, la anulación __cxa_throw solo funciona en la misma imagen que KSCrash, así que:

  • si KSCrash se construye como una biblioteca estática y se vincula a la aplicación principal, obtiene un seguimiento de la excepción de C++ si la excepción se lanza en la aplicación principal, pero no cuando se lanza desde bibliotecas cargadas dinámicamente
  • si KSCrash se crea como una biblioteca estática y se vincula a una biblioteca dinámica (envoltura), obtiene un seguimiento de la excepción de C++ si la excepción se lanza desde esa biblioteca dinámica, pero no cuando se lanza desde la aplicación principal u otras bibliotecas cargadas dinámicamente
  • si KSCrash se construye como una biblioteca compartida (marco), obtienes registros de excepción de C++ si la excepción se lanza desde KSCrash, pero no cuando se lanza desde la aplicación principal u otras bibliotecas cargadas dinámicamente

Este último afectaría a clientes como sentry-swift , https://docs.sentry.io/clients/cocoa/ , que incorpora KSCrash como marco.

Después de una mayor investigación, parece que mi comentario anterior era incorrecto:

Creo que este problema debería cambiarse de nombre a algo así como _"Faltan rastros de pila para las excepciones de C++ lanzadas desde imágenes no vinculadas estáticamente con KSCrash"_.

En el caso de que KSCrash esté vinculado a la aplicación principal como una biblioteca estática:

  • MyApplication ( main.o , libKSCrash.a )

    • libDynamicLibrary.dylib ( lib.o )

    • /usr/lib/libc++abi.dylib

Cualquier excepción lanzada desde MyApplication ingresará a libKSCrash.a::__cxa_throw . Además, dado que libKSCrash.a declara __cxa_throw como weak , vincular MyApplication no falla si main.o tiene su propio __cxa_throw anular. Hasta ahora tan bueno. Sin embargo, como se ha observado en este informe de error, cualquier excepción lanzada desde libDynamicLibrary.dylib no activará libKSCrash.a::__cxa_throw y terminará en /usr/lib/libc++abi.dylib::__cxa_throw .

En el caso de que KSCrash esté vinculado a la aplicación principal como una biblioteca dinámica:

  • MyApplication ( main.o )

    • libKSCrash.dylib

    • /usr/lib/libc++abi.dylib

    • libDynamicLibrary.dylib ( lib.o )

    • /usr/lib/libc++abi.dylib

Cualquier excepción lanzada desde MyApplication entrará en libKSCrash.dylib::__cxa_throw , pero solo si:

  1. __cxa_throw se exporta desde libKSCrash.dylib
  2. __cxa_throw no está marcado como weak

Si __cxa_throw está marcado como weak ( 0000000000002e90 (__TEXT,__text) weak external ___cxa_throw ), entonces, al vincular libKSCrash.dylib el vinculador parece mirar /usr/lib/libc++abi.dylib , encuentra un no -débil __cxa_throw , y concluye que libKSCrash.dylib::__cxa_throw debe ignorarse. Durante la búsqueda de símbolos en tiempo de ejecución en el momento del lanzamiento desde MyApplication , se ignora libKSCrash.dylib::__cxa_throw .

Esto me lleva a creer que el atributo weak debe agregarse condicionalmente solo cuando se crea KSCrash como una biblioteca estática.

Al igual que en el primer caso de uso, cualquier excepción lanzada desde libDynamicLibrary.dylib no activará libKSCrash.dylib::__cxa_throw y terminará en /usr/lib/libc++abi.dylib::__cxa_throw .

Este problema podría solucionarse potencialmente con algunos parches de tiempo de ejecución de bajo nivel de imágenes cargadas con referencias no definidas a __cxa_throw . Usando la aplicación de prueba a la que se hace referencia en el artículo, verifiqué que la técnica también funciona por __cxa_throw .

cooked_throw

Estoy tratando de verificar el bloqueo en un marco en mi aplicación. Cuando trato de obtener el registro de bloqueo, veo que el seguimiento de la pila no muestra el bloqueo real, sino que KSCrash se bloquea.

¿Es este el mismo problema?

Subproceso 0 bloqueado:
0 libsystem_kernel.dylib 0x0000000181cc5348 0x181ca4000 + 136008 (__pthread_kill + 8)
1 libsystem_pthread.dylib 0x0000000181ddd7a4 0x181dd6000 + 30628 (+ 360)
2 libsystem_c.dylib 0x0000000181c34fd8 0x181bd2000 + 405464 (abortar + 140)
3 libc++abi.dylib 0x0000000181698068 0x181696000 + 8296 (+ 132)
4 libc++abi.dylib 0x0000000181698210 0x181696000 + 8720 (+ 304)
5 libobjc.A.dylib 0x00000001816c0810 0x1816b8000 + 34832 (+ 124)
6 KSCrash 0x00000001054a0590 0x10549c000 + 17808 (kscm_cppexception_getAPI + 280)
7 libc++abi.dylib 0x00000001816b054c 0x181696000 + 107852 (+ 16)
8 libc++abi.dylib 0x00000001816b0158 0x181696000 + 106840 (__cxa_rethrow + 144)
9 libobjc.A.dylib 0x00000001816c06e8 0x1816b8000 + 34536 (objc_exception_rethrow + 44)
10 CoreFoundation 0x0000000182072344 0x18206a000 + 33604 (CFRunLoopRunSpecific + 544)
11 Servicios gráficos 0x0000000183f03f84 0x183ef9000 + 44932 (GSEventRunModal + 100)
12 UIKit 0x000000018b61e880 0x18b5ab000 + 473216 (UIApplicationMain + 208)

@torarnv Hola, ¿podría compartir su exitosa demostración de mach-o-hook con nosotros? Me encontré con EXC_BAD_ACCESS cuando intento mach_hook __cxa_throw.

void ter_handler(){
    printf("custom handler\n");
}

void test(){
    throw std::runtime_error("test function");
}

static void (*orig_throw)(void * thrown_exception, std::type_info *tinfo, void (*dest)(void *));

void hooked_throw(void * thrown_exception, std::type_info *tinfo, void (*dest)(void *)){
    printf("hooked_throw...\n");
    return orig_throw(thrown_exception, tinfo, dest);
}

int main(int argc, char * argv[])
{
    <strong i="5">@autoreleasepool</strong> {

        struct rebinding binds[1];
        struct rebinding bind1 = {"__cxa_throw", (void *)hooked_throw, (void **)&orig_throw};
        binds[0] = bind1;
        rebind_symbols(binds, 1);

        std::set_terminate(ter_handler);
        try {
            throw std::runtime_error("test error");
        }
        catch (...){
            printf  ("catch exception\n");
        }

        test();
    }
}

prueba esto... buena suerte

Me encuentro con un accidente con KSCrash,
kscrash

¿como resolverlo?

Hemos creado un gancho dinámico de implementación de relaciones públicas mencionado anteriormente por @huakucha : #375

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

chzhij5 picture chzhij5  ·  17Comentarios

kstenerud picture kstenerud  ·  4Comentarios

1t2t3t4t picture 1t2t3t4t  ·  3Comentarios

happy201993 picture happy201993  ·  10Comentarios

ferrous777 picture ferrous777  ·  30Comentarios