Implements a non-modifying GNU basename() function for libunwindstack

This function is added in compat.h, which provides functions that are
available within libunwindstack's normal build environment but
unavailable within its Chromium build environment. We call the
function "compat_basename".

This function isn't available in Android NDK version 16 (it was first
made available in 23, as per https://bit.ly/31Gkyl9). Although there's a
POSIX basename() available via <libgen.h>, this function has different
behavior with regards to trailing slashes and can also modify its
argument under some circumstances.

Bug: 991960
Change-Id: I876d13d8e3c1797705c34c6d85ad663e2848f410
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/third_party/libunwindstack/+/1875324
Reviewed-by: Mike Wittman <wittman@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 5610496..cfe9380 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -8,6 +8,7 @@
   ]
   include_dirs = [
     "//third_party/libunwindstack/src/android-base/include",
+    "//third_party/libunwindstack/src/compat",
     "//third_party/libunwindstack/src/demangle/include",
     "//third_party/libunwindstack/src/liblog/include",
     "//third_party/libunwindstack/src/libprocinfo/include",
@@ -85,6 +86,8 @@
     "src/android-base/stringprintf.cpp",
     "src/android-base/strings.cpp",
     "src/android-base/threads.cpp",
+    "src/compat/compat.cc",
+    "src/compat/compat.h",
     "src/demangle/Demangler.cpp",
     "src/demangle/Demangler.h",
     "src/demangle/include/demangle.h",
diff --git a/README.chromium b/README.chromium
index 16839b9..4f05e5d 100644
--- a/README.chromium
+++ b/README.chromium
@@ -118,4 +118,14 @@
   remove the dependency on android-base/logging.cpp. logging.cpp is problematic
   because it attempts to use BSD's getprogname(), which isn't available in
   Android NDK version 16 (it was first made available in 21, as per
-  https://bit.ly/2nX0nBs).
\ No newline at end of file
+  https://bit.ly/2nX0nBs).
+
+- 0004-gnu-basename.patch:
+
+  Adds a short implementation of the non-modifying GNU basename() function
+  which isn't available in Android NDK version 16 (it was first made available
+  in 23, as per https://bit.ly/31Gkyl9). We call this function `compat_basename`.
+
+  Although there's a POSIX basename() available via <libgen.h>, that function
+  has different behavior with regards to trailing slashes and can also modify
+  its argument under some circumstances.
\ No newline at end of file
diff --git a/patches/0004-gnu-basename.patch b/patches/0004-gnu-basename.patch
new file mode 100644
index 0000000..4e62929
--- /dev/null
+++ b/patches/0004-gnu-basename.patch
@@ -0,0 +1,87 @@
+diff --git a/src/compat/compat/compat.cc b/src/compat/compat/compat.cc
+new file mode 100644
+index 0000000..7a671e0
+--- /dev/null
++++ b/src/compat/compat/compat.cc
+@@ -0,0 +1,14 @@
++// Copyright 2019 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include <string.h>
++
++const char* compat_basename(const char* file) {
++  // Implementation based off of the one in <android-base/logging.cpp>.
++  const char* last_slash = strrchr(file, '/');
++  if (last_slash != nullptr) {
++    return last_slash + 1;
++  }
++  return file;
++}
+diff --git a/src/compat/compat/compat.h b/src/compat/compat/compat.h
+new file mode 100644
+index 0000000..7fbc5d1
+--- /dev/null
++++ b/src/compat/compat/compat.h
+@@ -0,0 +1,19 @@
++// Copyright 2019 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#ifndef COMPAT_COMPAT_H_
++#define COMPAT_COMPAT_H_
++
++// Provides functions that are available in libunwindstack's normal build
++// environment (platform/system/core, built at Android ToT) but unavailable in
++// libunwindstack's build environment within Chrome (currenty built at Android
++// NDK 16).
++
++// Replicates the non-modifying GNU basename() function available in <string.h>
++// that was introduced in Android NDK 23: https://bit.ly/31Gkyl9. We rename the
++// function to avoid colliding with any existing basename() functions defined at
++// the global scope.
++const char* compat_basename(const char* file);
++
++#endif  // COMPAT_COMPAT_H_
+diff --git a/src/libunwindstack/Global.cpp b/src/libunwindstack/Global.cpp
+index a20be00..a1fbfc4 100644
+--- a/src/libunwindstack/Global.cpp
++++ b/src/libunwindstack/Global.cpp
+@@ -21,6 +21,7 @@
+ #include <string>
+ #include <vector>
+ 
++#include <compat/compat.h>
+ #include <unwindstack/Global.h>
+ #include <unwindstack/MapInfo.h>
+ #include <unwindstack/Maps.h>
+@@ -42,7 +43,7 @@ void Global::SetArch(ArchEnum arch) {
+ uint64_t Global::GetVariableOffset(MapInfo* info, const std::string& variable) {
+   if (!search_libs_.empty()) {
+     bool found = false;
+-    const char* lib = basename(info->name.c_str());
++    const char* lib = compat_basename(info->name.c_str());
+     for (const std::string& name : search_libs_) {
+       if (name == lib) {
+         found = true;
+diff --git a/src/libunwindstack/Unwinder.cpp b/src/libunwindstack/Unwinder.cpp
+index 3f2e1c1..be70b8a 100644
+--- a/src/libunwindstack/Unwinder.cpp
++++ b/src/libunwindstack/Unwinder.cpp
+@@ -28,6 +28,7 @@
+ 
+ #include <demangle.h>
+ 
++#include <compat/compat.h>
+ #include <unwindstack/Elf.h>
+ #include <unwindstack/JitDebug.h>
+ #include <unwindstack/MapInfo.h>
+@@ -196,7 +197,7 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
+ 
+     if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
+         std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
+-                  basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
++                  compat_basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
+       if (regs_->dex_pc() != 0) {
+         // Add a frame to represent the dex file.
+         FillInDexFrame();
diff --git a/src/compat/compat/compat.cc b/src/compat/compat/compat.cc
new file mode 100644
index 0000000..7a671e0
--- /dev/null
+++ b/src/compat/compat/compat.cc
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string.h>
+
+const char* compat_basename(const char* file) {
+  // Implementation based off of the one in <android-base/logging.cpp>.
+  const char* last_slash = strrchr(file, '/');
+  if (last_slash != nullptr) {
+    return last_slash + 1;
+  }
+  return file;
+}
diff --git a/src/compat/compat/compat.h b/src/compat/compat/compat.h
new file mode 100644
index 0000000..7fbc5d1
--- /dev/null
+++ b/src/compat/compat/compat.h
@@ -0,0 +1,19 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPAT_COMPAT_H_
+#define COMPAT_COMPAT_H_
+
+// Provides functions that are available in libunwindstack's normal build
+// environment (platform/system/core, built at Android ToT) but unavailable in
+// libunwindstack's build environment within Chrome (currenty built at Android
+// NDK 16).
+
+// Replicates the non-modifying GNU basename() function available in <string.h>
+// that was introduced in Android NDK 23: https://bit.ly/31Gkyl9. We rename the
+// function to avoid colliding with any existing basename() functions defined at
+// the global scope.
+const char* compat_basename(const char* file);
+
+#endif  // COMPAT_COMPAT_H_
diff --git a/src/libunwindstack/Global.cpp b/src/libunwindstack/Global.cpp
index a20be00..a1fbfc4 100644
--- a/src/libunwindstack/Global.cpp
+++ b/src/libunwindstack/Global.cpp
@@ -21,6 +21,7 @@
 #include <string>
 #include <vector>
 
+#include <compat/compat.h>
 #include <unwindstack/Global.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
@@ -42,7 +43,7 @@
 uint64_t Global::GetVariableOffset(MapInfo* info, const std::string& variable) {
   if (!search_libs_.empty()) {
     bool found = false;
-    const char* lib = basename(info->name.c_str());
+    const char* lib = compat_basename(info->name.c_str());
     for (const std::string& name : search_libs_) {
       if (name == lib) {
         found = true;
diff --git a/src/libunwindstack/Unwinder.cpp b/src/libunwindstack/Unwinder.cpp
index 3f2e1c1..be70b8a 100644
--- a/src/libunwindstack/Unwinder.cpp
+++ b/src/libunwindstack/Unwinder.cpp
@@ -28,6 +28,7 @@
 
 #include <demangle.h>
 
+#include <compat/compat.h>
 #include <unwindstack/Elf.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/MapInfo.h>
@@ -196,7 +197,7 @@
 
     if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
         std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
-                  basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
+                  compat_basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
       if (regs_->dex_pc() != 0) {
         // Add a frame to represent the dex file.
         FillInDexFrame();