2016-11-04 4 views
-1

Я только что обнаружил недостающую функцию в одном C API, который я использую (он компилируется, но он не связан). Я хотел протестировать весь набор функций API. Идея состояла в том, чтобы держать вещи простыми, такие как:Как проверить, связана ли функция C

include <myapi.h> 
void main(void) { 
    func1; 
    func2; 
    ... 
} 

Но это компилируется и хорошо, потому что компилятор (GCC) оптимизирует прочь неиспользованные указатели на функции. Если я использую func1() вместо этого, я получаю ошибку компоновщика, если эта функция действительно отсутствует. Но все вызовы функций сложны, и для создания явных параметров для них потребуется несколько дней.

Я мог бы использовать nm для запросов к библиотекам, но сначала это целый набор .so-файлов, а во-вторых, он, вероятно, зависит от #defines в заголовке API.

Есть ли простой синтаксис, который я могу использовать в файле C? Или, может быть, просто вариант оптимизации (un) для gcc?

+0

Попробуйте перекомпилировать gcc '-O0', он должен отключить все оптимизации – Arseniy

+1

Очень неясный вопрос. Не могли бы вы быть более конкретными и дать реальный прецедент и мотивацию. Я даже не понимаю, что означает «недостающая функция в API», учитывая, что API является документированным набором публичных функций и т. Д. Поэтому, пожалуйста, ** отредактируйте свой вопрос **, чтобы улучшить его и мотивировать. Быть более конкретным и конкретным. –

+1

Каково практическое применение? Вы хотите взять все C-функции из Стандарта и проверить, поддерживает ли ваш компилятор? Это большая работа. Почему бы не спросить автора компилятора, что поддерживает компилятор и каковы ограничения? –

ответ

2

Одним из возможных обходного пути может быть использование указателей на функцию фальсифицировать использование:

include <myapi.h> 
typedef void Func_T(void); // Function type for easier handling 
Func_T * volatile Func; // Function pointer where functions are stored 
/* Macro to do the assignment */ 
#define TEST(f) Func = (Func_T*)f 
int main(void) { 
    TEST(func1); 
    TEST(func2); 
    ... 
} 

Linker еще может удалить функции, но это стоит попробовать.

Компиляторы часто предлагают атрибуты или прагмы для принудительного сохранения символа. Может быть полезно сохранить Func, если линкер пытается его удалить.

2

В вашем очень неясном вопросе упоминаются файлы .so (но не указано ни одной операционной системы) и nm. Поэтому я предполагаю, что вы на Linux, и мой ответ специфичен для Linux. Я не понимаю, хотите ли вы работать при компиляции & времени сборки или во время выполнения.

Учитывая общий объект /some/path/to/foo.so можно использовать dlopen(3) и dlsym(3) функции, чтобы выяснить (во время выполнения), если это общий объект определяет данный символ. Но имейте в виду, что в ELF символами файлов являются почти нетипизированы (например, вы не можете знать подпись некоторой функции в общем объекте ELF от своего имени, без наличия некоторого файла заголовка C, объявляющего его).

Возможно, у вас может быть более сложная процедура software build (например, добавив специальные правила для вашего Makefile). Помните, что вы можете использовать методы metaprogramming и иметь встроенный генератор кода C. Если ваше программное обеспечение достаточно сложно (например, стоит потратить несколько недель на такие инструменты), вы даже можете настроить компилятор GCC с помощью GCC MELT (или написать собственный плагин GCC).

Обратите внимание, что некоторые файлы заголовков (для данной библиотеки) можно определить функцию inline или может определить макрос с аргументами для него (например, см waitpid(2), часть POSIX API; WIFEXITED практически макро). В обоих случаях эта функция не будет символом общей библиотеки ELF, но может быть использована из исходного кода правильно с использованием этой библиотеки (и правильно #include -в соответствующих заголовках). Другими словами, API не совпадает с набором символов ELF.

Читайте также Good Practices in Library Design, Implementation, and Maintenance и How To Write Shared Libraries и D.Wheeler Program Library HOWTO.

Наконец, компилятор не может оптимизировать, если добавить код в зависимости от условий, которые, как известно, всегда являются ложными (см. Примерно opaque predicates), например.

int main(int argc, char**argv) { 
    // in practice, all the tests above are false, 
    // but the compiler is not clever enough to optimize 
    if (getpid()==0) funct1(); // always false 
    if (argc<0) funct2(); //always false 
    if (argv[0][0]==(char)0) funct3(); //always false 
    /// etc 

В случае сигнатура функции требуют некоторые аргументы, вы могли бы просто проверить свой адрес:

extern void func1(int); // actually, in some included header 
    if (argv[0]==NULL || (void*)func1 == NULL 
     || (void*)func1 == (void*)3) abort(); 

(я считаю, что стандарты C позволяют компилятору оптимизировать (void*)func1 == NULL -as всегда ложно - но это не будет оптимизировать (void*)func1 == (void*)3, которые в практике всегда ложна на Linux ...)

, но опять же, API больше набор символов ELF и функция «API» API могут фактически быть inline или макросом. Вас может заинтересовать weak symbols.

+0

Интересное чтение, спасибо. – dargaud

Смежные вопросы