Tôi đang cố gắng gọi một số hàm C++ thông qua bảng con trỏ hàm được xuất dưới dạng biểu tượng C từ một đối tượng được chia sẻ. Mã này được thực sự làm việc nhưng hành vi undefined Clang của khử trùng (= UBSan) thấy cuộc gọi tôi đã làm là bất hợp pháp như sau:Con trỏ chức năng và con trỏ của Clang: Đây có phải là bất hợp pháp không?
==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
Do hành vi undefined Clang của khử trùng, nó là hợp pháp để gián tiếp gọi một hàm trả về một tham chiếu của đối tượng lớp chuẩn C++ thông qua một con trỏ hàm nhưng nó là bất hợp pháp đối với một lớp do người dùng định nghĩa. Ai đó bạn có thể vui lòng cho tôi biết có gì sai với nó không?
Tôi đã cố gắng để xây dựng dự án trên Ubuntu 14.04 với Clang-llvm 3.4-1ubuntu3 và CMake 2.8.12.2. Để tái tạo hiện tượng này, vui lòng đặt 5 tệp sau vào cùng thư mục và gọi build.sh. Nó sẽ tạo một makefile và xây dựng dự án và chạy tệp thi hành.
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
Tôi đã cố gắng để tìm thấy một đầu mối để thâm nhập vào vấn đề và nhận ra vấn đề này đã bị bắt bởi "chức năng" tùy chọn chất khử trùng (-fsanitize = function) nhưng không quá nhiều tài liệu. Tôi sẽ đánh giá cao nếu các bạn có thể cho tôi một lời giải thích hợp lý cho một thông báo lỗi thời gian chạy như thế đến từ một hành tinh khác. Cảm ơn.
Clang chỉ ra là "không xác định" trong đầu ra?
Dưới đây là kết quả từ addr2line để kiểm tra những gì là "không rõ" cho khử trùng:
$ addr2line -Cfe _build/libFoo.so 0x20af0
foo::CFoo::GetUdClass()
path/to/Foo.cpp:12
Hmm, nó thực sự trông giống như chức năng tôi đã mong đợi để gọi cho tôi. Bạn có thể đoán nó trông khác như thế nào với Clang?
'FptrInFoo' không phải là con trỏ hàm trong không gian tên' foo', nó là toàn cầu! Lý do đơn giản là nó được khai báo là 'extern" C "'. Cố gắng tuyên bố một trong một không gian tên khác nhau và bạn sẽ thấy.Những gì tôi đang tự hỏi bây giờ là liệu định nghĩa sẽ tạo ra một đối tượng bên trong không gian tên (và với liên kết tĩnh, bởi vì nó là một hằng số) hoặc nếu nó định nghĩa toàn cục bên ngoài. BTW: Tại sao bạn sử dụng "typedef struct ...", bạn không thể sử dụng mã đó trong C anyway. –