2

I have an embedded IoT project that I like to first develop in part by using PC tools such as VisualStudio. My embedded project has only a flash memory for a file system, and I'd like to redirect fopen fread etc. to my own private implementation on Windows. But what I'm encountering is an inability to have my private CRT library take precedence over the built-in CRT (e.g., built-in behavior driven by /MD compiler switch).

I have a simple three project solution.

Project 1 is a test executable. It has a one line main:

int main()
{
    test();
}

Project 2 and 3 are static libraries. Project 2 has:

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

void test()
{
    printf("%s\n", strchr("x", 'x'));
}

Project 3 has:

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

I expect output to be overridden but instead it is

x

But if I add this to Project 1:

printf("%s\n", strchr("y", 'y'));

The output will be

overridden
overridden

First one from test() in library, second from executable main() directly.

Any suggestions?

jws
  • 2,171
  • 19
  • 30
  • What have you tried that does not work? Knowing that will save anyone from suggesting things you have already tried, or enable then to see where you might be going wrong if you approach is nearly correct. – Clifford Mar 22 '19 at 20:29
  • I am not sure how having "_only a flash memory for a file system_" precludes the use of the standard file system I/O on the test system - that is rather the purpose of the stdio abstraction. A more usual requirement is to emulate an embedded non-volatile persistent storage API using the test environment's file-system. Unless of course it is teh file system itself you are testing. – Clifford Mar 22 '19 at 20:37
  • Agreed, it uses a special CRT on the embedded device and is the point of using fopen etc. But development tools for the embedded device are sub-par. I like to have the richness of PC tools, such as nice debugging, code coverage, etc., that the embedded device does not offer. So what I need to do is emulate or mock embedded CRT on Windows, but, it's falling into actual Windows CRT instead. – jws Mar 22 '19 at 20:40
  • If you are doing cross platform compiling and want to test an embedded program on a PC, you will certainly need some conditional preprocessor magic. You are basically writing your own HAL, and including ` directly in your files means your are skipping the abstraction layer and going straight for the implementation. So start by creating your own header which will do the necessary wiring, and avoid using standard headers which access hardware directly (like ``). It's likely you will want to avoid `` and anything related to `malloc` too. – vgru Mar 25 '19 at 08:58
  • @Groo I am able to redirect CRT of a third party lib, which is proprietary and doesn't come with source. It's not necessary to create an entire abstraction layer. (I will give you this - the link solution isn't pretty.) – jws Mar 25 '19 at 20:17

2 Answers2

1

The linker resolves symbols on a first match basis - priority to separately linked .obj files, then .lib files in the order presented to the linker of the command line. So you can normally override a library symbol just be linking your own replacement. I have never tried it with MSVC, and it is a somewhat brutal approach.

An alternative solution that does not rely on specific linker behaviour is to use the pre-processor to replace standard symbols with your own alternatives. For example:

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

add the other macros and declarations you need in a header file called testlib.h for example then use a "forced include" (/FI testlib.h in MSVC) to invisibly include the test interface everywhere. Then when built on Windows, all fopen calls will be replaced with test_fopen calling your replacement functions instead.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • MSVCRT is not passed as an input. It's pulled from OBJ metadata from compiler /MD or /MT switch. – jws Mar 22 '19 at 20:35
  • @jws : In that case link the replacements as .obj code rather than .lib files. From your edit, it seems that this will work. That said, what is wrong with including the source for the replacements directly in the project since you have shown that to work? It makes for easier debugging and test instrumentation perhaps. – Clifford Mar 22 '19 at 20:43
  • Right what I really want is to put the CRT lib last on the command line. But there's no way to say "No CRT - I will provide it manually" as far as I can tell. The static or dynamic CRT doesn't make a difference, I tried. The compile time solution won't work for me because some of the code is lib only from third party. – jws Mar 22 '19 at 20:45
  • 1
    Could you perhaps switch to using MinGW GCC where you may have more control? That said I don't believe it cannot be done in MSVC - perhaps the linker option [/NODEFAULTLIB](https://learn.microsoft.com/en-us/cpp/build/reference/nodefaultlib-ignore-libraries?view=vs-2017) is what you need. – Clifford Mar 22 '19 at 20:58
  • MinGW with GCC is a possibility to try. For me is massive toolchain restructure and I'll probably just live with my ugly newly discovered workaround. – jws Mar 22 '19 at 21:07
  • I couldn't get far with /NODEFAULTLIB because I still want the majority of MSVCRT. But hmm I never tried also manually putting the MSVCRT lib on the command line. – jws Mar 22 '19 at 21:11
  • The proper answer is indeed `/NODEFAULTLIB` and the following at the end of the list of inputs: `MSVCRTD.lib ucrtd.lib vcruntimed.lib` (and 'd' removed for release libs) – jws Mar 22 '19 at 21:39
  • Meh, /NODEFAULTLIB is intermittently working. It fixed the small test project but does not fix my big project - despite all libs specified in the right order, it is still linking to MSVCRT over the private code. – jws Mar 22 '19 at 22:01
1

If the problem is caused by CRT usage in a static library only, and linker is finding MSVCRT DLL export lib before it finds private CRT static lib, a workaround might be to force a reference to the private CRT in the executable source files.

Example matching code above, placed in main as a global:

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

It is less than ideal but it alters linker search order in a reliable way.

More findings

There may be combinations (that I don't totally understand) where the linker will emit a LNK1169 "one or more multiply defined symbols" (more than one definition) linker error. For example, it might say fopen is defined more than once, and if you reverse the order of the private and MS libraries on the command line, then it might say fread is defined more than once. Perhaps it is caused by MSVC lib having DLL export signature, and the private lib being an import symbol. The behavior is hard to determine as it doesn't error on all of the functions that are being overridden.

If it is possible to switch all private libraries to /MT compiler switch (use static CRT), then the LNK1169 problem is resolved.

jws
  • 2,171
  • 19
  • 30