ARMv7下的debian,汇编程序跳转出现段错误

我在ARMv7的平台上进行编程(AM3358),在linux中通过汇编语言的指令跳转到一个代码段,但多次调用后出现段错误。然后我用gdb单步调试则不会出现问题,程序能够正常执行。请问大佬对这个问题有没有思路?

程序和报错呢?

您好,我刚用这个论坛,还不太清楚怎么传文件,如果可以我把源码传给您。另外错误类型显示的是段错误Segmentation fault (core dumped)

下面贴上程序和运行结果,另外该怎么在原贴上进行修改??
代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>

#define CODE_AREA_START_ADDRESS 0x8d000000
#define CODE_AREA_SIZE          0x1000000

unsigned int addrx;
unsigned int test_i;
unsigned int ADDR_BUFF_x[10] = {0x8d000000, 0x8d001000, 0x8d003100, 0x8d008400, 0x8d008500,
				0x8d009020, 0x8d009060, 0x8d100000, 0x8d00ff00, 0x8d010000};
int main()
{
	int i = 0;
	unsigned int user_a;
	i = mlockall(MCL_CURRENT|MCL_FUTURE);
	printf("the result is %d\n", i);
	i = 0;
	void* start_addr;
	start_addr = (void*)CODE_AREA_START_ADDRESS;
	int page_num = CODE_AREA_SIZE/4096;
	for(i=0;i<page_num;i++)
	{
		start_addr=mmap(start_addr, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED|MAP_ANONYMOUS|MAP_FIXED, -1, 0);		
		if(start_addr == NULL) printf("map fault\n");
		start_addr = start_addr + 4096;
	}
	//void* execbuf = mmap((void*)CODE_AREA_START_ADDRESS, CODE_AREA_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
	//i = mlock((void*)CODE_AREA_START_ADDRESS, CODE_AREA_SIZE);
	//printf("the lock mem result is %d\n",i);
	i=0;
	for(i=0;i<10;i++)
	{
		user_a = ADDR_BUFF_x[i];
		printf("the num: %d,\t the addr: 0x%08x \n", (i+1), user_a);
		//SysCpuCallFunc((void*)user_a, NULL, 8);
		unsigned int page_addr = user_a&0xfffff000;
		void* p = (void*)user_a;
		int bufx[4];
		int j;
		for(j=0;j<4;j++) bufx[j] = *((int*)p+j);
		*((int*)p+0) = 0xe1a00003;
		*((int*)p+1) = 0xe12fff1e;
		*((int*)p+2) = 0x00000000;
		*((int*)p+3) = 0x00000000;
		j = mprotect((void*)page_addr, 4096, PROT_WRITE|PROT_EXEC);
		__asm__ volatile(
			"push {r0-r12} \n"
			"mov r2, %0	\n"
			"mov lr, pc	\n"
			"mov pc, r2	\n"
			"pop {r0-r12} \n"
			//"blx %1		\n"
			: 
			: "r" (p)
			: "r2", "lr");
		//i = asm_code(p);
		printf("ASM finish\n");
		for(j=0;j<4;j++) *((int*)p+j) = bufx[j];
	}
	return 0;
}


其大致原理是,先用mmap开一段可执行内存,然后将机器码填入这段内存中:
((int)p+0) = 0xe1a00003;
((int)p+1) = 0xe12fff1e;
随后使用汇编代码进程程序跳转跳到这个机器码上,机器码的第二局是返回,即跳回原位置的下一句。
使用for循环将上述内容运行10次,发现在成功几次(有时一次,有时两次,有时三次)后发生段错误。
运行结果截图如下:

问题0:

i = mlockall(MCL_CURRENT|MCL_FUTURE);
	printf("the result is %d\n", i);

从你的截图来看这个调用失败了。


问题1:

RETURN VALUE
       On success, mmap() returns a pointer to the mapped area.  On error, the value MAP_FAILED (that is, (void *) -1) is returned, and errno is set to  indicate
       the error.

       On success, munmap() returns 0.  On failure, it returns -1, and errno is set to indicate the error (probably to EINVAL).

所以如果 mmap 失败了你根本不知道。而且为什么要调用 mmap 4096 次而不是只调用一次?


问题2:

#define CODE_AREA_START_ADDRESS 0x8d000000
#define CODE_AREA_SIZE          0x1000000

其中 0x8d000000 + 0x1000000 = 0x8d100000,但是后文中

		user_a = ADDR_BUFF_x[i];
		void* p = (void*)user_a;
		*((int*)p+0) = 0xe1a00003;
		*((int*)p+1) = 0xe12fff1e;
		*((int*)p+2) = 0x00000000;
		*((int*)p+3) = 0x00000000;

如果 user_a 的值为 0x8d100000 必然越界,因为不满足:(p + 3) < (0x8d000000 + 0x1000000)


建议检查所有指针边界情况和所有系统调用是否成功,去掉汇编部分检查程序是否正常,另外认真翻阅相关函数的使用手册。

您好,谢谢您的回复。
请允许我和您讨论,并向您请教。
关于第0问题,的确锁内存不成功,因为如果要执行这个函数需要超级用户的权限。但我开超级用户后程序同样出现崩溃,但Segmentation fault却打印不出来了。因此截图是用普通用户执行情况下截图的。
关于问题1,mmap失败的确是向您说的那样看不出来。另外mmap一开始我是只调用了一次,后来担心这里有问题,就把程序改成了每页调用一次,1页是4k字节,而非4096次。
关于问题2,ADDR_BUFF_x[7]的地址越界的确是我的一个疏忽,但程序还没执行难道ADDR_BUFF_x[7]就已经崩溃了,这是我百思不得其解的地方。
再次向您致谢

我对于 arm 汇编的了解为 0,但是我觉得你可以尝试让程序崩溃时生成一份内存转储,这样就可以知道崩溃时发生了什么,还不需要 gdb 介入。

需要注意的是 mlockall 是有极限的,一般来说是 64K。如果你使用了 MCL_FUTURE 那么后续的 mmap 调用是有可能失败的。

谢谢您的指教,我试一试。

总的来说可以从两方面入手,

  1. 删除不确定的代码寻找最小可复现现场
  2. 启用 core dump,可以使用 gdb 分析崩溃时什么指令触发了问题

0x1000 000 / 4096 = 0x1000

所以你调用了4096次 mmap 函数

还真是4096次, :joy:
我怀疑是栈的问题