gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Coding > First member of array is messed up

#113901 - sgeos - Mon Jan 01, 2007 12:52 am

I'm working on a PC side test scaffold for some code, and the first member of one of my arrays is messed up. I have never seen anything like this before. Could it be a compiler error?

Code:
CommandAction mList[] =
{
        {"printme", &print},
        DISPATCH_ALIAS(pr, print),
        DISPATCH_COMMAND(print),
        DISPATCH_ALIAS(q, quit),
        DISPATCH_COMMAND(quit),
        DISPATCH_ALIAS(set, set),
        DISPATCH_COMMAND(var),
        LAST_DISPATCH_COMMAND
};

The macros just spit out a string and function pointer, like the first line. The assembler looks fine:
Code:
LC0:
        .ascii "printme\0"
LC1:
        .ascii "pr\0"
LC2:
        .ascii "print\0"
LC3:
        .ascii "q\0"
LC4:
        .ascii "quit\0"
LC5:
        .ascii "set\0"
LC6:
        .ascii "var\0"
.globl _mList
        .data
        .align 32
_mList:
        .long   LC0
        .long   _print
        .long   LC1
        .long   _print
        .long   LC2
        .long   _print
        .long   LC3
        .long   _quit
        .long   LC4
        .long   _quit
        .long   LC5
        .long   _set
        .long   LC6
        .long   _var
        .long   0
        .long   0

A list of string constants followed by the array. The array members are lined up in memory in a regular fashion, but the location of the first string is odd:
Code:
    Array       Function    String      String
0:  0x00403000  0x004015C1  0x0040FC60    <----- Why is it here?!
1:  0x00403008  0x004015C1  0x00404038  pr
2:  0x00403010  0x004015C1  0x0040403B  print
3:  0x00403018  0x0040175A  0x00404041  q
4:  0x00403020  0x0040175A  0x00404043  quit
5:  0x00403028  0x004016BD  0x00404048  set
6:  0x00403030  0x00401620  0x0040404C  var

The function pointers appear to be correct. The "printme" string is in the binary. It lives right in front of "pr", where it should be, but address appears to have been botched.

-Brendan

#113907 - tepples - Mon Jan 01, 2007 2:14 am

Unless the asm file is putting the strings in the .text segment. In addition, string constants should go in .rodata, not .data, unless you want them copied to RAM.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#113912 - poslundc - Mon Jan 01, 2007 2:40 am

Since mList is in the .data section, it's going to be in RAM next to other non-constant variables.

At first glance - especially seeing as it's the very first element of your array that's botched - it looks like bad data for some other pointer being written off the end of some adjacent buffer, thereby corrupting your array.

If you have the ability to place read/write breakpoints on your target, this would be an excellent time to do it.

Dan.

#113913 - sgeos - Mon Jan 01, 2007 2:40 am

Code:
        .file   "dispatch.c"
        .section .rdata,"dr"
LC0:
        .ascii "printme\0"
...

Evidently, the strings live in rdata and the array in data. This is a PC side tool, but thanks for the advice. The array should be const.

-Brendan

#113922 - sgeos - Mon Jan 01, 2007 4:52 am

I had two variables named mList in different modules. As soon I made the above list const, the program segfaulted like crazy. (Something is seriosly wrong when changing something to const segfaults your program.) This bug was especially confusing because it segfaulted on (what appeared to be) a proper assignment segfault:
Code:
mList = pBuffer;

Really confusing. (Yes, the types are the same. Yes, it looks right. Yes, the types are the same. Yes, there is memory available. Yes, the types are the same. Yes, it is being called correctly. ...)

In the future I'll be wary of global variables with the same name. I could swear that you can have two global variables/functions with the same name as long as they are constrained to module scope. Then again, something might be exposed somewhere. And I might just be wrong about items with the same name.

I'll give my thanks to tepples. Sub-thanks to Dan (I must have just missed your comment).

I should probably figure out why things broke as they did, but things (appear) to work now and I want a break. =P

Is there any way get gcc to spit out a human readable symbol table? That would be really nice to have.

-Brendan

#113926 - tepples - Mon Jan 01, 2007 6:13 am

sgeos wrote:
Is there any way get gcc to spit out a human readable symbol table? That would be really nice to have.

arm-eabi-nm -n something.elf
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#113936 - Peter - Mon Jan 01, 2007 1:54 pm

sgeos wrote:
In the future I'll be wary of global variables with the same name. I could swear that you can have two global variables/functions with the same name as long as they are constrained to module scope.

You can use the same names in each compilation unit when the variables/functions are declared as static.

#113962 - sajiimori - Mon Jan 01, 2007 11:03 pm

Is this what happened?
Code:

// Module1.c
const char* const thing[5];

Code:

// Module2.c
extern int thing;

void corruptFirstPointer()
{
  thing = 10;
}

There should be a link error without putting the 'extern'.

About changing things to const: An x86 application could write-protect const data. If the 'thing' array was protected in this manner, it could cause a segfault on write.

#113963 - poslundc - Mon Jan 01, 2007 11:12 pm

sgeos wrote:
In the future I'll be wary of global variables with the same name. I could swear that you can have two global variables/functions with the same name as long as they are constrained to module scope. Then again, something might be exposed somewhere. And I might just be wrong about items with the same name.


In GCC (which I realize you almost definitely aren't using, so the particulars may differ), module-level global variable declarations are by default global to the entire project unless you explicitly declare them as static (which your code snippet did not do).

But they will also create a linker error if you reuse the same name, so I don't know why you didn't get that happening there.

Dan.

#113965 - sgeos - Mon Jan 01, 2007 11:40 pm

sajiimori wrote:
Is this what happened?
Code:
* SNIP *

Close. This is what happened:
Code:
// variablelist.c
Variable *mList;  // pointer to user provided buffer

// dispatch.c
// list of word/action pairs; this became const
CommandAction mList[] =
{
        DISPATCH_COMMAND(print),
        DISPATCH_ALIAS(q, quit),
        // ...
        LAST_DISPATCH_COMMAND
};


sajiimori wrote:
There should be a link error without putting the 'extern'.

No link error. Maybe the compiler treated both variables as (const void *) to the array? I'm not sure.

sajiimori wrote:
About changing things to const: An x86 application could write-protect const data. If the 'thing' array was protected in this manner, it could cause a segfault on write.

Yes, but I didn't write to it. I would have, but I didn't get that far. I tried to change the pointer variable, and it segfaulted on that assignment. If the compiler thought that was a const variable, I could see that causing problems.

poslundc wrote:
In GCC (which I realize you almost definitely aren't using, so the particulars may differ),

Actually, I'm using gcc. =P

poslundc wrote:
module-level global variable declarations are by default global to the entire project unless you explicitly declare them as static (which your code snippet did not do).

I'll keep this in mind. Will this have any adverse effects when porting to other compilers?

poslundc wrote:
But they will also create a linker error if you reuse the same name, so I don't know why you didn't get that happening there.

No link error. gdb reported the buffer pointer as a generic pointer. I didn't look the table because I didn't segfault that way, and by the time I realized it was the problem I fixed it. The table may be a generic pointer as well. This is why I'd like to dump the symbol table- so I can inspect it.

-Brendan

#113974 - sajiimori - Tue Jan 02, 2007 1:17 am

I was just reading that GCC, by default, shares memory if you declare a global variable (without extern) twice. Use -fno-common to disable this (dangerous and obsolete) behavior.
Quote:
I tried to change the pointer variable, and it segfaulted on that assignment.
Right -- you didn't declare the mList pointer const, but if it's sharing space with a const array of the same name, then it might be in a read-only section anyway. x_x

Edit: Better yet, compile as C++. This kind of error could only happen in C.