web-dev-qa-db-ja.com

コンパイラによって関数名の前にアンダースコアが付いている理由は何ですか?

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:
37
user142019

から リンカーとローダー

UNIXが1974年頃にCで書き直されたとき、その作成者はすでに広範なアセンブラ言語ライブラリを持っていて、既存のすべてのコードに戻って修正するよりも、新しいCおよびC互換コードの名前を壊す方が簡単でした。 20年後、アセンブラコードはすべて5回書き直され、UNIX Cコンパイラ、特にCOFFおよびELFオブジェクトファイルを作成するコンパイラは、アンダースコアを付加しなくなりました。

Cコンパイルのアセンブリ結果でアンダースコアを前に付けることは、回避策として生じた単なる名前マングリング規則です。それは(私が知る限り)特別な理由もなく立ち往生し、今ではClangに浸透しています。

アセンブリの外部では、C標準ライブラリには、魔法の概念を伝えるためにアンダースコアが前に付いた実装定義関数があり、これに触れないでくださいそれらに出くわすプログラマー。

29
Jon Purdy

多くのコンパイラは、Cをアセンブリ言語に翻訳し、その上でアセンブラを実行してオブジェクトファイルを生成するために使用されていました。バイナリコードを直接生成するよりもはるかに簡単です。 (AFAIK GCCは引き続きこれを行いますが、独自のアセンブラーもあります。)この変換中、関数名はアセンブリソースのラベルになります。ただし、(たとえば)retという関数がある場合、一部のアセンブラは混乱して、ラベルではなく命令であると考える可能性があります。 (たとえば、YASMは、ほとんどの場合、ラベルはほとんどどこにでも表示でき、コロンを必要としないためです。retというラベルが必要な場合は、$を付加する必要があります。)

Cで生成されたラベルの前に文字(アンダースコアなど)を付ける方が、独自のC対応アセンブラを作成したり、ラベルがアセンブリ命令/ディレクティブと衝突することを心配したりするよりもはるかに簡単でした。

最近、アセンブラーとコンパイラーは少し進化していて、ほとんどの人はとにかく経営幹部レベル以上で働いています。したがって、Cで名前をマングルする本来の必要性はほとんどなくなりました。

5
cHao