По какой причине имена функций префиксируются с подчеркиванием компилятором? - PullRequest
37 голосов
/ 06 мая 2011

Когда я вижу код сборки приложения C, например:

emacs hello.c
clang -S -O hello.c -o hello.s
cat hello.s

Имена функций имеют префикс подчеркивания (например, callq _printf). Почему это сделано и какие у него преимущества?


Пример:

hello.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main() {
  char *myString = malloc(strlen("Hello, World!") + 1);
  memcpy(myString, "Hello, World!", strlen("Hello, World!") + 1);
  printf("%s", myString);
  return 0;
}

hello.s

_main:                       ; Here
Leh_func_begin0:
    pushq   %rbp
Ltmp0:
    movq    %rsp, %rbp
Ltmp1:
    movl    $14, %edi
    callq   _malloc          ; Here
    movabsq $6278066737626506568, %rcx
    movq    %rcx, (%rax)
    movw    $33, 12(%rax)
    movl    $1684828783, 8(%rax)
    leaq    L_.str1(%rip), %rdi
    movq    %rax, %rsi
    xorb    %al, %al
    callq   _printf          ; Here
    xorl    %eax, %eax
    popq    %rbp
    ret
Leh_func_end0:

Ответы [ 3 ]

29 голосов
/ 06 мая 2011

С Компоновщики и загрузчики :

В то время, когда UNIX был переписан на C примерно в 1974 году, его авторы уже имели обширные библиотеки языков ассемблера, и было проще манипулировать именами нового C и C-совместимого кода, чем вернуться назад и исправить все существующие код. Теперь, 20 лет спустя, код ассемблера был переписан пять раз, и компиляторы UNIX C, особенно те, которые создают объектные файлы COFF и ELF, больше не ставят подчеркивание.

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

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

5 голосов
/ 23 октября 2011

Многие компиляторы использовали для перевода C на язык ассемблера, а затем запускали на нем ассемблер для создания объектного файла.Это намного проще, чем генерировать двоичный код напрямую.(AFAIK GCC все еще делает это. Но у него также есть свой собственный ассемблер.) Во время этого перевода имена функций становятся метками в источнике сборки.Если у вас есть функция (например) ret, некоторые ассемблеры могут запутаться и подумать, что это инструкция, а не метка.(Например, YASM в основном потому, что метки могут появляться практически везде и не требуют двоеточий. Вы должны добавить $, если хотите, чтобы метка называлась ret.)

Добавление символа(как, скажем, подчеркивание) для сгенерированных C-меток было намного проще, чем писать собственный C-friendly ассемблер или беспокоиться о конфликте меток с инструкциями / директивами по сборке.

В наши дни ассемблеры и компиляторынемного развились, и большинство людей все равно работают на уровне C или выше.Таким образом, первоначальная потребность искажать имена в Си в значительной степени исчезла.

0 голосов
/ 29 августа 2013

На первый взгляд, операционная система Unix / Unix-like работает на ПК.По моему мнению, нет ничего удивительного в том, чтобы найти _printf в сгенерированном ассемблере.C printf - это функция, которая выполняет ввод / вывод.Поэтому ядро ​​+ драйвер отвечает за выполнение запрошенного ввода-вывода.

Путь машинных инструкций, взятый в любой Unix / Unix-подобной ОС, следующий:

printf (Cкод) -> _printf (libc) -> trap -> ядро ​​+ работа драйвера -> возврат из trap -> возврат из _printf (libc) -> завершение и возврат printf -> следующая машинная инструкция в коде C

В случае этого извлечения кода сборки, похоже, что C printf встроен компилятором, из-за которого точка входа _printf была видна в коде сборки.

Чтобы убедиться, что C printf не декорируетсяс префиксом (подчеркивание в данном случае), лучше всего, если во всех заголовках C поиск _printf выполняется с помощью такой команды:

find / usr / include -name * .h -exec grep _printf {} \;-print

...