Ich versuche, einige C++ - Funktionen über eine Funktionszeigertabelle aufzurufen, die als C-Symbol aus einem gemeinsamen Objekt exportiert wird. Der Code ist tatsächlich funktioniert, aber Clang des undefinierten Verhalten Sanitizer (= UBSan) sieht der Anruf, den ich gemacht illegal ist wie folgt:Clangs UBSan & Function Pointer: Ist das illegal?
==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
Aufgrund Clang des undefinierten Verhalten Sanitizer, es legal ist, um indirekt eine Funktion aufrufen, die einen Verweis zurückgibt eines C++ - Standardklassenobjekts durch einen Funktionszeiger , aber es ist für eine benutzerdefinierte Klasse nicht zulässig. Könntest du mir bitte sagen, was nicht stimmt?
Ich habe versucht, das Projekt auf Ubuntu 14.04 mit Clang-llvm 3.4-1ubuntu3 und CMake 2.8.12.2 zu bauen. Um das Phänomen zu reproduzieren, legen Sie bitte die folgenden 5 Dateien in das gleiche Verzeichnis und rufen Sie build.sh auf. Es wird ein Makefile erstellen und das Projekt erstellen und die ausführbare Datei ausführen.
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
Ich habe einen Anhaltspunkt zu finden versucht, in das Problem zu graben und realisiert die Ausgabe von „Funktion“ Option gefangen wurde von das Desinfektionsmittel (-sanitize = Funktion), aber es ist nicht so viel dokumentiert. Ich würde mich freuen, wenn ihr mir eine vernünftige Erklärung für solch eine Laufzeitfehlermeldung geben könnt, die aussieht, als käme sie von einem anderen Planeten. Vielen Dank.
Worauf wies Clang in der Ausgabe als "unbekannt" hin?
Im Folgenden finden Sie die Ausgabe von addr2line zu überprüfen, was für die Sanitizer „unbekannt“ war:
$ addr2line -Cfe _build/libFoo.so 0x20af0
foo::CFoo::GetUdClass()
path/to/Foo.cpp:12
Hmm, es sieht wirklich wie die Funktion, die ich erwartet hatte für mich anzurufen. Kannst du raten, wie es für Clang anders aussah?
'FptrInFoo' ist kein Funktionszeiger im Namensraum' foo', es ist ein globaler! Der einfache Grund ist, dass es als "extern" C "erklärt wird. Versuchen Sie, einen in einem anderen Namensraum zu deklarieren, und Sie werden sehen.Ich frage mich jetzt, ob die Definition ein Objekt im Namespace (und mit statischer Verknüpfung, weil es eine Konstante ist) erzeugt oder ob es das externe Globale definiert. BTW: Warum verwenden Sie "typedef struct ...", Sie können diesen Code in C sowieso nicht verwenden. –