qemu调试kernel启动(从第一行汇编开始)

一、背景

大部分qemu调试kernel 都是讲解从start_kernel开始设置断点,然后开启调试; 但是我们熟悉linux启动流程的伙伴肯定知道,在start_kernel之前还有一段汇编,包括初始化页表及mmu等操作, 这部分如何调试呢?

二、如何从第一行代码开始调试?

无论是gdb调试还是JTAG调试,其中最重要的一个就是加载symbols 到正确的物理/虚拟地址(是物理地址还是虚拟地址取决于此时mmu是否有打开); 我们需要知道kernel 的第一行地址是什么? 对应的symbols的区域在vmlinux的哪里?

qemu 启动kernel的物理地址:

qemu 启动增加-S 选项时(启动时停止等待gdb 连接,这时会显示一个地址,这个就是当前启动的物理地址)

vmlinux中的的起始地址(虚拟地址):

在源码的System.map或者通过gdb打开vmlinux查看,内核的入口是_text,虚拟地址0xffff800080000000

_text的定义在arch/arm64/kernel/vmlinux.lds.S (注意,这里的section name:.head.text 放的是_text 不要和.text段名搞混了)

ENTRY(_text)
...
SECTIONS
{
...
	.head.text : {
		_text = .;
		HEAD_TEXT
	}
	.text : ALIGN(SEGMENT_ALIGN) {	/* Real text segment		*/
		_stext = .;		/* Text and read-only data	*/
			IRQENTRY_TEXT
			SOFTIRQENTRY_TEXT
			ENTRY_TEXT
			TEXT_TEXT
			SCHED_TEXT
			LOCK_TEXT
			KPROBES_TEXT
			HYPERVISOR_TEXT
			*(.gnu.warning)
	}

	. = ALIGN(SEGMENT_ALIGN);
	_etext = .;			/* End of text section */
...

qemu启动的物理地址和vmlinux 中启动地址(_text 虚拟地址)的关系

先来看qemu的启动地址0x0000000040000000 附近内容

(gdb) x /16i 0x0000000040000000
   0x40000000:	ldr	x0, 0x40000018
   0x40000004:	mov	x1, xzr
   0x40000008:	mov	x2, xzr
   0x4000000c:	mov	x3, xzr
   0x40000010:	ldr	x4, 0x40000020
   0x40000014:	br	x4
   0x40000018:	stxrh	w0, w0, [x0]
   0x4000001c:	udf	#0
   0x40000020:	.inst	0x40200000 ; undefined
   0x40000024:	udf	#0
   0x40000028:	udf	#0
   0x4000002c:	udf	#0
   0x40000030:	udf	#0
   0x40000034:	udf	#0
   0x40000038:	udf	#0
   0x4000003c:	udf	#0

(gdb)  x /16x 0x0000000040000000
0x40000000:	0x580000c0	0xaa1f03e1	0xaa1f03e2	0xaa1f03e3
0x40000010:	0x58000084	0xd61f0080	0x48000000	0x00000000
0x40000020:	0x40200000	0x00000000	0x00000000	0x00000000
0x40000030:	0x00000000	0x00000000	0x00000000	0x00000000

注意:不同的qemu版本可能起始的物理地址不同,本人电脑使用ubuntu22.04自带版本,6.2.0

geek@geek-virtual-machine:~/workspace/linux/linux-6.6.1$ qemu-system-aarch64 --version
QEMU emulator version 6.2.0 (Debian 1:6.2+dfsg-2ubuntu6.15)
Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers

源码路径:https://gitlab.com/qemu-project/qemu.git ,切换到6.2.0版本

geek@geek-virtual-machine:~/workspace/linux/qemu_src/qemu$ git tag | grep 6.2
v1.6.2
v2.6.2
v6.2.0
v6.2.0-rc0
v6.2.0-rc1
v6.2.0-rc2
v6.2.0-rc3
v6.2.0-rc4
geek@geek-virtual-machine:~/workspace/linux/qemu_src/qemu$ git reset --hard v6.2.0

qemu启动kernel的部分在qemu源码路径:hw/arm/boot.c

(gdb) si
0x0000000040000010 in ?? ()
=> 0x0000000040000010:	84 00 00 58	ldr	x4, 0x40000020

(gdb) x /x 0x40000020
0x40000020:	0x40200000

ldr	x4, 0x4000002  //把0x40000020 地址存储的值读取到x4,实际就是上面bootloader_aarch64[]数组定义的
                       //FIXUP_ENTRYPOINT_LO + FIXUP_ENTRYPOINT_HI
br	x4    //跳转到x4 并执行

通过qemu 代码的注释也可以看到,在这个版本的qemu中arm64的kernel 起始地址是放在0x40200000,并从这里开始执行第一条指令;

所以我们要在qemu中做的就是将物理地址0x40200000 与vmlinux中的第一条指令地址0xffff800080000000 (_text) 对齐即可;

gdb 已经连接qemu linux kernel
(gdb) x /16x 0x40200000
0x40200000:	0xfa405a4d	0x146a6427	0x00000000	0x00000000
0x40200010:	0x02860000	0x00000000	0x0000000a	0x00000000
0x40200020:	0x00000000	0x00000000	0x00000000	0x00000000
0x40200030:	0x00000000	0x00000000	0x644d5241	0x00000040



gdb vmlinux直接查看_text处的汇编
(gdb) x /16x _text
0x80000000 <_text>:	0xfa405a4d	0x146a6427	0x00000000	0x00000000
0x80000010 <$d+8>:	0x02860000	0x00000000	0x0000000a	0x00000000
0x80000020 <$d+24>:	0x00000000	0x00000000	0x00000000	0x00000000
0x80000030 <$d+40>:	0x00000000	0x00000000	0x644d5241	0x00000040

通过readelf确认那些段需要映射

geek@geek-virtual-machine:~/workspace/linux/linux-6.6.1$ aarch64-none-linux-gnu-readelf -S vmlinux
There are 43 section headers, starting at offset 0x18ff9fb8:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .head.text        PROGBITS         ffff800080000000  00010000
       0000000000010000  0000000000000000  AX       0     0     65536
  [ 2] .text             PROGBITS         ffff800080010000  00020000
       000000000102b000  0000000000000000  AX       0     0     65536
  [ 3] .rodata           PROGBITS         ffff800081040000  01050000
       00000000009dc8c8  0000000000000000  WA       0     0     4096
......
  [15] .rodata.text      PROGBITS         ffff800081a94800  01aa4800
       0000000000005800  0000000000000000  AX       0     0     2048
  [16] .init.text        PROGBITS         ffff800081aa0000  01ab0000
       000000000008c6f8  0000000000000000  AX       0     0     8
......
  [19] .init.data        PROGBITS         ffff800081b95000  01ba5000
       00000000000c551a  0000000000000000  WA       0     0     256
......

地址映射关系:

section namevirtual addrphy addr
.head.text0xffff8000800000000x40200000
.text0xffff8000800100000x40210000
.rodata0xffff8000810400000x40240000
.rodata.text0xffff800081a948000x41C94800
.init.text0xffff800081aa00000x41CA0000
.init.data0xffff800081b950000x41D95000

启动gdb 时不要加载vmlinux, 通过add-symbol-file 指定section 要加载的物理地址

add-symbol-file vmlinux -s .head.text 0x40200000 -s .text 0x40210000 -s .rodata 0x40240000 -s .rodata.text 0x41C94800 -s .init.text 0x41CA0000 -s .init.data 0x41D95000

设置断点:b _text

然后就可以单步调试:

三、总结

其实不管使用什么调试器(gdb/T32/Crash/lldb),第一步要做的都是将elf和调试target的执行地址做一个对齐,当然这个对齐可能是物理地址对齐(无mmu,如bootloader,elf编译的地址就是代码运行的物理地址),也有可能是虚拟地址对齐(开启了mmu 比如kernel start_kernel 之后部分),也有可能是物理地址与虚拟地址对齐(比如本文中的_text到start_kernel), 掌握了这个规律也就掌握的调试的入口密码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/355142.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

cocos添加节点事件的3种方式

我们以button为例来说明一下cocos怎样为节点添加事件&#xff1a; 直接通过cocos熟悉检查器绑定 添加事件脚本 import { _decorator, Component, Node, input, Input, Button, EventKeyboard } from cc; const { ccclass, property } _decorator;ccclass(Attack) export cla…

【vue】图片加载骨架

一、前言 在网速较低或者网站的服务器宽带只有几MB的情况下&#xff0c;网页中的图片加载时&#xff0c;要么空白&#xff0c;要么像打印机一样一行一行地“扫描”出来&#xff0c;为了提升用户体验&#xff0c;可以给图片标签外加一层骨架。 无骨架 有骨架 二、详细设计 每张…

无人机在三维空间中的转动问题

前提 这篇博客是对最近一个有关无人机拍摄图像项目中所学到的新知识的一个总结&#xff0c;比较杂乱&#xff0c;没有固定的写作顺序。 无人机坐标系旋转问题 上图是无人机坐标系&#xff0c;绕x轴是翻滚(Roll)&#xff0c;绕y轴是俯仰(Pitch)&#xff0c;绕z轴是偏航(Yaw)。…

sqli-labs第一关

1.判断是否存在注入&#xff0c;注入是字符型还是数字型? ?id1 and 11 ?id1 and 12 因为输入and 11与and 12 回显正常&#xff0c;所以该地方不是数字型。 ?id1 ?id1-- 输入单引号后报错&#xff0c;在单引号后添加--恢复正常&#xff0c;说明存在字符注入 2.猜解SQL查…

Spark Exchange节点和Partitioning

​Exchange 在explain时&#xff0c;常看到Exchange节点&#xff0c;这个节点其实就是发生了数据交换 此图片来自于网络截取 BroadcastExchangeExec 主要是用来广播的 ShuffleExchangeExec 里面决定了数据分布的方式和采用哪种shuffle 在这里可以看到好几种不同的分区器 shuf…

Windows11搭建GPU版本PyTorch环境详细过程

Anaconda安装 https://www.anaconda.com/ Anaconda: 中文大蟒蛇&#xff0c;是一个开源的Python发行版本&#xff0c;其包含了conda、Python等180多个科学包及其依赖项。从官网下载Setup&#xff1a;点击安装&#xff0c;之后勾选上可以方便在普通命令行cmd和PowerShell中使用…

聊聊Git合并和变基

一、 Git Merge 合并策略 1.1 Fast-Forward Merge&#xff08;快进式合并&#xff09; //在分支1下操作&#xff0c;会将分支1合并到分支2中 git merge <分支2>最简单的合并算法&#xff0c;它是在一条不分叉的两个分支之间进行合并。快进式合并是默认的合并行为&#…

微信小程序wx.getRealtimeLogManager无法查看log内容

解决方案&#xff1a; 首先&#xff0c;检查在we分析是否启用实时日志&#xff0c;入口如下&#xff1a; 其次&#xff0c;检查基本语法是否正确&#xff0c;参考如下&#xff1a; var logger wx.getRealtimeLogManager() logger.error("error message") 最后&a…

你好,C++对象

你好&#xff0c;对象 面向对象开发对象的定义 类与对象类的定义类的访问限定符及封装类的实例化类对象模型结构体内存对齐规则 this指针this指针的引入 this指针的特性 类的默认成员函数构造函数析构函数拷贝构造函数结语 面向对象开发 对象的定义 对象的含义是指具体的某一…

在docker中安装MQTT教程

网上的好多关于在docker中安装MQTT教程都是错误的不完整的。这篇博客是完整的&#xff0c;实践过的&#xff0c;踩过了很多的坑得来的&#xff0c;欢迎大家享用&#xff01; 1、首先在docker中拉取镜像 docker pull eclipse-mosquitto2、创建配置文件目录 mkdir -p /docker/…

axure如何载入元件库

首先&#xff0c;安装axure后打开 1.打开axure软件后&#xff0c;左侧可以看到一个叫元件库的菜单 2.点击元件库右侧的选项按钮 然后在出现的下拉菜单中点击载入元件库 3.找到自己下载好的元件库&#xff08;Axure元件库合集&#xff09;&#xff0c;双击它就可以载入了 可以看…

Linux:共享内存

文章目录 System V共享内存的原理管理共享内存shmgetshmatshmdtshmctl 共享内存和管道实现进程间同步通信 前面介绍完了匿名管道和命名管道&#xff0c;那么本篇要引入的主题是共享内存 System V 作为进程通信部分的内容&#xff0c;共享内存必然有其存在的意义和价值&#x…

༺༽༾ཊ—Unity之-04-工厂方法模式—ཏ༿༼༻

首先创建一个项目&#xff0c; 在这个初始界面我们需要做一些准备工作&#xff0c; 建基础通用文件夹&#xff0c; 创建一个Plane 重置后 缩放100倍 加一个颜色&#xff0c; 任务&#xff1a;使用工厂方法模式 创建 飞船模型&#xff0c; 首先资源商店下载飞船模型&#xff0c…

mkcert的安装和使用,5分学会在本地开启localhost的https访问方式

mkcert官方仓库地址&#xff1a;https://github.com/FiloSottile/mkcert#installation mkcert 是一个简单的工具&#xff0c;用于制作本地信任的开发证书。它不需要配置。 简化我们在本地搭建 https 环境的复杂性&#xff0c;无需操作繁杂的 openssl 实现自签证书了&#xff…

风速编辑一下

clear clc % 假设已知的南北和东西风速分量时程 time 0:1:999; % 时间步长为1秒 north_south_wind_speed 8 2 * sin(2 * pi * 0.1 * time); % 南北风向分量 east_west_wind_speed 6 1 * sin(2 * pi * 0.1 * time); % 东西风向分量 % 计算合风速和风向 total_wind_spe…

Node.js的学习1

Node.js简介 浏览器是JavaScript的前端运行环境Node.js是JavaScript的后端运行环境Node.js中无法调用DOM和BOM等浏览器内置API 终端中的快捷键 使用向上箭头&#xff0c;可以快速定位到上一次执行的命令使用tab键&#xff0c;可以快速补全路径使用esc键&#xff0c;可以快速清…

django 访问后台数据库管理程序报错:CSRF verihcation failed. Request aborted.

解决方案 在 settings.py 最后加上这个 CSRF_TRUSTED_ORIGINS ["https://<你自己 heroku 的项目部署地址>.herokuapp.com" ]注意下面的事项&#xff1a; https 前面和 .com 后面不要带空格&#xff0c;因为你在复制的时候可能引入空格.com 后面不要加斜杠&a…

Rust循环和函数

下面聊聊以下主题&#xff1a; 基于条件的分支循环函数属性测试 基于条件的分支 基于条件的分支&#xff0c;可以通过常见的 if、if else 或 if else if else 构造来完成&#xff0c;例如下面的示例&#xff1a; fn main() { let dead false; let health 48; if dead { p…

第十八章 Redis查看配置文件和数据类型

文章目录 前言1、查看配置2、修改配置项2.1、配置项说明2.2、配置支持远程访问 3、数据类型3.1、String3.1.1、string扩容规则3.1.2、字符串命令3.1.3、string 常用命令 3.2、Hash3.2.1、数据存储3.2.2、常用命令 3.3、list3.3.1、常用命令 3.4、set3.4.1、常用命令 3.5、zset有…

SpringCloud-高级篇(十六)

前面学习了Lua的语法&#xff0c;就可以在nginx去做编程&#xff0c;去实现nginx类里面的业务&#xff0c;查询Redis&#xff0c;查询tomcat等 &#xff0c;业务逻辑的编写依赖于其他组件&#xff0c;这些组件会用到OpenResty的工具去实现 &#xff08;1&#xff09;安装OpenRe…