1

Can someone please explain why a member variable (char m_DBFileName[257]) of static variable (g_JournalDB) getting initialized with a random value? I expect it to be populated with '\0's.

enter image description here

More info: g_JournalDB is part of a dynamic library loaded on app startup via

public class MyApplication extends Application {
    static {
        System.loadLibrary("mylibrary");
        ...
        System.loadLibrary("mylibraryN");
    }
    @override
    public void onCreate() {...}
    ...
}

The screenshot above was taken from a breakpoint in onCreate() of MyApplication where g_JournalDB gets created. I can provide more info if needed.

EDIT: Is it possible that, since I am loading multiple .so files, one ore more .so files have overlapping memory map?

EDIT2: In the class constructor of cAMPDatabase, I am doing memset(m_DBFileName, 0, sizeof(m_DBFileName)) so I really expect that it is populated with '\0's.

UPDATE1: Later on in the app, I tried to update the g_JournalDB.m_DBFileName, I found out that I can no longer access the first 20 indexes. When I did a strncpy(m_DBFileName, "/data", 256);, the new value started in index 20. As you can see below, my string "/data" starts at index 20.

enter image description here

UPDATE2: I was able to determine that the issue is caused by error in memory:

09-07 07:57:11.417 309-309/? I/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
09-07 07:57:11.417 309-309/? I/DEBUG: Build fingerprint: 'qcom/msm7808/msm7808:5.1.1/WMY57L/ittech01220402:userdebug/release-keys'
09-07 07:57:11.417 309-309/? I/DEBUG: Revision: '0'
09-07 07:57:11.417 309-309/? I/DEBUG: ABI: 'arm'
09-07 07:57:11.417 309-309/? I/DEBUG: pid: 22437, tid: 22437, name: zapplication.zapp  >>> com.zapplication.zapp <<<
09-07 07:57:11.418 309-309/? I/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x69e793d8
09-07 07:57:11.433 309-309/? I/DEBUG:     r0 0056a27c  r1 69e371ac  r2 0004222c  r3 a0bcab7c
09-07 07:57:11.433 309-309/? I/DEBUG:     r4 ffffffff  r5 a0e29428  r6 be876678  r7 be876618
09-07 07:57:11.433 309-309/? I/DEBUG:     r8 be897ab0  r9 b7a7f1c8  sl be897a40  fp b7a7f1c8
09-07 07:57:11.434 309-309/? I/DEBUG:     ip a09df2f8  sp be876600  lr a094afa9  pc a094afd8  cpsr 300f0030
09-07 07:57:11.434 309-309/? I/DEBUG: backtrace:
09-07 07:57:11.434 309-309/? I/DEBUG:     #00 pc 0001bfd8  /data/app/com.zapplication.zapp-1/lib/arm/libmylibrary.so (_ZN12cAMPDatabase11CreateTableEPKcP18DB_DATA_DEFINITION+79)

UPDATE3: For those who asked, here is the cAMPDatabase class constructor:

cAMPDatabase::cAMPDatabase() {
    m_DBHandle = NULL;
    memset(m_DBFileName, 0, sizeof(m_DBFileName));
    memset(m_Tables, 0, sizeof(m_Tables));
    m_TblCount=0;
    this->m_SqlObj = this->NewStmt();
}

Here is the header definition (full definition here) for the class:

class cAMPDatabase {    
    friend class cAMPSqlStmt;
public:
    cAMPDatabase();
    virtual ~cAMPDatabase();
    // the rest of public variables and functions here ...
protected:  
    char m_DBFileName[257];
    // the rest of protected variables and functions here ...
}
user1506104
  • 6,554
  • 4
  • 71
  • 89
  • Does your `cAMPdatabase` class have any constructors? – Botje Sep 03 '21 at 08:53
  • 'static global' is a contradiction in terms in both C and C++. Clarify. – user207421 Sep 03 '21 at 10:03
  • @user207421 sorry if my term is incorrect. I am a java developer. anyways, I said it is static global because it was declared as `static cAMPDatabase g_JournalDB;` – user1506104 Sep 05 '21 at 09:36
  • @Botje Yes, it has a constructor. It is in libmylibrary.so. – user1506104 Sep 05 '21 at 09:41
  • If it is `static` in C++ it cannot also be global. Clarify. – user207421 Sep 05 '21 at 10:16
  • @user207421 I don't know what the proper term is. I am just referring to this variable declaration when i say it is static global: `static cAMPDatabase g_JournalDB;` – user1506104 Sep 05 '21 at 11:53
  • Does the constructor contain code to set the characters to 0? Declaring arrays does not initialise them: `#include int main() { char a[10]; for (auto b:a) printf ("%c\n", b); return 0; }` just prints some random stuff. (sorry cannot get the line breaks to work) – alle_meije Sep 06 '21 at 07:46
  • hey @alle_meije I updated the question. Thank you all for your time. – user1506104 Sep 06 '21 at 08:13
  • Did you attach a debugger to the process to verify that the class constructor can run? – Botje Sep 06 '21 at 08:32
  • Yes, the constructor ran when i used the debugger. – user1506104 Sep 06 '21 at 08:41
  • First , it should be `strncpy(m_DBFileName, "/data", 6);` because 6 is the length to copy and second, what happens when you do `strncpy(m_DBFileName+22, "/data", 6);`. At what index does the `"\data"` string now starts ? – mr.loop Sep 07 '21 at 07:18
  • Also I would suggest to set breakpoint right before the `memset` line and right after it. Do the first 16 elements value remains same ? – mr.loop Sep 07 '21 at 07:20
  • @mr.loop yes i put a break point before and after the strncpy. The "/data" starts at index 20 as shown in the screenshot. Also, I don't want to do the +22 as this won't answer the question why the first 20 indexes are no longer available. – user1506104 Sep 07 '21 at 07:31
  • 1
    @user1506104 well, I want to see if doing `+22` the string starts at `22` or at `42` or some other number. This would answer a significant part of the problem. – mr.loop Sep 07 '21 at 08:16
  • @user1506104 Also, in first comment I actually asked whether the garbabge characters that occupy first 20 indexes **change OR remain same** before and after `memset` – mr.loop Sep 07 '21 at 08:17
  • Hello @mr.loop. I used +22. This time, the value was copied to index 42, instead of index 20. Regarding your first comment, the garbage characters remain same before and after the memset(). – user1506104 Sep 07 '21 at 10:01
  • Strangely it looks as if like your array name is pointing to the 20th index instead of 1st. I would check if `printf("%p %p %p",array,array+20,&array[0])` have same value although I believe the issue can be something related to memory. Also is there anywhere a `#define` or `typedef` statement that includes `m_DBFileName` ? – mr.loop Sep 07 '21 at 10:49
  • How is `m_DBFileName` defined? ` Why not show the C++ code? At least show the definition of `cAMPDatabase` and its constructors. – Ted Lyngmo Sep 07 '21 at 21:31
  • @user1506104 Please put the full definition of `cAMPDatabase` in the question and include the implementation of all the constructors if you have more than the one you showed. Is `g_JournalDB` the only `cAMPDatabase` instance? – Ted Lyngmo Sep 08 '21 at 06:36
  • @TedLyngmo by the way, i only have one implementation of the constructor as a wrote in the question. – user1506104 Sep 08 '21 at 07:56
  • @TedLyngmo i added the full definition in this gist gist.github.com/ierosvin/d6ede02883cbf688b84c85734699131c. Regarding other instances of cAMPDatabase, I actually have two instances. But the other one gets instantiated later in the program. Much much later than the timing of this issue. – user1506104 Sep 08 '21 at 07:58

4 Answers4

1

Did you try m_DBFileName as std::string instead of char * or array?

And please try to share a minimal reproducible example for static variable initialization.

codesnerd
  • 767
  • 2
  • 8
  • 23
tamil
  • 99
  • 8
1

Glad to know that you were able to find the memory error which eventually became the most probable case.

Although, std::string class is preferred in most cases since it is the standard and more convinient to handle.

Also, other possible workaround to this problem if it persisted (non-standard compiler?) may be to explicitly selecting pointer to 0th index by memset(&m_DBFileName[0], 0, sizeof(m_DBFileName)) and in strncpy too rather than simply array name m_DBFileName since it is not exacty a pointer to first element but decays into that.

mr.loop
  • 818
  • 6
  • 20
1

I think there are two possible solutions:

  1. use std::string -- for obvious reasons, but you may not have that freedom
  2. initialise the char array, instead of altering it in the constructor.

There is a difference between

  • static char m_DBFileName[257]; (declaration only, values frozen)
  • static char m_DBFileName[257] = {0,0,...0}; (if you must, but this is declaration + initialisation)

If you don't want to use the last option, you can also do

class cAMPDatabase {
   static char m_DBFileName[257];
public:
   cAMPDatabase(): m_DBFileName({0,0,...0}) {};
};

If you really need the initialisation to be the same in different compilation units (not sure how that goes with your multiple DLLs) then you may want to use static inline instead of just static, or more elegantly, constexpr. See this answer and the text below.

alle_meije
  • 2,424
  • 1
  • 19
  • 40
0

You have a massive class and I recommend that you initialize all member variables in the one contructor you have.

Example:

cAMPDatabase::cAMPDatabase() :
    m_DBHandle{nullptr},
    m_SqlObj{nullptr},    // see note
    m_ErrorCode{0},
    m_DBFileName{},
    m_TableName{},
    m_TableFldDef{},
    m_Tables{},
    m_TblCount{}
{
    m_SqlObj = NewStmt();
}

Note: The pointer m_SqlObj is created by calling the non-static member function NewStmt(). For that to make sense, the cAMPDatabase instance would have to carry some information but there is nothing in the code that puts anything in the member variables before calling NewStmt() - except the this pointer.

I therefore assume that the definition of NewStmt() looks roughly like this:

cAMPSqlStmt* cAMPDatabase::NewStmt(void) {
    return new cAMPSqlStmt(this);
}

Unless cAMPSqlStmt requires cAMPDatabase to be fully initialized, the initialization of cAMPDatabase could then be made like this:

cAMPDatabase::cAMPDatabase() :
    m_DBHandle{nullptr},
    m_SqlObj{NewStmt()}, // or m_SqlObj{new cAMPSqlStmt(this)}
    m_ErrorCode{0},
    m_DBFileName{},
    m_TableName{},
    m_TableFldDef{},
    m_Tables{},
    m_TblCount{}
{}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • 1
    we'll try your suggestion. i will revert back. thank you. – user1506104 Sep 08 '21 at 08:20
  • so far it is working. i'll do some more tests and accept your answer, and award the bounty. meanwhile, would you mind sharing book/blog that explains what's happening here? cheers! – user1506104 Sep 08 '21 at 10:35
  • @user1506104 Great! In short, it makes sure that all member variables are properly initialized upon creation. There are however a few classes in your framework that seems to be having _owning_ pointers (like this `cAMPDatabase`) and if it was my project, I'd try to replace all those with existing classes (like `std::string` instead of `char*`), `unique_ptr`s or RAII classes that you write yourself. – Ted Lyngmo Sep 08 '21 at 11:15