depmod.rs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. //! depmod is used to build the modules.alias file to assist with loading
  2. //! kernel modules.
  3. //!
  4. //! This implementation is incredibly naive and is only designed to work within
  5. //! the constraints of the test environment. Not for production use.
  6. use std::{
  7. fs::File,
  8. io::{BufWriter, Write as _},
  9. path::PathBuf,
  10. };
  11. use anyhow::{Context as _, anyhow};
  12. use clap::Parser;
  13. use object::{Object, ObjectSection, ObjectSymbol, Section};
  14. use test_distro::{read_to_end, resolve_modules_dir};
  15. use walkdir::WalkDir;
  16. #[derive(Parser)]
  17. struct Args {
  18. #[clap(long, short)]
  19. base_dir: Option<PathBuf>,
  20. }
  21. fn main() -> anyhow::Result<()> {
  22. let Args { base_dir } = Parser::parse();
  23. let modules_dir = if let Some(base_dir) = base_dir {
  24. base_dir
  25. } else {
  26. resolve_modules_dir().context("failed to resolve modules dir")?
  27. };
  28. let modules_alias = modules_dir.join("modules.alias");
  29. let f = std::fs::OpenOptions::new()
  30. .create(true)
  31. .write(true)
  32. .truncate(true)
  33. .open(&modules_alias)
  34. .with_context(|| format!("failed to open: {}", modules_alias.display()))?;
  35. let mut output = BufWriter::new(&f);
  36. for entry in WalkDir::new(modules_dir) {
  37. let entry = entry.context("failed to read entry in walkdir")?;
  38. if !entry.file_type().is_file() {
  39. continue;
  40. }
  41. let path = entry.path();
  42. let module_name = path
  43. .file_name()
  44. .ok_or_else(|| anyhow!("{} does not have a file name", path.display()))?
  45. .to_str()
  46. .ok_or_else(|| anyhow!("{} is not valid utf-8", path.display()))?;
  47. let (module_name, compressed) = if let Some(module_name) = module_name.strip_suffix(".xz") {
  48. (module_name, true)
  49. } else {
  50. (module_name, false)
  51. };
  52. let module_name = if let Some(module_name) = module_name.strip_suffix(".ko") {
  53. module_name
  54. } else {
  55. // Not a kernel module
  56. continue;
  57. };
  58. let contents = read_to_end(path, compressed)
  59. .with_context(|| format!("read_to_end({})", path.display()))?;
  60. read_aliases_from_module(&contents, module_name, &mut output)
  61. .with_context(|| format!("failed to read aliases from module {}", path.display()))?;
  62. }
  63. Ok(())
  64. }
  65. fn read_aliases_from_module(
  66. contents: &[u8],
  67. module_name: &str,
  68. output: &mut BufWriter<&File>,
  69. ) -> Result<(), anyhow::Error> {
  70. let obj = object::read::File::parse(contents).context("failed to parse")?;
  71. let section = (|| -> anyhow::Result<Option<Section<'_, '_, &[u8]>>> {
  72. for s in obj.sections() {
  73. let name = s
  74. .name_bytes()
  75. .with_context(|| format!("failed to get name of section idx {}", s.index()))?;
  76. if name == b".modinfo" {
  77. return Ok(Some(s));
  78. }
  79. }
  80. Ok(None)
  81. })()?;
  82. let section = section.context("failed to find .modinfo section")?;
  83. let section_idx = section.index();
  84. let data = section
  85. .data()
  86. .context("failed to get modinfo section data")?;
  87. for s in obj.symbols() {
  88. if s.section_index() != Some(section_idx) {
  89. continue;
  90. }
  91. let name = s
  92. .name()
  93. .with_context(|| format!("failed to get name of symbol idx {}", s.index()))?;
  94. if name.contains("alias") {
  95. let start = s.address() as usize;
  96. let end = start + s.size() as usize;
  97. let sym_data = &data[start..end];
  98. let cstr = std::ffi::CStr::from_bytes_with_nul(sym_data)
  99. .with_context(|| format!("failed to convert {sym_data:?} to cstr"))?;
  100. let sym_str = cstr
  101. .to_str()
  102. .with_context(|| format!("failed to convert {cstr:?} to str"))?;
  103. let alias = sym_str
  104. .strip_prefix("alias=")
  105. .with_context(|| format!("failed to strip prefix 'alias=' from {sym_str}"))?;
  106. writeln!(output, "alias {alias} {module_name}").expect("write");
  107. }
  108. }
  109. Ok(())
  110. }