Замена функции CRT MSVC частной реализацией

#windows #visual-studio #embedded #msvcrt

#Windows #visual-studio #встроенный #msvcrt

Вопрос:

У меня есть встроенный проект IoT, который я хотел бы сначала частично разработать с использованием инструментов для ПК, таких как VisualStudio. В моем встроенном проекте есть только флэш-память для файловой системы, и я хотел бы перенаправить fopen fread etc. На мою собственную частную реализацию в Windows. Но с чем я сталкиваюсь, так это с невозможностью того, чтобы моя частная библиотека CRT имела приоритет над встроенной CRT (например, встроенное поведение, определяемое /MD переключателем компилятора).

У меня есть простое решение из трех проектов.

Проект 1 является тестовым исполняемым файлом. Она имеет однострочный основной:

 int main()
{
    test();
}
  

Проекты 2 и 3 являются статическими библиотеками. Проект 2 имеет:

 #include <string.h>
#include <stdio.h>

void test()
{
    printf("%sn", strchr("x", 'x'));
}
  

Проект 3 имеет:

 char * strchr(const char * s, int c)  // exact signature of MSVC
{
    return "overridden";
}
  

Я ожидаю, что результат будет overridden но вместо этого это

 x
  

Но если я добавлю это в проект 1:

 printf("%sn", strchr("y", 'y'));
  

Результат будет

 overridden
overridden
  

Первый из test() библиотеки, второй из исполняемого main() файла напрямую.

Есть предложения?

Комментарии:

1. Что вы пробовали, что не работает? Знание этого избавит кого-либо от предложения того, что вы уже пробовали, или позволит затем увидеть, где вы можете ошибиться, если ваш подход почти правильный.

2. Я не уверен, каким образом наличие » только флэш-памяти для файловой системы » исключает использование стандартного ввода-вывода файловой системы в тестовой системе — это скорее цель абстракции stdio. Более обычным требованием является эмуляция встроенного API энергонезависимого постоянного хранилища с использованием файловой системы тестовой среды. Если, конечно, вы не тестируете саму файловую систему.

3. Согласен, он использует специальный CRT на встроенном устройстве и является целью использования fopen и т.д. Но инструменты разработки для встроенного устройства не соответствуют требованиям. Мне нравится иметь все богатство инструментов ПК, таких как хорошая отладка, покрытие кода и т.д., Которых не предлагает встроенное устройство. Итак, что мне нужно сделать, это эмулировать или имитировать встроенный CRT в Windows, но вместо этого он попадает в реальную Windows CRT.

4. Если вы выполняете кроссплатформенную компиляцию и хотите протестировать встроенную программу на ПК, вам, безусловно, понадобится немного магии условного препроцессора. По сути, вы пишете свой собственный HAL, и включение <stdio.h > непосредственно в ваши файлы означает, что вы пропускаете уровень абстракции и переходите прямо к реализации. Итак, начните с создания собственного заголовка, который выполнит необходимую проводку, и избегайте использования стандартных заголовков, которые напрямую обращаются к оборудованию (например <stdio.h> ). Скорее всего, вы захотите избежать <stdlib.h> и всего, что связано с malloc .

5. @Groo Я могу перенаправить CRT из сторонней библиотеки, которая является частной и не поставляется с исходным кодом. Нет необходимости создавать целый уровень абстракции. (Я дам вам это — решение со ссылкой не очень.)

Ответ №1:

Компоновщик разрешает символы на основе первого совпадения — приоритет для отдельно связанных .obj файлов, затем.файлы библиотеки в порядке, представленном компоновщику командной строки. Таким образом, вы обычно можете переопределить символ библиотеки, просто связав свою собственную замену. Я никогда не пробовал это с MSVC, и это несколько жестокий подход.

Альтернативным решением, которое не зависит от специфического поведения компоновщика, является использование препроцессора для замены стандартных символов вашими собственными альтернативами. Например:

 #if defined _WIN32
    #define fopen test_fopen
    FILE* test_fopen( const char * filename, const char * mode ) ;
#endif
  

добавьте другие необходимые макросы и объявления в заголовочный файл с именем testlib.h, например, затем используйте «принудительное включение» ( /FI testlib.h в MSVC), чтобы везде незаметно включить тестовый интерфейс. Затем при сборке в Windows все fopen вызовы будут заменены на test_fopen вызов ваших функций замены вместо этого.

Комментарии:

1. MSVCRT не передается в качестве входных данных. Она извлекается из метаданных OBJ с помощью переключателя compiler / MD или / MT.

2. @jws : В этом случае свяжите замены как .obj-код, а не . lib-файлы. Судя по вашему редактированию, это сработает. Тем не менее, что плохого в том, чтобы включить исходный код для замен непосредственно в проект, поскольку вы показали, что это работает? Возможно, это упрощает отладку и инструментарий тестирования.

3. Правильно, чего я действительно хочу, так это поместить библиотеку CRT последней в командной строке. Но, насколько я могу судить, нет способа сказать «Нет CRT — я предоставлю это вручную». Статический или динамический CRT не имеет значения, я пробовал. Решение во время компиляции не будет работать для меня, потому что часть кода является библиотекой только от третьей стороны.

4. Не могли бы вы, возможно, переключиться на использование MinGW GCC, где у вас может быть больше контроля? Тем не менее, я не верю, что это невозможно сделать в MSVC — возможно, опция компоновщика / NODEFAULTLIB — это то, что вам нужно.

5. MinGW с GCC — это возможность попробовать. Для меня это масштабная реструктуризация цепочки инструментов, и я, вероятно, просто смирюсь с моим уродливым недавно обнаруженным обходным путем.

Ответ №2:

Если проблема вызвана использованием CRT только в статической библиотеке, и компоновщик находит библиотеку экспорта MSVCRT DLL до того, как найдет статическую библиотеку private CRT, обходным решением может быть принудительное включение ссылки на private CRT в исполняемые исходные файлы.

Пример, соответствующий приведенному выше коду, помещенный в main как глобальный:

 static char * (*p_strchr)(const char *, int) = strchr;
  

Это далеко не идеально, но оно надежным образом изменяет порядок поиска компоновщика.

Дополнительные результаты

Могут быть комбинации (которые я не совсем понимаю), при которых компоновщик выдает ошибку компоновщика LNK1169 «один или несколько многократно определенных символов» (более одного определения). Например, может быть указано, что fopen определено более одного раза, и если вы измените порядок библиотек private и MS в командной строке, то может быть указано, что fread определено более одного раза. Возможно, это вызвано тем, что библиотека MSVC имеет подпись экспорта DLL, а частная библиотека является символом импорта. Поведение трудно определить, поскольку оно не выдает ошибку во всех переопределяемых функциях.

Если возможно переключить все частные библиотеки на /MT переключатель компилятора (использовать статический CRT), то проблема LNK1169 решена.