module的认识和写法

 当我们使用 EXPORT_SYMBOL 把一些 function 或 variable export 出来之后,我们使用 ksyma -a 去看一些结果。我们发现 EXPORT_SYMBOL(full) 的确是把 full export出来了 :

c8822200 full [my_module]
c01b8e08 pci_find_slot_R454463b5
. . .

但是,结果怎幺跟我们想象中的不太一样,照理说,应该是 full_Rxxxxxx 之类的东西才对啊,怎幺才出现 full 而已呢 ? 奇怪,问题在那里呢 ?

其实,问题就在于我们没有对本身的 module 所 export 出来的 function 或 variable 的名字做 encode。想想,如果在 module 的开头。我们加入一行

#define full full_Rxxxxxx

之后,我们再重新 compile module 一次,载到 kernel 之后,就可以发现 ksyms -a 显示的是

c8822200 full_Rxxxxxx [my_module]
c01b8e08 pci_find_slot_R454463b5
. . . . .

了。那是不是说,我们要去对每一个 export 出来的 variable 和 function 做 define 的动作呢 ? 当然不是啰。记得吗,前头我们讲去使用 kernel export 的 function 时,由于 include 了一些 .ver 的档案,以致于我们不用再做 define 的动作。现在,我们也要利用 .ver 的档案来帮我们,使我们 module export 出来的 function 也可以自动加入 kernel version 的 information。也就是变成 full_Rxxxxxx 之类的东西。

Linux 里提供了一个 command,叫 genksyms,就是用来帮我们产生这种 .ver 的档案的。它会从 stdin 里读取 source code,然后检查 source code 里是否有 export 的 variable 或 function。如果有,它就会自动为每个 export 出来的东西产生一些 define。这些 define 就是我们之前说的。等我们有了这些 define 之后,只要在我们的 module 里加入这些 define,那 export 出来的 function 或 variable 就会变成上面那个样子。

假设我们的程序都放在一个叫 main.c 的档案里,我们可以使用下列的方式产生这些 define。

gcc -E -D__GENKSYMS__ main.c | genksyms -k 2.2.1 > main.ver

gcc 的 -E 参数是指将 preprocessing 的结果 show 出来。也就是说将它 include 的档案,一些 define 的结果都展开。-D__GENKSYMS__ 是一定要的。如果没有定义这个 constant,你将不会看到任何的结果。用一个管线是因为 genksyms 是从 stdin 读资料的,所以,经由管线将 gcc 的结果传给 genksyms。-k 2.2.1 是指目前使用的 kernel 版本是 2.2.1,如果你的 kernel 版本不一样,必须指定你的 kernel 的版本。产生的 define 将会被放到 main.ver 里。产生完 main.ver 档之后,在 main.c 里将它 include 进来,那一切就 OK 了。有件事要告诉各位的是,使用这个方式产生的 module,其 export 出来的东西会经由 main.ver 的 define 改头换面。所以如果你要让别人使用,那你必须将 main.ver 公开,不然,别人就没办法使用你 export 出来的东西了。

讲了这幺多,相信各位应该都已经比较清楚 module 在 kernel 中是怎幺样一回事,也应该知道为什幺有时候 module 会无法加载了。除此之外,各位应该还知道如何使自己 module export 出来的东西也具有 kernel version 的 information。

接下来,要跟各位讲的就是,如何写一个 module 了。其实,写一个 module 很简单的。如果你了解我上面所说的东西。那我再讲一次,再用个例子,相信大家就都会了。要写一个 module,必须要提供两个 function。这两个 function 是给 insmod 和 rmmod 使用的。它们分别是 init_module(),以及 cleanup_module()。

int init_module();
void cleanup_module();

相信大家都知道在 Linux 里可以使用 insmod 这个 command 来将某个 module 加载。比方说,我有一个 module 叫 hello.o,那使用 insmod hello.o 就可以将 hello 这个 module 载到 kernel 里。观察 /etc/modules 应该就可以看到 hello 这个 module 的名字。如果要将 hello 这个 module 移除,则只要使用 rmmod hello 就可以了。insmod 在加载 module 之后,就会去呼叫 module 所提供的 init_module()。如果传回 0 表示成功,那 module 就会被加载。如果失败,那加载的动作就会失败。一般来讲,我们在 init_module() 做的事都是一些初始化的工作。比方说,你的 module 需要一块内存,那你就可以在 init_module() 做 kmalloc 的动作。想当然尔。cleanup_module() 就是在 module 要移除的时候做的事。做的事一般来讲就是一些善后的工作,比方像把之前 kmalloc 的内存 free 掉。

由于 module 是载到 kernel 使用的,所以,可能别的 module 会使用你的 module,甚至某些 process 也会使用到你的 module,为了避免 module 还有人使用时就被移除,每个 module 都有一个 use count。用来记录目前有多少个 process 或 module 正在使用这个 module。当 module 的 use count 不等于 0 时,module 是不会被移除掉的。也就是说,当 module 的 use count 不等于 0 时,cleanup_module() 是不会被呼叫的。

在此,我要介绍三个 macro,是跟 module 的 use count 有关的。

MOD_INC_USE_COUNT
MOD_DEC_USE_COUNT
MOD_IN_USE

MOD_INC_USE_COUNT 是用来增加 module 的 use count,而 MOD_DEC_USE_COUNT 是用来减少 module 的 use count。至于 MOD_IN_USE 则是用来检查目前这个 module 是不是被使用中。也就是检查 use count 是否为 0。module 的 use count 必须由写 module 的人自己来 maintain。系统并不会自动为你把 use count 加一或减一。一切都得由自己控制。下面有一个例子,但是,并不会介绍这三个 macro 的使用方法。将来如果有机会,我再来介绍这三个 macro 的用法。

这个例子很简单。其实只是示范如何使用 init_module() 以及 cleanup_module() 来写一个 module。当然,这两个 function 只是构成 module 的基本条件罢了。至于 module 里要提供的功能则是看各人的需要。

main.c
#define MODULE
#include <linux/module.h>
#include <asm/uaccess.h>
int full;
EXPORT_SYMBOL(full); /* 将 full export 出去 */
int init_module( void )
{
printk( "<5> Module is loaded\n" );
return 0;
}
void cleanup_module( void )
{
printk( "<5> Module is unloaded\n" );
}

关于 printk 是这样子的,它是 kernel 所提供的一个打印讯息的 function。kernel 有 export 这个 function。所以你可以自由的使用它。它的用法跟 printf 几乎一模一样。唯独讯息的开头是 <5>,其实,不见得这三个字符啦。也可以是 <4>,<3>,<7> 等等的东西。这是代表这个讯息的 prioirty 或 level。<5> 表示的是跟 KERNEL 有关的讯息。

main.ver:

利用 genksyms 产生出来的。

gcc -E -D__GENKSYMS__ main.c | genksyms -k 2.2.1 > main.ver

接下来,就是要把 main.c compile 成 main.o

gcc -D__KERNEL__ -DMODVERSIONS -DEXPORT_SYMTAB -c \
-I/usr/src/linux/include/linux -include \
/usr/src/linux/include/linux/modversions.h \
-include ./main.ver main.c

好了。main.o 已经成功的 compile 出来了,现在下一个 command,

insmod main.o

检查看 /proc/modules 里是否有 main 这个 module。如果有,表示 main 这个 module 已经载到 kernel 了。再下一个指令,看看 full export 出去的结果。

ksyms

结果显示

Address Symbol Defined by
c40220e0 full_R355b84b2 [main]
c401d04c ne_probe [ne]
c401a04c ei_open [8390]
c401a094 ei_close [8390]
c401a504 ei_interrupt [8390]
c401af1c ethdev_init [8390]
c401af80 NS8390_init [8390]

可以看到 full_R355b84b2,表示,我们已经成功的将 full 的名字加上 kernel version 的 information 了。当我们不需要这个 module 时,我们就可以下一个 command,

rmmod main

这样 main 就会被移除掉了。再检查看看 /proc/modules 就可以发现 main 那一行不见了。各位现在可以看一下 /var/log/message 这个档案,应该可以发现以两行

Apr 12 14:19:05 host kernel: Module is loaded
Apr 12 14:39:29 host kernel: Module is unloaded

这两行就是 printk 印出来的。

关于 module 的介绍已经到此告一段落了。其实,使用 module 实在是很简单的一件事。对于要发展 driver 或是增加 kernel 某些新功能的人来讲,用 module 不啻为一个方便的方式。希望这篇文章对各位能有所帮助。
<think>好的,我现在需要帮助用户了解如何使用VCS编译加密的源代码。首先,我要回忆一下之前提供的引用内容,特别是引用[1]中提到的加密方法。用户可能想知道具体的步骤注意事项。 根据引用[1],VCS支持通过编译器指令来加密代码,比如在Verilog中使用`protect128``endprotect128`包裹需要加密的代码段。这可能意味着用户需要在源代码中插入这些指令,然后使用VCS进行编译。不过,用户可能不清楚具体操作步骤,比如是否需要额外的编译选项或者配置。 接下来,我需要确认VCS的编译选项是否有特别的要求。引用[2][5]提到VCS有编译选项,比如使用`-cm`或者`-debug`等,但不确定是否与加密相关。不过引用[1]提到加密后的代码只能在VCS中使用,所以可能不需要额外选项,只需正确放置保护指令即可。但需要确保VCS版本支持AES128加密算法。 另外,用户可能遇到的问题包括加密指令的格式是否正确,比如VerilogVHDL的不同注释风格。例如,Verilog使用`//`,而VHDL使用`--`,所以必须正确书写保护指令,否则加密可能失败。 还需要考虑加密后的代码在仿真时的行为,比如是否会影响调试功能。引用[5]提到生成32-bit可执行文件,但不确定是否与加密相关,可能需要忽略这部分,除非用户有特定环境需求。 最后,用户可能需要示例代码来说明如何插入保护指令。比如在Verilog文件中,将核心代码用`protect128``endprotect128`包裹,然后正常编译。同时,提醒用户注意加密后的代码不可逆,需备份原始文件。 总结步骤:1. 插入保护指令;2. 使用VCS编译命令;3. 处理可能的问题。还需要验证这些步骤是否符合引用中的信息,确保准确性。</think>### 如何使用VCS编译加密的源代码 #### 步骤说明 1. **在源代码中添加加密指令** - **Verilog**:使用`protect128``endprotect128`包裹需要加密的代码段,例如: ```verilog // protect128 module counter (input clk, output reg [3:0] count); always @(posedge clk) begin count <= count + 1; end endmodule // endprotect128 ``` - **VHDL**:使用`--protect128``--endprotect128`包裹代码段,例如: ```vhdl -- protect128 entity counter is port (clk: in bit; count: out integer); end entity; -- endprotect128 ``` - **注意**:加密指令需严格遵循格式,否则可能导致编译失败[^1]。 2. **使用VCS编译加密代码** - 执行常规编译命令,无需额外加密选项。例如: ```bash vcs -full64 -sverilog counter.v ``` - 加密后的代码会被VCS自动识别并处理,生成可执行仿真文件[^2]。 3. **验证加密效果** - 加密后的代码在仿真时功能正常,但直接查看生成的中间文件(如`simv`)将无法获取原始逻辑[^3]。 #### 注意事项 - **兼容性**:加密后的代码仅支持在VCS环境中使用,其他工具(如Xilinx Vivado)无法直接解析[^1]。 - **备份源码**:加密过程不可逆,需保留原始未加密文件以便后续修改[^1]。 - **调试限制**:加密代码可能影响调试功能(如波形查看),建议在加密前完成核心验证[^5]。 #### 示例完整流程 ```bash # 1. 编写加密后的counter.v(如步骤1所示) # 2. 编译 vcs -full64 -sverilog counter.v # 3. 运行仿真 ./simv ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值