[llvm] [Bolt] Solving pie support issue (PR #65494)

via llvm-commits llvm-commits at lists.llvm.org
Sun Nov 12 07:35:17 PST 2023


https://github.com/JohnLee1243 updated https://github.com/llvm/llvm-project/pull/65494

>From c312e120f77016bd1122c3adfebf2ef64e5dc88c Mon Sep 17 00:00:00 2001
From: JohnLee1243 <lizhuohang911 at 126.com>
Date: Sun, 12 Nov 2023 23:23:27 +0800
Subject: [PATCH] [Bolt] Solving pie support issue

Now PIE is default supported. It cause parsing error when using
perf2bolt. The reason is the base address can not get correctly.
Fix the method of geting base address. If SegInfo.Alignment is not
equal to pagesize, alignDown(SegInfo.FileOffset, SegInfo.Alignment)
can not equal to FileOffset. So the SegInfo.FileOffset and
FileOffset should be aligned by SegInfo.Alignment first and then
judge whether they are equal.
The .text segment's offset from base address in VAS  is aligned by
pagesize. So MMapAddress's offset from base address is
alignDown(SegInfo.Address, pagesize) instead of
alignDown(SegInfo.Address, SegInfo.Alignment). So the base address
calculate way should be changed.
---
 bolt/lib/Core/BinaryContext.cpp       | 25 +++++++++++++++++++++----
 bolt/test/Inputs/perf_test.c          | 26 ++++++++++++++++++++++++++
 bolt/test/Inputs/perf_test.lds        | 13 +++++++++++++
 bolt/test/lit.cfg.py                  |  4 ++++
 bolt/test/perf_test.test              | 17 +++++++++++++++++
 bolt/unittests/Core/BinaryContext.cpp | 21 +++++++++++++++++++++
 6 files changed, 102 insertions(+), 4 deletions(-)
 create mode 100644 bolt/test/Inputs/perf_test.c
 create mode 100644 bolt/test/Inputs/perf_test.lds
 create mode 100644 bolt/test/perf_test.test

diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index 00725a2ff2225a1..5e3b9b2539e030b 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -1924,10 +1924,27 @@ BinaryContext::getBaseAddressForMapping(uint64_t MMapAddress,
   // Find a segment with a matching file offset.
   for (auto &KV : SegmentMapInfo) {
     const SegmentInfo &SegInfo = KV.second;
-    if (alignDown(SegInfo.FileOffset, SegInfo.Alignment) == FileOffset) {
-      // Use segment's aligned memory offset to calculate the base address.
-      const uint64_t MemOffset = alignDown(SegInfo.Address, SegInfo.Alignment);
-      return MMapAddress - MemOffset;
+    // FileOffset is got from perf event,
+    // and it is equal to alignDown(SegInfo.FileOffset, pagesize).
+    // If the pagesize is not equal to SegInfo.Alignment.
+    // FileOffset and SegInfo.FileOffset should be aligned first,
+    // and then judge whether they are equal.
+    if (alignDown(SegInfo.FileOffset, SegInfo.Alignment) ==
+        alignDown(FileOffset, SegInfo.Alignment)) {
+      // The function's offset from base address in VAS is aligned by pagesize
+      // instead of SegInfo.Alignment. Pagesize can't be got from perf events.
+      // However, The ELF document says that SegInfo.FileOffset should equal
+      // to SegInfo.Address, modulo the pagesize.
+      // Reference: https://refspecs.linuxfoundation.org/elf/elf.pdf
+
+      // So alignDown(SegInfo.Address, pagesize) can be calculated by:
+      // alignDown(SegInfo.Address, pagesize)
+      //   = SegInfo.Address - (SegInfo.Address % pagesize)
+      //   = SegInfo.Address - (SegInfo.FileOffset % pagesize)
+      //   = SegInfo.Address - SegInfo.FileOffset +
+      //     alignDown(SegInfo.FileOffset, pagesize)
+      //   = SegInfo.Address - SegInfo.FileOffset + FileOffset
+      return MMapAddress - (SegInfo.Address - SegInfo.FileOffset + FileOffset);
     }
   }
 
diff --git a/bolt/test/Inputs/perf_test.c b/bolt/test/Inputs/perf_test.c
new file mode 100644
index 000000000000000..397ab3b5633f789
--- /dev/null
+++ b/bolt/test/Inputs/perf_test.c
@@ -0,0 +1,26 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int add(int a, int b) { return a + b; }
+int minus(int a, int b) { return a - b; }
+int multiple(int a, int b) { return a * b; }
+int divide(int a, int b) {
+  if (b == 0)
+    return 0;
+  return a / b;
+}
+
+int main() {
+  int a = 16;
+  int b = 8;
+
+  for (int i = 1; i < 1000; i++) {
+    add(a, b);
+    minus(a, b);
+    multiple(a, b);
+    divide(a, b);
+  }
+
+  return 0;
+}
\ No newline at end of file
diff --git a/bolt/test/Inputs/perf_test.lds b/bolt/test/Inputs/perf_test.lds
new file mode 100644
index 000000000000000..66d925a05bebca0
--- /dev/null
+++ b/bolt/test/Inputs/perf_test.lds
@@ -0,0 +1,13 @@
+SECTIONS {
+  . = SIZEOF_HEADERS;
+  .interp : { *(.interp) }
+  .note.gnu.build-id : { *(.note.gnu.build-id) }
+  . = 0x212e8;
+  .dynsym         : { *(.dynsym) }
+  . = 0x31860;
+  .text : { *(.text*) }
+  . = 0x41c20;
+  .fini_array : { *(.fini_array) }
+  . = 0x54e18;
+  .data : { *(.data) }
+}
\ No newline at end of file
diff --git a/bolt/test/lit.cfg.py b/bolt/test/lit.cfg.py
index 3a6da210e01f080..427e13b7e561d91 100644
--- a/bolt/test/lit.cfg.py
+++ b/bolt/test/lit.cfg.py
@@ -3,6 +3,7 @@
 import os
 import platform
 import re
+import shutil
 import subprocess
 import tempfile
 
@@ -75,6 +76,9 @@
 if lit.util.which("fuser"):
     config.available_features.add("fuser")
 
+if shutil.which('perf') != None:
+    config.available_features.add('perf')
+
 llvm_config.use_default_substitutions()
 
 llvm_config.config.environment["CLANG"] = config.bolt_clang
diff --git a/bolt/test/perf_test.test b/bolt/test/perf_test.test
new file mode 100644
index 000000000000000..44db899ab30de56
--- /dev/null
+++ b/bolt/test/perf_test.test
@@ -0,0 +1,17 @@
+# Check perf2bolt binary function which was compiled with pie
+
+REQUIRES: system-linux, perf
+
+RUN: %clang %S/Inputs/perf_test.c -fuse-ld=lld -Wl,--script=%S/Inputs/perf_test.lds -o %t
+RUN: perf record -e cycles:u -o %t2 -- %t
+RUN: perf2bolt %t -p=%t2 -o %t3 -nl -ignore-build-id 2>&1 | FileCheck %s
+
+CHECK-NOT: PERF2BOLT-ERROR
+CHECK-NOT: !! WARNING !! This high mismatch ratio indicates the input binary is probably not the same binary used during profiling collection.
+
+RUN: %clang %S/Inputs/perf_test.c -no-pie -fuse-ld=lld -o %t4
+RUN: perf record -e cycles:u -o %t5 -- %t4
+RUN: perf2bolt %t4 -p=%t5 -o %t6 -nl -ignore-build-id 2>&1 | FileCheck %s --check-prefix=CHECK-NO-PIE
+
+CHECK-NO-PIE-NOT: PERF2BOLT-ERROR
+CHECK-NO-PIE-NOT: !! WARNING !! This high mismatch ratio indicates the input binary is probably not the same binary used during profiling collection.
\ No newline at end of file
diff --git a/bolt/unittests/Core/BinaryContext.cpp b/bolt/unittests/Core/BinaryContext.cpp
index 64af859128c7677..b95d3f5154031b7 100644
--- a/bolt/unittests/Core/BinaryContext.cpp
+++ b/bolt/unittests/Core/BinaryContext.cpp
@@ -123,3 +123,24 @@ TEST_P(BinaryContextTester, BaseAddress) {
   BaseAddress = BC->getBaseAddressForMapping(0x7f13f5556000, 0x137a000);
   ASSERT_FALSE(BaseAddress.has_value());
 }
+
+TEST_P(BinaryContextTester, BaseAddress2) {
+  // Check that base address calculation is correct for a binary if the
+  // alignment in ELF file are different from pagesize.
+  // The segment layout is as follows:
+  BC->SegmentMapInfo[0] = SegmentInfo{0, 0x2177c, 0, 0x2177c, 0x10000};
+  BC->SegmentMapInfo[0x31860] =
+      SegmentInfo{0x31860, 0x370, 0x21860, 0x370, 0x10000};
+  BC->SegmentMapInfo[0x41c20] =
+      SegmentInfo{0x41c20, 0x1f8, 0x21c20, 0x1f8, 0x10000};
+  BC->SegmentMapInfo[0x54e18] =
+      SegmentInfo{0x54e18, 0x51, 0x24e18, 0x51, 0x10000};
+
+  Optional<uint64_t> BaseAddress =
+      BC->getBaseAddressForMapping(0xaaaaea444000, 0x21000);
+  ASSERT_TRUE(BaseAddress.hasValue());
+  ASSERT_EQ(*BaseAddress, 0xaaaaea413000ULL);
+
+  BaseAddress = BC->getBaseAddressForMapping(0xaaaaea444000, 0x11000);
+  ASSERT_FALSE(BaseAddress.hasValue());
+}



More information about the llvm-commits mailing list