这是Linux手册页中的代码:
#include <stdio.h>
#include <stdlib.h>
extern char etext, edata, end;
int main() {
printf("First address past:\n");
printf(" program text (etext) %10p\n", &etext);
printf(" initialized data (edata) %10p\n", &edata);
printf(" uninitialized data (end) %10p\n", &end);
exit(EXIT_SUCCESS);
}
运行时,下面的程序产生如下输出:
$ ./a.out
First address past:
program text (etext) 0x8048568
initialized data (edata) 0x804a01c
uninitialized data (end) 0x804a024
etext
,edata
end
在哪里定义?如何为这些符号赋值?它是由链接器还是别的?
答案 0 :(得分:8)
请注意,在Mac OS X上,上面的代码可能无效!相反,你可以:
#include <stdio.h>
#include <stdlib.h>
#include <mach-o/getsect.h>
int main(int argc, char *argv[])
{
printf(" program text (etext) %10p\n", (void*)get_etext());
printf(" initialized data (edata) %10p\n", (void*)get_edata());
printf(" uninitialized data (end) %10p\n", (void*)get_end());
exit(EXIT_SUCCESS);
}
答案 1 :(得分:7)
这些符号在linker script file中定义。
答案 2 :(得分:3)
这些符号对应于各种程序段的开头。它们由链接器设置。
答案 3 :(得分:3)
GCC做什么
进一步扩展kgiannakakis。
这些符号由链接描述文件的PROVIDE
关键字定义,记录在https://sourceware.org/binutils/docs-2.25/ld/PROVIDE.html#PROVIDE
默认脚本是在构建Binutils时生成的,并嵌入到ld
可执行文件中:默认情况下,不会使用/usr/lib/ldscripts
中可能安装在您的发行版中的外部文件。
回显要使用的链接描述文件:
ld -verbose | less
在binutils 2.24中它包含:
.text :
{
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
*(.text.exit .text.exit.*)
*(.text.startup .text.startup.*)
*(.text.hot .text.hot.*)
*(.text .stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
}
.fini :
{
KEEP (*(SORT_NONE(.fini)))
}
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
所以我们也发现:
__etext
和_etext
也可以使用etext
不是.text
部分的结尾,而是.fini
,其中还包含代码etext
不在细分的末尾,跟随.rodata
,因为Binutils会将所有只读部分转储到同一个细分中 PROVIDE
生成弱符号:如果您还在C代码中定义了这些符号,那么您的定义将获胜并隐藏此符号。
最小的Linux 32位示例
要真正理解事物是如何运作的,我想创造最少的例子!
main.S
:
.section .text
/* Exit system call. */
mov $1, %eax
/* Exit status. */
mov sdata, %ebx
int $0x80
.section .data
.byte 2
link.ld
:
SECTIONS
{
. = 0x400000;
.text :
{
*(.text)
sdata = .;
*(.data)
}
}
编译并运行:
gas --32 -o main.o main.S
ld -m elf_i386 -o main -T link.ld main.o
./main
echo $?
输出:
2
说明:sdata
指向后面.data
部分开头的第一个字节。
因此,通过控制该部分的第一个字节,我们可以控制退出状态!