0

I'm writing my own init code that should traverse an array of global constructors and call them. The pointers to these constructors are stored in the .init_array section. My code looks like this:

extern void (**_init_array_start)();
extern void (**_init_array_end)();


void _init()
{
    void (**ctor)();
    for (ctor = _init_array_start ; ctor != _init_array_end ; ctor++) {
        (*ctor)();
    }
}

The externals are defined in a linker script like this:

.init_array :
{
    . = ALIGN(4);
    _init_array_start = .;
    KEEP(*(SORT(.init_array.*)))
    KEEP(*(.init_array))
    . = ALIGN(4);
    _init_array_end = .;
} >rom

However, the final assembler code looks like this:

00000010 <_init>:
  10:   b538        push    {r3, r4, r5, lr}
  12:   4b05        ldr r3, [pc, #20]   ; (28 <_init+0x18>)
  14:   4d05        ldr r5, [pc, #20]   ; (2c <_init+0x1c>)
  16:   681c        ldr r4, [r3, #0]
  18:   682b        ldr r3, [r5, #0]
  1a:   429c        cmp r4, r3
  1c:   d003        beq.n   26 <_init+0x16>
  1e:   f854 3b04   ldr.w   r3, [r4], #4
  22:   4798        blx r3
  24:   e7f8        b.n 18 <_init+0x8>
  26:   bd38        pop {r3, r4, r5, pc}
  28:   00000144    andeq   r0, r0, r4, asr #2
  2c:   00000150    andeq   r0, r0, r0, asr r1

Now, the interesting part here is 0x16 and 0x18. These two instructions dereference _init_array_start and _init_array_end in the loop header. This is not what I intended. I want to work on the pointers to the function pointers, not on their values (i.e. the function pointers directly). Now, when I change the extern declarations to the following, the two dereferencing instructions magically disappear:

extern void (*_init_array_start[0])();
extern void (*_init_array_end[0])();

So, why does the compiler dereference the pointers in the first place, and why does it not do that with the array syntax? Shouldn't these two syntaxes be equivalent?

3 Answers 3

1

Symbols are (almost) always addresses, and pointers are not the same as arrays.

extern int* i; means "the symbol 'i' is the address of an int*." i would then mean "the value at the address specified by symbol 'i'" which is a pointer dereference. &i would mean "the address specified by symbol 'i'" which is not.

In the case of arrays, "extern int a[42];" means "the symbol 'a' is the address of an array of 42 ints." When an array name is used by itself in C, it refers to the address of the array, so a here would be equivalent to &i from the previous example. &a also points to the same address, but has a different type. Both have the address specified by the symbol 'a'.

If you don't want to use array syntax, the first code you posted should work if you change extern void (**_init_array_start)(); to extern void (*_init_array_start)(); and _init_array_start to &_init_array_start.

Edit: Another way of looking at this is that _init_array_start is not a pointer to the first initializer - it is the first initializer - so declaring it as a pointer to a function pointer is wrong. Array syntax declares it as an array of function pointers, which is what it actually is.

Sign up to request clarification or add additional context in comments.

Comments

0

_init_array_start Is the address where the first pointer to a constructor is stored. You must take the address from this variable and iterate to the address of _init_array_end. When you have this in mind you will have to adapt the pointer types to fit this approach.

Comments

0

This is a declaration of an object of pointer to pointer type:

extern void (**_init_array_start)();

This is a definition of a symbol name associated with the beginning of an array:

_init_array_start = .;

But you didn't allocate storage for anything of pointer to pointer type, only the array.

It sounds like you might want to handle the C side this way instead:

extern void (*_init_array_start)();
extern void (*_init_array_end)(); // One past the end; not a real object.

void _init()
{
    void (**ctor)();
    for (ctor = &_init_array_start ; ctor != &_init_array_end ; ctor++) {
        (*ctor)();
    }
}

It would probably be cleaner to generate a global integer value to represent the size of the array, and access that from C, although this would mean allocating storage for said integer.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.