This commit is contained in:
Ciro Santilli
2016-07-30 09:38:06 +01:00
commit d45ceace5d
9 changed files with 318 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
*.cmd
*.ko
*.mod.c
*.o
*.out
*.tmp
.tmp_versions
Module.symvers
modules.order

7
README.md Normal file
View File

@@ -0,0 +1,7 @@
# Linux Kernel Module Cheat
1. [Introduction](introduction.md)
1. [Build](build.md)
1. [kmod](kmod.md)
1. Examples
1. [Host](host/)

15
build.md Normal file
View File

@@ -0,0 +1,15 @@
# Build
The module building system.
Kernel modules are built using a makefile located at:
/lib/modules/$(uname -r)/build
## includes
Header files come from the same directory as the makefile: `/lib/modules/$(uname -r)/build`.
TODO how is that different from: `/usr/src/linux-headers-$(uname -r)/` ?
Those come directly from the kernel source tree.

33
host/Makefile Normal file
View File

@@ -0,0 +1,33 @@
OUT_EXT := .ko
OBJ_EXT := .o
RUN := hello
RUN_EXT := $(RUN)$(OUT_EXT)
obj-m += hello.o
ccflags-y := -Wno-declaration-after-statement -std=gnu99
.PHONY: clean ins log rm run my-ins
all: $(RUN_EXT) ins_rm_mod.out
hello.ko: hello.c
make -C /lib/modules/$(shell uname -r)/build M="$(PWD)" modules
clean:
make -C /lib/modules/$(shell uname -r)/build M="$(PWD)" clean
rm -f *.out
ins: all
sudo insmod '$(RUN_EXT)'
log:
dmesg
rm:
if lsmod | grep -Eq '^$(RUN) '; then sudo rmmod '$(RUN_EXT)'; fi
ins_rm_mod.out: ins_rm_mod.c
gcc -Wall -std=gnu99 -o '$@' '$<'
ins_rm_run: ins_rm_mod.out $(RUN_EXT)
sudo ./ins_rm_mod.out

34
host/README.md Normal file
View File

@@ -0,0 +1,34 @@
# Host
Simple things that can be demonstrated by inserting a module into the currently running host. Tested on Ubuntu 16.04.
1. [hello](hello.c)
1. [ins_rm_mod.c](ins_rm_mod.c)
## Rationale
This method easier to setup, but it is not recommended for development, as:
- it may break your system.
- you can't control which kernel version to use
Use VMs instead.
## Usage
We only use it for super simple examples.
Build, insert and remove a hello world module:
make ins
make rm
make log
The last lines of the log should contain:
init_module
cleanup_module
Insert and remove a module from a C program:
make ins_rm_run

13
host/hello.c Normal file
View File

@@ -0,0 +1,13 @@
#include <linux/module.h>
#include <linux/kernel.h>
int init_module(void)
{
printk(KERN_INFO "init_module\n");
return 0;
}
void cleanup_module(void)
{
printk(KERN_INFO "cleanup_module\n");
}

36
host/ins_rm_mod.c Normal file
View File

@@ -0,0 +1,36 @@
/*
http://stackoverflow.com/questions/5947286/how-can-linux-kernel-modules-be-loaded-from-c-code/38606527#38606527
*/
#define _GNU_SOURCE
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
#define delete_module(name, flags) syscall(__NR_delete_module, name, flags)
int main(void) {
int fd = open("hello.ko", O_RDONLY);
struct stat st;
fstat(fd, &st);
size_t image_size = st.st_size;
void *image = malloc(image_size);
read(fd, image, image_size);
close(fd);
if (init_module(image, image_size, "") != 0) {
perror("init_module");
return EXIT_FAILURE;
}
free(image);
if (delete_module("hello", O_NONBLOCK) != 0) {
perror("delete_modul");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

66
introduction.md Normal file
View File

@@ -0,0 +1,66 @@
# Introduction
There are things which are hard to do from regular user programs such as directly talking to hardware.
Some operations can be done via system calls, but if you want flexibility and speed, using the kernel ring is fundamental
However:
- it would be very complicated to recompile the kernel and reboot every time you make some modification
- the kernel would be huge if it were to support all possible hardware
Modules overcome those two problems exactly because they can be loaded into the kernel *while it is running* and use symbols that the kernel chooses to export TODO which
It then runs in the same address space as the kernel and with the same permissions as the kernel (basically do anything)
Compiled modules are special object files that have a `.ko` extension instead of `.o` they also contain module specific metadata
Device drivers (programs that enables the computer to talk to hardware) are one specific type of kernel modules
Two devices can map to the same hardware!
## Stable kernel interface
Kernel modules can use any internal interface of the kernel, although some are more visible than others.
But there is no stable kernel API for modules: if you don't add your driver to the kernel tree, it can break any time: <https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/stable_api_nonsense.txt?id=v4.0>
## Configuration files
If file it gets read, if dir, all files in dir get read:
sudo ls /etc/modprobe.d
sudo ls /etc/modprobe.conf
Modules loaded at boot:
sudo cat /etc/modules
## Hardware communication
Talking to hardware always comes down to writing bytes in specific registers at a given memory addresses.
Some processors implement a single address space for memory and other hardware devices, and others do not.
However, since x86 is the most popular and it separates address spaces, every architecture must at least mimic this separation.
On x86, the following specialized instructions exist for port IO:
- `IN`: Read from a port
- `OUT`: Write to a port
- `INS/INSB`: Input string from port/Input byte string from port
- `INS/INSW`: Input string from port/Input word string from port
- `INS/INSD`: Input string from port/Input `doubleword` string from port
- `OUTS/OUTSB`: Output string to port/Output byte string to port
- `OUTS/OUTSW`: Output string to port/Output word string to port
- `OUTS/OUTSD`: Output string to port/Output `doubleword` string to port
However, you should avoid using those instructions directly in your device driver code since Linux functions abstract over multiple architectures (when possible) making your code more portable.
Those instructions cannot be used from an user space program since the kernel prevents those from accessing hardware directly.
The memory space for non-memory locations is called I/O ports or I/O space.
To use a port, you must first reserve it. To see who reserved what:
sudo cat /proc/ioports

105
kmod.md Normal file
View File

@@ -0,0 +1,105 @@
# kmod
Implements `lsmod`, `insmod`, `rmmod`, and other tools.
The other tools are just symlinks to it.
## module-init-tools
Name of a predecessor set of tools.
## package version
From any of the commands, `--version`:
modinfo --version
Package that provides utilities
## lsmod
List loaded kernel modules.
Info is taken from `/proc/modules`
lsmod
Sample output:
cfg80211 175574 2 rtlwifi,mac80211
^^^^^^^^ ^^^^^^ ^ ^^^^^^^,^^^^^^^^
1 2 3 4 5
1. Name.
2. Size.
3. Number of running instances.
If negative, TODO
4. Depends on 1.
5. Depends on 2.
To get more info:
cat /proc/modules
Also contains two more columns:
- status: Live, Loading or Unloading
- memory offset: 0x129b0000
## modinfo
Get info about a module by filename or by module name:
modinfo ./a.ko
modinfo a
TODO must take a `.ko` file?
## insmod
sudo insmod hello.ko
Loads the module.
Does not check for dependencies.
## rmmod
Remove a module. Takes either the module name or the `.ko` file:
sudo rmmod hello
sudo rmmod ./hello.ko
## modprobe
List available modules relative path to `/lib/modules/VERSION/`:
sudo modprobe -l
Load the module:
sudo modprobe $m
Checks for dependencies.
Load module under different name to avoid conflicts:
sudo modprobe vmhgfs -o vm_hgfs
Remove module:
sudo modprobe -r $m
Check if dependencies are OK:
sudo depmod -a
Get info about given `.ko` module file:
m=a
sudo rmmod $m