You are on page 1of 13

Привет из свободного от libc мира (Часть 2)

В предыдущем посте мы справились с компиляцией, написав маленькую программу,


которая может быть собрана без использования libc. Понимание объектного кода и
деталей структуры ELF исполняемых файлов — следующий шаг в нашем приключении.
Мы остановились на следующем коде:

jesstess@kid-charlemagne:~$ cat stubstart.S


.globl _start
_start:
call main
movl $1, %eax
xorl %ebx, %ebx
int $0x80
jesstess@kid-charlemagne:~$ cat hello.c
int main()
{
char *str = "Hello World";
return 0;
}
jesstess@kid-charlemagne:~/c$ gcc -nostdlib stubstart.S hello.c -o hello

И что мы получили, проделав всю эту работу?

jesstess@kid-charlemagne:~/c$ wc -c hello
1373 hello
jesstess@kid-charlemagne:~$ objdump -D hello | wc -l
93

Теперь наш исполняемый файл занимает немногим более 1300 байт и менее 100
линий, это довольно разумное количество для анализа. Маленькая часть ассемблерного
кода (assembly) не может испугать нас к этому моменту, давайте теперь взглянем на него
с использованием objdump -D, который покажет содержимое всех секций (вывод см. в
приложении). Если вывод кажется пугающим, то быстро пробегите по нему, я обещаю
вам, что к концу этого поста от страха ничего не останется.
Итак, мы имеем 5 секций: .text, которая содержит знакомые символы _start и
main, .rodata, .eh_frame_hdr, .eh_frame и .comment.

Шаг 1: Сосредоточились – что же такое «секция»?

Если мы стряхнем пыль с нашей любимой копии Tool Interface Standard ELF
Specification и заглянем в нее, то она скажет нам:

У исполняемого файла, как результата нашей компиляции, есть два


представления: он содержит program header, описывающий сегменты, которые содержат
информацию, используемую во время выполнения, и section header, описывающий
секции, которые содержат информацию для линковки и перемещения. Мы можем получить
информацию о сегментах или секциях с помощью readelf -l или readelf -S
соответственно (вывод см. в приложении). Результат работы этих двух команд для
нашей программы сведен в рисунок 1.
Рис.1. Сегменты и секции нашего ELF

Шаг 2: Что происходит в нашей секции?

Спецификация также сообщает нам, что и в каком порядке располагается в нашем


исполняемом файле:
.text — содержит исполняемые инструкции программы (попросту код программы);
.rodata — содержит константные значения, это сегмент «только для чтения»;
.eh_frame — информация, необходимая для frame-unwinding во время обработки
исключений;
.eh_frame_hdr — цитата из Linux Standard Base Specification: “Эта секция
содержит указатель на секцию .eh_frame, которая доступна для «runtime support
code» С++ приложения. Эта секция также может содержать двоичную таблицу поиска,
которая может быть использована «runtime support code» для более эффективного
доступа к записям секции .eh_frame”.
Мы не должны волноваться об исключениях в нашем примере, поэтому .eh_frame и
.eh_frame_hdr не должны нас беспокоить и компилирование с флагом -fno-
asynchronous-unwind-tables подавит создание этих двух секций;
.comment — информация о версии компилятора.
Говоря о избавлении от секций: для нас, приверженцев минимализма, strip(1)
хороший друг. Мы можем воспользоваться --remove-section по несущественным
разделам, таким как .comment, чтобы полностью от них избавиться; file(1) сообщит
нам, если исполняемая программа была разделена (stripped).

Мы не видим другие секции в нашем примере (они не были включены в


исполняемый файл), потому что они были пустыми:
.data — в этой секции инициализируются глобальные и локальные переменные;
.bss — секция неинициализированных глобальных и локальных переменных, то
есть заполненных нулями.
На этом все о секциях. Теперь мы знаем, что символы, такие как _start и main,
находятся в этих секциях, но есть ли в программе еще символы?

Шаг 3: Понимание символов и почему они расположены там, где расположены.

Мы можем получить информацию о символах нашего исполняемого файла с помощью


objdump -t:

jesstess@kid-charlemagne:~/c$ objdump -t hello

hello: file format elf64-x86-64

SYMBOL TABLE:
00000000004000e8 l d .text 0000000000000000 .text
0000000000400107 l d .rodata 0000000000000000 .rodata
0000000000400114 l d .eh_frame_hdr 0000000000000000 .eh_frame_hdr
0000000000400128 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 l df *ABS* 0000000000000000 hello.c
00000000004000e8 g .text 0000000000000000 _start
0000000000600fe8 g *ABS* 0000000000000000 __bss_start
00000000004000f4 g F .text 0000000000000013 main
0000000000600fe8 g *ABS* 0000000000000000 _edata
0000000000600fe8 g *ABS* 0000000000000000 _end

Таблица символов для нашего исполняемого файла имеет 11 записей. Странным


образом только редкие версии документации (man pages) по objdump, как эта,
объясняет таблицу символов столбец за столбцом. Таблица поделена следующим
образом:

Столбец 1: значение/адрес символа.


Столбец 2: набор символов и пробелов представляющих флаговые биты, установленные
для символа. Есть 7 групп, три из которых представлены в этой таблице символов.
Значение из первой группы может быть — l, g, <пробел> или !, если символ
локальный, глобальный, ни то ни другое или оба сразу, соответственно. Значение из
шестой группы может быть — d, D или <пробел> для debugging, dynamic или normal
соответственно. Значение из седьмой может быть — F, f, O или <пробел> для функции,
файла, объекта или нормального символа, соответственно. Описание оставшихся 4
групп может быть найдено в unusally comprehensive objdump manpage.
Столбец 3: в какой секции расположен символ. *ABS* (абсолютный) означает символ,
не связанный с определенной секцией.
Столбец 4: размер/выравнивание символа.
Столбец 5: имя символа.

Все наши 5 секций имеют связи с local (l), debugging (d) и symbols (s). main
действительно функция (F), hello.c действительно файл (f), и он не связан с какой-
либо секцией (*ABS*). _start и main — часть исполняемых инструкций для нашей
программы и, таким образом, расположены в секции .text, как мы и предполагали.
Единственной причудой здесь является __bss_start, _edata и _end, все *ABS*,
глобальные символы, которые мы не писали в нашей программе. Откуда они взялись?
Виновником на этот раз является скрипт компоновщика. gcc неявно вызывает ld,
как часть процесса компиляции. ld --verbose предоставит сценарий компоновщика,
который был использован и, глядя на него (вывод см. в приложении), мы видим, что
_edata определен как конец секции data, __bss_start и _end отмечают начало и конец
секции .bss. Эти символы могли быть использованы механизмом управления памятью
(например, если sbrk хочет знать, где начинается «куча») и сборщиком «мусора».
Следует отметить, что str, наша инициализированная локальная переменная, не
представлена в таблице символов. Почему? Потому что она размещается в стеке
(возможно в регистре) во время выполнения. Однако, что-то связанное с str
находиться в секции .rodata, несмотря на то, что мы не видим это в таблице
символов...
Теперь о char *str = «Hello World»; на самом деле мы создаем два различных
объекта. Первый это строковый литерал «Hello World», который представляет собой
просто массив символов и имеет некоторый адрес, но явного имени не имеет. Это
массив «только для чтения» и расположен в секции .rodata. Второй — локальная
переменная str, которая имеет тип «pointer to char». Она и располагается в стеке,
а ее начальное значение — адрес строкового литерала, который был создан.
Мы можем доказать это и получить некоторую другую полезную информацию,
смотря на содержимое наших секций, используя декодирование строк:

jesstess@kid-charlemagne:~$ objdump -s hello

hello: file format elf64-x86-64

Contents of section .text:


4000e8 e80b0000 00b80100 000031db cd809090 ..........1.....
4000f8 554889e5 48c745f8 0b014000 b8000000 UH..H.E...@.....
400108 00c9c3 ...
Contents of section .rodata:
40010b 48656c6c 6f20576f 726c6400 Hello World.
Contents of section .eh_frame_hdr:
400118 011b033b 14000000 01000000 e0ffffff ...;............
400128 30000000 0...
Contents of section .eh_frame:
400130 14000000 00000000 017a5200 01781001 .........zR..x..
400140 030c0708 90010000 1c000000 1c000000 ................
400150 f8004000 13000000 00410e10 8602430d ..@......A....C.
400160 06000000 00000000 ........
Contents of section .comment:
0000 00474343 3a202855 62756e74 7520342e .GCC: (Ubuntu 4.
0010 332e332d 35756275 6e747534 2920342e 3.3-5ubuntu4) 4.
0020 332e3300 3.3.

Вуаля! Наша строка «Hello World» расположена в .rodata и наша секция


.comment объяснена: она просто содержит строку с версией gcc, используемой для
компиляции программы.

Шаг 4: Исключим лишнее и соединим все вместе

Исполняемый файл содержит 5 секций: .text, .rodata, .eh_frame_hdr, .eh_frame


и .comment. В действительности только одна из них (.text) содержит код,
определяющий, что делает эта маленькая программа. В этом можно убедиться,
используя objdump -d (дизассемблирует только те секции, которые должны содержать
инструкции) вместо objdump -D (дизассемблирует содержимое всех секций, не только
содержащих инструкции), использованного в начале поста, отмечая, что выведено
только содержимое .text (используя objudump -d).
.rodata в действительности содержит только строку «Hello World», а .comment
содержит только версию gcc. «Инструкции» для этих секций, показанные в выводе
objdump -D получаются в следствии ошибочного представления objdump ASCII символов
как инструкций и попытке дизассемблировать их. Мы можем конвертировать первую пару
чисел из секции .comment в ASCII символы, чтобы доказать это. На Python:

>>>"".join(chr(int(x, 16)) for x in "47 43 43 3a 20 28 55 62 75 6e 74 75".split())


'GCC: (Ubuntu'

В секции .text, _start вызывает main, и в main в стек вталкивается указатель


на адрес в памяти, где хранится «Hello World», 0x40010b (где начинается секция
.rodata, как видно из вывода objdump -D). Затем мы возвращаемся из main в _start,
который заботится о выходе из программы, как описано в Части 1.

И это все! Все секции и символы учтены. Никакого волшебства (я имею ввиду
волшебство в хорошем смысле Я-бы-прошел-это-испытание, а не в смысле прости-Jimmy-
Santa-не-настоящий). Вот так. Whew.

Первая часть - http://blog.ksplice.com/2010/03/libc-free-world/


Перевод первой части - http://habrahabr.ru/blogs/nix_coding/88101/
Оригинал второй части - http://blog.ksplice.com/2010/04/libc-free-world-2/
Вывод objdump -D

jesstess@kid-charlemagne:~$ objdump -D hello


hello: file format elf64-x86-64

Disassembly of section .text:

00000000004000e8 <_start>:
4000e8: e8 0b 00 00 00 callq 4000f8 <main>
4000ed: b8 01 00 00 00 mov $0x1,%eax
4000f2: 31 db xor %ebx,%ebx
4000f4: cd 80 int $0x80
4000f6: 90 nop
4000f7: 90 nop

00000000004000f8 <main>:
4000f8: 55 push %rbp
4000f9: 48 89 e5 mov %rsp,%rbp
4000fc: 48 c7 45 f8 0b 01 40 movq $0x40010b,-0x8(%rbp)
400103: 00
400104: b8 00 00 00 00 mov $0x0,%eax
400109: c9 leaveq
40010a: c3 retq

Disassembly of section .rodata:

000000000040010b <.rodata>:
40010b: 48 rex.W
40010c: 65 gs
40010d: 6c insb (%dx),%es:(%rdi)
40010e: 6c insb (%dx),%es:(%rdi)
40010f: 6f outsl %ds:(%rsi),(%dx)
400110: 20 57 6f and %dl,0x6f(%rdi)
400113: 72 6c jb 400181 <main+0x89>
400115: 64 fs
...

Disassembly of section .eh_frame_hdr:

0000000000400118 <.eh_frame_hdr>:
400118: 01 1b add %ebx,(%rbx)
40011a: 03 3b add (%rbx),%edi
40011c: 14 00 adc $0x0,%al
40011e: 00 00 add %al,(%rax)
400120: 01 00 add %eax,(%rax)
400122: 00 00 add %al,(%rax)
400124: e0 ff loopne 400125 <main+0x2d>
400126: ff (bad)
400127: ff 30 pushq (%rax)
400129: 00 00 add %al,(%rax)
...

Disassembly of section .eh_frame:

0000000000400130 <.eh_frame>:
400130: 14 00 adc $0x0,%al
400132: 00 00 add %al,(%rax)
400134: 00 00 add %al,(%rax)
400136: 00 00 add %al,(%rax)
400138: 01 7a 52 add %edi,0x52(%rdx)
40013b: 00 01 add %al,(%rcx)
40013d: 78 10 js 40014f <main+0x57>
40013f: 01 03 add %eax,(%rbx)
400141: 0c 07 or $0x7,%al
400143: 08 90 01 00 00 1c or %dl,0x1c000001(%rax)
400149: 00 00 add %al,(%rax)
40014b: 00 1c 00 add %bl,(%rax,%rax,1)
40014e: 00 00 add %al,(%rax)
400150: f8 clc
400151: 00 40 00 add %al,0x0(%rax)
400154: 13 00 adc (%rax),%eax
400156: 00 00 add %al,(%rax)
400158: 00 41 0e add %al,0xe(%rcx)
40015b: 10 86 02 43 0d 06 adc %al,0x60d4302(%rsi)
400161: 00 00 add %al,(%rax)
400163: 00 00 add %al,(%rax)
400165: 00 00 add %al,(%rax)
...

Disassembly of section .comment:

0000000000000000 <.comment>:
0: 00 47 43 add %al,0x43(%rdi)
3: 43 3a 20 rex.XB cmp (%r8),%spl
6: 28 55 62 sub %dl,0x62(%rbp)
9: 75 6e jne 79 <_start-0x40006f>
b: 74 75 je 82 <_start-0x400066>
d: 20 34 2e and %dh,(%rsi,%rbp,1)
10: 33 2e xor (%rsi),%ebp
12: 33 2d 35 75 62 75 xor 0x75627535(%rip),%ebp # 7562754d
<__bss_start+0x75026565>
18: 6e outsb %ds:(%rsi),(%dx)
19: 74 75 je 90 <_start-0x400058>
1b: 34 29 xor $0x29,%al
1d: 20 34 2e and %dh,(%rsi,%rbp,1)
20: 33 2e xor (%rsi),%ebp
22: 33 00 xor (%rax),%eax
Вывод readelf -l и readelf -S

jesstess@kid-charlemagne:~$ readelf -l hello

Elf file type is EXEC (Executable file)


Entry point 0x4000e8
There are 3 program headers, starting at offset 64

Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000000168 0x0000000000000168 R E 200000
GNU_EH_FRAME 0x0000000000000118 0x0000000000400118 0x0000000000400118
0x0000000000000014 0x0000000000000014 R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RWE 8

Section to Segment mapping:


Segment Sections...
00 .text .rodata .eh_frame_hdr .eh_frame
01 .eh_frame_hdr
02
jesstess@kid-charlemagne:~$ readelf -S hello
There are 9 section headers, starting at offset 0x1d8:

Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 00000000004000e8 000000e8
0000000000000023 0000000000000000 AX 0 0 4
[ 2] .rodata PROGBITS 000000000040010b 0000010b
000000000000000c 0000000000000000 A 0 0 1
[ 3] .eh_frame_hdr PROGBITS 0000000000400118 00000118
0000000000000014 0000000000000000 A 0 0 4
[ 4] .eh_frame PROGBITS 0000000000400130 00000130
0000000000000038 0000000000000000 A 0 0 8
[ 5] .comment PROGBITS 0000000000000000 00000168
0000000000000024 0000000000000000 0 0 1
[ 6] .shstrtab STRTAB 0000000000000000 0000018c
000000000000004a 0000000000000000 0 0 1
[ 7] .symtab SYMTAB 0000000000000000 00000418
0000000000000120 0000000000000018 8 7 8
[ 8] .strtab STRTAB 0000000000000000 00000538
000000000000002d 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
Вывод ld --verbose

jesstess@kid-charlemagne:~$ ld --verbose

GNU ld (GNU Binutils for Ubuntu) 2.19.1


Supported emulations:
elf_x86_64
elf_i386
i386linux
using internal linker script:
==================================================
/* Script for -z combreloc: combine and sort reloc sections */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
"elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("/usr/local/lib64");
SEARCH_DIR("/lib64"); SEARCH_DIR("/usr/lib64"); SEARCH_DIR("/usr/x86_64-linux-
gnu/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib");
SEARCH_DIR("/usr/lib");
SECTIONS
{
/* Read-only sections, merged into text segment: */
PROVIDE (__executable_start = 0x400000); . = 0x400000 + SIZEOF_HEADERS;
.interp : { *(.interp) }
.note.gnu.build-id : { *(.note.gnu.build-id) }
.hash : { *(.hash) }
.gnu.hash : { *(.gnu.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.dyn :
{
*(.rel.init)
*(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
*(.rel.fini)
*(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
*(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*)
*(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
*(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
*(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
*(.rel.ctors)
*(.rel.dtors)
*(.rel.got)
*(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
*(.rel.ldata .rel.ldata.* .rel.gnu.linkonce.l.*)
*(.rel.lbss .rel.lbss.* .rel.gnu.linkonce.lb.*)
*(.rel.lrodata .rel.lrodata.* .rel.gnu.linkonce.lr.*)
}
.rela.dyn :
{
*(.rela.init)
*(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
*(.rela.fini)
*(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
*(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
*(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
*(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
*(.rela.ctors)
*(.rela.dtors)
*(.rela.got)
*(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
*(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*)
*(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*)
*(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*)
}
.rel.plt : { *(.rel.plt) }
.rela.plt : { *(.rela.plt) }
.init :
{
KEEP (*(.init))
} =0x90909090
.plt : { *(.plt) }
.text :
{
*(.text .stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
} =0x90909090
.fini :
{
KEEP (*(.fini))
} =0x90909090
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
.eh_frame_hdr : { *(.eh_frame_hdr) }
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. */
. = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT
(MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT
(COMMONPAGESIZE));
/* Exception handling */
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
/* Thread Local Storage sections */
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
}
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
}
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
PROVIDE_HIDDEN (__fini_array_end = .);
}
.ctors :
{
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*crtbegin?.o(.ctors))
/* We don't want to include the .ctor section from
the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
}
.dtors :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*crtbegin?.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
}
.jcr : { KEEP (*(.jcr)) }
.data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*)
*(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
.dynamic : { *(.dynamic) }
.got : { *(.got) }
. = DATA_SEGMENT_RELRO_END (24, .);
.got.plt : { *(.got.plt) }
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
_edata = .; PROVIDE (edata = .);
__bss_start = .;
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections.
FIXME: Why do we need it? When there is no .bss section, we don't
pad the .data section. */
. = ALIGN(. != 0 ? 64 / 8 : 1);
}
.lbss :
{
*(.dynlbss)
*(.lbss .lbss.* .gnu.linkonce.lb.*)
*(LARGE_COMMON)
}
. = ALIGN(64 / 8);
.lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
{
*(.lrodata .lrodata.* .gnu.linkonce.lr.*)
}
.ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
{
*(.ldata .ldata.* .gnu.linkonce.l.*)
. = ALIGN(. != 0 ? 64 / 8 : 1);
}
. = ALIGN(64 / 8);
_end = .; PROVIDE (end = .);
. = DATA_SEGMENT_END (.);
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/* DWARF 3 */
.debug_pubtypes 0 : { *(.debug_pubtypes) }
.debug_ranges 0 : { *(.debug_ranges) }
.gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) }
}

==================================================