Reputation: 17114
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:
global
variable has address 0x600918get_global
got 0x4004adBasically, the question is, how do I use gcc and ld to produce the code for a function that would:
global
variable and/or function on exactly addresses mentionedget_global
implementation)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
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
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