Resolve paths of dir.${section} if possible

Some devices place some of their partitions under /system. If the linker
resolves that paths, verboseness of ld.config.txt will be reduced as we
don't need to add both /system/{partition} and /{partition}.

Bug: http://b/80422611
Test: m -j, boot on taimen, atest
Change-Id: I6b712170bb89229b764026e2cc517c426e6e6063
diff --git a/linker/linker_config.cpp b/linker/linker_config.cpp
index c00b734..f23ae83 100644
--- a/linker/linker_config.cpp
+++ b/linker/linker_config.cpp
@@ -39,6 +39,7 @@
 
 #include <async_safe/log.h>
 
+#include <limits.h>
 #include <stdlib.h>
 
 #include <string>
@@ -223,8 +224,8 @@
       }
 
       // remove trailing '/'
-      while (value[value.size() - 1] == '/') {
-        value = value.substr(0, value.size() - 1);
+      while (!value.empty() && value.back() == '/') {
+        value.pop_back();
       }
 
       if (value.empty()) {
@@ -234,7 +235,21 @@
         continue;
       }
 
-      if (file_is_under_dir(binary_realpath, value)) {
+      // If the path can be resolved, resolve it
+      char buf[PATH_MAX];
+      std::string resolved_path;
+      if (realpath(value.c_str(), buf)) {
+        resolved_path = buf;
+      } else {
+        DL_WARN("%s:%zd: warning: path \"%s\" couldn't be resolved: %s",
+                ld_config_file_path,
+                cp.lineno(),
+                value.c_str(),
+                strerror(errno));
+        resolved_path = value;
+      }
+
+      if (file_is_under_dir(binary_realpath, resolved_path)) {
         section_name = name.substr(4);
         break;
       }
diff --git a/linker/tests/linker_config_test.cpp b/linker/tests/linker_config_test.cpp
index e716879..9208d9d 100644
--- a/linker/tests/linker_config_test.cpp
+++ b/linker/tests/linker_config_test.cpp
@@ -246,3 +246,47 @@
             "error: both shared_libs and allow_all_shared_libs are set for default->system link.",
             error_msg);
 }
+
+TEST(linker_config, dir_path_resolve) {
+  // This unit test ensures the linker resolves paths of dir.${section}
+  // properties to real path.
+
+  TemporaryDir tmp_dir;
+
+  std::string sub_dir = std::string(tmp_dir.path) + "/subdir";
+  mkdir(sub_dir.c_str(), 0755);
+
+  auto subdir_guard =
+      android::base::make_scope_guard([&sub_dir] { rmdir(sub_dir.c_str()); });
+
+  std::string symlink_path = std::string(tmp_dir.path) + "/symlink";
+  symlink(sub_dir.c_str(), symlink_path.c_str());
+
+  auto symlink_guard =
+      android::base::make_scope_guard([&symlink_path] { unlink(symlink_path.c_str()); });
+
+  std::string config_str =
+      "dir.test = " + symlink_path + "\n"
+      "\n"
+      "[test]\n";
+
+  TemporaryFile tmp_file;
+  close(tmp_file.fd);
+  tmp_file.fd = -1;
+
+  android::base::WriteStringToFile(config_str, tmp_file.path);
+
+  std::string executable_path = sub_dir + "/some-binary";
+
+  const Config* config = nullptr;
+  std::string error_msg;
+
+  ASSERT_TRUE(Config::read_binary_config(tmp_file.path,
+                                         executable_path.c_str(),
+                                         false,
+                                         &config,
+                                         &error_msg)) << error_msg;
+
+  ASSERT_TRUE(config != nullptr) << error_msg;
+  ASSERT_TRUE(error_msg.empty()) << error_msg;
+}