通过vmap
对映射内核模块的内存有任何约束吗?在我的系统上,我编写了一个简单的KMOD,它映射了一个内核函数(printk)和一个模块函数(printx),并检查映射是否等于。结果显示映射模块的printx
存在问题 - 映射和函数的代码不相等。有人可以解释一下我做错了什么吗?这是代码:
// vmap-test.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
int printx(void)
{
return 0;
}
void vmap_action(unsigned long address)
{
void * mapping;
struct page * page;
page = virt_to_page(address);
mapping = vmap(&page, 1, VM_MAP, PAGE_KERNEL);
if (mapping) {
int i = 0;
void * data = mapping + offset_in_page(address);
printk("VMAP: src %p -> dst %p\n", (void *)address, data);
for (i=0; i<16; i++) {
printk("%.02x %.02x\n", ((u8 *)address)[i], ((u8 *)data)[i]);
}
vunmap(mapping);
}
}
int my_module_init(void)
{
vmap_action((unsigned long)printk);
vmap_action((unsigned long)printx);
return 0;
}
module_init(my_module_init);
void my_module_exit(void)
{
}
module_exit(my_module_exit);
dmesg
的结果是:
VMAP(printk的)
[88107.398146] VMAP: src ffffffff813dfaef -> dst ffffc900117ddaef
[88107.398148] 55 55
[88107.398149] 48 48
[88107.398150] 89 89
[88107.398151] e5 e5
[88107.398152] 48 48
[88107.398153] 83 83
[88107.398154] ec ec
[88107.398155] 50 50
[88107.398156] 0f 0f
[88107.398156] 1f 1f
[88107.398157] 44 44
[88107.398158] 00 00
[88107.398159] 00 00
[88107.398160] 48 48
[88107.398161] 8d 8d
[88107.398162] 45 45
VMAP(printx)
[88107.398164] VMAP: src ffffffffa009a010 -> dst ffffc900117fd010
[88107.398166] 55 35
[88107.398167] 48 fb
[88107.398168] 89 53
[88107.398169] e5 d5
[88107.398170] 0f f7
[88107.398171] 1f 97
[88107.398171] 44 ee
[88107.398172] 00 fd
[88107.398173] 00 d5
[88107.398174] 31 2d
[88107.398175] c0 bf
[88107.398176] 5d f6
[88107.398177] c3 2d
[88107.398178] 0f bd
[88107.398179] 1f b7
[88107.398180] 00 99
欢迎任何建议:)谢谢。
答案 0 :(得分:0)
好吧,我发现在KSplice
项目中实现了类似的功能,现在就是:
/*
* map_writable creates a shadow page mapping of the range
* [addr, addr + len) so that we can write to code mapped read-only.
*
* It is similar to a generalized version of x86's text_poke. But
* because one cannot use vmalloc/vfree() inside stop_machine, we use
* map_writable to map the pages before stop_machine, then use the
* mapping inside stop_machine, and unmap the pages afterwards.
*/
static void *map_writable(void *addr, size_t len)
{
void *vaddr;
int nr_pages = DIV_ROUND_UP(offset_in_page(addr) + len, PAGE_SIZE);
struct page **pages = kmalloc(nr_pages * sizeof(*pages), GFP_KERNEL);
void *page_addr = (void *)((unsigned long)addr & PAGE_MASK);
int i;
if (pages == NULL)
return NULL;
for (i = 0; i < nr_pages; i++) {
if (__module_address((unsigned long)page_addr) == NULL) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) || !defined(CONFIG_X86_64)
pages[i] = virt_to_page(page_addr);
#else /* LINUX_VERSION_CODE < && CONFIG_X86_64 */
/* e3ebadd95cb621e2c7436f3d3646447ac9d5c16d was after 2.6.21
* This works around a broken virt_to_page() from the RHEL 5 backport
* of x86-64 relocatable kernel support.
*/
pages[i] =
pfn_to_page(__pa_symbol(page_addr) >> PAGE_SHIFT);
#endif /* LINUX_VERSION_CODE || !CONFIG_X86_64 */
WARN_ON(!PageReserved(pages[i]));
} else {
pages[i] = vmalloc_to_page(addr);
}
if (pages[i] == NULL) {
kfree(pages);
return NULL;
}
page_addr += PAGE_SIZE;
}
vaddr = vmap(pages, nr_pages, VM_MAP, PAGE_KERNEL);
kfree(pages);
if (vaddr == NULL)
return NULL;
return vaddr + offset_in_page(addr);
}
因此,对内核和模块的内存有不同的处理方式。如果页面不属于任何模块,则使用vmalloc_to_page
而不是virt_to_phys
。我会检查它是否有帮助并稍后发布结果。