Почему выходные данные сборки GCC не создают .GLOBAL для printf

У меня есть тривиальный пример программы C: -

#include <stdio.h>
int main()
{
    printf("hello world!");
    return 1;
}

Я использую следующую команду для компиляции и создания сборки:

riscv32-unknown-elf-gcc -S hello.c -o hello.asm

Что генерирует следующую сборку: -

    .file   "hello.c"
    .option nopic
    .section    .rodata
    .align  2
.LC0:
    .string "hello world!"
    .text
    .align  2
    .globl  main
    .type   main, @function
main:
    addi    sp,sp,-16
    sw  ra,12(sp)
    sw  s0,8(sp)
    addi    s0,sp,16
    lui a5,%hi(.LC0)
    addi    a0,a5,%lo(.LC0)
    call    printf
    li  a5,1
    mv  a0,a5
    lw  ra,12(sp)
    lw  s0,8(sp)
    addi    sp,sp,16
    jr  ra
    .size   main, .-main
    .ident  "GCC: (GNU) 7.2.0"

Есть ожидаемая строка call printf, но поскольку в этом файле сборки нет реализации printf, я ожидал увидеть, что он запрашивает внешнюю реализацию с чем-то вроде этого...

.global printf

Но такой строчки в сборке нет. Я думал, что без глобальной директивы это означало, что компоновщик будет пытаться преобразовать ее только в метки внутри этого единственного файла сборки. Я думал, что в этом весь смысл глобальной директивы, так что все метки являются локальными для одного файла сборки, если только они не экспортируются с использованием .global для доступа из других объектных файлов или не импортируются из другого объектного файла с использованием также .global.

Что мне здесь не хватает?


person Phil Wright    schedule 29.11.2017    source источник
comment
Компоновщик автоматически компонуется в среде выполнения C. Я считаю, что глобальная директива не нужна по этой причине.   -  person Banex    schedule 30.11.2017
comment
.global пометит метку в текущем файле как имеющую глобальную область действия (доступную для других модулей). Возможно, вы имели в виду .extern. Хотя с помощью .extern можно указать, что метка является внешней, эта директива фактически игнорируется. Ассемблер GNU предполагает, что любая метка, о которой он не знает в текущем файле, автоматически считается внешней (определить, является ли она неопределенной, зависит от компоновщика). Вот почему вы не видите никакой директивы, помечающей printf как внешнюю — в этом нет необходимости.   -  person Michael Petch    schedule 30.11.2017
comment
Итак, вы говорите, что вам нужно использовать .global для экспорта из модуля, но не нужно использовать .global для импорта? Думаю, это объясняет, почему он все еще использовал .global для основного.   -  person Phil Wright    schedule 30.11.2017
comment
Да, именно так.   -  person Peter Cordes    schedule 30.11.2017


Ответы (1)


.global пометит метку в текущем файле как имеющую глобальную область действия (доступную для других модулей). Возможно, вы имели в виду .extern. Хотя .extern можно использовать, чтобы сказать, что метка является внешней, эта директива на самом деле игнорируется ассемблером GNU. Из руководства:

.extern принимается в исходной программе — для совместимости с другими ассемблёрами — но игнорируется. as обрабатывает все неопределенные символы как внешние.

as = ассемблер GNU.

Ассемблер GNU предполагает, что любая метка в текущем файле, о которой он не знает, является внешней ссылкой. Компоновщик должен определить, является ли он неопределенным или нет. Вот почему вы не видите никакой директивы, помечающей printf как внешнюю. В ассемблере GNU это просто не нужно.

Примечание. Частично путаница может заключаться в том, что такие ассемблеры, как NASM/YASM, требуют явной инструкции extern для обозначения того, что символ не находится внутри собираемого локального модуля. Эти ассемблеры вернутся с ошибкой, что символ не определен. Это одно из различий между GNU Assembler и NASM/YASM.


Директива .global .directive не импортирует метки, так как по сути является экспортом. Он только помечает метки в текущем файле как глобально доступные для других модулей. Он не используется для импорта этикеток из других модулей. Из руководства:

.global делает символ видимым для ld. Если вы определяете символ в своей частичной программе, его значение становится доступным для других частичных программ, связанных с ним. В противном случае символ берет свои атрибуты из символа с тем же именем из другого файла, связанного с той же программой.

Оба варианта написания («.globl» и «.global») допускаются для совместимости с другими ассемблёрами.


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

person Michael Petch    schedule 29.11.2017
comment
Связано: другие ассемблеры, такие как NASM, требуют extern printf. - person Peter Cordes; 30.11.2017
comment
@ПитерКордес. Да, но ассемблер GNU - нет. И именно об этом этот вопрос, поскольку он конкретно о том, почему GCC не излучает его. - person Michael Petch; 30.11.2017
comment
Верно, но я думаю, стоит упомянуть, что есть и другие ассемблеры, разработанные по-другому, поэтому ожидания ОП не сумасшедшие. И почему я сказал связанное, а не исправление :P - person Peter Cordes; 30.11.2017