android读寄存器的工具-devmem
一、概述
在Linux/android开发中着实用到的调试工具并不是很多。devmem的方式是提供给驱动开发人员,在应用层能够侦测内存地址中的数据变化,以此来检测驱动中对内存或者相关配置的正确性验证。基本原理通过设备文件/dev/mem 实现对物理内存的读写。
二、用法
- 内核中配置CONFIG_DEVMEM的宏
Usage: devmem ADDRESS [WIDTH [VALUE]]
- 读取:在地址0x97000000读取32bit值(WIDTH默认等于32, 可选值为[8, 16, 32, 64])
1 2 | devmem 0x97000000 0x11111111 |
- 读取:在地址0x97000000读取16bit值
1 2 | devmem 0x97000000 16 0x1111 |
- 写入:在地址0x97000000写入32bit值0x7777ABCD
1 2 3 | devmem 0x97000000 32 0x7777ABCD devmem 0x97000000 0x7777ABCD |
注意:如果/dev下没有mem这个node,会出现错误:
1 2 | devmem 0x97000000 devmem: can 't open ' /dev/mem': No such file or directory |
需要配置CONFIG_DEVMEM宏
三、dev/mem介绍
/dev/mem是linux下的一个字符设备, 源文件是kernel/drivers/char/mem.c, 这个设备文件是专门用来读写物理地址用的。里面的内容是所有物理内存的地址以及内容信息。通常只有root用户对其有读写权限。
利用mmap和/dev/mem建立起直接读写系统物理内存的渠道。利用/dev/mem和mmap导出系统物理地址,免去了用户虚拟地址到内核逻辑地址的繁琐拷贝,提升效率。
四、devmem的源码分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <signal.h> #include <fcntl.h> #include <ctype.h> #include <termios.h> #include <sys/types.h> #include <sys/mman.h> #define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \ __LINE__, __FILE__, errno , strerror ( errno )); exit (1); } while (0) #define MAP_SIZE 4096UL //映射的内存区大小(一般为一个叶框大小) #define MAP_MASK (MAP_SIZE - 1) //MAP_MASK = 0XFFF int main( int argc, char **argv) { int fd; void *map_base, *virt_addr; unsigned long read_result, writeval; off_t target; int access_type = 'w' ; if (argc < 2) { //若参数个数少于两个则打印此工具的使用方法 fprintf (stderr, "\nUsage:\t%s { address } [ type [ data ] ]\n" "\taddress : memory address to act upon\n" "\ttype : access operation type : [b]yte, [h]alfword, [w]ord\n" "\tdata : data to be written\n\n" , argv[0]); exit (1); } target = strtoul (argv[1], 0, 0); //读取第一个参数,并转换成无符号长整型 if (argc > 2) access_type = tolower (argv[2][0]); //如果存在第二参数,则读取第二个参数的第一字符 if ((fd = open( "/dev/mem" , O_RDWR | O_SYNC)) == -1) FATAL; //打开文件(/dev/mem) printf ( "/dev/mem opened.\n" ); fflush (stdout); /* Map one page */ //利用字符设备文件dev/mem的mmap方法将内核空间映射到用户空间 map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target & ~MAP_MASK); if (map_base == ( void *) -1) FATAL; printf ( "Memory mapped at address %p.\n" , map_base); fflush (stdout); virt_addr = map_base + (target & MAP_MASK); //针对不同的参数获取不同类型内存数据 switch (access_type) { case 'b' : read_result = *((unsigned char *) virt_addr); break ; case 'h' : read_result = *((unsigned short *) virt_addr); break ; case 'w' : read_result = *((unsigned long *) virt_addr); break ; default : fprintf (stderr, "Illegal data type '%c'.\n" , access_type); exit (2); } printf ( "Value at address 0x%X (%p): 0x%X\n" , target, virt_addr, read_result); fflush (stdout); //若参数大于3个,则说明为写入操作,针对不同参数写入不同类型的数据 if (argc > 3) { writeval = strtoul (argv[3], 0, 0); switch (access_type) { case 'b' : *((unsigned char *) virt_addr) = writeval; read_result = *((unsigned char *) virt_addr); break ; case 'h' : *((unsigned short *) virt_addr) = writeval; read_result = *((unsigned short *) virt_addr); break ; case 'w' : *((unsigned long *) virt_addr) = writeval; read_result = *((unsigned long *) virt_addr); break ; } printf ( "Written 0x%X; readback 0x%X\n" , writeval, read_result); fflush (stdout); } if (munmap(map_base, MAP_SIZE) == -1) FATAL; close(fd); return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2021-12-18 openwrt有线和无线网络测试
2021-12-18 openwrt有线网卡的停用与开启
2017-12-18 sed的用法
2017-12-18 svn的常用命令
2017-12-18 python脚本发送邮件
2017-12-18 makefile中PHONY的重要性
2017-12-18 crontab在/var/log/目录下没有cron.log文件