我有一个简单的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从另一个目标文件导入。
我在这里缺少什么?
答案 0 :(得分:6)
.global
会将当前文件中的标签标记为具有全局范围(可供其他模块使用)。也许你的意思是.extern
。尽管可以使用.extern
来表示标签是外部的,但GNU汇编程序实际上忽略了该指令。来自manual:
源程序中接受.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 代码条目的最后一步。