20

这是来自 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

在哪里etextedata end定义?这些符号是如何赋值的?是通过链接器还是其他方式?

4

4 回答 4

12

请注意,在 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);
}
于 2012-02-08T02:38:12.530 回答
8

这些符号在链接器脚本文件( archive.org 上的死链接副本)中定义。

于 2009-11-19T19:41:49.620 回答
7

海合会做什么

进一步扩展kgiannakakis

这些符号由PROVIDE链接描述文件的关键字定义,记录在https://sourceware.org/binutils/docs-2.25/ld/PROVIDE.html#PROVIDE

默认脚本是在您构建 Binutils 时生成的,并嵌入到可执行文件中:默认情况下不使用ld可能安装在您的发行版中的外部文件(如 in) 。/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

所以通过控制那个部分的第一个字节,我们控制了退出状态!

这个例子在 GitHub 上

于 2015-05-29T15:20:58.290 回答
3

这些符号对应于各个程序段的开头。它们由链接器设置。

于 2009-11-19T19:40:08.157 回答