Compare commits
1 Commits
5de9c9864d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6de58db258 |
+1
-1
@@ -258,7 +258,7 @@ endif
|
|||||||
|
|
||||||
if enable_depmod
|
if enable_depmod
|
||||||
vx_sources += depmod_src
|
vx_sources += depmod_src
|
||||||
vx_dependencies += [dependency('zlib'), dependency('libzstd')]
|
vx_dependencies += [dependency('zlib'), dependency('libzstd'), dependency('liblzma')]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
vx = executable('vx',
|
vx = executable('vx',
|
||||||
|
|||||||
+403
-98
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <elf.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
#include <lzma.h>
|
||||||
#include <zstd.h>
|
#include <zstd.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -42,10 +44,8 @@ constexpr uint32_t INDEX_VERSION = (0x0002u << 16) | 0x0001u;
|
|||||||
constexpr uint32_t INDEX_NODE_PREFIX = 0x80000000u;
|
constexpr uint32_t INDEX_NODE_PREFIX = 0x80000000u;
|
||||||
constexpr uint32_t INDEX_NODE_VALUES = 0x40000000u;
|
constexpr uint32_t INDEX_NODE_VALUES = 0x40000000u;
|
||||||
constexpr uint32_t INDEX_NODE_CHILDS = 0x20000000u;
|
constexpr uint32_t INDEX_NODE_CHILDS = 0x20000000u;
|
||||||
constexpr uint32_t INDEX_NODE_MASK = 0x0fffffffu;
|
|
||||||
|
|
||||||
struct Options {
|
struct Options {
|
||||||
bool all = false;
|
|
||||||
bool quick = false;
|
bool quick = false;
|
||||||
bool dry_run = false;
|
bool dry_run = false;
|
||||||
bool errsyms = false;
|
bool errsyms = false;
|
||||||
@@ -54,12 +54,17 @@ struct Options {
|
|||||||
std::string basedir = "/";
|
std::string basedir = "/";
|
||||||
std::string moduledir = "/lib/modules";
|
std::string moduledir = "/lib/modules";
|
||||||
std::string outdir;
|
std::string outdir;
|
||||||
std::string config;
|
std::vector<std::string> config_paths;
|
||||||
std::string symvers;
|
std::string symvers;
|
||||||
std::string filesyms;
|
std::string filesyms;
|
||||||
char symbol_prefix = '\0';
|
char symbol_prefix = '\0';
|
||||||
std::string version;
|
std::string version;
|
||||||
std::vector<std::string> files;
|
std::vector<std::string> files;
|
||||||
|
std::set<std::string> external_symbols;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DepmodConfig {
|
||||||
|
std::set<std::string> excluded_dirs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Module {
|
struct Module {
|
||||||
@@ -74,7 +79,6 @@ struct Module {
|
|||||||
std::vector<std::string> exports;
|
std::vector<std::string> exports;
|
||||||
std::vector<std::string> undefined;
|
std::vector<std::string> undefined;
|
||||||
int order = 0x3fffffff;
|
int order = 0x3fffffff;
|
||||||
size_t scan_index = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void warnx(const char *fmt, ...)
|
static void warnx(const char *fmt, ...)
|
||||||
@@ -140,6 +144,8 @@ static std::string strip_module_suffix(std::string name)
|
|||||||
{
|
{
|
||||||
if (has_suffix(name, ".zst"))
|
if (has_suffix(name, ".zst"))
|
||||||
name.resize(name.size() - 4);
|
name.resize(name.size() - 4);
|
||||||
|
else if (has_suffix(name, ".xz"))
|
||||||
|
name.resize(name.size() - 3);
|
||||||
else if (has_suffix(name, ".gz"))
|
else if (has_suffix(name, ".gz"))
|
||||||
name.resize(name.size() - 3);
|
name.resize(name.size() - 3);
|
||||||
|
|
||||||
@@ -155,6 +161,9 @@ static std::string normalize_modname(std::string name)
|
|||||||
if (c == '-')
|
if (c == '-')
|
||||||
c = '_';
|
c = '_';
|
||||||
}
|
}
|
||||||
|
size_t dot = name.find('.');
|
||||||
|
if (dot != std::string::npos)
|
||||||
|
name.resize(dot);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,45 +253,75 @@ static bool decompress_gzip(const std::vector<uint8_t> &in, std::vector<uint8_t>
|
|||||||
|
|
||||||
static bool decompress_zstd(const std::vector<uint8_t> &in, std::vector<uint8_t> &out)
|
static bool decompress_zstd(const std::vector<uint8_t> &in, std::vector<uint8_t> &out)
|
||||||
{
|
{
|
||||||
unsigned long long frame_size = ZSTD_getFrameContentSize(in.data(), in.size());
|
|
||||||
if (frame_size != ZSTD_CONTENTSIZE_ERROR && frame_size != ZSTD_CONTENTSIZE_UNKNOWN) {
|
|
||||||
out.resize(static_cast<size_t>(frame_size));
|
|
||||||
size_t ret = ZSTD_decompress(out.data(), out.size(), in.data(), in.size());
|
|
||||||
if (ZSTD_isError(ret))
|
|
||||||
return false;
|
|
||||||
out.resize(ret);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ZSTD_DStream *ds = ZSTD_createDStream();
|
ZSTD_DStream *ds = ZSTD_createDStream();
|
||||||
if (!ds)
|
if (!ds)
|
||||||
return false;
|
return false;
|
||||||
ZSTD_initDStream(ds);
|
size_t init = ZSTD_initDStream(ds);
|
||||||
|
if (ZSTD_isError(init)) {
|
||||||
|
ZSTD_freeDStream(ds);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
ZSTD_inBuffer input{in.data(), in.size(), 0};
|
ZSTD_inBuffer input{in.data(), in.size(), 0};
|
||||||
std::vector<uint8_t> buf(ZSTD_DStreamOutSize());
|
std::vector<uint8_t> buf(ZSTD_DStreamOutSize());
|
||||||
while (input.pos < input.size) {
|
size_t ret = 1;
|
||||||
|
while (ret != 0 || input.pos < input.size) {
|
||||||
ZSTD_outBuffer output{buf.data(), buf.size(), 0};
|
ZSTD_outBuffer output{buf.data(), buf.size(), 0};
|
||||||
size_t ret = ZSTD_decompressStream(ds, &output, &input);
|
ret = ZSTD_decompressStream(ds, &output, &input);
|
||||||
if (ZSTD_isError(ret)) {
|
if (ZSTD_isError(ret)) {
|
||||||
ZSTD_freeDStream(ds);
|
ZSTD_freeDStream(ds);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out.insert(out.end(), buf.begin(), buf.begin() + output.pos);
|
out.insert(out.end(), buf.begin(), buf.begin() + output.pos);
|
||||||
|
if (ret == 0 && input.pos < input.size) {
|
||||||
|
ret = ZSTD_initDStream(ds);
|
||||||
|
if (ZSTD_isError(ret)) {
|
||||||
|
ZSTD_freeDStream(ds);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (input.pos == input.size && output.pos == 0 && ret != 0) {
|
||||||
|
ZSTD_freeDStream(ds);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ZSTD_freeDStream(ds);
|
ZSTD_freeDStream(ds);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool decompress_xz(const std::vector<uint8_t> &in, std::vector<uint8_t> &out)
|
||||||
|
{
|
||||||
|
lzma_stream stream = LZMA_STREAM_INIT;
|
||||||
|
if (lzma_stream_decoder(&stream, UINT64_MAX, LZMA_CONCATENATED) != LZMA_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
stream.next_in = in.data();
|
||||||
|
stream.avail_in = in.size();
|
||||||
|
std::vector<uint8_t> buf(256 * 1024);
|
||||||
|
lzma_ret ret = LZMA_OK;
|
||||||
|
while (ret == LZMA_OK) {
|
||||||
|
stream.next_out = buf.data();
|
||||||
|
stream.avail_out = buf.size();
|
||||||
|
ret = lzma_code(&stream, LZMA_FINISH);
|
||||||
|
size_t have = buf.size() - stream.avail_out;
|
||||||
|
out.insert(out.end(), buf.begin(), buf.begin() + have);
|
||||||
|
}
|
||||||
|
lzma_end(&stream);
|
||||||
|
return ret == LZMA_STREAM_END;
|
||||||
|
}
|
||||||
|
|
||||||
static bool read_module_image(const std::string &path, std::vector<uint8_t> &out)
|
static bool read_module_image(const std::string &path, std::vector<uint8_t> &out)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> raw;
|
std::vector<uint8_t> raw;
|
||||||
if (!read_file_raw(path, raw))
|
if (!read_file_raw(path, raw))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (has_suffix(path, ".ko.gz"))
|
if (raw.size() >= 4 && raw[0] == 0x28 && raw[1] == 0xb5 &&
|
||||||
return decompress_gzip(raw, out);
|
raw[2] == 0x2f && raw[3] == 0xfd)
|
||||||
if (has_suffix(path, ".ko.zst"))
|
|
||||||
return decompress_zstd(raw, out);
|
return decompress_zstd(raw, out);
|
||||||
|
if (raw.size() >= 6 && raw[0] == 0xfd && raw[1] == '7' &&
|
||||||
|
raw[2] == 'z' && raw[3] == 'X' && raw[4] == 'Z' && raw[5] == 0x00)
|
||||||
|
return decompress_xz(raw, out);
|
||||||
|
if (raw.size() >= 2 && raw[0] == 0x1f && raw[1] == 0x8b)
|
||||||
|
return decompress_gzip(raw, out);
|
||||||
|
|
||||||
out = std::move(raw);
|
out = std::move(raw);
|
||||||
return true;
|
return true;
|
||||||
@@ -333,7 +372,8 @@ struct ElfReader {
|
|||||||
|
|
||||||
bool parse()
|
bool parse()
|
||||||
{
|
{
|
||||||
if (data.size() < 64 || data[0] != 0x7f || data[1] != 'E' || data[2] != 'L' || data[3] != 'F')
|
if (data.size() < EI_NIDENT || data[0] != ELFMAG0 || data[1] != ELFMAG1 ||
|
||||||
|
data[2] != ELFMAG2 || data[3] != ELFMAG3)
|
||||||
return false;
|
return false;
|
||||||
is64 = data[4] == 2;
|
is64 = data[4] == 2;
|
||||||
if (data[4] != 1 && data[4] != 2)
|
if (data[4] != 1 && data[4] != 2)
|
||||||
@@ -345,6 +385,11 @@ struct ElfReader {
|
|||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
const size_t header_size = is64 ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr);
|
||||||
|
const size_t section_size = is64 ? sizeof(Elf64_Shdr) : sizeof(Elf32_Shdr);
|
||||||
|
if (data.size() < header_size)
|
||||||
|
return false;
|
||||||
|
|
||||||
uint64_t shoff = 0;
|
uint64_t shoff = 0;
|
||||||
uint16_t shentsize = 0, shnum = 0, shstrndx = 0;
|
uint16_t shentsize = 0, shnum = 0, shstrndx = 0;
|
||||||
if (is64) {
|
if (is64) {
|
||||||
@@ -358,9 +403,9 @@ struct ElfReader {
|
|||||||
shnum = rd<uint16_t>(0x30);
|
shnum = rd<uint16_t>(0x30);
|
||||||
shstrndx = rd<uint16_t>(0x32);
|
shstrndx = rd<uint16_t>(0x32);
|
||||||
}
|
}
|
||||||
if (shoff == 0 || shentsize == 0 || shnum == 0 || shstrndx >= shnum)
|
if (shoff == 0 || shentsize != section_size || shnum == 0 || shstrndx >= shnum)
|
||||||
return false;
|
return false;
|
||||||
if (shoff + static_cast<uint64_t>(shentsize) * shnum > data.size())
|
if (shoff > data.size() || static_cast<uint64_t>(shentsize) * shnum > data.size() - shoff)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
std::vector<uint32_t> name_offsets(shnum);
|
std::vector<uint32_t> name_offsets(shnum);
|
||||||
@@ -383,7 +428,7 @@ struct ElfReader {
|
|||||||
s.link = rd<uint32_t>(off + 0x18);
|
s.link = rd<uint32_t>(off + 0x18);
|
||||||
s.entsize = rd<uint32_t>(off + 0x24);
|
s.entsize = rd<uint32_t>(off + 0x24);
|
||||||
}
|
}
|
||||||
if (s.offset + s.size > data.size())
|
if (s.offset > data.size() || s.size > data.size() - s.offset)
|
||||||
return false;
|
return false;
|
||||||
sections[i] = std::move(s);
|
sections[i] = std::move(s);
|
||||||
}
|
}
|
||||||
@@ -395,8 +440,11 @@ struct ElfReader {
|
|||||||
continue;
|
continue;
|
||||||
size_t start = static_cast<size_t>(shstr.offset + noff);
|
size_t start = static_cast<size_t>(shstr.offset + noff);
|
||||||
size_t end = start;
|
size_t end = start;
|
||||||
while (end < data.size() && data[end] != '\0')
|
const size_t shstr_end = static_cast<size_t>(shstr.offset + shstr.size);
|
||||||
|
while (end < shstr_end && data[end] != '\0')
|
||||||
end++;
|
end++;
|
||||||
|
if (end == shstr_end)
|
||||||
|
continue;
|
||||||
sections[i].name.assign(reinterpret_cast<const char *>(data.data() + start), end - start);
|
sections[i].name.assign(reinterpret_cast<const char *>(data.data() + start), end - start);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -417,8 +465,11 @@ struct ElfReader {
|
|||||||
return {};
|
return {};
|
||||||
size_t start = static_cast<size_t>(s.offset + off);
|
size_t start = static_cast<size_t>(s.offset + off);
|
||||||
size_t end = start;
|
size_t end = start;
|
||||||
while (end < data.size() && data[end] != '\0')
|
const size_t section_end = static_cast<size_t>(s.offset + s.size);
|
||||||
|
while (end < section_end && data[end] != '\0')
|
||||||
end++;
|
end++;
|
||||||
|
if (end == section_end)
|
||||||
|
return {};
|
||||||
return std::string(reinterpret_cast<const char *>(data.data() + start), end - start);
|
return std::string(reinterpret_cast<const char *>(data.data() + start), end - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,8 +507,6 @@ struct ElfReader {
|
|||||||
mod.softdeps.push_back(val);
|
mod.softdeps.push_back(val);
|
||||||
} else if (key == "weakdep") {
|
} else if (key == "weakdep") {
|
||||||
mod.weakdeps.push_back(val);
|
mod.weakdeps.push_back(val);
|
||||||
} else if (key == "name" && !val.empty()) {
|
|
||||||
mod.modname = normalize_modname(val);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -467,8 +516,6 @@ struct ElfReader {
|
|||||||
|
|
||||||
void read_symbols(Module &mod, char prefix) const
|
void read_symbols(Module &mod, char prefix) const
|
||||||
{
|
{
|
||||||
constexpr uint32_t SHT_SYMTAB = 2;
|
|
||||||
constexpr uint16_t SHN_UNDEF = 0;
|
|
||||||
static const char *export_prefixes[] = {
|
static const char *export_prefixes[] = {
|
||||||
"__ksymtab_unused_gpl_",
|
"__ksymtab_unused_gpl_",
|
||||||
"__ksymtab_gpl_future_",
|
"__ksymtab_gpl_future_",
|
||||||
@@ -478,6 +525,21 @@ struct ElfReader {
|
|||||||
"__ksymtab_strings+",
|
"__ksymtab_strings+",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Section *ksymtab_strings = find_section("__ksymtab_strings");
|
||||||
|
if (ksymtab_strings) {
|
||||||
|
size_t pos = static_cast<size_t>(ksymtab_strings->offset);
|
||||||
|
size_t end = static_cast<size_t>(ksymtab_strings->offset + ksymtab_strings->size);
|
||||||
|
while (pos < end) {
|
||||||
|
size_t next = pos;
|
||||||
|
while (next < end && data[next] != '\0')
|
||||||
|
next++;
|
||||||
|
if (next > pos)
|
||||||
|
mod.exports.push_back(strip_known_symbol_prefix(
|
||||||
|
std::string(reinterpret_cast<const char *>(data.data() + pos), next - pos), prefix));
|
||||||
|
pos = next + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t si = 0; si < sections.size(); si++) {
|
for (size_t si = 0; si < sections.size(); si++) {
|
||||||
const Section &symsec = sections[si];
|
const Section &symsec = sections[si];
|
||||||
if (symsec.type != SHT_SYMTAB || symsec.entsize == 0 || symsec.link >= sections.size())
|
if (symsec.type != SHT_SYMTAB || symsec.entsize == 0 || symsec.link >= sections.size())
|
||||||
@@ -487,23 +549,19 @@ struct ElfReader {
|
|||||||
for (size_t i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
size_t off = static_cast<size_t>(symsec.offset + i * symsec.entsize);
|
size_t off = static_cast<size_t>(symsec.offset + i * symsec.entsize);
|
||||||
uint32_t st_name = 0;
|
uint32_t st_name = 0;
|
||||||
uint8_t st_info = 0;
|
|
||||||
uint16_t st_shndx = 0;
|
uint16_t st_shndx = 0;
|
||||||
if (is64) {
|
if (is64) {
|
||||||
st_name = rd<uint32_t>(off + 0x00);
|
st_name = rd<uint32_t>(off + 0x00);
|
||||||
st_info = rd<uint8_t>(off + 0x04);
|
|
||||||
st_shndx = rd<uint16_t>(off + 0x06);
|
st_shndx = rd<uint16_t>(off + 0x06);
|
||||||
} else {
|
} else {
|
||||||
st_name = rd<uint32_t>(off + 0x00);
|
st_name = rd<uint32_t>(off + 0x00);
|
||||||
st_info = rd<uint8_t>(off + 0x0c);
|
|
||||||
st_shndx = rd<uint16_t>(off + 0x0e);
|
st_shndx = rd<uint16_t>(off + 0x0e);
|
||||||
}
|
}
|
||||||
std::string name = str_at(strsec, st_name);
|
std::string name = str_at(strsec, st_name);
|
||||||
if (name.empty())
|
if (name.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
unsigned bind = st_info >> 4;
|
if (st_shndx == SHN_UNDEF) {
|
||||||
if (st_shndx == SHN_UNDEF && (bind == 1 || bind == 2)) {
|
|
||||||
mod.undefined.push_back(strip_known_symbol_prefix(name, prefix));
|
mod.undefined.push_back(strip_known_symbol_prefix(name, prefix));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -517,6 +575,26 @@ struct ElfReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Section *versions = find_section("__versions");
|
||||||
|
if (versions) {
|
||||||
|
const size_t crc_size = is64 ? sizeof(uint64_t) : sizeof(uint32_t);
|
||||||
|
constexpr size_t record_size = 64;
|
||||||
|
for (size_t pos = static_cast<size_t>(versions->offset);
|
||||||
|
pos + record_size <= versions->offset + versions->size;
|
||||||
|
pos += record_size) {
|
||||||
|
const size_t name_start = pos + crc_size;
|
||||||
|
size_t name_end = name_start;
|
||||||
|
const size_t section_end = static_cast<size_t>(versions->offset + versions->size);
|
||||||
|
while (name_end < std::min(section_end, name_start + record_size - crc_size) &&
|
||||||
|
data[name_end] != '\0')
|
||||||
|
name_end++;
|
||||||
|
if (name_end > name_start)
|
||||||
|
mod.undefined.push_back(strip_known_symbol_prefix(
|
||||||
|
std::string(reinterpret_cast<const char *>(data.data() + name_start),
|
||||||
|
name_end - name_start), prefix));
|
||||||
|
}
|
||||||
|
}
|
||||||
std::sort(mod.exports.begin(), mod.exports.end());
|
std::sort(mod.exports.begin(), mod.exports.end());
|
||||||
mod.exports.erase(std::unique(mod.exports.begin(), mod.exports.end()), mod.exports.end());
|
mod.exports.erase(std::unique(mod.exports.begin(), mod.exports.end()), mod.exports.end());
|
||||||
std::sort(mod.undefined.begin(), mod.undefined.end());
|
std::sort(mod.undefined.begin(), mod.undefined.end());
|
||||||
@@ -545,23 +623,31 @@ static bool parse_module(Module &mod, const Options &opts)
|
|||||||
|
|
||||||
static bool is_module_file(const std::string &path)
|
static bool is_module_file(const std::string &path)
|
||||||
{
|
{
|
||||||
return has_suffix(path, ".ko") || has_suffix(path, ".ko.gz") || has_suffix(path, ".ko.zst");
|
return has_suffix(path, ".ko") || has_suffix(path, ".ko.gz") ||
|
||||||
|
has_suffix(path, ".ko.xz") || has_suffix(path, ".ko.zst");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void scan_modules_recursive(const std::string &root, const std::string &dir, std::vector<std::string> &out)
|
static bool excluded_module_dir(const char *name)
|
||||||
|
{
|
||||||
|
return strcmp(name, ".") == 0 || strcmp(name, "..") == 0 ||
|
||||||
|
strcmp(name, "build") == 0 || strcmp(name, "source") == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scan_modules_recursive(const std::string &dir, const DepmodConfig &config,
|
||||||
|
std::vector<std::string> &out)
|
||||||
{
|
{
|
||||||
DIR *d = opendir(dir.c_str());
|
DIR *d = opendir(dir.c_str());
|
||||||
if (!d)
|
if (!d)
|
||||||
return;
|
return;
|
||||||
while (dirent *de = readdir(d)) {
|
while (dirent *de = readdir(d)) {
|
||||||
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
|
if (excluded_module_dir(de->d_name) || config.excluded_dirs.count(de->d_name) != 0)
|
||||||
continue;
|
continue;
|
||||||
std::string p = path_join(dir, de->d_name);
|
std::string p = path_join(dir, de->d_name);
|
||||||
struct stat st{};
|
struct stat st{};
|
||||||
if (lstat(p.c_str(), &st) != 0)
|
if (stat(p.c_str(), &st) != 0)
|
||||||
continue;
|
continue;
|
||||||
if (S_ISDIR(st.st_mode)) {
|
if (S_ISDIR(st.st_mode)) {
|
||||||
scan_modules_recursive(root, p, out);
|
scan_modules_recursive(p, config, out);
|
||||||
} else if (S_ISREG(st.st_mode) && is_module_file(p)) {
|
} else if (S_ISREG(st.st_mode) && is_module_file(p)) {
|
||||||
out.push_back(p);
|
out.push_back(p);
|
||||||
}
|
}
|
||||||
@@ -569,6 +655,131 @@ static void scan_modules_recursive(const std::string &root, const std::string &d
|
|||||||
closedir(d);
|
closedir(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_config_file(const std::string &path, const std::string &version,
|
||||||
|
DepmodConfig &config)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(path.c_str(), "r");
|
||||||
|
if (!f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char *line = nullptr;
|
||||||
|
size_t cap = 0;
|
||||||
|
while (getline(&line, &cap, f) >= 0) {
|
||||||
|
std::string text(line);
|
||||||
|
size_t comment = text.find('#');
|
||||||
|
if (comment != std::string::npos)
|
||||||
|
text.resize(comment);
|
||||||
|
std::vector<std::string> words;
|
||||||
|
size_t pos = 0;
|
||||||
|
while (pos < text.size()) {
|
||||||
|
while (pos < text.size() && std::isspace(static_cast<unsigned char>(text[pos])))
|
||||||
|
pos++;
|
||||||
|
if (pos == text.size())
|
||||||
|
break;
|
||||||
|
size_t end = pos;
|
||||||
|
while (end < text.size() && !std::isspace(static_cast<unsigned char>(text[end])))
|
||||||
|
end++;
|
||||||
|
words.emplace_back(text, pos, end - pos);
|
||||||
|
pos = end;
|
||||||
|
}
|
||||||
|
if (words.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (words[0] == "exclude") {
|
||||||
|
for (size_t i = 1; i < words.size(); i++)
|
||||||
|
config.excluded_dirs.insert(words[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
fclose(f);
|
||||||
|
(void)version;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string> config_files_in(const std::string &path)
|
||||||
|
{
|
||||||
|
std::vector<std::string> files;
|
||||||
|
struct stat st{};
|
||||||
|
if (stat(path.c_str(), &st) != 0)
|
||||||
|
return files;
|
||||||
|
if (S_ISREG(st.st_mode)) {
|
||||||
|
files.push_back(path);
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
if (!S_ISDIR(st.st_mode))
|
||||||
|
return files;
|
||||||
|
|
||||||
|
DIR *dir = opendir(path.c_str());
|
||||||
|
if (!dir)
|
||||||
|
return files;
|
||||||
|
while (dirent *de = readdir(dir)) {
|
||||||
|
if (de->d_name[0] == '.' || !has_suffix(de->d_name, ".conf"))
|
||||||
|
continue;
|
||||||
|
std::string candidate = path_join(path, de->d_name);
|
||||||
|
if (stat(candidate.c_str(), &st) == 0 && S_ISREG(st.st_mode))
|
||||||
|
files.push_back(std::move(candidate));
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
std::sort(files.begin(), files.end());
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DepmodConfig load_depmod_config(const Options &opts)
|
||||||
|
{
|
||||||
|
DepmodConfig config;
|
||||||
|
std::vector<std::string> paths = opts.config_paths;
|
||||||
|
if (paths.empty()) {
|
||||||
|
paths = {
|
||||||
|
path_join(opts.basedir, "etc/depmod.d"),
|
||||||
|
path_join(opts.basedir, "run/depmod.d"),
|
||||||
|
path_join(opts.basedir, "usr/local/lib/depmod.d"),
|
||||||
|
path_join(opts.basedir, "usr/lib/depmod.d"),
|
||||||
|
path_join(opts.basedir, "lib/depmod.d"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &path : paths) {
|
||||||
|
for (const auto &file : config_files_in(path))
|
||||||
|
parse_config_file(file, opts.version, config);
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_external_symbols(const Options &opts, std::set<std::string> &symbols)
|
||||||
|
{
|
||||||
|
const std::string &path = opts.symvers.empty() ? opts.filesyms : opts.symvers;
|
||||||
|
if (path.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
FILE *f = fopen(path.c_str(), "r");
|
||||||
|
if (!f)
|
||||||
|
return;
|
||||||
|
char line[10240];
|
||||||
|
while (fgets(line, sizeof(line), f)) {
|
||||||
|
if (!opts.symvers.empty()) {
|
||||||
|
char *save = nullptr;
|
||||||
|
char *crc = strtok_r(line, " \t\r\n", &save);
|
||||||
|
char *name = strtok_r(nullptr, " \t\r\n", &save);
|
||||||
|
char *owner = strtok_r(nullptr, " \t\r\n", &save);
|
||||||
|
if (crc && name && owner && strcmp(owner, "vmlinux") == 0)
|
||||||
|
symbols.insert(strip_known_symbol_prefix(name, opts.symbol_prefix));
|
||||||
|
} else {
|
||||||
|
char *name = strrchr(line, ' ');
|
||||||
|
if (!name)
|
||||||
|
continue;
|
||||||
|
name++;
|
||||||
|
while (*name == ' ' || *name == '\t')
|
||||||
|
name++;
|
||||||
|
char *end = name + strcspn(name, " \t\r\n");
|
||||||
|
*end = '\0';
|
||||||
|
static const char prefix[] = "__ksymtab_";
|
||||||
|
if (strncmp(name, prefix, sizeof(prefix) - 1) == 0)
|
||||||
|
symbols.insert(strip_known_symbol_prefix(name + sizeof(prefix) - 1,
|
||||||
|
opts.symbol_prefix));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
static std::string rel_to_root(const std::string &root, const std::string &path)
|
static std::string rel_to_root(const std::string &root, const std::string &path)
|
||||||
{
|
{
|
||||||
std::string r = trim_slashes_right(root);
|
std::string r = trim_slashes_right(root);
|
||||||
@@ -617,7 +828,7 @@ static bool module_less(const Module &a, const Module &b)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void resolve_dependencies(std::vector<Module> &mods, const Options &opts)
|
static bool resolve_dependencies(std::vector<Module> &mods, const Options &opts)
|
||||||
{
|
{
|
||||||
std::unordered_map<std::string, size_t> by_name;
|
std::unordered_map<std::string, size_t> by_name;
|
||||||
std::unordered_map<std::string, size_t> export_owner;
|
std::unordered_map<std::string, size_t> export_owner;
|
||||||
@@ -642,31 +853,55 @@ static void resolve_dependencies(std::vector<Module> &mods, const Options &opts)
|
|||||||
auto it = export_owner.find(sym);
|
auto it = export_owner.find(sym);
|
||||||
if (it != export_owner.end() && it->second != i)
|
if (it != export_owner.end() && it->second != i)
|
||||||
direct[i].insert(it->second);
|
direct[i].insert(it->second);
|
||||||
else if (opts.errsyms)
|
else if (opts.errsyms && opts.external_symbols.count(sym) == 0)
|
||||||
warnx("%s needs unknown symbol %s", mods[i].relpath.c_str(), sym.c_str());
|
warnx("%s needs unknown symbol %s", mods[i].relpath.c_str(), sym.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cycle = false;
|
||||||
for (size_t i = 0; i < mods.size(); i++) {
|
for (size_t i = 0; i < mods.size(); i++) {
|
||||||
std::set<size_t> seen;
|
std::set<size_t> seen;
|
||||||
std::vector<size_t> flat;
|
std::vector<size_t> flat;
|
||||||
|
std::vector<uint8_t> state(mods.size(), 0);
|
||||||
|
std::vector<size_t> stack;
|
||||||
std::function<void(size_t)> dfs = [&](size_t n) {
|
std::function<void(size_t)> dfs = [&](size_t n) {
|
||||||
|
if (state[n] == 2)
|
||||||
|
return;
|
||||||
|
if (state[n] == 1) {
|
||||||
|
std::string cycle_line;
|
||||||
|
for (size_t node : stack) {
|
||||||
|
if (!cycle_line.empty())
|
||||||
|
cycle_line += " -> ";
|
||||||
|
cycle_line += mods[node].modname;
|
||||||
|
}
|
||||||
|
if (!cycle_line.empty())
|
||||||
|
cycle_line += " -> ";
|
||||||
|
cycle_line += mods[n].modname;
|
||||||
|
warnx("Cycle detected: %s", cycle_line.c_str());
|
||||||
|
cycle = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state[n] = 1;
|
||||||
|
stack.push_back(n);
|
||||||
std::vector<size_t> children(direct[n].begin(), direct[n].end());
|
std::vector<size_t> children(direct[n].begin(), direct[n].end());
|
||||||
std::sort(children.begin(), children.end(), [&](size_t a, size_t b) {
|
std::sort(children.begin(), children.end(), [&](size_t a, size_t b) {
|
||||||
return module_less(mods[a], mods[b]);
|
return module_less(mods[a], mods[b]);
|
||||||
});
|
});
|
||||||
for (size_t d : children) {
|
for (size_t d : children) {
|
||||||
if (d == i || seen.count(d))
|
if (seen.count(d))
|
||||||
continue;
|
continue;
|
||||||
seen.insert(d);
|
seen.insert(d);
|
||||||
dfs(d);
|
dfs(d);
|
||||||
flat.push_back(d);
|
flat.push_back(d);
|
||||||
}
|
}
|
||||||
|
stack.pop_back();
|
||||||
|
state[n] = 2;
|
||||||
};
|
};
|
||||||
dfs(i);
|
dfs(i);
|
||||||
for (size_t d : flat)
|
for (size_t d : flat)
|
||||||
mods[i].deps_paths.push_back(mods[d].relpath);
|
mods[i].deps_paths.push_back(mods[d].relpath);
|
||||||
}
|
}
|
||||||
|
return !cycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string dep_line(const Module &m)
|
static std::string dep_line(const Module &m)
|
||||||
@@ -677,28 +912,30 @@ static std::string dep_line(const Module &m)
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string alias_normalize(const std::string &alias)
|
static bool alias_normalize(const std::string &alias, std::string &out)
|
||||||
{
|
{
|
||||||
// Match kmod's alias index normalization: dashes are stored as
|
out.clear();
|
||||||
// underscores, except inside bracket ranges such as [0-9].
|
|
||||||
std::string out;
|
|
||||||
out.reserve(alias.size());
|
out.reserve(alias.size());
|
||||||
bool in_range = false;
|
bool in_range = false;
|
||||||
for (char c : alias) {
|
for (char c : alias) {
|
||||||
if (c == '[')
|
if (c == '[')
|
||||||
in_range = true;
|
in_range = true;
|
||||||
if (c == ']' && in_range)
|
if (c == ']') {
|
||||||
|
if (!in_range)
|
||||||
|
return false;
|
||||||
in_range = false;
|
in_range = false;
|
||||||
|
}
|
||||||
if (c == '-' && !in_range)
|
if (c == '-' && !in_range)
|
||||||
out.push_back('_');
|
out.push_back('_');
|
||||||
else
|
else
|
||||||
out.push_back(c);
|
out.push_back(c);
|
||||||
}
|
}
|
||||||
return out;
|
return !in_range;
|
||||||
}
|
}
|
||||||
|
|
||||||
class BinaryIndex {
|
class BinaryIndex {
|
||||||
struct Node {
|
struct Node {
|
||||||
|
std::string prefix;
|
||||||
std::map<uint8_t, std::unique_ptr<Node>> children;
|
std::map<uint8_t, std::unique_ptr<Node>> children;
|
||||||
std::vector<std::pair<uint32_t, std::string>> values;
|
std::vector<std::pair<uint32_t, std::string>> values;
|
||||||
uint32_t size = 0;
|
uint32_t size = 0;
|
||||||
@@ -721,6 +958,8 @@ class BinaryIndex {
|
|||||||
uint32_t m = 0;
|
uint32_t m = 0;
|
||||||
if (!n.children.empty())
|
if (!n.children.empty())
|
||||||
m |= INDEX_NODE_CHILDS;
|
m |= INDEX_NODE_CHILDS;
|
||||||
|
if (!n.prefix.empty())
|
||||||
|
m |= INDEX_NODE_PREFIX;
|
||||||
if (!n.values.empty())
|
if (!n.values.empty())
|
||||||
m |= INDEX_NODE_VALUES;
|
m |= INDEX_NODE_VALUES;
|
||||||
return m;
|
return m;
|
||||||
@@ -730,6 +969,8 @@ class BinaryIndex {
|
|||||||
{
|
{
|
||||||
n.size = 0;
|
n.size = 0;
|
||||||
n.total = 0;
|
n.total = 0;
|
||||||
|
if (!n.prefix.empty())
|
||||||
|
n.size += static_cast<uint32_t>(n.prefix.size() + 1);
|
||||||
if (!n.children.empty()) {
|
if (!n.children.empty()) {
|
||||||
uint8_t first = n.children.begin()->first;
|
uint8_t first = n.children.begin()->first;
|
||||||
uint8_t last = n.children.rbegin()->first;
|
uint8_t last = n.children.rbegin()->first;
|
||||||
@@ -758,6 +999,8 @@ class BinaryIndex {
|
|||||||
static bool write_node(FILE *f, const Node &n, uint32_t offset)
|
static bool write_node(FILE *f, const Node &n, uint32_t offset)
|
||||||
{
|
{
|
||||||
std::map<uint8_t, uint32_t> child_offsets;
|
std::map<uint8_t, uint32_t> child_offsets;
|
||||||
|
if (!n.prefix.empty() && fwrite(n.prefix.c_str(), 1, n.prefix.size() + 1, f) != n.prefix.size() + 1)
|
||||||
|
return false;
|
||||||
if (!n.children.empty()) {
|
if (!n.children.empty()) {
|
||||||
uint32_t child_base = offset + n.size;
|
uint32_t child_base = offset + n.size;
|
||||||
uint32_t accum = 0;
|
uint32_t accum = 0;
|
||||||
@@ -796,27 +1039,51 @@ class BinaryIndex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void insert(const std::string &key, const std::string &value, uint32_t priority)
|
bool insert(const std::string &key, const std::string &value, uint32_t priority)
|
||||||
{
|
{
|
||||||
if (!ascii_ok(key) || !ascii_ok(value))
|
if (!ascii_ok(key) || !ascii_ok(value))
|
||||||
return;
|
return false;
|
||||||
Node *n = &root_;
|
Node *n = &root_;
|
||||||
for (unsigned char c : key) {
|
size_t pos = 0;
|
||||||
auto &child = n->children[c];
|
for (;;) {
|
||||||
if (!child)
|
size_t common = 0;
|
||||||
child = std::make_unique<Node>();
|
while (common < n->prefix.size() && pos + common < key.size() &&
|
||||||
n = child.get();
|
n->prefix[common] == key[pos + common])
|
||||||
|
common++;
|
||||||
|
if (common != n->prefix.size()) {
|
||||||
|
const std::string original = n->prefix;
|
||||||
|
auto old = std::make_unique<Node>(std::move(*n));
|
||||||
|
const uint8_t edge = static_cast<uint8_t>(original[common]);
|
||||||
|
old->prefix.erase(0, common + 1);
|
||||||
|
n->prefix = original.substr(0, common);
|
||||||
|
n->children.clear();
|
||||||
|
n->values.clear();
|
||||||
|
n->children.emplace(edge, std::move(old));
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += n->prefix.size();
|
||||||
|
if (pos == key.size()) {
|
||||||
|
const bool duplicate = std::find_if(n->values.begin(), n->values.end(),
|
||||||
|
[&](const auto &entry) { return entry.second == value; }) != n->values.end();
|
||||||
|
auto it = n->values.begin();
|
||||||
|
while (it != n->values.end() && it->first < priority)
|
||||||
|
++it;
|
||||||
|
n->values.insert(it, {priority, value});
|
||||||
|
return duplicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t edge = static_cast<uint8_t>(key[pos]);
|
||||||
|
auto it = n->children.find(edge);
|
||||||
|
if (it == n->children.end()) {
|
||||||
|
auto child = std::make_unique<Node>();
|
||||||
|
child->prefix = key.substr(pos + 1);
|
||||||
|
child->values.emplace_back(priority, value);
|
||||||
|
n->children.emplace(edge, std::move(child));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
n = it->second.get();
|
||||||
|
pos++;
|
||||||
}
|
}
|
||||||
auto dup = std::find_if(n->values.begin(), n->values.end(), [&](const auto &p) {
|
|
||||||
return p.second == value;
|
|
||||||
});
|
|
||||||
if (dup == n->values.end())
|
|
||||||
n->values.emplace_back(priority, value);
|
|
||||||
std::sort(n->values.begin(), n->values.end(), [](const auto &a, const auto &b) {
|
|
||||||
if (a.first != b.first)
|
|
||||||
return a.first < b.first;
|
|
||||||
return a.second < b.second;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool write(FILE *f)
|
bool write(FILE *f)
|
||||||
@@ -946,8 +1213,6 @@ static std::string build_modules_devname(const std::vector<Module> &mods)
|
|||||||
out += buf;
|
out += buf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (out.empty())
|
|
||||||
out = "# Device nodes to trigger on-demand module loading.\n";
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -991,8 +1256,11 @@ static void add_builtin_alias_index(const std::string &module_root, BinaryIndex
|
|||||||
std::string mod = e.substr(0, dot);
|
std::string mod = e.substr(0, dot);
|
||||||
std::string key = e.substr(dot + 1, eq - dot - 1);
|
std::string key = e.substr(dot + 1, eq - dot - 1);
|
||||||
std::string val = e.substr(eq + 1);
|
std::string val = e.substr(eq + 1);
|
||||||
if (key == "alias")
|
if (key == "alias") {
|
||||||
idx.insert(alias_normalize(val), normalize_modname(mod), prio++);
|
std::string normalized;
|
||||||
|
if (alias_normalize(val, normalized))
|
||||||
|
idx.insert(normalized, normalize_modname(mod), prio++);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pos = next + 1;
|
pos = next + 1;
|
||||||
@@ -1034,11 +1302,16 @@ static bool output_all(const std::string &module_root, const std::string &out_ro
|
|||||||
BinaryIndex builtinaliasidx;
|
BinaryIndex builtinaliasidx;
|
||||||
for (uint32_t i = 0; i < mods.size(); i++) {
|
for (uint32_t i = 0; i < mods.size(); i++) {
|
||||||
const Module &m = mods[i];
|
const Module &m = mods[i];
|
||||||
depidx.insert(m.modname, dep_line(m), i);
|
if (depidx.insert(m.modname, dep_line(m), i) && opts.warn)
|
||||||
for (const auto &a : m.aliases)
|
warnx("duplicate module deps:\n%s", dep_line(m).c_str());
|
||||||
aliasidx.insert(alias_normalize(a), m.modname, i);
|
for (const auto &a : m.aliases) {
|
||||||
|
std::string normalized;
|
||||||
|
if (alias_normalize(a, normalized) && aliasidx.insert(normalized, m.modname, i) && opts.warn)
|
||||||
|
warnx("duplicate module alias:\n%s %s", normalized.c_str(), m.modname.c_str());
|
||||||
|
}
|
||||||
for (const auto &s : m.exports)
|
for (const auto &s : m.exports)
|
||||||
symidx.insert("symbol:" + s, m.modname, i);
|
if (symidx.insert("symbol:" + s, m.modname, i) && opts.warn)
|
||||||
|
warnx("duplicate module syms:\n%s %s", s.c_str(), m.modname.c_str());
|
||||||
}
|
}
|
||||||
add_builtin_index(module_root, builtinidx);
|
add_builtin_index(module_root, builtinidx);
|
||||||
add_builtin_alias_index(module_root, builtinaliasidx);
|
add_builtin_alias_index(module_root, builtinaliasidx);
|
||||||
@@ -1046,7 +1319,9 @@ static bool output_all(const std::string &module_root, const std::string &out_ro
|
|||||||
ok = write_index_file(path_join(out_root, "modules.dep.bin"), depidx) && ok;
|
ok = write_index_file(path_join(out_root, "modules.dep.bin"), depidx) && ok;
|
||||||
ok = write_index_file(path_join(out_root, "modules.alias.bin"), aliasidx) && ok;
|
ok = write_index_file(path_join(out_root, "modules.alias.bin"), aliasidx) && ok;
|
||||||
ok = write_index_file(path_join(out_root, "modules.symbols.bin"), symidx) && ok;
|
ok = write_index_file(path_join(out_root, "modules.symbols.bin"), symidx) && ok;
|
||||||
|
if (access(path_join(module_root, "modules.builtin").c_str(), F_OK) == 0)
|
||||||
ok = write_index_file(path_join(out_root, "modules.builtin.bin"), builtinidx) && ok;
|
ok = write_index_file(path_join(out_root, "modules.builtin.bin"), builtinidx) && ok;
|
||||||
|
if (access(path_join(module_root, "modules.builtin.modinfo").c_str(), F_OK) == 0)
|
||||||
ok = write_index_file(path_join(out_root, "modules.builtin.alias.bin"), builtinaliasidx) && ok;
|
ok = write_index_file(path_join(out_root, "modules.builtin.alias.bin"), builtinaliasidx) && ok;
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
@@ -1078,24 +1353,29 @@ static void usage(FILE *out)
|
|||||||
{
|
{
|
||||||
fputs("Usage:\n"
|
fputs("Usage:\n"
|
||||||
"\tdepmod -[aA] [options] [forced_version]\n"
|
"\tdepmod -[aA] [options] [forced_version]\n"
|
||||||
"\tdepmod [options] [forced_version] [modules...]\n"
|
"\n"
|
||||||
|
"If no arguments (except options) are given, \\\"depmod -a\\\" is assumed\n"
|
||||||
|
"\n"
|
||||||
|
"depmod will output a dependency list suitable for the modprobe utility.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Options:\n"
|
"Options:\n"
|
||||||
"\t-a, --all Probe all modules\n"
|
"\t-a, --all Probe all modules\n"
|
||||||
"\t-A, --quick Skip if modules.dep is newer than modules\n"
|
"\t-A, --quick Only does the work if there's a new module\n"
|
||||||
"\t-b, --basedir=DIR Input root path (default: /)\n"
|
"\t-e, --errsyms Report not supplied symbols\n"
|
||||||
"\t-m, --moduledir=DIR Module directory (default: /lib/modules)\n"
|
"\t-n, --show Write the dependency file on stdout only\n"
|
||||||
"\t-o, --outdir=DIR Output root path (default: basedir)\n"
|
"\t-P, --symbol-prefix Architecture symbol prefix\n"
|
||||||
"\t-C, --config=PATH Accepted for compatibility\n"
|
"\t-C, --config=PATH Read configuration from PATH\n"
|
||||||
"\t-e, --errsyms Warn about unresolved module symbols\n"
|
"\t-v, --verbose Enable verbose mode\n"
|
||||||
"\t-F, --filesyms=FILE Accepted for compatibility\n"
|
"\t-w, --warn Warn on duplicates\n"
|
||||||
"\t-E, --symvers=FILE Accepted for compatibility\n"
|
|
||||||
"\t-n, --show Write text output to stdout only\n"
|
|
||||||
"\t-P, --symbol-prefix Ignore architecture symbol prefix\n"
|
|
||||||
"\t-v, --verbose Print scanned modules\n"
|
|
||||||
"\t-w, --warn Enable duplicate warnings\n"
|
|
||||||
"\t-V, --version Show version\n"
|
"\t-V, --version Show version\n"
|
||||||
"\t-h, --help Show this help\n", out);
|
"\t-h, --help Show this help\n"
|
||||||
|
"\n"
|
||||||
|
"The following options are useful for people managing distributions:\n"
|
||||||
|
"\t-b, --basedir=DIR Root path (default: /).\n"
|
||||||
|
"\t-m, --moduledir=DIR Module directory (default: /lib/modules).\n"
|
||||||
|
"\t-o, --outdir=DIR Output root path (default: same as <basedir>).\n"
|
||||||
|
"\t-F, --filesyms=FILE Use the file instead of the current kernel symbols.\n"
|
||||||
|
"\t-E, --symvers=FILE Use Module.symvers to check symbol versions.\n", out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool looks_like_version(const std::string &s)
|
static bool looks_like_version(const std::string &s)
|
||||||
@@ -1105,6 +1385,17 @@ static bool looks_like_version(const std::string &s)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_version_number(const std::string &s)
|
||||||
|
{
|
||||||
|
size_t dot = s.find('.');
|
||||||
|
if (dot == std::string::npos || dot == 0 || dot + 1 == s.size())
|
||||||
|
return false;
|
||||||
|
for (size_t i = 0; i < dot; i++)
|
||||||
|
if (!std::isdigit(static_cast<unsigned char>(s[i])))
|
||||||
|
return false;
|
||||||
|
return std::isdigit(static_cast<unsigned char>(s[dot + 1]));
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_args(int argc, char **argv, Options &opts)
|
static int parse_args(int argc, char **argv, Options &opts)
|
||||||
{
|
{
|
||||||
static const option long_opts[] = {
|
static const option long_opts[] = {
|
||||||
@@ -1131,20 +1422,26 @@ static int parse_args(int argc, char **argv, Options &opts)
|
|||||||
int c;
|
int c;
|
||||||
while ((c = getopt_long(argc, argv, "aAb:m:o:C:E:F:evnP:wVh", long_opts, nullptr)) != -1) {
|
while ((c = getopt_long(argc, argv, "aAb:m:o:C:E:F:evnP:wVh", long_opts, nullptr)) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'a': opts.all = true; break;
|
case 'a': break;
|
||||||
case 'A': opts.quick = true; break;
|
case 'A': opts.quick = true; break;
|
||||||
case 'b': opts.basedir = optarg; break;
|
case 'b': opts.basedir = optarg; break;
|
||||||
case 'm': opts.moduledir = optarg; break;
|
case 'm': opts.moduledir = optarg; break;
|
||||||
case 'o': opts.outdir = optarg; break;
|
case 'o': opts.outdir = optarg; break;
|
||||||
case 'C': opts.config = optarg; break;
|
case 'C': opts.config_paths.emplace_back(optarg); break;
|
||||||
case 'E': opts.symvers = optarg; break;
|
case 'E': opts.symvers = optarg; break;
|
||||||
case 'F': opts.filesyms = optarg; break;
|
case 'F': opts.filesyms = optarg; break;
|
||||||
case 'e': opts.errsyms = true; break;
|
case 'e': opts.errsyms = true; break;
|
||||||
case 'v': opts.verbose = true; break;
|
case 'v': opts.verbose = true; break;
|
||||||
case 'n': opts.dry_run = true; break;
|
case 'n': opts.dry_run = true; break;
|
||||||
case 'P': opts.symbol_prefix = optarg && optarg[0] ? optarg[0] : '\0'; break;
|
case 'P':
|
||||||
|
if (!optarg || strlen(optarg) != 1) {
|
||||||
|
warnx("-P only takes a single char");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
opts.symbol_prefix = optarg[0];
|
||||||
|
break;
|
||||||
case 'w': opts.warn = true; break;
|
case 'w': opts.warn = true; break;
|
||||||
case 'V': puts("vx depmod 0.1"); return 1;
|
case 'V': puts("kmod version 34.2"); return 1;
|
||||||
case 'h': usage(stdout); return 1;
|
case 'h': usage(stdout); return 1;
|
||||||
default: usage(stderr); return -1;
|
default: usage(stderr); return -1;
|
||||||
}
|
}
|
||||||
@@ -1168,6 +1465,10 @@ static int parse_args(int argc, char **argv, Options &opts)
|
|||||||
warnx("could not determine kernel release");
|
warnx("could not determine kernel release");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (!is_version_number(opts.version)) {
|
||||||
|
warnx("Bad version passed %s", opts.version.c_str());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (opts.outdir.empty())
|
if (opts.outdir.empty())
|
||||||
opts.outdir = opts.basedir;
|
opts.outdir = opts.basedir;
|
||||||
|
|
||||||
@@ -1192,7 +1493,14 @@ extern "C" int depmod_main(int argc, char **argv)
|
|||||||
std::string out_parent = path_join(opts.outdir, opts.moduledir);
|
std::string out_parent = path_join(opts.outdir, opts.moduledir);
|
||||||
std::string out_root = path_join(out_parent, opts.version);
|
std::string out_root = path_join(out_parent, opts.version);
|
||||||
|
|
||||||
|
if (opts.errsyms && opts.symvers.empty() && opts.filesyms.empty()) {
|
||||||
|
warnx("-e needs -E or -F");
|
||||||
|
opts.errsyms = false;
|
||||||
|
}
|
||||||
|
load_external_symbols(opts, opts.external_symbols);
|
||||||
|
|
||||||
std::vector<std::string> paths;
|
std::vector<std::string> paths;
|
||||||
|
DepmodConfig config = load_depmod_config(opts);
|
||||||
if (!opts.files.empty()) {
|
if (!opts.files.empty()) {
|
||||||
for (const auto &f : opts.files) {
|
for (const auto &f : opts.files) {
|
||||||
if (f.empty())
|
if (f.empty())
|
||||||
@@ -1203,8 +1511,7 @@ extern "C" int depmod_main(int argc, char **argv)
|
|||||||
paths.push_back(path_join(module_root, f));
|
paths.push_back(path_join(module_root, f));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
opts.all = true;
|
scan_modules_recursive(module_root, config, paths);
|
||||||
scan_modules_recursive(module_root, module_root, paths);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(paths.begin(), paths.end());
|
std::sort(paths.begin(), paths.end());
|
||||||
@@ -1228,7 +1535,6 @@ extern "C" int depmod_main(int argc, char **argv)
|
|||||||
m.path = p;
|
m.path = p;
|
||||||
m.relpath = rel_to_root(module_root, p);
|
m.relpath = rel_to_root(module_root, p);
|
||||||
m.modname = normalize_modname(m.relpath);
|
m.modname = normalize_modname(m.relpath);
|
||||||
m.scan_index = mods.size();
|
|
||||||
if (parse_module(m, opts)) {
|
if (parse_module(m, opts)) {
|
||||||
if (opts.verbose)
|
if (opts.verbose)
|
||||||
fprintf(stdout, "%s\n", m.relpath.c_str());
|
fprintf(stdout, "%s\n", m.relpath.c_str());
|
||||||
@@ -1238,9 +1544,8 @@ extern "C" int depmod_main(int argc, char **argv)
|
|||||||
|
|
||||||
load_modules_order(module_root, mods);
|
load_modules_order(module_root, mods);
|
||||||
std::sort(mods.begin(), mods.end(), module_less);
|
std::sort(mods.begin(), mods.end(), module_less);
|
||||||
for (size_t i = 0; i < mods.size(); i++)
|
if (!resolve_dependencies(mods, opts))
|
||||||
mods[i].scan_index = i;
|
return 1;
|
||||||
resolve_dependencies(mods, opts);
|
|
||||||
|
|
||||||
if (!output_all(module_root, out_root, mods, opts)) {
|
if (!output_all(module_root, out_root, mods, opts)) {
|
||||||
warnx("failed to write dependency files under %s", out_root.c_str());
|
warnx("failed to write dependency files under %s", out_root.c_str());
|
||||||
|
|||||||
@@ -2,3 +2,27 @@
|
|||||||
set -eu
|
set -eu
|
||||||
"$VX" depmod --help >/dev/null
|
"$VX" depmod --help >/dev/null
|
||||||
"$VX" depmod --version >/dev/null
|
"$VX" depmod --version >/dev/null
|
||||||
|
|
||||||
|
if command -v zstd >/dev/null 2>&1 && command -v xz >/dev/null 2>&1; then
|
||||||
|
_test_dir=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$_test_dir"' EXIT
|
||||||
|
_module_dir="$_test_dir/root/lib/modules/7.1.3-vertex/kernel"
|
||||||
|
mkdir -p "$_module_dir/build" "$_module_dir/source"
|
||||||
|
cp /bin/true "$_module_dir/build/ignored.ko"
|
||||||
|
cp /bin/true "$_module_dir/source/ignored.ko"
|
||||||
|
|
||||||
|
cp /bin/true "$_module_dir/acpi_pad.ko"
|
||||||
|
zstd -q -f "$_module_dir/acpi_pad.ko" -o "$_module_dir/acpi_pad.ko.zst"
|
||||||
|
rm "$_module_dir/acpi_pad.ko"
|
||||||
|
cp /bin/true "$_module_dir/p4-clockmod.ko"
|
||||||
|
xz -f "$_module_dir/p4-clockmod.ko"
|
||||||
|
cp /bin/true "$_module_dir/soundwire-bus.ko"
|
||||||
|
|
||||||
|
"$VX" depmod -b "$_test_dir/root" 7.1.3-vertex 2>"$_test_dir/stderr"
|
||||||
|
test ! -s "$_test_dir/stderr"
|
||||||
|
grep -Fx 'kernel/acpi_pad.ko.zst:' "$_test_dir/root/lib/modules/7.1.3-vertex/modules.dep"
|
||||||
|
grep -Fx 'kernel/p4-clockmod.ko.xz:' "$_test_dir/root/lib/modules/7.1.3-vertex/modules.dep"
|
||||||
|
grep -Fx 'kernel/soundwire-bus.ko:' "$_test_dir/root/lib/modules/7.1.3-vertex/modules.dep"
|
||||||
|
test "$(wc -l < "$_test_dir/root/lib/modules/7.1.3-vertex/modules.dep")" -eq 3
|
||||||
|
test ! -s "$_test_dir/root/lib/modules/7.1.3-vertex/modules.devname"
|
||||||
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user