2 * libkmod - interface to kernel module operations
4 * Copyright (C) 2011 ProFUSION embedded systems
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 #include "libkmod-private.h"
31 KMOD_ELF_32
= (1 << 1),
32 KMOD_ELF_64
= (1 << 2),
33 KMOD_ELF_LSB
= (1 << 3),
34 KMOD_ELF_MSB
= (1 << 4)
37 #ifdef WORDS_BIGENDIAN
38 static const enum kmod_elf_class native_endianess
= KMOD_ELF_MSB
;
40 static const enum kmod_elf_class native_endianess
= KMOD_ELF_LSB
;
44 const uint8_t *memory
;
47 enum kmod_elf_class
class;
48 struct kmod_elf_header
{
55 uint16_t section
; /* index of the strings section */
58 uint32_t nameoff
; /* offset in strings itself */
63 //#define ENABLE_ELFDBG 1
65 #if defined(ENABLE_LOGGING) && defined(ENABLE_ELFDBG)
66 #define ELFDBG(elf, ...) \
67 _elf_dbg(elf, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__);
69 static inline void _elf_dbg(const struct kmod_elf
*elf
, const char *fname
, unsigned line
, const char *func
, const char *fmt
, ...)
73 fprintf(stderr
, "ELFDBG-%d%c: %s:%u %s() ",
74 (elf
->class & KMOD_ELF_32
) ? 32 : 64,
75 (elf
->class & KMOD_ELF_MSB
) ? 'M' : 'L',
78 vfprintf(stderr
, fmt
, args
);
82 #define ELFDBG(elf, ...)
86 static int elf_identify(const void *memory
, uint64_t size
)
88 const uint8_t *p
= memory
;
91 if (size
<= EI_NIDENT
|| memcmp(p
, ELFMAG
, SELFMAG
) != 0)
94 switch (p
[EI_CLASS
]) {
96 if (size
<= sizeof(Elf32_Ehdr
))
101 if (size
<= sizeof(Elf64_Ehdr
))
103 class |= KMOD_ELF_64
;
109 switch (p
[EI_DATA
]) {
111 class |= KMOD_ELF_LSB
;
114 class |= KMOD_ELF_MSB
;
123 static inline uint64_t elf_get_uint(const struct kmod_elf
*elf
, uint64_t offset
, uint16_t size
)
129 assert(size
<= sizeof(uint64_t));
130 assert(offset
+ size
<= elf
->size
);
131 if (offset
+ size
> elf
->size
) {
132 ELFDBG(elf
, "out of bounds: %"PRIu64
" + %"PRIu16
" = %"PRIu64
"> %"PRIu64
" (ELF size)\n",
133 offset
, size
, offset
+ size
, elf
->size
);
137 p
= elf
->memory
+ offset
;
138 if (elf
->class & KMOD_ELF_MSB
) {
139 for (i
= 0; i
< size
; i
++)
140 ret
= (ret
<< 8) | p
[i
];
142 for (i
= 1; i
<= size
; i
++)
143 ret
= (ret
<< 8) | p
[size
- i
];
146 ELFDBG(elf
, "size=%"PRIu16
" offset=%"PRIu64
" value=%"PRIu64
"\n",
152 static inline int elf_set_uint(struct kmod_elf
*elf
, uint64_t offset
, uint64_t size
, uint64_t value
)
157 ELFDBG(elf
, "size=%"PRIu16
" offset=%"PRIu64
" value=%"PRIu64
" write memory=%p\n",
158 size
, offset
, value
, elf
->changed
);
160 assert(size
<= sizeof(uint64_t));
161 assert(offset
+ size
<= elf
->size
);
162 if (offset
+ size
> elf
->size
) {
163 ELFDBG(elf
, "out of bounds: %"PRIu64
" + %"PRIu16
" = %"PRIu64
"> %"PRIu64
" (ELF size)\n",
164 offset
, size
, offset
+ size
, elf
->size
);
168 if (elf
->changed
== NULL
) {
169 elf
->changed
= malloc(elf
->size
);
170 if (elf
->changed
== NULL
)
172 memcpy(elf
->changed
, elf
->memory
, elf
->size
);
173 elf
->memory
= elf
->changed
;
174 ELFDBG(elf
, "copied memory to allow writing.\n");
177 p
= elf
->changed
+ offset
;
178 if (elf
->class & KMOD_ELF_MSB
) {
179 for (i
= 1; i
<= size
; i
++) {
180 p
[size
- i
] = value
& 0xff;
181 value
= (value
& 0xffffffffffffff00) >> 8;
184 for (i
= 0; i
< size
; i
++) {
186 value
= (value
& 0xffffffffffffff00) >> 8;
193 static inline const void *elf_get_mem(const struct kmod_elf
*elf
, uint64_t offset
)
195 assert(offset
< elf
->size
);
196 if (offset
>= elf
->size
) {
197 ELFDBG(elf
, "out-of-bounds: %"PRIu64
" >= %"PRIu64
" (ELF size)\n",
201 return elf
->memory
+ offset
;
204 static inline const void *elf_get_section_header(const struct kmod_elf
*elf
, uint16_t idx
)
206 assert(idx
!= SHN_UNDEF
);
207 assert(idx
< elf
->header
.section
.count
);
208 if (idx
== SHN_UNDEF
|| idx
>= elf
->header
.section
.count
) {
209 ELFDBG(elf
, "invalid section number: %"PRIu16
", last=%"PRIu16
"\n",
210 idx
, elf
->header
.section
.count
);
213 return elf_get_mem(elf
, elf
->header
.section
.offset
+
214 idx
* elf
->header
.section
.entry_size
);
217 static inline int elf_get_section_info(const struct kmod_elf
*elf
, uint16_t idx
, uint64_t *offset
, uint64_t *size
, uint32_t *nameoff
)
219 const uint8_t *p
= elf_get_section_header(elf
, idx
);
220 uint64_t min_size
, off
= p
- elf
->memory
;
223 ELFDBG(elf
, "no section at %"PRIu16
"\n", idx
);
230 #define READV(field) \
231 elf_get_uint(elf, off + offsetof(typeof(*hdr), field), sizeof(hdr->field))
233 if (elf
->class & KMOD_ELF_32
) {
234 const Elf32_Shdr
*hdr
= (const Elf32_Shdr
*)p
;
235 *size
= READV(sh_size
);
236 *offset
= READV(sh_offset
);
237 *nameoff
= READV(sh_name
);
239 const Elf64_Shdr
*hdr
= (const Elf64_Shdr
*)p
;
240 *size
= READV(sh_size
);
241 *offset
= READV(sh_offset
);
242 *nameoff
= READV(sh_name
);
246 min_size
= *offset
+ *size
;
247 if (min_size
>= elf
->size
) {
248 ELFDBG(elf
, "out-of-bounds: %"PRIu64
" >= %"PRIu64
" (ELF size)\n",
249 min_size
, elf
->size
);
253 ELFDBG(elf
, "section=%"PRIu16
" is: offset=%"PRIu64
" size=%"PRIu64
" nameoff=%"PRIu32
"\n",
254 idx
, *offset
, *size
, *nameoff
);
259 static const char *elf_get_strings_section(const struct kmod_elf
*elf
, uint64_t *size
)
261 *size
= elf
->header
.strings
.size
;
262 return elf_get_mem(elf
, elf
->header
.strings
.offset
);
265 struct kmod_elf
*kmod_elf_new(const void *memory
, off_t size
)
267 struct kmod_elf
*elf
;
268 size_t hdr_size
, shdr_size
, min_size
;
271 assert(sizeof(uint16_t) == sizeof(Elf32_Half
));
272 assert(sizeof(uint16_t) == sizeof(Elf64_Half
));
273 assert(sizeof(uint32_t) == sizeof(Elf32_Word
));
274 assert(sizeof(uint32_t) == sizeof(Elf64_Word
));
276 class = elf_identify(memory
, size
);
282 elf
= malloc(sizeof(struct kmod_elf
));
287 elf
->memory
= memory
;
292 #define READV(field) \
293 elf_get_uint(elf, offsetof(typeof(*hdr), field), sizeof(hdr->field))
295 #define LOAD_HEADER \
296 elf->header.section.offset = READV(e_shoff); \
297 elf->header.section.count = READV(e_shnum); \
298 elf->header.section.entry_size = READV(e_shentsize); \
299 elf->header.strings.section = READV(e_shstrndx)
300 if (elf
->class & KMOD_ELF_32
) {
301 const Elf32_Ehdr
*hdr
= elf_get_mem(elf
, 0);
303 hdr_size
= sizeof(Elf32_Ehdr
);
304 shdr_size
= sizeof(Elf32_Shdr
);
306 const Elf64_Ehdr
*hdr
= elf_get_mem(elf
, 0);
308 hdr_size
= sizeof(Elf64_Ehdr
);
309 shdr_size
= sizeof(Elf64_Shdr
);
314 ELFDBG(elf
, "section: offset=%"PRIu64
" count=%"PRIu16
" entry_size=%"PRIu16
" strings index=%"PRIu16
"\n",
315 elf
->header
.section
.offset
,
316 elf
->header
.section
.count
,
317 elf
->header
.section
.entry_size
,
318 elf
->header
.strings
.section
);
320 if (elf
->header
.section
.entry_size
!= shdr_size
) {
321 ELFDBG(elf
, "unexpected section entry size: %"PRIu16
", expected %"PRIu16
"\n",
322 elf
->header
.section
.entry_size
, shdr_size
);
325 min_size
= hdr_size
+ shdr_size
* elf
->header
.section
.count
;
326 if (min_size
>= elf
->size
) {
327 ELFDBG(elf
, "file is too short to hold sections\n");
331 if (elf_get_section_info(elf
, elf
->header
.strings
.section
,
332 &elf
->header
.strings
.offset
,
333 &elf
->header
.strings
.size
,
334 &elf
->header
.strings
.nameoff
) < 0) {
335 ELFDBG(elf
, "could not get strings section\n");
339 const char *s
= elf_get_strings_section(elf
, &slen
);
340 if (slen
== 0 || s
[slen
- 1] != '\0') {
341 ELFDBG(elf
, "strings section does not ends with \\0\n");
354 void kmod_elf_unref(struct kmod_elf
*elf
)
360 const void *kmod_elf_get_memory(const struct kmod_elf
*elf
)
365 static int kmod_elf_get_section(const struct kmod_elf
*elf
, const char *section
, const void **buf
, size_t *buf_size
)
368 const char *names
= elf_get_strings_section(elf
, &nameslen
);
374 for (i
= 1; i
< elf
->header
.section
.count
; i
++) {
378 int err
= elf_get_section_info(elf
, i
, &off
, &size
, &nameoff
);
381 if (nameoff
>= nameslen
)
384 if (!streq(section
, n
))
387 *buf
= elf_get_mem(elf
, off
);
395 /* array will be allocated with strings in a single malloc, just free *array */
396 int kmod_elf_get_strings(const struct kmod_elf
*elf
, const char *section
, char ***array
)
398 uint64_t i
, last
, size
;
406 err
= kmod_elf_get_section(elf
, section
, &buf
, &size
);
410 if (strings
== NULL
|| size
== 0)
413 /* skip zero padding */
414 while (strings
[0] == '\0' && size
> 1) {
422 for (i
= 0, count
= 0; i
< size
; i
++) {
423 if (strings
[i
] == '\0') {
432 if (strings
[i
- 1] != '\0')
435 *array
= a
= malloc(size
+ 1 + sizeof(char *) * count
);
439 itr
= (char *)(a
+ count
);
441 for (i
= 0, count
= 0; i
< size
; i
++) {
442 if (strings
[i
] == '\0') {
443 size_t slen
= i
- last
;
449 memcpy(itr
, strings
+ last
, slen
);
456 if (strings
[i
- 1] != '\0') {
457 size_t slen
= i
- last
;
459 memcpy(itr
, strings
+ last
, slen
);
468 /* array will be allocated with strings in a single malloc, just free *array */
469 int kmod_elf_get_modversions(const struct kmod_elf
*elf
, struct kmod_modversion
**array
)
471 uint64_t size
, secsize
, slen
, off
;
472 struct kmod_modversion
*a
;
476 struct kmod_modversion32
{
478 char name
[64 - sizeof(uint32_t)];
480 struct kmod_modversion64
{
482 char name
[64 - sizeof(uint64_t)];
487 err
= kmod_elf_get_section(elf
, "__versions", &buf
, &size
);
490 if (buf
== NULL
|| size
== 0)
493 if (elf
->class & KMOD_ELF_32
)
494 secsize
= sizeof(struct kmod_modversion32
);
496 secsize
= sizeof(struct kmod_modversion64
);
498 if (size
% secsize
!= 0)
500 count
= size
/ secsize
;
502 off
= (const uint8_t *)buf
- elf
->memory
;
504 for (i
= 0; i
< count
; i
++, off
+= secsize
) {
506 if (elf
->class & KMOD_ELF_32
) {
507 struct kmod_modversion32
*mv
;
508 symbol
= elf_get_mem(elf
, off
+ sizeof(mv
->crc
));
510 struct kmod_modversion64
*mv
;
511 symbol
= elf_get_mem(elf
, off
+ sizeof(mv
->crc
));
513 slen
+= strlen(symbol
) + 1;
516 *array
= a
= malloc(sizeof(struct kmod_modversion
) * count
+ slen
);
520 itr
= (char *)(a
+ count
);
521 off
= (const uint8_t *)buf
- elf
->memory
;
522 for (i
= 0; i
< count
; i
++, off
+= secsize
) {
526 if (elf
->class & KMOD_ELF_32
) {
527 struct kmod_modversion32
*mv
;
528 crc
= elf_get_uint(elf
, off
, sizeof(mv
->crc
));
529 symbol
= elf_get_mem(elf
, off
+ sizeof(mv
->crc
));
531 struct kmod_modversion64
*mv
;
532 crc
= elf_get_uint(elf
, off
, sizeof(mv
->crc
));
533 symbol
= elf_get_mem(elf
, off
+ sizeof(mv
->crc
));
538 symbollen
= strlen(symbol
) + 1;
539 memcpy(itr
, symbol
, symbollen
);
546 int kmod_elf_strip_section(struct kmod_elf
*elf
, const char *section
)
550 int err
= kmod_elf_get_section(elf
, section
, &buf
, &size
);
554 off
= (const uint8_t *)buf
- elf
->memory
;
556 #define WRITEV(field, value) \
557 elf_set_uint(elf, off + offsetof(typeof(*hdr), field), sizeof(hdr->field), value)
558 if (elf
->class & KMOD_ELF_32
) {
559 const Elf32_Shdr
*hdr
= buf
;
560 uint32_t val
= ~(uint32_t)SHF_ALLOC
;
561 return WRITEV(sh_flags
, val
);
563 const Elf64_Shdr
*hdr
= buf
;
564 uint64_t val
= ~(uint64_t)SHF_ALLOC
;
565 return WRITEV(sh_flags
, val
);
570 int kmod_elf_strip_vermagic(struct kmod_elf
*elf
)
577 err
= kmod_elf_get_section(elf
, ".modinfo", &buf
, &size
);
581 if (strings
== NULL
|| size
== 0)
584 /* skip zero padding */
585 while (strings
[0] == '\0' && size
> 1) {
592 for (i
= 0; i
< size
; i
++) {
596 if (strings
[i
] == '\0')
602 len
= sizeof("vermagic=") - 1;
605 if (strncmp(s
, "vermagic=", len
) != 0) {
610 off
= (const uint8_t *)s
- elf
->memory
;
612 if (elf
->changed
== NULL
) {
613 elf
->changed
= malloc(elf
->size
);
614 if (elf
->changed
== NULL
)
616 memcpy(elf
->changed
, elf
->memory
, elf
->size
);
617 elf
->memory
= elf
->changed
;
618 ELFDBG(elf
, "copied memory to allow writing.\n");
622 ELFDBG(elf
, "clear .modinfo vermagic \"%s\" (%zd bytes)\n",
624 memset(elf
->changed
+ off
, '\0', len
);
628 ELFDBG(elf
, "no vermagic found in .modinfo\n");