¿Cuál es la razón por la cual los nombres de las funciones tienen como prefijo un subrayado del comstackdor?

Cuando veo el código ensamblador de una aplicación C, así:

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

Los nombres de las funciones tienen como prefijo un subrayado (por ejemplo, callq _printf ). ¿Por qué se hace esto y qué ventajas tiene?


Ejemplo:

Hola C

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

hola.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: 

De Enlazadores y cargadores :

En el momento en que UNIX se reescribió en C en 1974, sus autores ya tenían extensas bibliotecas de idiomas assember, y era más fácil manipular los nombres de los nuevos códigos compatibles con C y C que retroceder y corregir todo el código existente. Ahora, 20 años después, el código ensamblador se ha reescrito cinco veces, y los comstackdores C de UNIX, particularmente los que crean archivos de objeto COFF y ELF, ya no preceden al guión bajo.

El anteponer un guión bajo en los resultados del ensamblaje de la comstackción C es solo una convención de manipulación de nombres que surgió como una solución alternativa. Se mantuvo alrededor (hasta donde sé) ninguna razón en particular, y ahora ha llegado a Clang.

Fuera del ensamblaje, la biblioteca estándar de C a menudo tiene funciones definidas por la implementación con un guión bajo para transmitir nociones de magia y no tocar esto con los progtwigdores ordinarios que tropiezan con ellos.

Muchos comstackdores solían traducir C al lenguaje ensamblador y luego ejecutaban un ensamblador para generar un archivo objeto. Es mucho más fácil que generar código binario directamente. (AFAIK GCC todavía lo hace, pero también tiene su propio ensamblador). Durante esta traducción, los nombres de las funciones se convierten en tags en el origen del ensamblaje. Sin embargo, si tiene una función llamada (por ejemplo) ret , algunos ensambladores pueden confundirse y pensar que es una instrucción en lugar de una etiqueta. (YASM lo hace, por ejemplo, principalmente porque las tags pueden aparecer prácticamente en cualquier lugar y no requieren dos puntos. Tiene que anteponer un $ si desea una etiqueta llamada ret ).

Agregar un carácter (como, por ejemplo, un guión bajo) a las tags generadas en C fue mucho más fácil que escribir el propio ensamblador C o preocuparse por las tags que chocan con las instrucciones / instrucciones de ensamblaje.

En la actualidad, los ensambladores y los comstackdores han evolucionado un poco, y la mayoría de las personas trabajan en el nivel C o superior de todos modos. Entonces, la necesidad original de modificar los nombres en C ya no existe.

A primera vista, el sistema operativo se ejecuta en Unix / Unix en una PC. Según mi opinión, no hay nada sorprendente que encuentre _printf en el lenguaje ensamblador generado. C printf es una función que realiza una E / S. Por lo tanto, es responsabilidad del kernel + driver realizar la E / S solicitada.

La ruta de las instrucciones de la máquina tomada en cualquier sistema operativo tipo Unix / Unix es la siguiente:

printf (código C) -> _printf (libc) -> trap -> kernel + driver work -> return from trap -> return desde _printf (libc) -> printf completion y return -> next machine instruction en código C

En el caso de este extracto de código de ensamblaje, parece que C printf está en línea por el comstackdor lo que hizo que el punto de entrada _printf sea visible en el código de ensamblaje.

Para asegurarse de que C printf no se decore con un prefijo (un guion bajo en este caso), lo mejor es buscar en todos los encabezados C un _printf con un comando como:

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