Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I get segfaults when declaring a struct globally or extern?

I have a struct defined in a header as follows:

#define LC_ERR_LEN 300
typedef struct dLC_ERRMSG {
   short nr;
   short strategy;
   char tx[LC_ERR_LEN];
} LC_ERRMSG;

Which I use in my code as such:

LC_ERRMSG err;
char *szError;
szError = strerror(sStatus);
snprintf(err.tx,LC_ERR_LEN," %s - %s",szFilename,szError);
/* do something with our error string */

That works. If however, I declare LC_ERRMSG err; globally - i.e. outside the function it is used, or even extern LC_ERRMSG err; (which was my original intention, as I would want to be able to read out the error status in a central location), the code segfaults at the snprintf call.

Can you give me any clues why?

ddd tells me that the memory is initialized to either all zeroes when declared globally, or at least initialized and readable when declared extern. The values szFilename, szError and LC_ERR_LEN are all correct and meaningful.

like image 596
relet Avatar asked Jul 01 '10 13:07

relet


3 Answers

Your linker can simply throw away the symbols, which it believes are not used (the GNU linker does so). In this case you can explicitly link the object file with that symbol.

With C++ you can not control the order of initialization of global objects defined in other compilation units without any additional efforts (see http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12).

Use the "construct on first use" idiom, which simply means to wrap your static object inside a function.

like image 132
Sergey Avatar answered Oct 21 '22 02:10

Sergey


If you have:

// structs.hpp
#define LC_ERR_LEN 300
typedef struct dLC_ERRMSG {
    short nr;
    short strategy;
    char tx[LC_ERR_LEN];
} LC_ERRMSG;

and:

// main.cpp
#include "structs.hpp"
LC_ERRMSG err;

int main()
{
    // ...

    char *szError;
    szError = strerror(sStatus);
    snprintf(err.tx, LC_ERR_LEN, "%s - %s", szFilename, szError);
}

then this should work. However, if you switch the second line of main.cpp to:

extern LC_ERRMSG err;

then you need to make sure that the storage for err is compiled into one of your object files. For example, you could compile this source:

// globals.cpp
#include "structs.hpp"

LC_ERRMSG err;

and link the resulting globals.o into main.o.

Either of the approaches should not cause a segmentation fault. If you are getting a segmentation fault, then the problem could be that LC_ERR_LEN has a different value when globals.cpp is being compiled than when main.cpp is being compiled. Or, perhaps szFilename or szError are NULL/bad. The printf family cannot print NULL or bad pointers with format flag %s; the following code causes a segmentation fault:

#include <stdio.h>

int main()
{
    printf("%s\n", NULL);
}

EDIT: I thought of another potential cause of the problem. You could have a symbol clash if you are using a C compiler as err is a symbol that could be in use as the name of several, different global variables in a large project. If you are using a C++ compiler, then the name mangling process should ensure that each err has its own symbol. Just make sure that you are compiling as C++.

like image 32
Daniel Trebbien Avatar answered Oct 21 '22 02:10

Daniel Trebbien


+1 for Daniel's Answer. Here's a Works-for-me. Works for you? Upvote Daniel's answer.


// structs.hpp
#define LC_ERR_LEN 300
typedef struct dLC_ERRMSG {
    short nr;
    short strategy;
    char tx[LC_ERR_LEN];
} LC_ERRMSG;

// error.cpp
#include "structs.hpp"

LC_ERRMSG err;

// main.cpp
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "structs.hpp"
extern LC_ERRMSG err;

int main()
{
    // ...
    char *szFilename = "EXAMPLE.LOG";
    int sStatus = 0;
    char *szError;
    szError = strerror(sStatus);
    snprintf(err.tx, LC_ERR_LEN, "%s - %s", szFilename, szError);

    printf( "err.tx: %s", err.tx );
}

// Output:
err.tx: EXAMPLE.LOG - No error
like image 45
maxwellb Avatar answered Oct 21 '22 00:10

maxwellb