I'm trying to change the start location of my stack using a linker script on x86_64. I was able to move my executable start address using this:
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x200000)); . = SEGMENT_START("text-segment", 0x200000) + SIZEOF_HEADERS;
I changed my globals like this:
.data ALIGN(0x10000000) :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
I tried to use the following for moving the stack region:
. = 0x50000000;
.stack :
{
stack_start = .;
PROVIDE( stack_start = . );
*(.stack)
. += 0x2000;
stack_end = . ;
PROVIDE( stack_end = . );
}
But that didn't get me anywhere.
Here is the test program that I use to test my stack location:
#include <stdio.h>
#include <stdlib.h>
int global_var = 555;
void test()
{
int local_test = 666;
printf("address of global_var: %p\n", &global_var);
printf("address of local_test: %p\n", &local_test);
}
int main()
{
int local_main = 5;
printf("address of local_main: %p\n", &local_main);
printf("address of test(): %p\n", &test);
printf("address of main(): %p\n", &main);
test();
return 0;
}
This is my output from gcc's default linker script(no modifications):
address of local_main: 0x7fffffffe26c <---- I want to move local vars
address of test(): 0x40050c
address of main(): 0x400547
address of global_var: 0x600a10
address of local_test: 0x7fffffffe24c <---- I want to move local vars
Here is the output from my linker script:
address of local_main: 0x7fffffffe26c <--- unchanged
address of test(): 0x2005ac
address of main(): 0x2005e7
address of global_var: 0x10000010
address of local_test: 0x7fffffffe24c <--- unchanged
I am really confused since nm outputs new locations:
$ nm -n test.out
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
w __gmon_start__
U __libc_start_main@@GLIBC_2.2.5
U printf@@GLIBC_2.2.5
0000000000200460 T _init
00000000002004a0 T _start
00000000002004cc t call_gmon_start
00000000002004f0 t deregister_tm_clones
0000000000200520 t register_tm_clones
0000000000200560 t __do_global_dtors_aux
0000000000200580 t frame_dummy
00000000002005ac T test
00000000002005e7 T main
0000000000200650 T __libc_csu_fini
0000000000200660 T __libc_csu_init
00000000002006ec T _fini
00000000002006f8 R _IO_stdin_used
0000000000200870 r __FRAME_END__
0000000000400878 t __frame_dummy_init_array_entry
0000000000400878 t __init_array_start
0000000000400880 t __do_global_dtors_aux_fini_array_entry
0000000000400880 t __init_array_end
0000000000400888 d __JCR_END__
0000000000400888 d __JCR_LIST__
0000000000400890 d _DYNAMIC
0000000000400a78 d _GLOBAL_OFFSET_TABLE_
0000000010000000 D __data_start
0000000010000000 W data_start
0000000010000008 D __dso_handle
0000000010000010 D global_var
0000000010000018 D __TMC_END__
0000000020000000 A __bss_start
0000000020000000 A _edata
0000000020000000 b completed.6092
0000000050000000 B stack_start
0000000050002000 A _end
0000000050002000 B stack_end
Though I'm not even sure if stack_start and stack_end are valid on x86_64, since I got that part off of linker script tutorial for arm online. I get no errors or warnings with my linker script, so I'm not sure what's going on.
In case you are wondering why would anyone do this - it has implications for security research.
Is there a way to do what I want using linker scripts? I just can't believe that I can move my .text, .bss and .data sections, but not the stack.
Update: Here is an explanation taken from http://www.lurklurk.org/linkers/linkers.html#os on why this is not doable with a linker:
"You may have noticed that all of the discussion of object files and linkers so far has only talked about global variables; there's been no mention of the local variables and dynamically allocated memory mentioned earlier.These pieces of data don't need any linker involvement, because their lifetime only occurs when the program is running—long after the linker has finished its business."
Is there a linker script directive that allows me to move my stack start address?
On Linux (which you appear to be using): no.
The stack location on Linux is determined by the kernel, and the current value of ulimit -s
; it is not encoded in any way into the main executable.
In case you are wondering why would anyone do this - it has implications for security research.
If you want to explicitly control stack location, you'll have to write a custom ELF
loader that would mmap()
and set up stack the way kernel does (and the way glibc expects it to be set up), then transfer control to a.out
or ld-linux
.
What about Windows?
It has been a long time since I touched Windows, but I believe the situation there is similar -- the kernel determines stack location.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With