Существует ли директива сценария компоновщика, которая позволяет мне перемещать начальный адрес моего стека?
Я пытаюсь изменить начальное местоположение моего стека, используя скрипт компоновщика на x86_64. Я смог переместить мой исполняемый начальный адрес с помощью этого:
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x200000)); . = SEGMENT_START("text-segment", 0x200000) + SIZEOF_HEADERS;
Я изменил свои глобальные переменные следующим образом:
.data ALIGN(0x10000000) :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
Я попытался использовать следующее для перемещения области стека:
. = 0x50000000;
.stack :
{
stack_start = .;
PROVIDE( stack_start = . );
*(.stack)
. += 0x2000;
stack_end = . ;
PROVIDE( stack_end = . );
}
Но это никуда меня не привело.
Вот тестовая программа, которую я использую для проверки местоположения моего стека:
#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;
}
Это мой вывод из скрипта компоновщика gcc по умолчанию (без изменений):
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
Вот вывод из моего скрипта компоновщика:
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
Я действительно запутался, так как nm выводит новые локации:
$ 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
Хотя я даже не уверен, что stack_start и stack_end действительны для x86_64, так как я получил эту часть из учебника по скриптам компоновщика для arm online. Я не получаю ошибок или предупреждений с помощью скрипта компоновщика, поэтому я не уверен, что происходит.
Если вам интересно, зачем кому-то это делать - это имеет значение для исследований в области безопасности.
Есть ли способ сделать то, что я хочу, используя скрипты компоновщика? Я просто не могу поверить, что могу перемещать свои секции.text,.bss и.data, но не стек.
Обновление: вот объяснение, взятое с http://www.lurklurk.org/linkers/linkers.html о том, почему это невозможно с компоновщиком:
"Вы, возможно, заметили, что все обсуждения объектных файлов и компоновщиков до сих пор говорили только о глобальных переменных; там не упоминалось о локальных переменных и динамически распределенной памяти, упомянутых ранее. Эти фрагменты данных не нуждаются в участии компоновщика потому что их время жизни происходит только тогда, когда программа работает - еще долго после того, как компоновщик завершил свою работу ".
1 ответ
Существует ли директива сценария компоновщика, которая позволяет мне перемещать начальный адрес моего стека?
В Linux (которую вы, похоже, используете): нет.
Расположение стека в Linux определяется ядром, а текущее значение ulimit -s
; он никак не закодирован в основной исполняемый файл.
Если вам интересно, зачем кому-то это делать - это имеет значение для исследований в области безопасности.
Если вы хотите явно контролировать расположение стека, вам придется написать ELF
загрузчик, который бы mmap()
и установить стек так, как это делает ядро (и способ, которым glibc ожидает его установки), а затем передать управление a.out
или же ld-linux
,
А как насчет Windows?
Прошло много времени с тех пор, как я коснулся Windows, но я полагаю, что ситуация там аналогичная - ядро определяет местоположение стека.