Возможно вы искали: 'Skullcracker'

May 15 2025 19:52:44
  • Как сделать 8Gamers.Ru домашней страницей?
  • Игры
    • База данных по играх
    • Игровые новости
    • Игровая индустрия
    • Обзоры на игры
    • Прохождения игр
    • Гайды к играм
    • Превью о играх
    • Игровые тизеры
    • Игровые арты
    • Игровые обои
    • Игровые скриншоты
    • Игровые обложки
    • Игровые трейлеры
    • Игровое видео
    • Вышедшие игры
    • Ближайшие релизы игр
  • Кино и ТВ
    • База данных по кино
    • Статьи о кино
    • Постеры
    • Кадры из кино
    • Кино трейлеры
    • Сегодня в кино
    • Скоро в кино
  • Комиксы и манга
    • Манга по алфавиту
    • База данных по комиксах
    • Читать онлайн комиксы
    • Читать онлайн манга
    • База персонажей
  • Читы и коды
    • Чит-коды для PC игр
    • Чит-коды для консольных игр
    • Трейнеры
    • Коды Game Genie
  • Моддинг
    • Модификации
    • Карты к играм
    • Программы для моддинга
    • Статьи о моддинге
  • Геймдев
    • Всё о создании игр
    • Список движков
    • Утилиты в помощь игроделу
    • Конструкторы игр
    • Игровые движки
    • Библиотеки разработки
    • 3D-модели
    • Спрайты и тайлы
    • Музыка и звуки
    • Текстуры и фоны
  • Рецензии
    • Игры
    • Кино
    • Аниме
    • Комиксы
    • Мангу
    • Саундтреки
  • Саундтреки
    • Лирика
  • Файлы
    • Патчи к играм
    • Русификаторы к играм
    • Сохранения к играм
    • Субтитры к кино
  • Медиа
    • Видео
    • Фото
    • Аудио
    • Фан-арты
    • Косплей
    • Фото с виставок
    • Девушки из игр
    • Рисунки
    • Рисуем онлайн
    • Фотохостинг
  • Юмор
    • Анекдоты
    • Афоризмы
    • Истории
    • Стишки и эпиграммы
    • Тосты
    • Цитаты
  • Флеш
    • Азартные
    • Аркады
    • Бродилки
    • Гонки
    • Для девочек
    • Для мальчиков
    • Драки
    • Квесты
    • Леталки
    • Логические
    • Мультфильмы
    • Открытки
    • Приколы
    • Разное
    • Спорт
    • Стратегии
    • Стрелялки
Статистика

Статей: 87772
Просмотров: 96111483
Игры
Injustice:  Gods Among Us
Injustice: Gods Among Us
...
Dark Souls 2
Dark Souls 2
Dark Souls II - вторая часть самой хардкорной ролевой игры 2011-2012 года, с новым героем, сюжето...
Battlefield 4
Battlefield 4
Battlefield 4 - продолжение венценосного мультиплеер-ориентированного шутера от первого ли...
Кино
Steins;Gate
Steins;Gate
Любители японской анимации уже давно поняли ,что аниме сериалы могут дать порой гораздо больше пи...
Ку! Кин-дза-дза
Ку! Кин-дза-дза
Начинающий диджей Толик и всемирно известный виолончелист Владимир Чижов встречают на шумной моск...
Обзоры на игры
• Обзор Ibara [PCB/PS2] 18357
• Обзор The Walking ... 18801
• Обзор DMC: Devil M... 19879
• Обзор на игру Valk... 15877
• Обзор на игру Stars! 17764
• Обзор на Far Cry 3 17948
• Обзор на Resident ... 16024
• Обзор на Chivalry:... 17508
• Обзор на игру Kerb... 17981
• Обзор игры 007: Fr... 16619
Превью о играх
• Превью к игре Comp... 17960
• Превью о игре Mage... 14464
• Превью Incredible ... 14721
• Превью Firefall 13479
• Превью Dead Space 3 16334
• Превью о игре SimC... 14730
• Превью к игре Fuse 15442
• Превью Red Orche... 15542
• Превью Gothic 3 16343
• Превью Black & W... 17354
Главная » Статьи » Разное » Внутренние устройство исполняемых файлов формата ELF. (gcc elf assembler)

Внутренние устройство исполняемых файлов формата ELF. (gcc elf assembler)

Ключевые слова: gcc, elf, assembler, (найти похожие документы)

From: Андрей Киселев <kis_an@mail.ru>
Newsgroups: http://gazette.linux.ru.net
Date: Mon, 14 Apr 2003 13:01:37 +0000 (UTC)
Subject: Внутренние устройство исполняемых файлов формата ELF.

Оригинал: http://gazette.linux.ru.net/lg84/kim.html
Как запускается функция main() в Linux
Автор (c): Hyouck "Hawk" Kim <http://gazette.linux.ru.net/authors/hawk.html>
Перевод (c): Андрей Киселев <kis_an@mail.ru>
_________________________________________________________________

Вступление

Так ли прост вопрос: "Как запускается функция main() в Linux"? Для
ответа на него я возьму, в качестве примера, простенькую программу на
языке C -- "simple.c"
main()
{
return(0);
}

Сборка

gcc -o simple simple.c

Что находится внутри исполняемого файла?

Для того, чтобы рассмотреть внутреннее устройство исполняемого файла
воспользуемся утилитой "objdump"
objdump -f simple


simple: file format elf32-i386

architecture: i386, flags 0x00000112:

EXEC_P, HAS_SYMS, D_PAGED

start address 0x080482d0

Отсюда видно, что файл, во-первых, имеет формат "ELF32", а во-вторых
-- адрес запуска программы "0x080482d0"

Что такое ELF?

ELF -- это аббревиатура от английского Executable and Linking Format
(Формат Исполняемых и Связываемых файлов). Это одна из разновидностей
форматов для исполняемых и объектных файлов, используемых в
UNIX-системах. Для нас особый интерес будет представлять заголовок
файла. Каждый файл формата ELF имеет ELF-заголовок следующей
структуры:
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Сигнатура и прочая информаци
я */
Elf32_Half e_type; /* Тип объектного файла */
Elf32_Half e_machine; /* Аппаратная платформа (архите
ктура) */
Elf32_Word e_version; /* Номер версии */
Elf32_Addr e_entry; /* Адрес точки входа (стартовый
адрес программы) */
Elf32_Off e_phoff; /* Смещение от начала файла таб
лицы программных заголовков */
Elf32_Off e_shoff; /* Смещение от начала файла таб
лицы заголовков секций */
Elf32_Word e_flags; /* Специфичные флаги процессора
(не используется в архитектуре i386) */
Elf32_Half e_ehsize; /* Размер ELF-заголовка в байта
х */
Elf32_Half e_phentsize; /* Размер записи в таблице прог
раммных заголовков */
Elf32_Half e_phnum; /* Количество записей в таблице
программных заголовков */
Elf32_Half e_shentsize; /* Размер записи в таблице заго
ловков секций */
Elf32_Half e_shnum; /* Количество записей в таблице
заголовков секций */
Elf32_Half e_shstrndx; /* Расположение сегмента, содер
жащего таблицy стpок */
} Elf32_Ehdr;

В этой структуре, поле "e_entry" содержит адрес запуска программы.

Что находится по адресу "0x080482d0", то есть по адресу запуска (starting
address)?

Для ответа на этот вопрос попробуем дизассемблировать программу
"simple". Для дизассемблирования исполняемых файлов я использую
objdump.
objdump --disassemble simple

Утилита objdump выдаст очень много информации, поэтому я не буду
приводить её всю. Нас интересует только адрес 0x080482d0. Вот эта
часть листинга:
080482d0 <_start>:
80482d0: 31 ed xor %ebp,%ebp
80482d2: 5e pop %esi
80482d3: 89 e1 mov %esp,%ecx
80482d5: 83 e4 f0 and $0xfffffff0,%esp
80482d8: 50 push %eax
80482d9: 54 push %esp
80482da: 52 push %edx
80482db: 68 20 84 04 08 push $0x8048420
80482e0: 68 74 82 04 08 push $0x8048274
80482e5: 51 push %ecx
80482e6: 56 push %esi
80482e7: 68 d0 83 04 08 push $0x80483d0
80482ec: e8 cb ff ff ff call 80482bc <_init+0x48>
80482f1: f4 hlt
80482f2: 89 f6 mov %esi,%esi

Похоже на то, что первой запускается процедура "_start". Все, что она
делает -- это очищает регистр ebp, "проталкивает" какие-то значения в
стек и вызывает подпрограмму. Согласно этим инструкциям содержимое
стека должно выглядеть так:
-----Дно стека-----
0x80483d
-------------------
esi
-------------------
ecx
-------------------
0x8048274
-------------------
0x8048420
-------------------
edx
-------------------
esp
-------------------
eax
-------------------

Теперь вопросов становится еще больше
1. Что за числа кладутся в стек?
2. Что находится по адресу 80482bc, который вызывается инструкцией
call в процедуре _start?
3. В приведенном листинге отсутствуют инструкции, инициализирующие
регистры (имеются ввиду eax, ecx, edx прим. перев.). Где они
инициализируются?

Попробуем ответить на все эти вопросы.

Вопрос 1> Что за числа кладутся в стек?

Если внимательно просмотреть весь листинг, создаваемый утилитой
objdump, то можно легко найти ответ

Вот он:

0x80483d0 : Это адрес функции main().

0x8048274 : адрес функции _init.

0x8048420 : адрес функции _fini. Функции _init и _fini -- это функции
инициализации и финализации (завершения) приложения, генерируемые
компилятором GCC.

Таким образом все приведенные числа являются указателями на функции
(точнее -- адресами функций прим. перев.)

Вопрос 2> Что находится по адресу 80482bc?

Снова обратимся к листингу.
80482bc: ff 25 48 95 04 08 jmp *0x8049548

Здесь *0x8049548 означает указатель.
Это просто косвенный переход по адресу, хранящемуся в памяти по адресу
0x8049548.

Дополнительно о формате ELF и динамическом связывании

Формат ELF предполагает возможность динамического связывания
исполняемой программы с библиотеками.
Где под словами "динамическое связывание" следует понимать то, что
связывание производится во время исполнения. В противоположность
динамическому связыванию существует "статическое связывание", т.е.
когда связывание с библиотеками происходит на этапе сборки программы,
что, как правило, приводит к "раздуванию" исполняемого файла до
огромных размеров. Если вы запустите команду:
"ldd simple"

libc.so.6 => /lib/i686/libc.so.6 (0x42000000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

Вы сможете увидеть полный список библиотек, связанных с программой
simple динамически.
Вкратце, концепция динамического связывания выглядит так.
1. На этапе сборки программы адреса переменных и функций в
динамической библиотеке не известны. Они становятся известны
только на этапе исполнения
2. Для того, чтобы иметь возможность обращаться к компонентам
динамической библиотеки (переменные, функции и т.д. прим. перев.)
необходимо предусмотреть указатели на эти компоненты
Указатели заполняются фактическими адресами во время загрузки.
3. Приложение может обращаться к динамическим компонентам только
косвенно, используя для этого указатели. Пример такой косвенной
адресации можно увидеть в листинге, приведенном выше, по адресу
80482bc, когда осуществляется косвенный переход.
Фактический адрес перехода сохраняется по адресу 0x8049548 во
время загрузки программы.
Косвенные ссылки можно посмотреть, выполнив команду
objdump -R simple


simple: file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
0804954c R_386_GLOB_DAT __gmon_start__
08049540 R_386_JUMP_SLOT __register_frame_info
08049544 R_386_JUMP_SLOT __deregister_frame_info
08049548 R_386_JUMP_SLOT __libc_start_main
Здесь адрес 0x8049548 называется "jump slot" и имеет определенный
смысл. В соответствии с таблицей он означает вызов
__libc_start_main.

Что такое __libc_start_main?

Теперь "карты сдает" библиотека libc. __libc_start_main -- это функция
из библиотеки libc.so.6. Если отыскать функцию __libc_start_main в
исходном коде библиотеки glibc, то увидите примерно такое объявление.
extern int BP_SYM (__libc_start_main) (int (*main) (int, char **, char **),
int argc,
char *__unbounded *__unbounded ubp_av,
void (*init) (void),
void (*fini) (void),
void (*rtld_fini) (void),
void *__unbounded stack_end)
__attribute__ ((noreturn));

Теперь становится понятен смысл ассемблерных инструкций из листинга,
приведенного выше -- они кладут на стек входные параметры и вызывают
функцию __libc_start_main.
В задачу этой функции входят некоторые действия по инициализации среды
исполнения и вызов функции main().
Рассмотрим содержимое стека с новых позиций.

Дно стека ------------------
0x80483d0 main
------------------
esi argc
------------------
ecx argv
------------------
0x8048274 _init
------------------
0x8048420 _fini
------------------
edx _rtlf_fini
------------------
esp stack_end
------------------
eax это ноль (0)
------------------

Согласно такому представлению стека, понятно, что перед вызовом
__libc_start_main() в регистры esi, ecx, edx, esp и eax должны быть
записаны соответствующие значения. Совершенно очевидно, что
дизассемблированный код, показанный выше, ничего в эти регистры не
пишет. Тогда кто? Остается только одно предположение -- ядро.
А теперь перейдем к третьему вопросу.

Вопрос 3> Что делает ядро?

Когда программа запускается из командной строки, выполняются следующие
действия.
1. Командная оболочка (shell) делает системный вызов "execve" с
параметрами argc/argv.
2. Обработчик системного вызова в ядре получает управление и начинает
его обработку. В ядре обработчик называется "sys_execve". На
платформе x86, пользовательское приложение передает аргументы
вызова в ядро через регистры.
+ ebx : указатель на строку с именем программы
+ ecx : указатель на массив argv
+ edx : указатель на массив переменных окружения
3. Универсальный обработчик системного вызова в ядре называется
do_execve. Он создает и заполняет определенные структуры данных,
копирует необходимую информацию из пространства пользователя в
пространство ядра и, наконец, вызывает search_binary_handler().
Linux поддерживает множество форматов исполняемых файлов, например
a.out и ELF. Для обеспечения такой поддержки в ядре имеется
структура "struct linux_binfmt", которая содержит указатели на
загрузчики каждого из поддерживаемых форматов. Таким образом,
search_binary_handler() просто отыскивает нужный загрузчик и
вызывает его. В нашем случае -- это load_elf_binary(). Описывать
эту функцию в подробностях слишком долгая и нудная работа, так что
я не буду заниматься этим здесь. За подробностями обращайтесь к
специальной литературе по данной тематике. (от себя могу
предложить ссылку на статью "Внутреннее устройство ядра Linux
2.4" прим. перев. http://www.opennet.ru/docs/RUS/lki/)
Вкратце процесс загрузки выглядит примерно так.
Сначала создаются и заполняются структуры в пространстве ядра и
файл программы считывается в память. Затем производится установка
дополнительных значений -- определяется размер сегмента кода,
определяется начало сегмента данных и сегмента стека и т.д.. В
пользовательском режиме выделяется память, в которую копируются
входные параметры (argv) и переменные окружения. Затем функция
create_elf_tables(), в пользовательском режиме, кладет на стек
argc, указатели на argv и массив переменных окружения, после чего
start_thread() запускает программу на исполнение.

Когда управление передается в точку _start, стек выглядит примерно
так:
Дно стека -------------
argc
-------------
указатель на argv
-------------
указатель на env
-------------

Теперь наш дизассемблированный листинг выглядит еще более
определенным.
pop %esi <--- со стека снимается argc
move %esp, %ecx <--- argv
т.е. теперь, фактически, адрес argv совпадает с указателе
м стека

Теперь все готово к запуску программы.

Что можно сказать по-поводу остальных регистров?

esp используется для указания вершины стека в прикладной программе.
После того как со стека будет снята вся необходимая информация,
процедура _start просто скорректирует указатель стека (esp), сбросив 4
младших бита в регистре esp. В регистр edx заносится указатель на,
своего рода деструктор приложения -- rtlf_fini. На платформе x86 эта
особенность не поддерживается, поэтому ядро заносит туда число 0
макрокомандой.
#define ELF_PLAT_INIT(_r) do {
_r->ebx = 0; _r->ecx = 0; _r->edx = 0;
_r->esi = 0; _r->edi = 0; _r->ebp = 0;
_r->eax = 0;
} while (0)

Откуда взялся весь этот дополнительный код

Откуда взялся весь этот дополнительный код? Он входит в состав
компилятора GCC. Вы можете найти его в
/usr/lib/gcc-lib/i386-redhat-linux/XXX и
/usr/lib где XXX -- номер версии gcc.
Файлы называются crtbegin.o,crtend.o, gcrt1.o.

Подведение итогов

Итак, выводы следующие.
1. При сборке программы, GCC присоединяет к ней код из объектных
модулей crtbegin.o/crtend.o/gcrt1.o а другие библиотеки,
по-умолчанию, связывает динамически. Адрес запуска приложения (в
ELF-заголовке прим. перев.) указывает на точку _start.
2. Ядро загружает программу и устанавливает сегменты
text/data/bss/stack, распределяет память для входных параметров и
переменных окружения и помещает на стек всю необходимую
информацию.
3. Управление передается в точку _start. Здесь информация снимается
со стека, на стеке размещаются входные параметры для функции
__libc_start_main, после чего ей передается управление.
4. Функция __libc_start_main выполняет все необходимые действия по
инициализации среды исполнения, особенно это касается библиотеки C
(malloc и т.п.) и вызывает функцию main() программы.
5. Функции main() передаются входные аргументы -- main(argc, argv).
Здесь есть один интересный момент. __libc_start_main
"представляет" себе сигнатуру функции main() как main(int, char
**, char **). Если вам это любопытно, то попробуйте запустить
следующую программу:
main(int argc, char** argv, char** env)
{
int i = 0;
while(env[i] != 0)
{
printf("%sn", env[i++]);
}
return(0);
}

Заключение

В Linux запуск функции main() является результатом взаимодействия GCC,
libc и загрузчика.

Ссылки

objdump -- "man objdump"

ELF-заголовок -- /usr/include/elf.h

__libc_start_main -- исходный код glibc
(./sysdeps/generic/libc-start.c)

sys_execve -- исходный код ядра linux (arch/i386/kernel/process.c)

do_execve -- исходный код ядра linux (fs/exec.c)

struct linux_binfmt -- исходный код ядра linux
(include/linux/binfmts.h)

load_elf_binary -- исходный код ядра linux (fs/binfmt_elf.c)

create_elf_tables -- исходный код ядра linux (fs/binfmt_elf.c)

start_thread -- исходный код ядра linux (include/asm/processor.h)

Copyright (C) 2002, Hyouck "Hawk" Kim. Copying license http://www.linuxgazette.com/copying.html
Published in Issue 84 of Linux Gazette, November 2002
541 Прочтений •  [Внутренние устройство исполняемых файлов формата ELF. (gcc elf assembler)] [08.05.2012] [Комментариев: 0]
Добавил: Ukraine Vova
Ссылки
HTML: 
[BB Url]: 
Похожие статьи
Название Добавил Добавлено
• Внутренние устройство исполняемых ф... Ukraine Vova 08.05.2012
Ни одного комментария? Будешь первым :).
Пожалуйста, авторизуйтесь для добавления комментария.

Проект входит в сеть сайтов «8Gamers Network»

Все права сохранены. 8Gamers.NET © 2011 - 2025

Статьи
Рецензия на Pressure
Рецензия на Pressure
Чтобы обратить на себя внимание, начинающие маленькие разработчики, как правило, уходят в жанры, ...
Рецензия на Lost Chronicles of Zerzura
Рецензия на Lost Chron...
Игры, сделанные без любви и старания, похожи на воздушный шар – оболочка есть, а внутри пусто. Lo...
Рецензия на The Bridge
Рецензия на The Bridge
«Верх» и «низ» в The Bridge — понятия относительные. Прогуливаясь под аркой, можно запросто перей...
Рецензия на SimCity
Рецензия на SimCity
Когда месяц назад состоялся релиз SimCity, по Сети прокатилось цунами народного гнева – глупые ош...
Рецензия на Strategy & Tactics: World War 2
Рецензия на Strategy &...
Название Strategy & Tactics: World War II вряд ли кому-то знакомо. Зато одного взгляда на ее скри...
Рецензия на игру Scribblenauts Unlimited
Рецензия на игру Scrib...
По сложившейся традиции в информационной карточке игры мы приводим в пример несколько похожих игр...
Рецензия на игру Walking Dead: Survival Instinct, The
Рецензия на игру Walki...
Зомби и продукция-по-лицензии — которые и сами по себе не лучшие представители игровой биосферы —...
Обратная связь | RSS | Донейт | Статистика | Команда | Техническая поддержка