Skip to content

Commit ae51ec8

Browse files
authored
[Bolt] Solving pie support issue (#65494)
Now PIE is default supported after clang 14. 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. Co-authored-by: Li Zhuohang <[email protected]>
1 parent 1949fe9 commit ae51ec8

File tree

6 files changed

+102
-4
lines changed

6 files changed

+102
-4
lines changed

bolt/lib/Core/BinaryContext.cpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1926,10 +1926,27 @@ BinaryContext::getBaseAddressForMapping(uint64_t MMapAddress,
19261926
// Find a segment with a matching file offset.
19271927
for (auto &KV : SegmentMapInfo) {
19281928
const SegmentInfo &SegInfo = KV.second;
1929-
if (alignDown(SegInfo.FileOffset, SegInfo.Alignment) == FileOffset) {
1930-
// Use segment's aligned memory offset to calculate the base address.
1931-
const uint64_t MemOffset = alignDown(SegInfo.Address, SegInfo.Alignment);
1932-
return MMapAddress - MemOffset;
1929+
// FileOffset is got from perf event,
1930+
// and it is equal to alignDown(SegInfo.FileOffset, pagesize).
1931+
// If the pagesize is not equal to SegInfo.Alignment.
1932+
// FileOffset and SegInfo.FileOffset should be aligned first,
1933+
// and then judge whether they are equal.
1934+
if (alignDown(SegInfo.FileOffset, SegInfo.Alignment) ==
1935+
alignDown(FileOffset, SegInfo.Alignment)) {
1936+
// The function's offset from base address in VAS is aligned by pagesize
1937+
// instead of SegInfo.Alignment. Pagesize can't be got from perf events.
1938+
// However, The ELF document says that SegInfo.FileOffset should equal
1939+
// to SegInfo.Address, modulo the pagesize.
1940+
// Reference: https://refspecs.linuxfoundation.org/elf/elf.pdf
1941+
1942+
// So alignDown(SegInfo.Address, pagesize) can be calculated by:
1943+
// alignDown(SegInfo.Address, pagesize)
1944+
// = SegInfo.Address - (SegInfo.Address % pagesize)
1945+
// = SegInfo.Address - (SegInfo.FileOffset % pagesize)
1946+
// = SegInfo.Address - SegInfo.FileOffset +
1947+
// alignDown(SegInfo.FileOffset, pagesize)
1948+
// = SegInfo.Address - SegInfo.FileOffset + FileOffset
1949+
return MMapAddress - (SegInfo.Address - SegInfo.FileOffset + FileOffset);
19331950
}
19341951
}
19351952

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <unistd.h>
4+
5+
int add(int a, int b) { return a + b; }
6+
int minus(int a, int b) { return a - b; }
7+
int multiple(int a, int b) { return a * b; }
8+
int divide(int a, int b) {
9+
if (b == 0)
10+
return 0;
11+
return a / b;
12+
}
13+
14+
int main() {
15+
int a = 16;
16+
int b = 8;
17+
18+
for (int i = 1; i < 100000; i++) {
19+
add(a, b);
20+
minus(a, b);
21+
multiple(a, b);
22+
divide(a, b);
23+
}
24+
25+
return 0;
26+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
SECTIONS {
2+
. = SIZEOF_HEADERS;
3+
.interp : { *(.interp) }
4+
.note.gnu.build-id : { *(.note.gnu.build-id) }
5+
. = 0x212e8;
6+
.dynsym : { *(.dynsym) }
7+
. = 0x31860;
8+
.text : { *(.text*) }
9+
. = 0x41c20;
10+
.fini_array : { *(.fini_array) }
11+
. = 0x54e18;
12+
.data : { *(.data) }
13+
}

bolt/test/perf2bolt/lit.local.cfg

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import shutil
2+
3+
if shutil.which("perf") != None:
4+
config.available_features.add("perf")

bolt/test/perf2bolt/perf_test.test

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Check perf2bolt binary function which was compiled with pie
2+
3+
REQUIRES: system-linux, perf
4+
5+
RUN: %clang %S/Inputs/perf_test.c -fuse-ld=lld -Wl,--script=%S/Inputs/perf_test.lds -o %t
6+
RUN: perf record -e cycles:u -o %t2 -- %t
7+
RUN: perf2bolt %t -p=%t2 -o %t3 -nl -ignore-build-id 2>&1 | FileCheck %s
8+
9+
CHECK-NOT: PERF2BOLT-ERROR
10+
CHECK-NOT: !! WARNING !! This high mismatch ratio indicates the input binary is probably not the same binary used during profiling collection.
11+
12+
RUN: %clang %S/Inputs/perf_test.c -no-pie -fuse-ld=lld -o %t4
13+
RUN: perf record -e cycles:u -o %t5 -- %t4
14+
RUN: perf2bolt %t4 -p=%t5 -o %t6 -nl -ignore-build-id 2>&1 | FileCheck %s --check-prefix=CHECK-NO-PIE
15+
16+
CHECK-NO-PIE-NOT: PERF2BOLT-ERROR
17+
CHECK-NO-PIE-NOT: !! WARNING !! This high mismatch ratio indicates the input binary is probably not the same binary used during profiling collection.

bolt/unittests/Core/BinaryContext.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,24 @@ TEST_P(BinaryContextTester, BaseAddress) {
123123
BaseAddress = BC->getBaseAddressForMapping(0x7f13f5556000, 0x137a000);
124124
ASSERT_FALSE(BaseAddress.has_value());
125125
}
126+
127+
TEST_P(BinaryContextTester, BaseAddress2) {
128+
// Check that base address calculation is correct for a binary if the
129+
// alignment in ELF file are different from pagesize.
130+
// The segment layout is as follows:
131+
BC->SegmentMapInfo[0] = SegmentInfo{0, 0x2177c, 0, 0x2177c, 0x10000};
132+
BC->SegmentMapInfo[0x31860] =
133+
SegmentInfo{0x31860, 0x370, 0x21860, 0x370, 0x10000};
134+
BC->SegmentMapInfo[0x41c20] =
135+
SegmentInfo{0x41c20, 0x1f8, 0x21c20, 0x1f8, 0x10000};
136+
BC->SegmentMapInfo[0x54e18] =
137+
SegmentInfo{0x54e18, 0x51, 0x24e18, 0x51, 0x10000};
138+
139+
std::optional<uint64_t> BaseAddress =
140+
BC->getBaseAddressForMapping(0xaaaaea444000, 0x21000);
141+
ASSERT_TRUE(BaseAddress.has_value());
142+
ASSERT_EQ(*BaseAddress, 0xaaaaea413000ULL);
143+
144+
BaseAddress = BC->getBaseAddressForMapping(0xaaaaea444000, 0x11000);
145+
ASSERT_FALSE(BaseAddress.has_value());
146+
}

0 commit comments

Comments
 (0)