From 12d6c8bd03fa64f36a0c0d180e0eb6d493e75060 Mon Sep 17 00:00:00 2001
From: Jiangjin Wang <kaymw@aosc.io>
Date: Tue, 13 Feb 2024 19:11:39 -0800
Subject: [PATCH] feat(abnativeelf): generate spiral pkgnames while scanning
 ELF files

---
 native/abnativeelf.cpp       | 96 ++++++++++++++++++++++++++++++++++--
 native/abnativeelf.hpp       |  5 +-
 native/abnativefunctions.cpp | 28 +++++++----
 3 files changed, 115 insertions(+), 14 deletions(-)

diff --git a/native/abnativeelf.cpp b/native/abnativeelf.cpp
index a0c8aa0..e8f5e64 100644
--- a/native/abnativeelf.cpp
+++ b/native/abnativeelf.cpp
@@ -11,6 +11,7 @@
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <thread>
+#include <algorithm>
 
 #include <elf.h>
 #include <sys/wait.h>
@@ -491,8 +492,75 @@ static inline int forked_execvp(const char *path, char *const argv[]) {
   return status;
 }
 
+std::vector<std::string>
+to_spiral_provides(const std::string &soname) {
+  std::vector<std::string> ret;
+
+  if (soname.empty()) return ret;
+
+  // Rule out strings without the ".so" extension name
+  const size_t libname_pos = soname.find(".so");
+  if (libname_pos == std::string::npos) return ret;
+
+  // Extract libname
+  auto libname = soname.substr(0, libname_pos);
+  for (auto &c : libname) {
+    // - is not allowed
+    if (c == '_') {
+      c = '-';
+      continue;
+    }
+    // Only [a-z0-9.-+] allowed
+    if ((c >= '0') && (c <= '9')) continue;
+    if ((c == '-') || (c == '+') || (c == '.')) continue;
+    c = tolower(c);
+
+    // Contains invalid character
+    if ((c < 'a') || (c > 'z')) {
+      return ret;
+    }
+  }
+
+  // Remove all occurrences of '-'
+  libname.erase(std::remove(libname.begin(), libname.end(), '-'), libname.end());
+
+  auto dev_name = fmt::format("{}-dev", libname);
+
+  // Extract the major part of sover, +3 for ".so"
+  const size_t sover_start = libname_pos + 3;
+  // If there's no sover
+  if (sover_start == soname.size()) {
+    ret.emplace_back(libname);
+    ret.emplace_back(fmt::format("{}-dev", libname));
+    return ret;
+  }
+  // Make sure there's the dot
+  if (soname[sover_start] != '.') return ret;
+  size_t sover_major = 0;
+  for (const auto &c : soname.substr(sover_start + 1, soname.size())) {
+    if (c == '.') break;
+    if ((c < '0') || c > '9') return ret;
+    sover_major *= 10;
+    sover_major += c - '0';
+  }
+
+  // Add a '-' in between libname and sover if libname ends with a digit
+  char libname_last = libname[libname.size() - 1];
+  if ((libname_last >= '0') && (libname_last <= '9')) {
+    ret.emplace_back(fmt::format("{}-{}", libname, sover_major));
+  } else {
+    ret.emplace_back(fmt::format("{}{}", libname, sover_major));
+  }
+
+  // Add -dev package name
+  ret.emplace_back(fmt::format("{}-dev", libname));
+
+  return ret;
+}
+
 int elf_copy_debug_symbols(const char *src_path, const char *dst_path,
-                           int flags, GuardedSet<std::string> &symbols) {
+                           int flags, GuardedSet<std::string> &symbols,
+                           GuardedSet<std::string> &spiral_provides) {
   int fd = open(src_path, O_RDONLY, 0);
   if (fd < 0) {
     perror("open");
@@ -513,6 +581,17 @@ int elf_copy_debug_symbols(const char *src_path, const char *dst_path,
   const char *data = static_cast<const char *>(file.addr());
   const ELFParseResult result = identify_binary_data(data, size);
 
+  if (flags & AB_ELF_GENERATE_SPIRAL_PROVIDES) {
+    const std::string filename(basename(src_path));
+    const auto spiral_filename = to_spiral_provides(filename);
+    spiral_provides.insert(spiral_filename.begin(), spiral_filename.end());
+
+    if (! result.soname.empty()) {
+      const auto spiral_soname = to_spiral_provides(result.soname);
+      spiral_provides.insert(spiral_soname.begin(), spiral_soname.end());
+    }
+  }
+
   if (flags & AB_ELF_FIND_SO_DEPS) {
     symbols.insert(result.needed_libs.begin(), result.needed_libs.end());
   }
@@ -629,22 +708,28 @@ public:
   ELFWorkerPool(std::string  symdir, int flags)
       : ThreadPool<std::string, int>([&, flags](const std::string& src_path) {
           return elf_copy_debug_symbols(src_path.c_str(), m_symdir.c_str(),
-                                        flags, m_sodeps);
+                                        flags, m_sodeps, m_spiral_provides);
         }),
-        m_symdir(std::move(symdir)), m_sodeps() {}
+        m_symdir(std::move(symdir)), m_sodeps(), m_spiral_provides() {}
 
   const std::unordered_set<std::string> get_sodeps() const {
     return m_sodeps.get_set();
   }
 
+  const std::unordered_set<std::string> get_spiral_provides() const {
+    return m_spiral_provides.get_set();
+  }
+
 private:
   const std::string m_symdir;
   GuardedSet<std::string> m_sodeps;
+  GuardedSet<std::string> m_spiral_provides;
 };
 
 int elf_copy_debug_symbols_parallel(const std::vector<std::string> &directories,
                                     const char *dst_path,
                                     std::unordered_set<std::string> &so_deps,
+                                    std::unordered_set<std::string> &spiral_provides,
                                     int flags) {
   ELFWorkerPool pool{dst_path, flags};
   for (const auto &directory : directories) {
@@ -666,5 +751,10 @@ int elf_copy_debug_symbols_parallel(const std::vector<std::string> &directories,
     so_deps.insert(pool_results.begin(), pool_results.end());
   }
 
+  if (flags & AB_ELF_GENERATE_SPIRAL_PROVIDES) {
+    const auto spiral_results = pool.get_spiral_provides();
+    spiral_provides.insert(spiral_results.begin(), spiral_results.end());
+  }
+
   return 0;
 }
diff --git a/native/abnativeelf.hpp b/native/abnativeelf.hpp
index e14d331..399f3be 100644
--- a/native/abnativeelf.hpp
+++ b/native/abnativeelf.hpp
@@ -44,12 +44,15 @@ constexpr int AB_ELF_USE_EU_STRIP = 1 << 1;
 constexpr int AB_ELF_FIND_SO_DEPS = 1 << 2;
 constexpr int AB_ELF_CHECK_ONLY = 1 << 3;
 constexpr int AB_ELF_SAVE_WITH_PATH = 1 << 4;
+constexpr int AB_ELF_GENERATE_SPIRAL_PROVIDES = 1 << 5;
 
 int elf_copy_to_symdir(const char *src_path, const char *dst_path,
                        const char *build_id);
 int elf_copy_debug_symbols(const char *src_path, const char *dst_path,
-                           int flags, GuardedSet<std::string> &symbols);
+                           int flags, GuardedSet<std::string> &symbols,
+                           GuardedSet<std::string> &spiral_provides);
 int elf_copy_debug_symbols_parallel(const std::vector<std::string> &directories,
                                     const char *dst_path,
                                     std::unordered_set<std::string> &so_deps,
+                                    std::unordered_set<std::string> &spiral_provides,
                                     int flags = AB_ELF_USE_EU_STRIP);
diff --git a/native/abnativefunctions.cpp b/native/abnativefunctions.cpp
index 9ca075e..6784cc5 100644
--- a/native/abnativefunctions.cpp
+++ b/native/abnativefunctions.cpp
@@ -807,13 +807,23 @@ static int abelf_copy_dbg(WORD_LIST *list) {
   if (!dst)
     return EX_BADUSAGE;
   GuardedSet<std::string> symbols{};
+  GuardedSet<std::string> spiral_provides;
   const int ret =
-      elf_copy_debug_symbols(src, dst, flags, symbols);
+      elf_copy_debug_symbols(src, dst, flags, symbols, spiral_provides);
   if (ret < 0)
     return 10;
   return 0;
 }
 
+static void ab_set_to_bash_array(const char *varname, const std::unordered_set<std::string> &set) {
+  auto *var = make_new_array_variable(const_cast<char *>(varname));
+  var->attributes |= att_readonly;
+  auto *var_a = array_cell(var);
+  for (const auto &elem : set) {
+    array_push(var_a, const_cast<char *>(elem.c_str()));
+  }
+}
+
 /**
  * Copy debug symbols for all files specified:
  * @param list arguments of the following form:
@@ -825,8 +835,9 @@ static int abelf_copy_dbg(WORD_LIST *list) {
  *      10  - error occurred during processing
  */
 static int abelf_copy_dbg_parallel(WORD_LIST *list) {
-  constexpr const char *varname = "__AB_SO_DEPS";
-  int flags = AB_ELF_FIND_SO_DEPS;
+  constexpr const char *varname_so_deps = "__AB_SO_DEPS";
+  constexpr const char *varname_spiral = "__AB_SPIRAL_PROVIDES";
+  int flags = AB_ELF_FIND_SO_DEPS | AB_ELF_GENERATE_SPIRAL_PROVIDES;
 
   reset_internal_getopt();
   int opt = 0;
@@ -855,17 +866,14 @@ static int abelf_copy_dbg_parallel(WORD_LIST *list) {
   const auto dst = std::string{args.back()};
   args.pop_back();
   std::unordered_set<std::string> so_deps{};
+  std::unordered_set<std::string> spiral_provides;
   const int ret =
-      elf_copy_debug_symbols_parallel(args, dst.c_str(), so_deps, flags);
+      elf_copy_debug_symbols_parallel(args, dst.c_str(), so_deps, spiral_provides, flags);
   if (ret < 0)
     return 10;
   // copy the data to the bash variable
-  auto *var = make_new_array_variable(const_cast<char *>(varname));
-  var->attributes |= att_readonly;
-  auto *var_a = array_cell(var);
-  for (const auto &so_dep : so_deps) {
-    array_push(var_a, const_cast<char *>(so_dep.c_str()));
-  }
+  ab_set_to_bash_array(varname_so_deps, so_deps);
+  ab_set_to_bash_array(varname_spiral, spiral_provides);
   return 0;
 }
 
-- 
GitLab