• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

SOC/IP验证工程师

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

ARM GNU中汇编器指令.global的详细用法

好的,我们来详细解析 ARM 汇编语言(GNU 汇编器语法)中 .global 伪指令的用法。这是一个非常简单但至关重要的概念,它定义了符号的链接可见性。

1. .global 的核心作用:声明全局符号

.global(有时也可以写作 .globl)的核心作用是将一个符号(通常是标签,如函数名或变量名)标记为“全局的”。

这意味着:

  1. 当前汇编文件可以访问它。
  2. 其他汇编文件或 C/C++ 文件也可以访问它。
  3. 在链接阶段,链接器(ld)能够看到这个符号,并可以将其他文件中对该符号的引用与这个定义正确地连接起来。

一个简单的比喻:

  • 没有 .global 的标签就像一个文件的局部变量,只在文件内部有效。
  • 用 .global 声明的标签就像一个文件的全局变量或公共函数,整个程序的所有文件都可以使用它。

2. 主要应用场景

.global 主要有两个用途:

场景一:让汇编函数或变量能被 C 代码调用

这是最常见的使用场景。你在汇编中写了一个函数,希望在主程序(用 C 语言编写)中能够调用它。

C 代码 (main.c):

#include <stdio.h>

// 声明外部函数,告诉编译器这个函数是在别处定义的
extern int add_numbers(int a, int b);
// 声明外部变量
extern int global_value;

int main() {
    int result = add_numbers(5, 3); // 调用汇编函数
    printf("Result: %d\n", result);
    printf("Global value: %d\n", global_value);
    return 0;
}

ARM 汇编代码 (assembly.S):

.data
/* 声明一个全局变量,C代码可以访问 */
.global global_value
global_value:
    .word 42 @ 初始化为42

.text
/* 声明一个全局函数(符号),C代码可以调用 */
.global add_numbers

add_numbers:
    add r0, r0, r1 @ 根据AAPCS调用约定,第一个参数在r0,第二个在r1,返回值在r0
    bx lr          @ 返回调用者(C语言的main函数)

在这个例子中,如果没有 .global add_numbers 和 .global global_value,C 代码中的 extern 声明就无法找到它们的定义,链接器会报 undefined reference 错误。

场景二:指定程序入口点(如裸机程序或Bootloader)

在许多嵌入式系统中,程序从复位向量开始执行,需要指定一个入口点。这个入口点必须是全局的,以便链接器和启动代码能够找到它。

.section .vector_table, "ax"
.global _reset_handler @ 声明复位向量处理程序为全局的

_reset_handler:
    @ 设置栈指针
    ldr sp, =_stack_top
    @ 跳转到main函数
    bl main
    @ 如果main返回,则循环
    b .

.text
.global main @ 声明main为全局的,以便_reset_handler可以跳转过来

main:
    @ 你的主程序代码
    bx lr

链接脚本会使用 ENTRY(_reset_handler) 来告诉链接器程序的入口点是这个全局符号。


3. 语法和用法

.global 的语法非常简单:

.global symbol_name
  • 它可以出现在文件的任何位置,但通常习惯放在符号定义之前。
  • 一个 .global 指令可以声明多个全局符号,用逗号分隔:
    .global func1, func2, my_var
    

4. 对比:.global vs .extern / .weak

  • .global:表示 “我在这里定义了这个符号,其他人可以来用我”。它创建了一个全局符号。
  • .extern (或 .global 在声明时):表示 “这个符号是在其他地方定义的,我要用它”。它引用了一个全局符号。
    • 在 GNU 汇编器中,通常不需要显式使用 .extern。直接使用一个未在当前文件定义的符号,链接器会自动将其视为外部引用。但在一些其他汇编器中(如 ARMasm),.extern 是必需的。
    • 在 C 语言中,extern 关键字就扮演了这个角色。
  • .weak:将一个符号声明为弱符号。如果链接器找不到该符号的强定义(没有 .global 定义),它不会报错,而是将这个引用静默地设置为 0(或一个特定值)。常用于定义可选的函数或钩子。

5. 完整的编译链接过程

理解 .global 如何参与整个过程至关重要:

  1. 汇编 (Assemble):

    • 汇编器 (as) 处理 assembly.S,遇到 .global add_numbers,它会在生成的目标文件 (assembly.o) 的符号表中标记 add_numbers 为一个可供导出的全局符号。
  2. 编译 (Compile):

    • C 编译器 (gcc) 处理 main.c,遇到 extern int add_numbers(...);,它会在 main.o 的符号表中标记 add_numbers 为一个需要解析的未定义引用。
  3. 链接 (Link):

    • 链接器 (ld) 接收 main.o 和 assembly.o。
    • 它看到 main.o 有一个未定义的符号 add_numbers。
    • 它在其他目标文件中查找,并在 assembly.o 的符号表中找到了一个已导出的全局符号 add_numbers。
    • 链接器将这个引用与定义绑定起来,并计算正确的地址填入最终的可执行文件中。

如果第 3 步失败,链接器就会报错:undefined reference to 'add_numbers',这通常意味着你忘记在定义它的汇编文件中加 .global。


总结

要点 描述
核心作用 定义全局符号,使其具有外部链接属性,可以被其他模块(汇编文件或C文件)访问。
主要用途 1. 混合编程:让C代码能调用汇编函数或访问汇编变量。
2. 定义入口点:为链接器和启动代码指定程序入口(如 _start, _reset_handler)。
语法 .global symbol_name 或 .globl symbol_name
反面 未被 .global 声明的符号是局部符号,仅在其定义的汇编文件内部可见。
相关指令 .weak(定义弱符号,可选覆盖),.extern(在某些汇编器中用于声明外部符号,GNU汇编中通常省略)。

简单来说:只要你希望一个汇编文件中的符号(函数或变量)能被其他文件使用,就必须在它的定义前加上 .global。 这是连接汇编世界和C世界,以及构建大型项目的基石。

posted on 2025-09-03 22:48  SOC验证工程师  阅读(34)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3