mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-24 10:03:05 +00:00
ac27f2c1ec
Currently, we load debug information for every kernel module that we find under /lib/modules/$(uname -r)/kernel. This has a few issues: 1. Distribution kernels have lots of modules (~3000 for Fedora and Debian). a) This can exceed the default soft limit on the number of open file descriptors. b) The mmap'd debug information can trip the overcommit heuristics and cause OOM kills. c) It can take a long time to parse all of the debug information. 2. Not all modules are under the "kernel" directory; some distros also have an "extra" directory. 3. The user is not made aware of loaded kernel modules that don't have debug information available. So, instead of walking /lib/modules, walk the list of loaded kernel modules and look up their debugging information.
233 lines
5.6 KiB
C
233 lines
5.6 KiB
C
// Copyright 2018-2019 - Omar Sandoval
|
|
// SPDX-License-Identifier: GPL-3.0+
|
|
|
|
/**
|
|
* @file
|
|
*
|
|
* Helpers for parsing values in memory.
|
|
*
|
|
* See @ref MemoryParsing.
|
|
*/
|
|
|
|
#ifndef DRGN_READ_H
|
|
#define DRGN_READ_H
|
|
|
|
#include <byteswap.h>
|
|
#include <stdint.h>
|
|
|
|
/**
|
|
* @ingroup Internals
|
|
*
|
|
* @defgroup MemoryParsing Memory parsing
|
|
*
|
|
* Helpers for parsing values in memory.
|
|
*
|
|
* This provides helpers for parsing values in memory (e.g., from an mmap'd
|
|
* file) with safe bounds checking.
|
|
*
|
|
* These helpers take a cursor (@p ptr) which is read from and advanced. They
|
|
* are bounds-checked against an end pointer (@p end). If desired, they will
|
|
* swap the byte order of the read value. The @c readN helpers are defined for N
|
|
* of 16, 32, and 64.
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
/** Return whether <tt>ptr + size</tt> is within @p end. */
|
|
static inline bool read_in_bounds(const char *ptr, const char *end, size_t size)
|
|
{
|
|
return ptr <= end && (size_t)(end - ptr) >= size;
|
|
}
|
|
|
|
/** Parse an unsigned 8-bit integer in memory. */
|
|
static inline bool read_u8(const char **ptr, const char *end, uint8_t *ret)
|
|
{
|
|
if (!read_in_bounds(*ptr, end, sizeof(uint8_t)))
|
|
return false;
|
|
*ret = *(const uint8_t *)*ptr;
|
|
*ptr += sizeof(uint8_t);
|
|
return true;
|
|
}
|
|
|
|
/** Parse an unsigned 8-bit integer in memory into a @c size_t. */
|
|
static inline bool read_u8_into_size_t(const char **ptr, const char *end,
|
|
size_t *ret)
|
|
{
|
|
uint8_t tmp;
|
|
|
|
if (!read_u8(ptr, end, &tmp))
|
|
return false;
|
|
if (tmp > SIZE_MAX)
|
|
return false;
|
|
*ret = tmp;
|
|
return true;
|
|
}
|
|
|
|
#ifdef DOXYGEN
|
|
/**
|
|
* Parse an unsigned N-bit integer in memory.
|
|
*
|
|
* This does not perform any bounds checking, so it should only be used if
|
|
* bounds checking was already done.
|
|
*
|
|
* This is defined for N of 16, 32, and 64.
|
|
*
|
|
* @param[in,out] ptr Pointer to read from and advance.
|
|
* @param[in] bswap Whether to swap the byte order of the read value.
|
|
* @param[out] ret Returned value.
|
|
*/
|
|
void read_uN_nocheck(const char **ptr, bool bswap, uintN_t *ret);
|
|
|
|
/**
|
|
* Parse an unsigned N-bit integer in memory, checking bounds.
|
|
*
|
|
* @sa read_uN_nocheck().
|
|
*
|
|
* @param[in] end Pointer to one after the last valid address.
|
|
* @return Whether the read was in bounds.
|
|
*/
|
|
bool read_uN(const char **ptr, const char *end, bool bswap, uintN_t *ret);
|
|
|
|
/**
|
|
* Parse an unsigned N-bit integer in memory into a @c uint64_t.
|
|
*
|
|
* @sa read_uN_nocheck().
|
|
*/
|
|
void read_uN_into_u64_nocheck(const char **ptr, bool bswap, uint64_t *ret);
|
|
|
|
/**
|
|
* Parse an unsigned N-bit integer in memory into a @c uint64_t, checking
|
|
* bounds.
|
|
*
|
|
* @sa read_uN().
|
|
*/
|
|
bool read_uN_into_u64(const char **ptr, const char *end, bool bswap,
|
|
uint64_t *ret);
|
|
|
|
/**
|
|
* Parse an unsigned N-bit integer in memory into a @c size_t, checking bounds.
|
|
*
|
|
* @sa read_uN().
|
|
*
|
|
* @return Whether the read was in bounds and the value was less than or equal
|
|
* to @c SIZE_MAX.
|
|
*/
|
|
bool read_uN_into_u64(const char **ptr, const char *end, bool bswap,
|
|
uint64_t *ret);
|
|
#endif
|
|
|
|
#define DEFINE_READ(size) \
|
|
static inline void read_u##size##_nocheck(const char **ptr, bool bswap, \
|
|
uint##size##_t *ret) \
|
|
{ \
|
|
uint##size##_t tmp; \
|
|
\
|
|
tmp = *(const uint##size##_t *)*ptr; \
|
|
if (bswap) \
|
|
tmp = bswap_##size(tmp); \
|
|
*ret = tmp; \
|
|
*ptr += sizeof(uint##size##_t); \
|
|
} \
|
|
\
|
|
static inline bool read_u##size(const char **ptr, const char *end, \
|
|
bool bswap, uint##size##_t *ret) \
|
|
{ \
|
|
if (!read_in_bounds(*ptr, end, sizeof(uint##size##_t))) \
|
|
return false; \
|
|
read_u##size##_nocheck(ptr, bswap, ret); \
|
|
return true; \
|
|
} \
|
|
\
|
|
static inline void read_u##size##_into_u64_nocheck(const char **ptr, \
|
|
bool bswap, \
|
|
uint64_t *ret) \
|
|
{ \
|
|
uint##size##_t tmp; \
|
|
\
|
|
read_u##size##_nocheck(ptr, bswap, &tmp); \
|
|
*ret = tmp; \
|
|
} \
|
|
\
|
|
static inline bool read_u##size##_into_u64(const char **ptr, \
|
|
const char *end, bool bswap, \
|
|
uint64_t *ret) \
|
|
{ \
|
|
uint##size##_t tmp; \
|
|
\
|
|
if (!read_u##size(ptr, end, bswap, &tmp)) \
|
|
return false; \
|
|
*ret = tmp; \
|
|
return true; \
|
|
} \
|
|
\
|
|
static inline bool read_u##size##_into_size_t(const char **ptr, \
|
|
const char *end, \
|
|
bool bswap, size_t *ret) \
|
|
{ \
|
|
uint##size##_t tmp; \
|
|
\
|
|
if (!read_u##size(ptr, end, bswap, &tmp)) \
|
|
return false; \
|
|
if (tmp > SIZE_MAX) \
|
|
return false; \
|
|
*ret = tmp; \
|
|
return true; \
|
|
}
|
|
|
|
DEFINE_READ(16)
|
|
DEFINE_READ(32)
|
|
DEFINE_READ(64)
|
|
|
|
static inline bool read_be32(const char **ptr, const char *end, uint32_t *ret)
|
|
{
|
|
return read_u32(ptr, end, __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__,
|
|
ret);
|
|
}
|
|
|
|
/** Advance @p ptr to the byte after the next null byte. */
|
|
static inline bool skip_string(const char **ptr, const char *end)
|
|
{
|
|
const char *nul;
|
|
|
|
if (*ptr >= end)
|
|
return false;
|
|
|
|
nul = memchr(*ptr, 0, end - *ptr);
|
|
if (!nul)
|
|
return false;
|
|
|
|
*ptr = nul + 1;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Parse a null terminated string in memory.
|
|
*
|
|
* @param[in,out] ptr Pointer to read from and advance.
|
|
* @param[in] end Pointer to one after the last valid address.
|
|
* @param[out] str_ret Returned string. Equal to the initial value of
|
|
* <tt>*ptr</tt>.
|
|
* @param[out] len_ret Returned string length not including the null byte.
|
|
*/
|
|
static inline bool read_string(const char **ptr, const char *end,
|
|
const char **str_ret, size_t *len_ret)
|
|
{
|
|
const char *nul;
|
|
|
|
if (*ptr >= end)
|
|
return false;
|
|
|
|
nul = memchr(*ptr, 0, end - *ptr);
|
|
if (!nul)
|
|
return false;
|
|
|
|
*str_ret = *ptr;
|
|
*len_ret = nul - *ptr;
|
|
*ptr = nul + 1;
|
|
return true;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
#endif /* DRGN_READ_H */
|