int module_init_notifier(struct notifier_block *self,
                         unsigned long event,
                         void *arg)
{
	struct module *mod = (struct module *)arg;
	unsigned int name_offset;
	unsigned int lsc;
	
	lsc = get_sample_count();
	
	name_offset = 0;


	if (event == MODULE_STATE_COMING)
	{
		if (mod->init_size > 0)
		{
			module_load_notif((char *)mod->name, name_offset, 0,
				              (__u32)mod->module_init, mod->init_size,
				              MODULE_FLAG_GLOBAL, lsc);
		}
		
		if (mod->core_size > 0)
		{	
			module_load_notif((char*)mod->name, name_offset, 0,
				              (__u32)mod->module_core, mod->core_size,
				              MODULE_FLAG_GLOBAL, lsc);
		}
	}
	
	return 0;
}
/* 
 * enumerate all modules for process
 * pid: process id
 * lsc: load sample count
 */
static void enum_modules_for_process(pid_t pid, unsigned long lsc)
{
	bool find = false;
	char *pname;
	unsigned long name_offset;
	unsigned int options;
	struct task_struct *task;
	struct vm_area_struct *mmap;
	struct mm_struct *mm;

	task = px_find_task_by_pid(pid);

	if (task == NULL)
		return;

	mm = get_task_mm(task);
	if (mm != NULL)
	{
		down_read(&mm->mmap_sem);

		for (mmap = mm->mmap; mmap; mmap = mmap->vm_next)
		{
			if (is_valid_module(mmap))
			{
				memset(name, 0, (PATH_MAX) * sizeof(char));
				pname = px_d_path(mmap->vm_file, name, PATH_MAX);

				if (pname != NULL)
				{
					options = 0;

					if (find == false)
					{
						options |= MODULE_FLAG_1ST;
						find = true;
					}
					
					name_offset = get_filename_offset(pname);
					
					module_load_notif(pname, name_offset, pid,
						              mmap->vm_start, mmap->vm_end - mmap->vm_start,
						              options, lsc);
				}
			}
		}

		up_read(&mm->mmap_sem);
		mmput(mm);
	}

	if (find == false)
	{
		module_load_notif(task->comm, 0, pid,
				  LINUX_APP_BASE_LOW, 0,//mmap->vm_end - mmap->vm_start,
				  MODULE_FLAG_1ST, lsc);
	}

	return;
}
/*
 * static enumeration kernel modules
 */
static void static_enum_kernel_modules(void)
{
	module_load_notif("swapper", 0, 0, 
				      LINUX_APP_BASE_LOW, 0, 
				      MODULE_FLAG_1ST, 0);

	/*
	 * PAGE_OFFSET is the load address of kernel
	 * 
	 * 0x8000 is the offset to the text section of kernel
	 * which is defined as TEXT_OFFSET in arch/arm/Makefile
	 */
	module_load_notif("vmlinux", 0, 0, 
	                  PAGE_OFFSET + 0x8000, 4096 * 2 * 1024,
	                  MODULE_FLAG_GLOBAL, 0);
}
asmlinkage int px_sys_mmap2(
		unsigned long addr, unsigned long len,
		unsigned long prot, unsigned long flgs,
		unsigned long fd, unsigned long pgoff
/*		unsigned long lRegSP*/
		)
{
	int ret = 0;
	int saved_r5;
	unsigned long lsc;
	struct file *file;

	INIT_STACK_FRAME; 

	// here we must save r5 since it will be used by the OS sys_mmap2 code
	__asm__("str r5, %0\n\t":"=m"(saved_r5):);

	APPEND_STACK_FRAME;

	lsc = get_sample_count();

	// restore r5 
	__asm__("ldr r5, %0\n\t"::"m"(saved_r5):"r5");

	ret = px_original_sys_mmap2( addr, len, prot, flgs, fd, pgoff);

	CUTTAIL_STACK_FRAME;

	if (gb_enable_os_hooks && !IS_ERR((void *)ret) && (prot & PROT_EXEC) && !(flgs & MAP_ANONYMOUS))
	{
		//rcu_read_lock();

		if ((file = fcheck(fd))!= NULL)
		{
			char *pname;
			
			memset(name, 0, PATH_MAX * sizeof(char));
			pname = px_d_path(file, name, PATH_MAX);
			
			if (pname)
			{
				unsigned long name_offset;

				name_offset = get_filename_offset(pname);

				module_load_notif(pname, name_offset, current->tgid,
						          ret, len, 0, lsc);
			}
			
		}
		//rcu_read_unlock();
	}
	
	return ret;
}
asmlinkage int px_sys_mmap(struct mmap_arg_struct *arg)
{
	int ret = 0;
	struct mmap_arg_struct tmp;
	struct file *file;
	unsigned long long lsc;
	char * name = NULL;

	lsc = get_sample_count();

	if (copy_from_user(&tmp, arg, sizeof(tmp)) != 0)
	{
		return -EFAULT;
	}

 	ret = px_original_sys_mmap(arg);

	if (gb_enable_os_hooks && (!IS_ERR((void*)ret)) && (tmp.prot & PROT_EXEC) && !(tmp.flags & MAP_ANONYMOUS))
	{
		//rcu_read_lock();
		if ((file = fcheck(tmp.fd)) != NULL)
		{
			char *filename;
			//memset(name, 0, PATH_MAX * sizeof(char));
			name = kzalloc(PATH_MAX, GFP_ATOMIC);

			if (name == NULL)
			{
				return ret;
			}

			filename = px_d_path(file, name, PATH_MAX);
			if (filename)
			{
				unsigned long name_offset;

				name_offset = get_filename_offset(filename);

				module_load_notif(filename, name_offset, current->tgid,
					              ret, tmp.len,
					              0, lsc);
			}

			kfree(name);
		}

		//rcu_read_unlock();
	}

	return ret;
}
/*
 * static enumeration kernel modules
 */
static void static_enum_kernel_modules(void)
{
	unsigned int text_offset;

#ifdef CONFIG_TEXT_OFFSET
	text_offset = CONFIG_TEXT_OFFSET;
#else
	/*
	 * PAGE_OFFSET is the load address of kernel
	 *
	 * 0x8000 is the offset to the text section of kernel
	 * which is defined as TEXT_OFFSET in arch/arm/Makefile
	 */
	text_offset = 0x8000;
#endif

	module_load_notif("swapper", 0, 0,
				      LINUX_APP_BASE_LOW, 0,
				      MODULE_FLAG_1ST, 0);

	module_load_notif("vmlinux", 0, 0,
	                  PAGE_OFFSET + text_offset, 4096 * 2 * 1024,
	                  MODULE_FLAG_GLOBAL, 0);
}
/*
 * enumerate all modules for process
 * pid: process id
 * lsc: load sample count
 */
static void enum_modules_for_process(pid_t pid, unsigned long long lsc, const char * proc_name)
{
	bool find = false;
	char *filename;
	unsigned long name_offset;
	unsigned int options;
	struct task_struct *task;
	struct vm_area_struct *mmap;
	struct mm_struct *mm;
	char * name = NULL;
	char * buffer = NULL;

	task = px_find_task_by_pid(pid);

	if (task == NULL)
		goto ret;

	name = kzalloc(PATH_MAX, GFP_ATOMIC);

	if (name == NULL)
		goto ret;

	buffer = kzalloc(PATH_MAX, GFP_ATOMIC);

	if (buffer == NULL)
		goto ret;

	mm = get_task_mm(task);
	if (mm != NULL)
	{
		down_read(&mm->mmap_sem);

		for (mmap = mm->mmap; mmap; mmap = mmap->vm_next)
		{
			if (is_valid_module(mmap))
			{
				memset(name, 0, (PATH_MAX) * sizeof(char));
				filename = px_d_path(mmap->vm_file, name, PATH_MAX);

				if (filename != NULL)
				{
					options = 0;

					if (find == false)
					{
						options |= MODULE_FLAG_1ST;
						find = true;

						if (proc_name != NULL)
						{
							/* for the first module (the executable image), if the process name is specified, use the specified one */
							strcpy(buffer, proc_name);
						}
						else
						{
							/*
							 * for the first module (the executable image), we need to get the process name again
							 * because it may be modified by changing the argv[0]
							 */
							if (get_proc_name(task, buffer) == 0)
							{
								/* failed to get the process name, use the orignal module name */
								strcpy(buffer, filename);
							}
						}

						/* save the orignal process name for checking if it will be updated later */
						notify_new_loaded_process(task, buffer);
					}
					else
					{
						memset(buffer, 0, sizeof(PATH_MAX));
						strcpy(buffer, filename);
					}

					name_offset = get_filename_offset(buffer);

					module_load_notif(buffer, name_offset, pid,
						              mmap->vm_start, mmap->vm_end - mmap->vm_start,
						              options, lsc);
				}
			}
		}

		up_read(&mm->mmap_sem);
		mmput(mm);
	}


	if (find == false)
	{
		module_load_notif(task->comm, 0, pid,
				  LINUX_APP_BASE_LOW, 0,//mmap->vm_end - mmap->vm_start,
				  MODULE_FLAG_1ST, lsc);
	}

ret:
	if (name != NULL)
		kfree(name);

	if (buffer != NULL)
		kfree(buffer);

	return;
}