ARM架构内核启动分析-head.S(1.1、vmlinux.lds 链接脚本分析)

2023-11-10

ARM架构内核启动分析

一、start kernel之前

首先需要明确的是,内核镜像在被解压之后执行,是执行哪段代码,这是个重要的问题,平时在编译生成应用程序或内核模块时,我们无需考虑链接的具体细节,如代码和数据放在哪里、代码执行入口在哪等等,但在编译生成内核镜像时就不能不考虑这些了,对于arm架构,下面是它的编译内核时的arm交叉链接器命令:

arm-linux-ld -EL -p --no-undefined -X --build-id -ovmlinux -T arch/arm/kernel/vmlinux.lds

可以通过man –ld命令查看,-T的意思是:为链接器ld指定一个链接脚本linker script,就是让链接器根据这个链接脚本的内容来生成最终二进制镜像。

以我手中的arm芯片(marvell 88F6500)为例,它对应的linux源码的arch/arm/kernel目录下都会有对应的vmlinux.lds文件,这个文件是由同一目录下的vmlinux.lds.S文件生成的,所以这个vmlinux.lds.S文件需要重点分析;

1.1、  链接脚本分析:

1.1.1、任何可执行程序的大致结构:

对于任何一种可执行程序,不论是ELF/EXE、so/dll、ko,应该都听说过代码段text、数据段data、未初始化数据段bss等等的说法,其实这些都是一个个的段(section);每个编译好的.o文件,都有该文件的text/data/bss段;事实上每个.o文件不仅仅这三个段,还有一些别的段,但不是分析重点;

链接脚本最终要把这一大堆.o文件生成最终的二进制可执行文件,也就是把每一个.o文件整合到一个大文件中,这个大文件有一个总的text/data/bss段,分别囊括每个.o文件的text/data/bss段;那具体是怎么使用的呢,举例如下:

SECTIONS

 {

       . = 0x10000;

       .text : { *(.text) }

       . = 0x8000000;

       .data : { *(.data) }

       .bss : { *(.bss) }

 }

第一行指示,链接地址为0x100000;即指定了后面的text段的链接地址

第二行指示:输出文件的text段内容由所有输入文件(*,理解为所有的.o文件,*.o)的text段组成;

第三行指示:链接地址变了,变为0x8000000;即重新指定了后面的data段的链接地址;

第四行指示:输出文件的data端由所有输入文件的data段组成;

第五行指示:输出文件的bss端由所有输入文件的bss段组成;

个人认为理解到此就差不多可以了,下面分析vmlinux.lds.S文件:

1.1.2、vmlinux.lds.S文件:

OUTPUT_ARCH(arm):表示输出文件基于ARM架构;

 

ENTRY(stext):ENTRY用来设置入口点。这里表示入口点是stext。这就是内核代码的入口!入口点的意思就是程序运行的第一条指令,内核本身也是一个程序,同样需要设置入口;

jiffies = jiffies_64:

/*下面开始正式定义输出文件的段!!!*/

SECTIONS

{

    /*1、设置链接地址(也即虚拟地址)为0xc0008000,这就是内核代码的虚拟地址为0xc0008000的由来值的依据就是PAGE_OFFSET和TEXT_OFFSET*/

#ifdef CONFIG_XIP_KERNEL

                  . = XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR);

#else

                  . = PAGE_OFFSET + TEXT_OFFSET;

#endif

/*2、定义一个.text.head段,由输入文件中所有.text.head段组成,并定义了两个地址变量下面描述链接脚本中变量的意思: 如

            start_of_ROM   = .ROM;

            end_of_ROM     = .ROM + sizeof (.ROM) - 1;

            start_of_FLASH = .FLASH;

     这三个变量分别指向ROM段的开始和结尾、FLASH段的开始,那么在C代码中是这样使用这些变量:

            extern char start_of_ROM, end_of_ROM, start_of_FLASH;

     注意,C代码这些变量指示的都是指针变量,比如希望在C代码中把ROM段的内容拷贝到FLASH段中:

            memcpy (&start_of_FLASH, &start_of_ROM, &end_of_ROM - &start_of_ROM);

     可见,注意其中的取地址符号&,C代码中只能通过这种方式来使用LS中定义的变量。start_of_ROM这个值本身是没有意义的,只有它的地址才有意义

     说白了,链接脚本中定义的变量其实就是地址,即_stext=0x100就是C代码中的一个地址:int *_stext=0x100 */

.text.head : {

                   _stext = .;     @@定义变量_stext存储当前地址(PAGE_OFFSET + TEXT_OFFSET)

                   _sinittext = .; @@定义变量_sinittext存储当前地址(PAGE_OFFSET + TEXT_OFFSET)

                   *(.text.head)   @@所有输入文件中所有.text.head段在此

}

/*3、定义一个.init段*/

.init : {                         /* Init code and data                 */

         /*INIT_TEXT在include/asm-generic/vmlinux.lds.h文件中定义,意为所有的.init.text/.cpuinit.text/.meminit.text在此/

                            INIT_TEXT

         /*定义变量_einittext,它其实是INIT_TEXT的结尾标识*/

                   _einittext = .;

         /*这是一种典型的用法,后面大量使用该方法,前面已知可以在链接脚本中定义变量存储地址,然后由C代码使用,这里就用这种方式,定义两个变量,两个变量之间是输出文件.proc.info.init,用这两个变量就把中间的内容牢牢卡住,供C代码使用*/

__proc_info_begin = .;

                            *(.proc.info.init)

                   __proc_info_end = .;

                   __arch_info_begin = .;

                            *(.arch.info.init)

                   __arch_info_end = .;

                   __tagtable_begin = .;

                            *(.taglist.init)

                   __tagtable_end = .;

                   . = ALIGN(16);

                   __setup_start = .;

                            *(.init.setup)

                   __setup_end = .;

                   __early_begin = .;

                            *(.early_param.init)

                   __early_end = .;

                   __initcall_start = .;

                            INITCALLS

                   __initcall_end = .;

                   __con_initcall_start = .;

                            *(.con_initcall.init)

                   __con_initcall_end = .;

                   __security_initcall_start = .;

                            *(.security_initcall.init)

                   __security_initcall_end = .;

/*

#ifdef CONFIG_BLK_DEV_INITRD

                   . = ALIGN(32);

                   __initramfs_start = .;

                            usr/built-in.o(.init.ramfs)

                   __initramfs_end = .;

#endif

*/

                   . = ALIGN(PAGE_SIZE);

                   __per_cpu_load = .;

                   __per_cpu_start = .;

                            *(.data.percpu.page_aligned)

                            *(.data.percpu)

                            *(.data.percpu.shared_aligned)

                   __per_cpu_end = .;

#ifndef CONFIG_XIP_KERNEL

                   __init_begin = _stext;

                   INIT_DATA

                   . = ALIGN(PAGE_SIZE);

                   __init_end = .;

#endif

         }

/*4、DISACARD是一种特殊的段,表示符合这个条件的输入段都不会写到输出段中,也就是输出文件中不包含下列段,不是分析重点*/

/DISCARD/ : {                     /* Exit code and data                */

                   EXIT_TEXT

                   EXIT_DATA

                   *(.exitcall.exit)

                   *(.discard)

                   *(.ARM.exidx.exit.text)

                   *(.ARM.extab.exit.text)

#ifndef CONFIG_HOTPLUG_CPU

                   *(.ARM.exidx.cpuexit.text)

                   *(.ARM.extab.cpuexit.text)

#endif

#ifndef CONFIG_HOTPLUG

                   *(.ARM.exidx.devexit.text)

                   *(.ARM.extab.devexit.text)

#endif

#ifndef CONFIG_MMU

                   *(.fixup)

                   *(__ex_table)

#endif

         }

/*5、这是text段*/

         .text : {                        /* Real text segment                */

                   _text = .;           /* Text and read-only data       */

                            __exception_text_start = .;

                            *(.exception.text)

                            __exception_text_end = .;

/* Then all the functions that are "hot" in profiles, to group them onto the same hugetlb entry */

#include "functionlist"

 /* Then the rest */

                            TEXT_TEXT

                            SCHED_TEXT

                            LOCK_TEXT

                            KPROBES_TEXT

#ifdef CONFIG_MMU

                            *(.fixup)

#endif

                            *(.gnu.warning)

                            *(.rodata)

                            *(.rodata.*)

                            *(.glue_7)

                            *(.glue_7t)

                   *(.got)                         /* Global offset table                */

         }

O_DATA(PAGE_SIZE)

_etext = .;                           /* End of text and rodata section */

. = ALIGN(THREAD_SIZE);

__data_loc = .;

/*6、这是data段*/

         .data : AT(__data_loc) {

                   _data = .;          /* address in memory */

                   _sdata = .;

                   /*

                    * first, the init task union, aligned

                    * to an 8192 byte boundary.

                    */

                   *(.data.init_task)

                   . = ALIGN(PAGE_SIZE);

                   __nosave_begin = .;

                   *(.data.nosave)

                   . = ALIGN(PAGE_SIZE);

                   __nosave_end = .;

                   /*

                    * then the cacheline aligned data

                    */

                   . = ALIGN(32);

                   *(.data.cacheline_aligned)

                   /*

                    * The exception fixup table (might need resorting at runtime)

                    */

                   . = ALIGN(32);

                   __start___ex_table = .;

                   *(__ex_table)

                   __stop___ex_table = .;

                   /*

                    * and the usual data section

                    */

                   DATA_DATA

                   CONSTRUCTORS

                   _edata = .;

         }

/*7、这是bss段*/

         .bss : {

                   __bss_start = .;        /* BSS                                   */

                   *(.bss)

                   *(COMMON)

                   __bss_stop = .;

                   _end = .;

         }

                                               /* Stabs debugging sections.  */

         .stab 0 : { *(.stab) }

         .stabstr 0 : { *(.stabstr) }

         .stab.excl 0 : { *(.stab.excl) }

         .stab.exclstr 0 : { *(.stab.exclstr) }

         .stab.index 0 : { *(.stab.index) }

         .stab.indexstr 0 : { *(.stab.indexstr) }

         .comment 0 : { *(.comment) }

}

上面罗列也描述了很多内容,重点把握以下几点:

1、  内核代码入口为stext;并且它应该是属于输入文件的.text.head段;并且stext的实现在arch/arm/kernel/head.S文件中,可找到.section ".text.head", "ax"声明;

2、  内核代码的链接地址从0xc0008000开始,不断增加,这体现了PAGE_OFFSET 、TEXT_OFFSET两个宏的作用何在;

3、  注意init段中的类似用__proc_info_begin、__proc_info_end来卡住中间内容的方式的原理,这在后面频繁使用;

其余的关于链接脚本的内容,可暂时不用分析的很细,后面会逐渐的理解。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ARM架构内核启动分析-head.S(1.1、vmlinux.lds 链接脚本分析) 的相关文章

  • udev使用笔记

    一 什么是udev udev是linux kernel的设备管理器 在最新的内核版本中kernel 3 10中udev已经代替了以前devfs hotplug等功能 意味着它要处理添加 删除硬件时 所有的用户空间行为 实际上为什么我关注这个
  • Linux内核——cli()和sti()——标志寄存器的中断标志

    cli 和sti 有点类似于汇编指令中的CLI和STL 当某个任务在执行的过程中不想被中断 则可以在任务的开始出执行cli 在任务的结束处执行sti 恢复中断的执行 为了避免竞争条件和中断对临界代码区的干扰 在Linux 0 12内核代码中
  • Linux操作系统进程的状态和转换(五态模型)

    1 进程的状态和装换 1 1进程的三态模型 按进程在执行过程中的不同情况至少要定义三种状态 运行 running 态 进程占有处理器正在运行的状态 进程已获得CPU 其程序正在执行 在单处理机系统中 只有一个进程处于执行状态 在多处理机系统
  • linux2.6.29 CFS调度详细分析

    linux2 6 29 CFS调度详细分析 众所周知 linux最新的内核采用了CFS的调度机制 网上也有不少文章对CFS调度的源码做了详细的分析 但是大部分的文章太注重细节了 所以没有把CFS的原理进行一下从整体上的概括 基于这个原因 本
  • local_irq_save和 local_irq_disable

    如果你要禁止所有的中断该怎么办 在2 6内核中 可以通过下面两个函数中的其中任何一个关闭当前处理器上的所有中断处理 这两个函数定义在
  • 趣谈操作系统原理,存储管理之页式、段式、段页式存储

    一 概述 非连续分配管理方式允许一个程序分散地装入到不相邻的内存分区 根据分区的大小是否固定分为分页式存储管理方式和分段式存储管理方式 分页存储管理方式中 又根据运行作业时是否要把作业的所有页面都装入内存才能运行分为基本分页式存储管理方式和
  • KVM内核代码结构

    KVM内核代码结构 因为KVM的源代码已经包含在了Linux的内核树中 因此我们只需直接从www kernel org下载代码即可 内核源码包打开较大 解开后目录结构大概是这个样子 涉及KVM的主要有两个目录 virt和arch x86 k
  • ARM 浮点运算详解

    一 早期ARM上的浮点模拟器 早期的ARM没有协处理器 所以浮点运算是由CPU来模拟的 即所需浮点运算均在浮点运算模拟器 float math emulation 上进行 需要的浮点运算 常要耗费数千个循环才能执行完毕 因此特别缓慢 直到今
  • linux的自旋锁struct spinlock_t的使用

    在linux中提供了一些机制用来避免竞争条件 最简单的一个种就是自旋锁 例如 当一个临界区的数据在多个函数之间被调用时 为了保护数据不被破坏 可以采用spinlock来保护临界区的数据 当然还有一个就是信号量也是可以实现临界区数据的保护的
  • linux的dirty page回写磁盘过程中是否允许并发写入更新page?

    概述 众所周知Linux内核write系统调用采用pagecache机制加速写入过程 避免write系统调用长时间block应用进程 用户态进程执行write调用的时候 内核只是将用户态buffer copy到内核的pagecache当中
  • Linux内核同步之RCU机制基础

    Why RCU 1 中断与抢占 当一个进程被时钟中断打断后 kernel运行tick中断处理程序 一般是top half 中断处理程序运行结束后 有两种情况 之前的进程获得CPU继续运行 另一个进程获得了CPU开始运行 而之前的进程则被抢占
  • Linux提权之内核漏洞提权篇

    前言 在渗透过程中 有时利用某些漏洞可以获取一个低权限的用户 然后想办法提权 提升到root用户权限 从而控制整个系统 在获取到低权限shell后 通常会检查操作系统的发行版本 内核版本 老版本的系统可能会存在一些漏洞 于是我们可以利用这些
  • 一文让你彻底了解Linux内核文件系统

    一 文件系统特点 文件系统要有严格的组织形式 使得文件能够以块为单位进行存储 文件系统中也要有索引区 用来方便查找一个文件分成的多个块都存放在了什么位置 如果文件系统中有的文件是热点文件 近期经常被读取和写入 文件系统应该有缓存层 文件应该
  • bootloader详解

    一 bootloader介绍 bootloader是硬件在加电开机后 除BIOS固化程序外最先运行的软件 负责载入真正的操作系统 可以理解为一个超小型的os 目前在Linux平台中主要有lilo grub等 在Windows平台上主要有nt
  • Linux内核分析:输入输出,字符与块设备 31-35

    CPU 并不直接和设备打交道 它们中间有一个叫作设备控制器 Device Control Unit 的组件 例如硬盘有磁盘控制器 USB 有 USB 控制器 显示器有视频控制器等 这些控制器就像代理商一样 它们知道如何应对硬盘 鼠标 键盘
  • diagnose-tools 编译报错

    在 Ubuntu 20 04 4 LTS 环境中 编译diagnose tools 执行make deps时报错 checking whether gcc m32 makes executables we can run no config
  • Linux内核--内存管理

    内存管理单元MMU memory management unit 的主要功能是虚拟地址 virtual memory addresses 到物理地址 physical addresses 的转换 除此之外 它还可以实现内存保护 memory
  • Linux进程管理:deadline调度器

    一 概述 实时系统是这样的一种计算系统 当事件发生后 它必须在确定的时间范围内做出响应 在实时系统中 产生正确的结果不仅依赖于系统正确的逻辑动作 而且依赖于逻辑动作的时序 换句话说 当系统收到某个请求 会做出相应的动作以响应该请求 想要保证
  • Chromium多进程架构,你知道多少?

    一 前言 国内外主流的浏览器 大多采用的是谷歌的Chromium 浏览器内核 Chromium是一个多进程多线程架构的Web引擎 很多应用和底层开发者希望了解Chromium中的进程和线程的种类和用途 以便能利用相关信息提升应用的性能 为此
  • linux内核学习(7)粗略走走kbuild Makefile编译流程

    今天看Makefile文件 我头大了 此Makefile非彼Makefile 里面多了很多内置命令 比如origin patsubst等等啦 这些都没听说过 更可恶的是 连网上都没有 可见 这是一件多么伤人的事情 分析这样的 真是让人折寿啊

随机推荐

  • c语言程序项目,C语言程序设计项目教程--详细介绍

    基 础 篇 项目1 通讯录信息输出 3 1 1 学习情境 3 1 2 项目分析 3 1 3 项目目标 4 1 4 项目实现 4 1 5 相关知识 8 1 5 1 C语言基础 数据类型 常量 变量 8 1 5 2 C程序的结构特点 10 1
  • 在Android Studio中下载Android SDK的两种方式(第二种好用)

    转自 https www cnblogs com mxj961116 p 10423479 html Android studio下载地址 http www android studio org 方式一 设置HTTP Proxy1 打开Se
  • synchronized 关键字和 volatile 关键字的区别

    synchronized 关键字和 volatile 关键字是两个互补的存在 而不是对立的存在 两者主要有一下区别 1 volatile 关键字是线程同步的轻量级实现 所以 volatile性能肯定比synchronized关键字要好 2
  • Spring3.0带来的新特性

    一 首先 模块的组织更加的细致 从那么多的jar分包就看的出来 Spring的构建系统以及依赖管理使用的是Apache Ivy 从源码包看出 也使用了Maven Maven确实是个好东西 好处不再多言 以后希望能进一步用好它 二 新特性如下
  • Response.Redirect和Server.Transfer的比较

    Response Redirect响应重定向方法 Response将url返回给客户端浏览器 客户端浏览器向服务器发送重定向请求 服务器接到请求后将响应头部返回给客户端 客户端收到响应头部后发送一个新的重定向请求给服务器 Server Tr
  • 端口号被占用,如何终止?

    目录 1 使用快捷键Windows R 输入cmd进入dos命令窗口 2 输入netstat nao 查看本地所有的端口号信息 3 然后在输入 taskkill pid 数字代号 f 4 回车即可 将数字代号所对应的端口号给结束 5 数字代
  • 五、webpack的基本使用,防止重复,入口文件,懒加载,预获取/预加载(Mhua)

    入口文件 webpack 打包文件时 可以拆分多个入口文件 首先安装 lodash 插件 npm install lodash 在入口文件 index js 配置如下内容 import imgSrc from assets 2 png im
  • 【Ensemble Learning】第 4 章:混合组合

    在前面的章节中 我们讨论了如何混合训练数据 以及如何混合机器学习模型来创建更强大的模型 利用集成学习的力量 让我们继续这个学习过程 在本章中 我们介绍并解释了两种强大的集成学习技术 它们利用机器学习模型的混合组合来构建更强大的模型 我们一次
  • 在windows 上安装 openSSH

    一 基础环境 操作系统 Microsoft Windows Server 2019 datacenter 64位 openSSH 版本 OpenSSH for Windows 8 6p1 LibreSSL 3 3 3 二 操作步骤 1 下载
  • python语言turtle库画图代码示例_5分钟轻松搞定,Python开发之turtle库的基本操作...

    文源网络 仅供学习之用 如有侵权请联系删除 基础总结 turtle库是python标准库之一 入门级绘图库 import turtle之后即可使用 turtle绘图原理 有一只海龟 其实在窗体正中心 在画布上游 走过的轨迹形成了绘制的图形
  • R及RStudio下载安装教程(超详细)

    R 语言是为数学研究工作者设计的一种数学编程语言 主要用于统计分析 绘图 数据挖掘 如果你是一个计算机程序的初学者并且急切地想了解计算机的通用编程 R 语言不是一个很理想的选择 可以选择 Python C 或 Java R 语言与 C 语言
  • 模板的类型萃取

    初次接触类型萃取是在运用模板实现seqlist的时候 拷贝构造和赋值运算符重载时 单纯的使用memcopy 函数进行拷贝 只是单纯的进行了浅拷贝 对于基本的数据类型是不会有任何错误的 但是如果是string类型时 单纯的值拷贝显然是不行的
  • AttributeError: ‘xxx‘ object has no attribute ‘__bases__‘ 问题解决

    问题描述 这是我的代码 本体为一个pytorch模型 希望通过查看父类继承确定是否为网络模型 class Net nn Module def init self super Net self init self conv1 nn Conv2
  • STM32F103XX扫描I2C从机地址main.c(改进版)

    include stm32f10x h include Delay h include OLED h include MyI2C h uint8 t i 0x00 Addr Count 0 int main void OLED Init M
  • 人人组队与人机组队的风险

    无论人与人之间还是人与机之间 只要有协同就会有风险 其原因主要是 协同可能导致合作伙伴之间的利益冲突 协同合作还可能引发信息共享的风险 协同可能面临合作对象的信任问题 协同合作还可能受到外部环境的影响等等 人人组队的风险主要涉及以下几个方面
  • 开发svn hook阻止svn仓库特定分支的commit log中不包含关键字的提交

    使用批处理脚本实现开发svn hook pre commit 阻止svn仓库特定分支的commit log中不包含关键字的提交 批处理脚本 shell脚本实现 实现逻辑 1 使用svnlook 的dirs changed子命令去获取当前仓库
  • Linux system函数返回值

    http blog cheyo net p 42 例 1 status system test sh 1 先统一两个说法 1 system返回值 指调用system函数后的返回值 比如上例中status为system返回值 2 shell返
  • 尼康D90使用心得

    文章目录 规格参数 快速指南 相机机身 模式拨盘 控制面板 取景器 拍摄信息展示 核心功能 指令拨盘 拍摄模式 自动模式 场景模式 快门速度和光圈 固件 软件 驱动升级 更多细节参考 规格参数 型号 尼康D90 发布日期 2008年08月
  • 全国计算机等级考试题库二级C操作题100套(第90套)

    第90套 函数fun的功能是 统计所有小于等于n n gt 2 的素数的个数 素数的个数作为函数值返回 请在程序的下划线处填入正确的内容并把下划线删除 使程序得出正确的结果 注意 源程序存放在考生文件夹下的BLANK1 C中 不得增行或删行
  • ARM架构内核启动分析-head.S(1.1、vmlinux.lds 链接脚本分析)

    ARM架构内核启动分析 一 start kernel之前 首先需要明确的是 内核镜像在被解压之后执行 是执行哪段代码 这是个重要的问题 平时在编译生成应用程序或内核模块时 我们无需考虑链接的具体细节 如代码和数据放在哪里 代码执行入口在哪等