2015-01-16 15 views
10

Sto provando a chiamare alcune funzioni C++ tramite una tabella di puntatori di funzione che viene esportata come simbolo C da un oggetto condiviso. Il codice è in realtà di lavoro, ma un comportamento indefinito di Clang disinfettante (= UBSan) vede la chiamata che ho fatto è illegale come segue:Clan's UBSan & Function Pointer: è illegale?

==11410==WARNING: Trying to symbolize code, but external symbolizer is not initialized! 
path/to/HelloWorld.cpp:25:13: runtime error: call to function (unknown) through pointer to incorrect function type 'foo::CBar &(*)()' 
(./libFoo.so+0x20af0): note: (unknown) defined here 

causa di un comportamento indefinito di Clang disinfettante, è legale chiamare indirettamente una funzione che restituisce un riferimento di un oggetto di classe standard C++ tramite un puntatore di funzione ma è illegale per una classe definita dall'utente. Qualcuno potrebbe dirmi per favore cosa c'è che non va?

Ho cercato di costruire il progetto su Ubuntu 14.04 con Clang-LLVM 3.4-1ubuntu3 e CMake 2.8.12.2. Per riprodurre il fenomeno, inserire i seguenti file 5 file nella stessa directory e richiamare build.sh. Creerà un makefile e costruirà il progetto ed eseguirà l'eseguibile.

foo.h

#ifndef FOO_H 
#define FOO_H 

#include <string> 

// 
#define EXPORT __attribute__ ((visibility ("default"))) 

namespace foo { 
    class CBar 
    { 
     // empty 
    }; 

    class CFoo 
    { 
    public: 
     static CBar& GetUdClass(); 
     static std::string& GetStdString(); 
    }; 

    // function pointer table. 
    typedef struct 
    { 
     CBar& (*GetUdClass)(); 
     std::string& (*GetStdString)(); 
    } fptr_t; 

    //! function pointer table which is exported. 
    extern "C" EXPORT const fptr_t FptrInFoo; 
} 

#endif 

Foo.cpp

#include "Foo.h" 
#include <iostream> 

using namespace std; 

namespace foo 
{ 
    // returns reference of a static user-defined class object. 
    CBar& CFoo::GetUdClass() 
    { 
     cout << "CFoo::GetUdClass" << endl; 
     return *(new CBar); 
    } 

    // returns reference of a static C++ standard class object. 
    std::string& CFoo::GetStdString() 
    { 
     cout << "CFoo::GetStdString" << endl; 
     return *(new string("Hello")); 
    } 

    // function pointer table which is to be dynamically loaded. 
    const fptr_t FptrInFoo = { 
     CFoo::GetUdClass, 
     CFoo::GetStdString, 
    }; 
} 

HelloWorld.cpp

#include <iostream> 
#include <string> 
#include <dirent.h> 
#include <dlfcn.h> 
#include "Foo.h" 

using namespace std; 
using namespace foo; 

int main() 
{ 
    // Retrieve a shared object. 
    const string LibName("./libFoo.so"); 
    void *pLibHandle = dlopen(LibName.c_str(), RTLD_LAZY); 
    if (pLibHandle != 0) { 
     cout << endl; 
     cout << "Info: " << LibName << " found at " << pLibHandle << endl; 
     // Try to bind a function pointer table: 
     const string SymName("FptrInFoo"); 
     const fptr_t *DynLoadedFptr = static_cast<const fptr_t *>(dlsym(pLibHandle, SymName.c_str())); 
     if (DynLoadedFptr != 0) { 
      cout << "Info: " << SymName << " found at " << DynLoadedFptr << endl; 
      cout << endl; 
      // Do something with the functions in the function table pointer. 
      DynLoadedFptr->GetUdClass(); // Q1. Why Clang UBSan find this is illegal?? 
      DynLoadedFptr->GetStdString(); // Q2. And why is this legal?? 
     } else { 
      cout << "Warning: Not found symbol" << endl; 
      cout << dlerror() << endl; 
     } 
    } else { 
     cout << "Warning: Not found library" << endl; 
     cout << dlerror() << endl; 
    } 
    cout << endl; 
    return 0; 
} 

CMakeLists.txt

project (test) 

if(COMMAND cmake_policy) 
     cmake_policy(SET CMP0003 NEW) 
endif(COMMAND cmake_policy) 

set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-rpath,$ORIGIN") 

add_library(Foo SHARED Foo.cpp) 

add_executable(HelloWorld HelloWorld.cpp) 
target_link_libraries (HelloWorld dl) 

build.sh

#!/bin/bash 

# 1. create a build directory. 
if [ -d _build ]; then 
    rm -rf _build 
fi 
mkdir _build 
cd _build 

# 2. generate a makefile. 
CC=clang CXX=clang++ CXXFLAGS="-fvisibility=hidden -fsanitize=undefined -O0 -g3" cmake .. 

# 3. build. 
make 

# 4. and run the executable. 
./HelloWorld 

Ho cercato di trovare un indizio per scavare la questione e capito il problema è stato catturato da opzione "funzione" di il disinfettante (-sanitize = function) ma non è tanto documentato. Apprezzerei che voi ragazzi poteste darmi una spiegazione ragionevole per un messaggio di errore simile a un runtime che sembra provenire da un altro pianeta. Grazie.

Che cosa ha indicato Clang come "sconosciuto" nell'output?

Di seguito si riporta l'uscita dal addr2line per controllare quello che era "sconosciuto" per il disinfettante:

$ addr2line -Cfe _build/libFoo.so 0x20af0 
foo::CFoo::GetUdClass() 
path/to/Foo.cpp:12 

Hmm, sembra proprio che la funzione che mi aspettavo di chiamare per me. Riesci a indovinare come è sembrato diverso per Clang?

+0

'FptrInFoo' non è un puntatore a funzione nel namespace' foo', è globale! Il semplice motivo è che è dichiarato come "extern" C "'. Prova a dichiararne uno in un namespace diverso e vedrai.Quello che mi chiedo ora è se la definizione creerà un oggetto all'interno dello spazio dei nomi (e con il collegamento statico, perché è una costante) o se definisce il globale esterno. BTW: Perché stai usando "typedef struct ...", non puoi comunque usare quel codice in C. –

risposta

7

CBar's typeinfo deve avere la visibilità predefinita per il tipo di funzione essere considerata uguale da Clang su Linux attraverso l'eseguibile e la libreria dinamica; cambiare Foo.h a:

class EXPORT CBar 
    { 
     ... 
    } 
+0

Cattivo. Mi chiedo se il compilatore potrebbe essere migliorato per fornire una diagnosi migliore. Buona cattura Stephan! –

+0

Poiché ho perso un paio d'ore su questo, posso aggiungere che si dovrebbe fare attenzione a usare '__attribute__ ((visibility (" default ")))' sia nella libreria che nell'eseguibile che lo chiama. – Arnaud

+0

Questo non sembra essere sufficiente se 'CBar' non è polimorfico. Ho il problema su 'void (SomeEnum, const SomeStruct &, const SomeClass &)'. Nessuno di questi tipi è polimorfico, quindi non vedo dove il RTTI per uno di questi tipi sarebbe stato emesso dal compilatore. Mi sembra che questo funzioni in conflitto con type_infos duplicati nello stesso modo in cui le classi di eccezioni inline causano mal di testa: https://marcmutz.wordpress.com/2010/08/04/fun-with-exceptions/ –

Problemi correlati