Search code examples
cstatic-librarieswin64

functions not working well when linked via a static lib, but working if copied in the prog


I have a static lib (.a) containing some useful functions that I'm using inside another program. The link goes well, the functions from the lib are found, but when the program gets executed it doesn't work well. On the other hand, if I copy/paste the lib functions I need directly in my program code, it works well. Moreover, it was working well on win32, but now I'm on Win64.

Edit: I know the code is horrible (it's not mine), but it IS working when directly copied in the prog, which mean the developper won't change anything about it. What i need is to understand why it does not work well when i link the library where the function is, when it was working perfectly on Linux64 and Win32. You may find a lot of problems in this code, but it's just one example; since it doesn't explain why it works in the prog but not linked by the lib, it'll be useless to me since the dev doesn't care at all.

Here's ONE of the problematic functions in the lib (i took the simplest, which does not depends to much on the lib, but i suspect others not to work as expected):

#if defined(mingwx64)
typedef long long         I64;
#else
typedef long              I64;
#endif
typedef unsigned char     UI8;

void readParamFile (char* filename, UI8* *Params, I64 *ParamsLen){
    FILE* fic;
    char* buf; int buf_Len;
    *Params=NULL; *ParamsLen=0;

    if(!(fic=fopen(filename, "rb"))){
        fprintf(stderr, "Error, can't open %s\n", filename);
        exit (-1);
    }
    fseek(fic, 0, SEEK_END);
    buf_Len=ftell(fic);
    fseek(fic, 0, SEEK_SET);

    if(!(buf=(char*)PtrAlloc(sizeof(char)*buf_Len))){
        fprintf(stderr, "Error: Can't allocate %d bytes", buf_Len);
        exit(-1);
    }

//the files are specifics, i read the lines until the first one which doesn't start with #
    do{
       if(!fgets(buf, buf_Len, fic)){
          fprintf(stderr,"Error: Can't read parameters");
          exit(-1);
       }
       buf[strlen(buf)-1]='\0';
    } while (buf[0]=='#');

    if(!(*Params= (UI8*)PtrAlloc(sizeof(UI8)*strlen(buf)))){
        fprintf(stderr,"Error: can't allocate %d butes", buf_Len);
        exit(-1);
    }

    strncpy((char*) *Params , buf, *ParamsLen=strlen(buf));

    PtrFree(buf);
    fclose(fic);
}

So, this function is in MyLib.a. I compiled it on Win64 using Mingw64 (just like I used Mingw on Win32), the compilation works. Then, when compiling MyProg, I link this lib. The compilation goes well. And when I launch MyProg, it will at some point use this function, and stop on the strncpy (before was memcpy used, but it wasn't better). It won't throw any error message, nothing: I wait a bit and the prog stops just as if it has done everything well, except it didn't. If I try to make a print after this strncpy, it never goes to it, but the prog end "normally" (no crash that i can see).

The weird point is, if I copy/paste this function inside the code of MyProg, let's say I call it readFileBis, then readFileBis works well.

However, i'm not going to copy/paste every function of my lib inside every prog which needs it. Would be a waste.

EDIT: tried to make a reproducible example for it, but still working on it. So let's say this is the only file of MyProg:

MyProg.c

#define FILENAME "MyDir/ParamFile.txt"

void readParamFileBis (char* filename, UI8* *Params, I64 *ParamsLen){
    //the exact same thing as in readFile, don't wanna make the question too long.
}

int main(int argc, char *argv[]){

    UI8 *Params = NULL; 
    I64 ParamsLen = 0;

//this is working:
    readParamFileBis (FILENAME, &Params, &ParamsLen);
//this is not working : 
    readParamFile (FILENAME, &Params, &ParamsLen);

    exit(0);
}

And ParamFile.txt looks like it (with a way more longer number):

# Introduction text
000500000000064F1B58372A27

I would like to know what may be wrong here. Knowing that it was working well on Win32, I guess it has something to do with the 64bit thing, but I don't know what. Since I recompiled both the lib and the prog on win64, it should be ok. I have no idea what's going on.

Oh, and there's another weird thing which may or may not has something to do with it: when compiling, I may use the flag -D${ARCHI}. On Win32, ARCHI was mnigw386; on win64, it is mingwx64. When I compile with this flag on Win64, I got some warnings about my print formats (like, I'm using %ld to print some I64, and it doesn't like it) that I didn't had on win32.

EDIT/Solution: So the problem was indeed the I64. I managed to change it by a int64_t and it is working. Moreover, i managed to refont the Makefiles (from 250lines and 16 targets to 90 lines and 5 targets...), and then found out that the architecture was indeed defined...but not included in any compilation flag. So i guess that the prog couldn't see that I64 was defined as a long long (since it's in a if mingwx64) and thought it was a long, and that was the big prob here.


Solution

  • There are multiple issues in the code fragments posted:

    • #define FILENAME "MyDir\ParamFile.txt" is incorrect. You should either escape the backslashes as #define FILENAME "MyDir\\ParamFile.txt" or use plain forward slashes that work with all Windows versions: #define FILENAME "MyDir/ParamFile.txt"

    • The arbitrary definition of type I64 is risky. You should include <stdint.h> and use typedef uint64_t I64;

    • the function prototype void readFile(char* filename, UI8 *Params, I64 *ParamsLen) is inconsistent with the expected API: readFile reads the file contents and stores a pointer to an allocated buffer of length *ParamsLen into *Params. The prototype should be:

      int readFile(const char *filename, UI8 **Params, I64 *ParamsLen);
      

      readFile should return an error code in case the file cannot be open or read.

    • There is a missing } after exit (-1); in function readFile.

    • The temporary buffer should be allocate with an extra byte, just in case the file contains a single line not terminated with a newline.

    • fgets() does not necessarily store a '\n' at the end of the array, especially if the file does not contain one. Stripping the last character unconditionally is incorrect.

    • why not use malloc() instead of the non portable Windows specific PtrAlloc().

    • If you expect to use the file contents as a C string, you should allocate an extra byte for the null terminator and store it at the end of the array.

    This function uses a contorted approach to read a line from a file. You should try harder to understand what it does and fix it from the above remarks. C is a sharp and unforgiving language, programming without a good understanding of the elements used leads to bugs and failures with maximum frustration.

    The main issue in and around this code is the definition of type I64. Due to the overwhelming amount of non portable Win32 code that assumed long to be exactly 32-bits, Microsoft decided to keep long 32-bits on Win64 and to make matters worse delayed support for standard 64-bit types in the C library, making %lld non portable to Microsoft Windows default libraries. This explains the warning you get from the gcc compiler that checks the consistency of printf arguments with the format string. If it worked in the win32 version, they probably did not use 64 bit values (long long) at all.

    A possible explanation for the different behavior between the static library and embedding the functions' code in the program is the use of different compilers, compiler options or command line definitions. Did you check all these? Do you compile the library yourself or do you get it in binary form from a different team?

    Porting software between 32- and 64-bit architectures is non-trivial, especially on Windows platforms that encouraged non portable constructs and have flaky C99 support.

    The code is not only poorly written, the prototype for the function is incorrect: Params is not a UI8 * it should be a UI8 **, the function works by chance and it will break easily. If the developper won't change anything about it, I suggest a few possible solutions:

    • copy the functions locally, fix the problems and don't use the bogus library,
    • write your own functions.
    • find yourself a better job at a different company: you are wasting your time there, you will learn so much more from a saner environment.