Какие внутренние процессы участвуют в компиляции Си? - PullRequest
3 голосов
/ 28 сентября 2010

У меня есть набор файлов * .C (встроенный связанный). Может ли кто-нибудь подробно описать мне этапы / процессы (внутреннюю информацию), которые были задействованы при компиляции, а затем ссылки для создания окончательного исполняемого файла (мне нужна информация / этапы, касающиеся того, что препроцессор / компилятор обычно выполняет с кодом Crc)

Также я просто хочу получить представление об общей структуре конечного исполняемого файла (например: заголовки, за которыми следуют таблицы символов и т. Д. И т. Д.)

Также, пожалуйста, сообщите мне, если кто-то уже обсуждал эту тему ранее.

__ Кану

Ответы [ 3 ]

3 голосов
/ 28 сентября 2010

С gcc, например, я думаю, что опция для использования -save-temps.

Грубо говоря, необходимо выполнить передачу файла для извлечения всех включений и создать по существу один файл для анализа.Многие инструменты в наши дни используют синтаксический анализатор, работающий по набору правил (bison, yacc, flex и т. Д.), Целью которого является синтаксический анализ ascii, превращающий вашу программу в своего рода очень широкий язык ассемблера за отсутствием лучшего термина.

a = a + 1;

может превратиться в

Load variable named a, size of blah, type unsigned foo
load immediate 1, size blah, unsigned
add
store result a

Затем возможны оптимизации, промежуточный язык компилятора может иметь функцию приращения и определять, что приращение лучше, чем загрузка 1и доп.В конце концов, эти оптимизации завершены, и этот промежуточный код проходит через бэкэнд к целевому набору команд.Обычно это выводится как сборка и подается в ассемблер, который превращает его в объектный файл, и может произойти целевая оптимизация.Затем объектные файлы подаются в компоновщик, который, ну, в общем, связывает их вместе.Одна функция в одной программе может вызывать функцию, которая не находится в этом объектном файле с именем bob, объектный файл не имеет адреса или смещения для достижения bob, он оставляет там дыру для адреса, который нужно вставить, и задача компоновщиков состоит в том, чтобы соединить всеиз них определите, где в двоичном файле будет жить функция bob (назначьте ей адрес), затем найдите все места, которые вызывают bob, и, когда они помещены в память, вставьте инструкцию или адрес, необходимые для вызова bob, чтобыконечный результат - исполняемый двоичный файл.

llvm, который уже является конкурентом gcc, обеспечивает хороший обзор этого процесса.Вы можете иметь код на C, скомпилированный в промежуточное звено.Начните с нашей функции bob

unsigned int bob ( unsigned int a )
{
    return(a+1);
}

скомпилируйте в битовый код

clang -c -o bob.bc -emit-llvm bob.c

разберите битовый код в удобочитаемую форму

llvm-dis bob.bc

В результате bob.ll

define i32 @bob(i32 %a) nounwind {
entry:
  %a.addr = alloca i32, align 4
  store i32 %a, i32* %a.addr, align 4
  %tmp = load i32* %a.addr, align 4
  %add = add i32 %tmp, 1
  ret i32 %add
}

Неоптимизируемый код любит часто храниться и извлекаться из памяти, и при частой передаче в функцию, сохраняемую и извлекаемую из стека.

В дополнение к тому, что вы легко можете видеть за кулисами,llvm хорош, потому что вы можете оптимизировать на любом уровне, комбинировать объекты и оптимизировать на всем уровне программы, где gcc ограничит вас только уровнем файлов или функций.Таким образом, мы можем оптимизировать этот битовый код.

opt -std-compile-opts bob.bc -o bob_opt.bc
llvm-dis bob_opt.bc

И эти дополнительные хранилища и загрузки исчезли, и ядро ​​функции осталось.

define i32 @bob(i32 %a) nounwind readnone {
entry:
  %add = add i32 %a, 1
  ret i32 %add
}

Затем llc используется для превращения этого в ассемблердля желаемой цели

llc -march=arm bob.bc
cat bob.s
...
bob:                                    @ @bob
@ BB#0:                                 @ %entry
    str r0, [sp, #-4]!
    add r0, r0, #1
    add sp, sp, #4
    bx  lr
...
llc -march=arm bob_opt.bc
cat bob_opt.s
...
bob:                                    @ @bob
@ BB#0:                                 @ %entry
    add r0, r0, #1
    bx  lr
...

Да, есть много книг.И много-много компиляторов и т. Д. В дополнение к llvm, у Фабриса Белларда (да, сотрудника qemu) есть супер простой компилятор, который создает промежуточный файл, который вы можете исследовать http://bellard.org/fbcc/, который скрыт так, что онвряд ли, интересно смотреть, хотя, если вы только начинаете понимать внутренности компиляторов.Кроме того, есть еще один хорошо известный tcc http://bellard.org/tcc/, в котором, в частности, отсутствует серверная часть, проходящая через ассемблер, коды операций генерируются непосредственно как для скорости, так и для (ре) компиляции в реальном времени.

3 голосов
/ 28 сентября 2010

Это, вероятно, слишком глубоко для SO вопроса. Если вам действительно нужно знать, как все это работает, я предлагаю вам прочитать Retargetable C Compiler . В нем пройдут все этапы построения компилятора Си (я думаю, что эта книга посвящена компилятору lcc).

1 голос
/ 28 сентября 2010

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

Работа компоновщика состоит в том, чтобы взять коллекцию объектных файлов и сопоставить ссылки / определения символов. Например, если a.c определяет функцию a, а b.c вызывает a(), компоновщик должен исправить объектный код b, чтобы «заполнить» правильный адрес a, как только он станет известен. (Это чрезмерное упрощение, поскольку есть также динамическая загрузка, общие библиотеки, перемещение и т. Д. И т. Д.)

Нет единого стандартного формата исполняемого файла; Некоторые распространенные форматы включают в себя .EXE (32 или 64 бит), ELF и Mach-O.

Эта статья в Linux Journal дает более подробное объяснение того, как работают компоновщики и загрузчики.

...