Kscrash: Rastreamentos de pilha incorretos para exceções C++ em estruturas incorporadas

Criado em 20 fev. 2017  ·  12Comentários  ·  Fonte: kstenerud/KSCrash

Parece que a função __cxa_throw em KSCrashMonitor_CPPException.c não está sendo chamada quando uma exceção C++ é lançada em uma estrutura incorporada, por exemplo, CrashLib no aplicativo Crash-Tester.

Não consegui encontrar uma solução para isso. Para mim, parece que qualquer biblioteca incorporada sempre usará __cxa_throw da libc++, e não a implementação no KSCrash.

Além disso: Quando uma exceção C++ não tratada é lançada em uma estrutura incorporada, o KSCrash falhará durante o relatório, pois stackCursor->advanceCursor será igual a NULL em KSCrashReport.c:writeBacktrace().

Editar: Adicionadas informações KSCrash travando durante o relatório.

Comentários muito úteis

Após uma investigação mais aprofundada, parece que meu comentário anterior estava incorreto:

Acho que esse problema deve ser renomeado para algo como _"Ausência de rastreamentos de pilha para exceções C++ lançadas de imagens não vinculadas estaticamente ao KSCrash"_.

No caso do KSCrash ser vinculado ao aplicativo principal como uma biblioteca estática:

  • MyApplication ( main.o , libKSCrash.a )

    • libDynamicLibrary.dylib ( lib.o )

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

Quaisquer exceções lançadas de MyApplication entrarão no libKSCrash.a::__cxa_throw . Além disso, como libKSCrash.a declara __cxa_throw como weak , vincular MyApplication não falha se main.o tiver seu próprio __cxa_throw substituir. Até agora tudo bem. No entanto, como este relatório de bug observou, quaisquer exceções lançadas de libDynamicLibrary.dylib não acionarão libKSCrash.a::__cxa_throw e terminarão em /usr/lib/libc++abi.dylib::__cxa_throw .

No caso do KSCrash ser vinculado ao aplicativo principal como uma biblioteca dinâmica:

  • MyApplication ( main.o )

    • libKSCrash.dylib

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

    • libDynamicLibrary.dylib ( lib.o )

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

Quaisquer exceções lançadas de MyApplication entrarão em libKSCrash.dylib::__cxa_throw , mas somente se:

  1. __cxa_throw é exportado de libKSCrash.dylib
  2. __cxa_throw não está marcado como weak

Se __cxa_throw estiver marcado como weak ( 0000000000002e90 (__TEXT,__text) weak external ___cxa_throw ), então ao vincular libKSCrash.dylib o vinculador parece olhar para /usr/lib/libc++abi.dylib , encontre um valor não -weak __cxa_throw e concluir que libKSCrash.dylib::__cxa_throw deve ser ignorado. Durante a pesquisa de símbolo de tempo de execução no tempo de lançamento de MyApplication , libKSCrash.dylib::__cxa_throw é então ignorado.

Isso me leva a acreditar que o atributo weak deve ser adicionado condicionalmente apenas ao construir o KSCrash como uma biblioteca estática.

Como no primeiro caso de uso, quaisquer exceções lançadas de libDynamicLibrary.dylib não acionarão libKSCrash.dylib::__cxa_throw e terminarão em /usr/lib/libc++abi.dylib::__cxa_throw .

Esse problema pode ser corrigido com alguns patches de tempo de execução de baixo nível de imagens carregadas com referências indefinidas a __cxa_throw . Usando o aplicativo de teste mencionado no artigo, verifiquei que a técnica também funciona para __cxa_throw .

cooked_throw

Todos 12 comentários

Alguma sorte com isso? Estou tentando integrar o KSCrash no meu aplicativo, mas travado por causa da estrutura incorporada.
Por favor, deixe-me saber se você tem alguma solução para isso.

Sem sorte, e não acredito que seja possível, pelo menos não em nosso caso de uso. Este é o meu entendimento da situação:

A maneira como o KSCrash registra o rastreamento de pilha é criando sua própria implementação de __cxa_throw, que permite o acesso à pilha antes de ser desenrolada pela libc++. Isso funciona desde que o KSCrash esteja vinculado estaticamente ao código C++, pois o vinculador encontrará e usará __cxa_throw no formato KSCrash em vez da implementação em libc++. Mas no caso de um framework embutido que se vincula a libc++, a implementação __cxa_throw do libc++ será usada, pois o framework não conhece o código no KSCrash. Talvez haja uma solução alternativa se você tiver controle sobre a estrutura incorporada, mas isso não funcionará em nosso caso de uso.

Por favor, deixe-me saber se você descobrir uma solução.

Eu tenho controle sobre a estrutura incorporada. Mas não tenho certeza de como corrigi-lo.
Eu também tentei o PLCrashReporter, mesmo problema com ele.

Isso significa que o KSCrash deve idealmente ser construído como uma biblioteca estática e vinculado ao aplicativo, para pelo menos capturar exceções C++ lançadas no código do aplicativo?

Mas isso ainda deixaria quaisquer exceções lançadas em qualquer biblioteca dinâmica não capturada pelo KSCrash se eu entender seu relatório de bug corretamente @pdrtrifork ? Se controlarmos as bibliotecas dinâmicas incorporadas, poderíamos vincular uma biblioteca estática a cada uma delas com apenas esse stub __cxa_throw, que apenas adiaria para um manipulador KSCrash compartilhado?

Hah, então ao investigar isso acabei aqui http://stackoverflow.com/questions/36846628/conditionally-overriding-cxa-throw , que foi aberto pelo nosso próprio @kstenerud 😄

De qualquer forma, o PR #219 deve pelo menos evitar falhas durante o relatório de falhas.

@kstenerud Acho que esse problema deve ser renomeado para algo como _"Rastreamentos de pilha ausentes para exceções C++ lançadas de imagens não vinculadas estaticamente ao KSCrash"_. Até onde eu sei, a substituição __cxa_throw só funciona na mesma imagem que o KSCrash, então:

  • se o KSCrash for construído como uma biblioteca estática e vinculado ao aplicativo principal, você obterá backtraces de exceção C++ se a exceção for lançada no aplicativo principal, mas não quando lançada de bibliotecas carregadas dinamicamente
  • se o KSCrash for construído como uma biblioteca estática e vinculado a uma biblioteca dinâmica (wrapper), você obterá backtraces de exceção C++ se a exceção for lançada dessa biblioteca dinâmica, mas não quando lançada do aplicativo principal ou de outras bibliotecas carregadas dinamicamente
  • se o KSCrash for construído como uma biblioteca compartilhada (framework), você obterá backtraces de exceção C++ se a exceção for lançada do KSCrash, mas não quando lançada do aplicativo principal ou de outras bibliotecas carregadas dinamicamente

O último afetaria clientes como sentry-swift , https://docs.sentry.io/clients/cocoa/ , que incorpora o KSCrash como um framework.

Após uma investigação mais aprofundada, parece que meu comentário anterior estava incorreto:

Acho que esse problema deve ser renomeado para algo como _"Ausência de rastreamentos de pilha para exceções C++ lançadas de imagens não vinculadas estaticamente ao KSCrash"_.

No caso do KSCrash ser vinculado ao aplicativo principal como uma biblioteca estática:

  • MyApplication ( main.o , libKSCrash.a )

    • libDynamicLibrary.dylib ( lib.o )

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

Quaisquer exceções lançadas de MyApplication entrarão no libKSCrash.a::__cxa_throw . Além disso, como libKSCrash.a declara __cxa_throw como weak , vincular MyApplication não falha se main.o tiver seu próprio __cxa_throw substituir. Até agora tudo bem. No entanto, como este relatório de bug observou, quaisquer exceções lançadas de libDynamicLibrary.dylib não acionarão libKSCrash.a::__cxa_throw e terminarão em /usr/lib/libc++abi.dylib::__cxa_throw .

No caso do KSCrash ser vinculado ao aplicativo principal como uma biblioteca dinâmica:

  • MyApplication ( main.o )

    • libKSCrash.dylib

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

    • libDynamicLibrary.dylib ( lib.o )

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

Quaisquer exceções lançadas de MyApplication entrarão em libKSCrash.dylib::__cxa_throw , mas somente se:

  1. __cxa_throw é exportado de libKSCrash.dylib
  2. __cxa_throw não está marcado como weak

Se __cxa_throw estiver marcado como weak ( 0000000000002e90 (__TEXT,__text) weak external ___cxa_throw ), então ao vincular libKSCrash.dylib o vinculador parece olhar para /usr/lib/libc++abi.dylib , encontre um valor não -weak __cxa_throw e concluir que libKSCrash.dylib::__cxa_throw deve ser ignorado. Durante a pesquisa de símbolo de tempo de execução no tempo de lançamento de MyApplication , libKSCrash.dylib::__cxa_throw é então ignorado.

Isso me leva a acreditar que o atributo weak deve ser adicionado condicionalmente apenas ao construir o KSCrash como uma biblioteca estática.

Como no primeiro caso de uso, quaisquer exceções lançadas de libDynamicLibrary.dylib não acionarão libKSCrash.dylib::__cxa_throw e terminarão em /usr/lib/libc++abi.dylib::__cxa_throw .

Esse problema pode ser corrigido com alguns patches de tempo de execução de baixo nível de imagens carregadas com referências indefinidas a __cxa_throw . Usando o aplicativo de teste mencionado no artigo, verifiquei que a técnica também funciona para __cxa_throw .

cooked_throw

Estou tentando verificar a falha em uma estrutura no meu aplicativo. Quando tento obter o log de falhas, vejo que o rastreamento de pilha não está mostrando a falha real, em vez disso, o KSCrash está falhando.

Esta é a mesma questão?

Tópico 0 travado:
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 Serviços Gráficos 0x0000000183f03f84 0x183ef9000 + 44932 (GSEventRunModal + 100)
12 UIKit 0x000000018b61e880 0x18b5ab000 + 473216 (UIApplicationMain + 208)

@torarnv Oi, você poderia compartilhar sua demonstração bem-sucedida de mach-o-hook conosco? Eu encontrei EXC_BAD_ACCESS quando tento 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();
    }
}

tente isso... boa sorte

Eu encontro uma falha com o KSCrash ,
kscrash

como resolver isso?

Criamos um gancho dinâmico de implementação de PR mencionado acima por @huakucha : #375

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

happy201993 picture happy201993  ·  10Comentários

chzhij5 picture chzhij5  ·  17Comentários

ferrous777 picture ferrous777  ·  30Comentários

1t2t3t4t picture 1t2t3t4t  ·  3Comentários

kstenerud picture kstenerud  ·  4Comentários