2015-01-16 23 views
10

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-1ubuntu3CMake 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?

+0

'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. –

Trả lời

7

CBar's typeinfo cần phải có khả năng hiển thị mặc định cho loại của hàm được Clang trên Linux xem qua cùng với thư viện động; thay đổi Foo.h thành:

class EXPORT CBar 
    { 
     ... 
    } 
+0

Khó chịu. Tôi sẽ trả lời nếu trình biên dịch có thể được cải thiện để cung cấp chẩn đoán tốt hơn. Tốt bắt Stephan! –

+0

Vì tôi mất một vài giờ trên này, tôi có thể thêm rằng một trong những nên cẩn thận để sử dụng '__attribute__ ((khả năng hiển thị (" mặc định ")))' cả trong thư viện và trong thực thi gọi nó. – Arnaud

+0

Điều này dường như không đủ nếu 'CBar' không đa hình. Tôi có vấn đề về 'void (SomeEnum, const SomeStruct &, const SomeClass &)'. Không có loại nào trong số này có tính đa hình, vì vậy tôi không thấy RTTI cho bất kỳ loại nào trong số này sẽ được trình biên dịch phát ra. Dường như với tôi rằng điều này chạy afoul của type_infos trùng lặp cùng một lớp ngoại lệ nội tuyến lớp học gây ra nhức đầu: https://marcmutz.wordpress.com/2010/08/04/fun-with-exceptions/ –

Các vấn đề liên quan