//函数原型:版本号linux-3.0.8

struct task_struct *__switch_to(structtask_struct *,
struct thread_info *, struct thread_info *);

#define switch_to(prev,next,last)                                       \

do {                                                                   \

last =__switch_to(prev,task_thread_info(prev), task_thread_info(next));        \

} while (0)

//首先我们看一下以下的宏:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER)

//以下事实上就是指向相应的struct成员

/*

CC_STACKPROTECT补丁是Tejun Heo在年给主线kernel提交的一个用来防止内核堆栈溢出的补丁。

默认的config是将这个选项关闭的,能够在编译内核的时候。改动.config文件为CONFIG_CC_STACKPROTECTOR=y

来启用。未来飞天内核能够将这个选项开启来防止利用内核stack溢出的day攻击。这个补丁的防

溢出原理是:在进程启动的时候,在每一个buffer的后面放置一个预先设置好的stack canary。你

能够把它理解成一个哨兵,当buffer发生缓冲区溢出的时候。肯定会破坏stack canary的值,当

stack canary的值被破坏的时候。内核就会直接当机。

那么是怎么推断stack canary被覆盖了呢?

事实上这个事情是gcc来做的,内核在编译的时候给gcc加了个-fstack-protector參数.

*/

DEFINE(TSK_STACK_CANARY,     offsetof(struct task_struct,stack_canary));

//task_struct

DEFINE(TI_TASK,              offsetof(struct thread_info, task));

//

/*

* Domain types

*/

/*

#define DOMAIN_NOACCESS 0

#define DOMAIN_CLIENT  1//是用户的域(运行程序,訪问数据),以及由所述接入加以防护

//个别章节和页面组成域的权限。

#ifdef CONFIG_CPU_USE_DOMAINS

#define DOMAIN_MANAGER 3//控制域的行为(当前域的sections和page。以及域訪问)。

#else

#define DOMAIN_MANAGER 1

#endif

*/

//相应图

//这个domain通过协处理器设置寄存器DomainAccess Control

DEFINE(TI_CPU_DOMAIN,        offsetof(struct thread_info,cpu_domain));

/*

struct cpu_context_save {

__u32   r4;

__u32   r5;

__u32   r6;

__u32   r7;

__u32   r8;

__u32   r9;

__u32   sl;

__u32   fp;

__u32   sp;

__u32   pc;

__u32   extra[2];               /* Xscale 'acc' register, etc */

};

*/

DEFINE(TI_CPU_SAVE,          offsetof(struct thread_info,cpu_context));

/*

在以下有个set_tls,相应我的平台set_tls_v6k

.macroset_tls_v6k, tp, tmp1, tmp2

mcr     p15, 0, \tp, c13, c0, 3         @ set TLS register

.endm

tp_value就是为了设置TLS register的值

在多线程应用程序。当中一个进程共享同样的地址空间中的全部线程。还有常常出现须要维护的数据是唯一

的一个线程。TLS或线程本地存储。由于你或许能够从它的名字如今弄清楚。是用于线程抽象的概念。它是

一种高速和有效的方式来存储每一个线程的本地数据。

线程的本地数据的偏移量是通过TLS寄存器(H / W或S

/ W块),它指向线程各自的线程控制块訪问。

之前ARM内核。甚至ARM9和ARM11核心的一些不具备这样的TLS注冊物理上可用。

操作系统(Linux从这里開始)

须要效仿的软件。新一代的ARM内核。Cortex-AX起,确实有这TLS的寄存器可用(CP15)。

内核对TLS须要做的事情是可以让用户态程序(一般是nptl——一个pthread的实现)在某个时刻可以设置

线程唯一的基址值到内核的线程信息结构内。

*/

DEFINE(TI_TP_VALUE,          offsetof(struct thread_info, tp_value));

/*

* These are the reasoncodes for the thread notifier.

*/

#define THREAD_NOTIFY_FLUSH    0

#define THREAD_NOTIFY_EXIT     1

#define THREAD_NOTIFY_SWITCH   2

#define THREAD_NOTIFY_COPY     3

  1. /*
  2. * Register switch for ARMv3 and ARMv4 processors
  3. * r0 = previous task_struct, r1 = previous thread_info, r2 = next thread_info
  4. * previous and next are guaranteed not to be the same.
  5. */
  6. ENTRY(__switch_to)
  7. UNWIND(.fnstart )
  8. UNWIND(.cantunwind )
  9. //ip就是上一个线程的thread_info里面的cpu_context的地址
  10. add ip, r1, #TI_CPU_SAVE
  11. //r3里面存着下一个线程tp值
  12. ldr r3, [r2, #TI_TP_VALUE]
  13. //存储r4 - sl, fp, sp, lr到thread_info->cpu_context里。
  14.  
  15. 分别使用armthumb实现
  16. //这就是保存现场。
  17. ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack
  18. THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack
  19. THUMB( str sp, [ip], #4 )
  20. THUMB( str lr, [ip], #4 )
  21. #ifdef CONFIG_CPU_USE_DOMAINS
  22. //r6存着下一个线程的DOMAIN属性
  23. ldr r6, [r2, #TI_CPU_DOMAIN]
  24. #endif
  25. //set_tls 上面已分析
  26. set_tls r3, r4, r5
  27. #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
  28. ldr r7, [r2, #TI_TASK]//下一个线程的task_struct
  29. ldr r8, =__stack_chk_guard//r8里面是__stack_chk_guard地址
  30. ldr r7, [r7, #TSK_STACK_CANARY]//到这里。r7里面是stack_canary值
  31. #endif
  32. #ifdef CONFIG_CPU_USE_DOMAINS
  33. //设置domain寄存器。
  34. mcr p15, 0, r6, c3, c0, 0 @ Set domain register
  35. #endif
  36. //r5里面是上一个线程的task_struct
  37. mov r5, r0
  38. //r4就是下一个线程的thread_info里面的cpu_context的地址
  39. add r4, r2, #TI_CPU_SAVE
  40. //r4 r5仅仅是暂时保存一下
  41.  
  42. //以下的thread_notify_head通知链,以下样例说明
  43. ldr r0, =thread_notify_head
  44. mov r1, #THREAD_NOTIFY_SWITCH
  45. bl atomic_notifier_call_chain
  46. #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
  47. str r7, [r8]//__stack_chk_guard = (next)threadinfo->task->stack_canary
  48. #endif
  49. THUMB( mov ip, r4 )//ip指向线程的thread_info里面的cpu_context的地址
  50. mov r0, r5//r0从新指向上一个线程的task_struct
  51. //以下相应了上面的保存现场,这里就是恢复现场。
  52.  
  53. pc相应了下个进程的cpu_context->pc
  54. //从上面看到这个cpu_context->pc就是之前保存现场的lr,就是下个线程要运行的地方。
  55. ARM( ldmia r4, {r4 - sl, fp, sp, pc} ) @ Load all regs saved previously
  56. THUMB( ldmia ip!, {r4 - sl, fp} ) @ Load all regs saved previously
  57. THUMB( ldr sp, [ip], #4 )
  58. THUMB( ldr pc, [ip] )
  59. UNWIND(.fnend )
  60. ENDPROC(__switch_to)

实验代码:

  1. #include <linux/kernel.h>
  2. #include <linux/notifier.h>
  3. #include <linux/module.h>
  4. #include <asm/thread_notify.h>
  5.  
  6. MODULE_LICENSE("GPL");
  7.  
  8. static int test_event(struct notifier_block *this, unsigned long event, void *ptr)
  9. {
  10. printk(KERN_INFO "In Event: Event Number is %ld\n",event);
  11.  
  12. return NOTIFY_DONE;
  13. }
  14.  
  15. static struct notifier_block test_notifier =
  16. {
  17. .notifier_call = test_event,
  18. };
  19.  
  20. static int __init reg_notifier(void)
  21. {
  22. int err = 0;
  23. printk(KERN_INFO "Begin to register:\n");
  24.  
  25. err = thread_register_notifier(&test_notifier);
  26. if (err)
  27. {
  28. printk(KERN_ERR "register test_notifier error\n");
  29.  
  30. goto fail1;
  31. }
  32.  
  33. printk(KERN_INFO "register reboot_notifier completed\n");
  34.  
  35. return 0;
  36.  
  37. fail1:
  38. return err;
  39. }
  40.  
  41. static void __exit unreg_notifier(void)
  42. {
  43. thread_unregister_notifier(&test_notifier);
  44.  
  45. printk(KERN_INFO "Unregister finished\n");
  46. }
  47.  
  48. module_init(reg_notifier);
  49. module_exit(unreg_notifier);

打印:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveHh4eHhsbGxsbHhs/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

2都是THREAD_NOTIFY_SWITCH,当然会不断的切换!

具体分析contrex-A9的汇编代码__switch_to(进程切换)的更多相关文章

  1. Linux内核分析--理解进程调度时机、跟踪分析进程调度和进程切换的过程

    ID:fuchen1994 姓名:江军 作业要求: 理解Linux系统中进程调度的时机,可以在内核代码中搜索schedule()函数,看都是哪里调用了schedule(),判断我们课程内容中的总结是否 ...

  2. Linux内核设计第八周学习总结 理解进程调度时机跟踪分析进程调度与进程切换的过程

    陈巧然 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.视频内容 Linux ...

  3. linux内核分析作业:以一简单C程序为例,分析汇编代码理解计算机如何工作

    一.实验 使用gcc –S –o main.s main.c -m32 命令编译成汇编代码,如下代码中的数字请自行修改以防与他人雷同 int g(int x) { return x + 3; } in ...

  4. linux内核分析作业4:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

    系统调用:库函数封装了系统调用,通过库函数和系统调用打交道 用户态:低级别执行状态,代码的掌控范围会受到限制. 内核态:高执行级别,代码可移植性特权指令,访问任意物理地址 为什么划分级别:如果全部特权 ...

  5. 简单C程序生成的汇编代码分析

    首先给出完整的C代码: int g(int x) { ; } int f(int x) { return g(x); } int main(void) { )+; } 使用命令:gcc –S –o h ...

  6. 分析一个C语言程序生成的汇编代码-《Linux内核分析》Week1作业

    署名信息 郭春阳 原创作品转载请注明出处 :<Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 C源码 这 ...

  7. 《Linux内核分析》week1作业-分析一个简单c语言的汇编代码

    1.C语言源码 #include <stdio.h> int g(int x){ ; } int f(int x){ return g(x); } int main(){ )+; } 2. ...

  8. 一个简单C程序的汇编代码分析

    几个重要的寄存器 eip - 用于存放当前所执行的指令地址 esp - 栈(顶)指针寄存器 ebp - 基址(栈底)指针寄存器 简单的C程序 int g(int x) { ; } int f(int ...

  9. 《linux内核分析》作业一:分析汇编代码

    通过汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的(王海宁) 姓名:王海宁                             学号:20135103 课程:<Linux内核分析& ...

随机推荐

  1. Writing buffer overflow exploits - a tutorial for beginners

    Buffer overflows in user input dependent buffers have become one of the biggest security hazards on ...

  2. 业余学习react 学习记录

    http://www.ruanyifeng.com/blog/2015/03/react (阮一峰 react 学习) 1.搭建环境:npm 使用 React npm install -g cnpm ...

  3. [Python] The get() method on Python dicts and its "default" arg

    # The get() method on dicts # and its "default" argument name_for_userid = { 382: "Al ...

  4. 学习笔记:_lodash.js常用函数2

    _.pick(object, [props]) 创建一个从object中选中的属性的对象. 示例: var object = { 'a': 1, 'b': '2', 'c': 3 }; _.pick( ...

  5. 看<Asp.net夜话>随笔(2013-10-13)

    1.Asp.net内置对象 1.1Request对象 封装了客户端请求信息 1.2Response对象 代表了服务器响应对象,可以向客户端返回数据 1.3Server对象 是用于获取服务器的相关信息的 ...

  6. ajax对服务端发送请求

    //兼容处理获取ajax对象 var req = ''; if (window.XMLHttpRequest)    req = new XMLHttpRequest(); else    req = ...

  7. Android中的Parcelable接口和Serializable使用方法和差别

    Parcelable接口: Interface for classes whose instances can be written to and restored from a Parcel. Cl ...

  8. Q13.cocoapod_卡在“analyzing_depengcies”问题解决

    Q13.CocoaPod 卡在"analyzing depengcies"问题解决 问题描写叙述: 当进入到项目目录后,pod init一个Podfile,然后键入你要的库连接信息 ...

  9. HTTP网络协议(四)

    确保Web安全的HTTPS HTTP存在三个比较明显的缺点: 通信使用明文(不加密),内容可能会被窃听. 不验证通信方的身份,因此有可能遭遇伪装. 无法证明报文的完整性,所以可能已遭篡改.  尽管HT ...

  10. (转) 通过UUID在vSphere虚拟机内外识别硬盘

    转自:http://ju.outofmemory.cn/entry/28398 简单介绍下应用场景:开发基于虚拟化IaaS的一些应用就免不了要跟虚拟机(VM)打交道,因为VM逻辑上独立于宿主机(hos ...