GreyCat
GreyCat

Reputation: 17114

gcc & GNU ld linker - produce a statically-linked code with forced fixed addresses / offsets

I have a (relatively big) static binary in which I'd like to replace a function with the other one. Given the function's complexity, I'd like to use C and gcc to compile spliced-in function and then just replace the code. For this to work, obviously, I need somehow to enforce functions and certain global variables I would access to be located at specific offsets. How do I do that using gcc and ld?

A very simple example - given a program like that:

int global = 42;
int get_global() { return global; }

int main() {
    return get_global();
}

Compiling and disassembling this function yields the following:

00000000004004ad <get_global>:
  4004ad:       55                      push   %rbp
  4004ae:       48 89 e5                mov    %rsp,%rbp
  4004b1:       8b 05 61 04 20 00       mov    0x200461(%rip),%eax        # 600918 <global>
  4004b7:       5d                      pop    %rbp
  4004b8:       c3                      retq   

Note that:

Basically, the question is, how do I use gcc and ld to produce the code for a function that would:

I thought that it could be possible with either some pragmas or compiler-specific attributes specified for function prototypes and/or globals, and, indeed, gcc has some variables attributes that control packing and placement of functions/variables in certain sections, but it's impossible to specify fixed addresses.

I strongly suspect that it could be done with manually invoking ld and using some ld linker script magic, but a quick look at its documentation doesn't seem to ring any bells for me. Am I missing something?

Definitely related: Fixed address variable in C

Upvotes: 4

Views: 1004

Answers (2)

chmeee
chmeee

Reputation: 3638

GNU ld allows you to use a custom linker script that might do what you want. A lot of embedded projects do this to place constants at specific locations in ROM. If you put the variables in a separate section using __attribute__((section("foo"))) you can put the section anywhere you want and specify the address in the linker script.

For example (from one of my projects, this is a modified script from TI):

MEMORY
{
    FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00100000
    SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00040000
}

SECTIONS
{
    .text :
    {
        _text = .;
        KEEP(*(.isr_vector))
        *(.text*)
        _devid_start = ALIGN(4);
        KEEP(*(.devid*))
        sys_devices = .;
        KEEP(*(.device*))
        sys_devices_end = .;
        sys_threads = .;
        KEEP(*(.threads*))
        sys_threads_end = .;
        *(.rodata*)
        _etext = .;
    } > FLASH

    .data : AT(ADDR(.text) + SIZEOF(.text))
    {
        _data = .;
        *(vtable)
        *(.data*)
        _edata = .;
    } > SRAM

    .bss :
    {
        _bss = .;
        *(.bss*)
        *(COMMON)
        _ebss = .;
    } > SRAM
}

So as you can see in this example, Everything that's put into the section '.devid*' and '.device*' gets merged into section '.text', in freeform. There are still only 3 sections in the resulting file: .text, .data, .bss, the sections you create in the C source code will get merged by this linker script.

If you want to do this with a hosted platform, extract the linker script (insert voodoo here), and modify that with your section merging.

Upvotes: 1

Soren
Soren

Reputation: 14708

I have seen that done before, using an ASM file for just pinning the variables to specific addresses. This was specifically used for global variable used in shared memory.

In any other source file, you will obviously need to declare the global variable extern

So write a short asm file with just your variables, and compile and link that with the rest of your code.

Upvotes: 1

Related Questions