simpleperf: add symbol for .plt section.

This avoid reporting unknown symbols when there are samples in .plt section.

Bug: 28911532
Test: run simpleperf_unit_test.
Change-Id: I62cb08776c99951ff845e98f0f601859d25ece5c
diff --git a/simpleperf/read_elf.cpp b/simpleperf/read_elf.cpp
index f59ec37..51b85b6 100644
--- a/simpleperf/read_elf.cpp
+++ b/simpleperf/read_elf.cpp
@@ -301,6 +301,39 @@
 }
 
 template <class ELFT>
+void AddSymbolForPltSection(const llvm::object::ELFObjectFile<ELFT>* elf,
+                            std::function<void(const ElfFileSymbol&)> callback) {
+  // We may sample instructions in .plt section if the program
+  // calls functions from shared libraries. Different architectures use
+  // different formats to store .plt section, so it needs a lot of work to match
+  // instructions in .plt section to symbols. As samples in .plt section rarely
+  // happen, and .plt section can hardly be a performance bottleneck, we can
+  // just use a symbol @plt to represent instructions in .plt section.
+  for (auto it = elf->section_begin(); it != elf->section_end(); ++it) {
+    const llvm::object::ELFSectionRef& section_ref = *it;
+    llvm::StringRef section_name;
+    std::error_code err = section_ref.getName(section_name);
+    if (err || section_name != ".plt") {
+      continue;
+    }
+    const auto* shdr = elf->getSection(section_ref.getRawDataRefImpl());
+    if (shdr == nullptr) {
+      return;
+    }
+    ElfFileSymbol symbol;
+    symbol.vaddr = shdr->sh_addr;
+    symbol.len = shdr->sh_size;
+    symbol.is_func = true;
+    symbol.is_label = true;
+    symbol.is_in_text_section = true;
+    symbol.name = "@plt";
+    callback(symbol);
+    return;
+  }
+}
+
+
+template <class ELFT>
 void ParseSymbolsFromELFFile(const llvm::object::ELFObjectFile<ELFT>* elf,
                              std::function<void(const ElfFileSymbol&)> callback) {
   auto machine = elf->getELFFile()->getHeader()->e_machine;
@@ -310,6 +343,7 @@
   } else if (elf->dynamic_symbol_begin()->getRawDataRefImpl() != llvm::object::DataRefImpl()) {
     ReadSymbolTable(elf->dynamic_symbol_begin(), elf->dynamic_symbol_end(), callback, is_arm);
   }
+  AddSymbolForPltSection(elf, callback);
   std::string debugdata;
   if (ReadSectionFromELFFile(elf, ".gnu_debugdata", &debugdata, false)) {
     LOG(VERBOSE) << "Read .gnu_debugdata from " << elf->getFileName().str();
diff --git a/simpleperf/read_elf_test.cpp b/simpleperf/read_elf_test.cpp
index f2649e0..5669d27 100644
--- a/simpleperf/read_elf_test.cpp
+++ b/simpleperf/read_elf_test.cpp
@@ -105,3 +105,10 @@
   ASSERT_FALSE(IsValidElfPath("/sys/devices/system/cpu/online"));
   ASSERT_TRUE(IsValidElfPath(GetTestData(ELF_FILE)));
 }
+
+TEST(read_elf, check_symbol_for_plt_section) {
+  std::map<std::string, ElfFileSymbol> symbols;
+  ASSERT_TRUE(ParseSymbolsFromElfFile(GetTestData(ELF_FILE), BuildId(),
+                                      std::bind(ParseSymbol, std::placeholders::_1, &symbols)));
+  ASSERT_NE(symbols.find("@plt"), symbols.end());
+}