Analysis of Linux driver kernel loading module process

Publisher:upsilon30Latest update time:2024-08-20 Source: cnblogs Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

modlen -= (size_t)ms.signer_len + ms.key_id_len;


*_modlen = modlen; //Return the real size of the module after removing the signature verification information

//Move the file pointer to the signature information

sig = mod + modlen;


/* For the moment, only support RSA and X.509 identifiers */

if (ms.algo != PKEY_ALGO_RSA || ms.id_type != PKEY_ID_X509)

return -ENOPKG;


if (ms.hash >= PKEY_HASH__LAST || !pkey_hash_algo[ms.hash])

return -ENOPKG;


//Read the signer name and key identifier from the file sig and return the key

key = request_asymmetric_key(sig, ms.signer_len,sig + ms.signer_len, ms.key_id_len);

if (IS_ERR(key))

return PTR_ERR(key);


//Digest the module contents.

pks = mod_make_digest(ms.hash, mod, modlen);

if (IS_ERR(pks)) {

ret = PTR_ERR(pks);

goto error_put_key;

}


//Extract an MPI array from the signature data. This represents the actual signature. Each raw MPI is prefaced by a BE 2-byte value indicating the size of the MPI in bytes.RSA signatures only have one MPI, so currently we only read one.

ret = mod_extract_mpi_array(pks, sig + ms.signer_len + ms.key_id_len,sig_len);

if (ret < 0)

goto error_free_pks;


//Initiate the use of an asymmetric key to verify a signature

//key: The asymmetric key to verify against

//sig: The signature to check

ret = verify_signature(key, pks);

pr_devel("verify_signature() = %dn", ret);


error_free_pks:

mpi_free(pks->rsa.s);

kfree(pks);

error_put_key:

key_put(key);

pr_devel("<==%s() = %dn", __func__, ret);

return ret;

}


static int elf_header_check(struct load_info *info)

{

if (info->len < sizeof(*(info->hdr)))

return -ENOEXEC;


// Check the elf file header, file type (.ko file must be a relocatable file), architecture and section size to see if they are set correctly

if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) != 0|| info->hdr->e_type != ET_REL

|| !elf_check_arch(info->hdr)|| info->hdr->e_shentsize != sizeof(Elf_Shdr))

return -ENOEXEC;


// Check the offset address of the section

if (info->hdr->e_shoff >= info->len

|| (info->hdr->e_shnum * sizeof(Elf_Shdr) >info->len - info->hdr->e_shoff))

return -ENOEXEC;


return 0;

}


static struct module *layout_and_allocate(struct load_info *info, int flags)

{

struct module *mod;

int err;


//Set the info structure, check the crc value of module_layout, and return a struct module structure stored in the .gnu.linkonce.this_module section. The starting address of the module is exactly the starting address of the .gnu.linkonce.this_module section.

mod = setup_load_info(info, flags);

if (IS_ERR(mod))

return mod;


// Check the information in the .modinfo section, including the version magic

err = check_modinfo(mod, info, flags);

if (err)

return ERR_PTR(err);


// Do nothing, return 0

err = module_frob_arch_sections(info->hdr, info->sechdrs,info->secstrings, mod);

if (err < 0)

return ERR_PTR(err);


//Remove the SHF_ALLOC flag

info->sechdrs[info->index.pcpu].sh_flags &= ~(unsigned long)SHF_ALLOC;


//Only if the SHF_ALLOC flag is set in the section header will it be stored in memory, and the memory is divided into init part and core part

layout_sections(mod, info);


// Arrange the position for the symbol section and its related string table, and update the related size

layout_symtab(mod, info);


// Allocate memory for the SHF_ALLOC section marked in the temporary image pointed to by mod, copy it from the temporary image to the final location, and modify the starting address of the section

err = move_module(mod, info);

if (err)

return ERR_PTR(err);


//Because the module has been moved to the final memory location, the starting addresses of each section have changed, and the address pointed to by mod is invalid, so the starting address of the new ".gnu.linkonce.this_module" section (pointing to a module object) is assigned to the mod object again.

mod = (void *)info->sechdrs[info->index.mod].sh_addr;


//Scan to check for memory leaks???

kmemleak_load_module(mod, info);

return mod;

}


static struct module *setup_load_info(struct load_info *info)

{

unsigned int i;

int err;

struct module *mod;


//Find the start address of the section header table

info->sechdrs = (void *)info->hdr + info->hdr->e_shoff;

//Find the starting address of the string section of the section name

info->secstrings = (void *)info->hdr + info->sechdrs[info->hdr->e_shstrndx].sh_offset;


//According to the virtual address copied to the kernel space memory in the elf format .ko file as the base address (ie (void *)info->hdr), rewrite the position where the first byte of the section should be located in the memory image

err = rewrite_section_headers(info);

if (err)

return ERR_PTR(err);


/*Traverse all sections to find the location of the symbol table (the only segment of type SHT_SYMTAB) and the associated symbol string table,

The sh_link of the former is the segment index of the latter*/

for (i = 1; i < info->hdr->e_shnum; i++) {

if (info->sechdrs[i].sh_type == SHT_SYMTAB){

//Find the symbol table section.symtab index

info->index.sym = i;

//Find the symbol table string section index, i.e. the .strtab section

info->index.str = info->sechdrs[i].sh_link;

//Start address of string section content

info->strtab = (char *)info->hdr + info->sechdrs[info->index.str].sh_offset;

break;

}

}


//In the .gnu.linkonce.this_module section, there is an instance of struct module. Most of the members of this structure are NULL. The compiler only initializes a few members such as name, init, exit and arch.

info->index.mod = find_sec(info, ".gnu.linkonce.this_module");

if (!info->index.mod) {

printk(KERN_WARNING "No module found in objectn");

return ERR_PTR(-ENOEXEC);

}


// mod points to an instance of struct module, which provides the module name and pointers to initialization and cleanup functions, but other members are still initialized to NULL or 0. The address of the module is temporarily set to the address given in the section of the temporary image, and the address of the module will be corrected later.

mod = (void *)info->sechdrs[info->index.mod].sh_addr;


if (info->index.sym == 0) {

printk(KERN_WARNING "%s: module has no symbols (stripped?)n", mod->name);

return ERR_PTR(-ENOEXEC);

}


//Find the ".data..percpu" section index number

info->index.pcpu = find_pcpusec(info);


// Check the crc value of the "module_layout" symbol in the module. If it fails, print the famous module loading error "Exec format error"

if (!check_modstruct_version(info->sechdrs, info->index.vers, mod))

return ERR_PTR(-ENOEXEC);


return mod;

}


static int rewrite_section_headers(struct load_info *info, int flags)

{

unsigned int i;

info->sechdrs[0].sh_addr = 0; //The address of section 0 is 0, indicating that this section does not appear in the memory image

for (i = 1; i < info->hdr->e_shnum; i++) {

Elf_Shdr *shdr = &info->sechdrs[i];

//Judge the section size

if (shdr->sh_type != SHT_NOBITS && info->len < shdr->sh_offset + shdr->sh_size) {

printk(KERN_ERR "Module len %lu truncatedn",info->len);

return -ENOEXEC;

}


//Reset the starting address of the section in the kernel space based on the base address of the elf file copied to the kernel space. shdr->sh_addr indicates the location of the first byte of the section in the memory image.

shdr->sh_addr = (size_t)info->hdr + shdr->sh_offset;

#ifndef CONFIG_MODULE_UNLOAD

/* Don't load .exit sections */

if (strstarts(info->secstrings+shdr->sh_name, ".exit"))

shdr->sh_flags &= ~(unsigned long)SHF_ALLOC;

#endif

}


//Find the __versions and .modinfo sections and record the section index

if (flags & MODULE_INIT_IGNORE_MODVERSIONS)

info->index.vers = 0;

else

info->index.vers = find_sec(info, "__versions");

info->index.info = find_sec(info, ".modinfo");

//These two sections clear the SHF_ALLOC flag, indicating that this section does not occupy memory during process execution

info->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC;

info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC;

return 0;

}


static inline int check_modstruct_version(Elf_Shdr *sechdrs,unsigned int versindex,struct module *mod)

{

const unsigned long *crc;


//Find the "module_layout" symbol in the system kernel and return the crc value of the symbol

if (!find_symbol(VMLINUX_SYMBOL_STR(module_layout), NULL,&crc, true, false))

BUG();


//Whether the crc value of the "module_layout" symbol in the system is consistent with the crc value of the "module_layout" symbol in the module

return check_version(sechdrs, versindex,VMLINUX_SYMBOL_STR(module_layout), mod, crc,NULL);

}


//Find the structure used by the symbol

struct find_symbol_arg {

//enter

const char *name; //Symbol name to be searched

bool gplok; //If true, it means the kernel module supports the GPL license

bool warn; //Warning message sign


// Output

struct module *owner; //The module to which the symbol belongs

const unsigned long *crc; //The crc value of the symbol

const struct kernel_symbol *sym; //symbol structure

};


const struct kernel_symbol *find_symbol(const char *name,struct module **owner,const unsigned long **crc,bool gplok,bool warn)

{

//Set the search symbol parameter structure

struct find_symbol_arg fsa;

fsa.name = name;

[1] [2] [3] [4] [5] [6] [7] [8]
Reference address:Analysis of Linux driver kernel loading module process

Previous article:Linux driver to create a hello module
Next article:S3C2440 Watchdog Timer Principle

Recommended ReadingLatest update time:2024-11-16 20:28

Linux driver: s3c2410_ts/s3c2440_ts module loading process
Preface By analyzing the s3c2410_ts/s3c2440_ts module loading process, the bus-device-driver model and input subsystem framework in the Linux driver are analyzed . Main process analysis diagram s3c2440_ts main process analysis system initialization MACHINE_START(SMDK2410,"SMDK2410") MACHINE_START(SMDK2410, "SMDK241
[Microcontroller]
[Linux bottom layer] U-boot ksz8081 network driver debugging
Micrel is an excellent PHY chip. For more information about the chip, please refer to: ksz8081 datasheet interpretation System version: Ubuntu18.04-64 Compiler version: gcc version 7.4.0 (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1) uboot version: 2018.07-linux4sam_6.0 Board model: at91sama5d3x-xplained MCU mode
[Microcontroller]
LCD device driver in Linux (1) - framebuffer
1. framebuffer Framebuffer is an interface provided by Linux system for display devices. It abstracts the display buffer, shields the underlying differences of image hardware, and allows upper-level applications to directly read and write the display buffer in graphics mode. Users do not need to care about the speci
[Microcontroller]
LCD device driver in Linux (1) - framebuffer
09-S3C2440 driver learning (Part 3) Embedded Linux platform bus driver and separation and layering driver framework
Introduction Platform: The kernel has IIC bus, PCI bus, serial bus, SPI bus, CAN bus, single bus, etc. Some devices can be mounted on these buses, and then the devices and drivers are matched through bus matching. However, some devices do not belong to these buses, so a virtual bus, that is, Platform bus, is introdu
[Microcontroller]
09-S3C2440 driver learning (Part 3) Embedded Linux platform bus driver and separation and layering driver framework
S3C6410 RTC driver in Linux (3)
The previous article talked about the s3c_rtc_probe function, but because it was too long, I didn’t finish it. This article continues the previous article. After talking about this function, we can go back to the s3c_rtc_probe function and continue talking about it in the next article. Do you still remember this sen
[Microcontroller]
S3C6410 RTC driver in Linux (3)
S3C2416 Linux2.6.21 driver porting - added UART3 and baud rate setting bug eliminated
1. Transplantation Environment (The red bold text indicates the modified content, and the blue bold text indicates the content with special attention) 1.Host environment: Ubuntu-10.10 under Virtualbox 2. Compilation Compilation environment: arm-linux-gcc v4.4.3 3.uboot: U-Boot 1.3.4 (provided by Youjian) 4. Li
[Microcontroller]
Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号