Skip to content

Commit 391cb55

Browse files
Propeller Teamcopybara-github
authored andcommitted
Add helper for reading AArch64 thunks from binaries
PiperOrigin-RevId: 703621699
1 parent 1e1e9de commit 391cb55

File tree

7 files changed

+111
-0
lines changed

7 files changed

+111
-0
lines changed

propeller/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ cc_library(
269269
deps = [
270270
":addr2cu",
271271
":status_macros",
272+
"@abseil-cpp//absl/container:btree",
272273
"@abseil-cpp//absl/container:flat_hash_map",
273274
"@abseil-cpp//absl/log",
274275
"@abseil-cpp//absl/log:check",
@@ -280,6 +281,7 @@ cc_library(
280281
"@llvm-project//llvm:DebugInfo",
281282
"@llvm-project//llvm:Object",
282283
"@llvm-project//llvm:Support",
284+
"@llvm-project//llvm:TargetParser",
283285
],
284286
)
285287

@@ -1080,6 +1082,7 @@ cc_test(
10801082
name = "binary_content_test",
10811083
srcs = ["binary_content_test.cc"],
10821084
data = [
1085+
"//propeller/testdata:fake_thunks.bin",
10831086
"//propeller/testdata:llvm_function_samples.binary",
10841087
"//propeller/testdata:propeller_barebone_nopie_buildid",
10851088
"//propeller/testdata:propeller_barebone_pie_nobuildid_bin",

propeller/binary_content.cc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <utility>
99
#include <vector>
1010

11+
#include "absl/container/btree_map.h"
1112
#include "absl/container/flat_hash_map.h"
1213
#include "absl/log/check.h"
1314
#include "absl/log/log.h"
@@ -35,6 +36,7 @@
3536
#include "llvm/Support/MemoryBuffer.h"
3637
#include "llvm/Support/MemoryBufferRef.h"
3738
#include "llvm/Support/raw_ostream.h"
39+
#include "llvm/TargetParser/Triple.h"
3840
#include "propeller/addr2cu.h"
3941
#include "propeller/status_macros.h"
4042

@@ -241,6 +243,30 @@ absl::Status ELFFileUtil<ELFT>::InitializeKernelModule(
241243
}
242244
} // namespace
243245

246+
// Read AArch64 thunks from the symbol table and store them in sorted order.
247+
absl::btree_map<uint64_t, llvm::object::ELFSymbolRef> ReadAArch64ThunkSymbols(
248+
const BinaryContent &binary_content) {
249+
absl::btree_map<uint64_t, llvm::object::ELFSymbolRef> thunk_map;
250+
for (llvm::object::SymbolRef sr : binary_content.object_file->symbols()) {
251+
llvm::object::ELFSymbolRef symbol(sr);
252+
uint8_t stt = symbol.getELFType();
253+
if (stt != llvm::ELF::STT_FUNC) continue;
254+
llvm::Expected<uint64_t> address = sr.getAddress();
255+
if (!address || !*address) continue;
256+
257+
llvm::Expected<llvm::StringRef> func_name = symbol.getName();
258+
if (!func_name || (!func_name->starts_with("__AArch64ADRPThunk_") &&
259+
!func_name->starts_with("__AArch64AbsLongThunk_")))
260+
continue;
261+
262+
const uint64_t func_size = symbol.getSize();
263+
if (func_size == 0) continue;
264+
265+
thunk_map.insert({*address, sr});
266+
}
267+
return thunk_map;
268+
}
269+
244270
namespace propeller {
245271
absl::flat_hash_map<uint64_t, llvm::SmallVector<llvm::object::ELFSymbolRef>>
246272
ReadSymbolTable(const BinaryContent &binary_content) {
@@ -279,6 +305,14 @@ ReadSymbolTable(const BinaryContent &binary_content) {
279305
return symtab;
280306
}
281307

308+
std::optional<absl::btree_map<uint64_t, llvm::object::ELFSymbolRef>>
309+
ReadThunkSymbols(const BinaryContent &binary_content) {
310+
if (binary_content.object_file->getArch() == llvm::Triple::aarch64)
311+
return ::ReadAArch64ThunkSymbols(binary_content);
312+
313+
return std::nullopt;
314+
}
315+
282316
absl::StatusOr<std::vector<llvm::object::BBAddrMap>> ReadBbAddrMap(
283317
const BinaryContent &binary_content) {
284318
auto *elf_object = llvm::dyn_cast<llvm::object::ELFObjectFileBase>(

propeller/binary_content.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <string>
88
#include <vector>
99

10+
#include "absl/container/btree_map.h"
1011
#include "absl/container/flat_hash_map.h"
1112
#include "absl/status/status.h"
1213
#include "absl/status/statusor.h"
@@ -116,6 +117,14 @@ absl::StatusOr<int64_t> GetSymbolAddress(
116117
absl::flat_hash_map<uint64_t, llvm::SmallVector<llvm::object::ELFSymbolRef>>
117118
ReadSymbolTable(const BinaryContent &binary_content);
118119

120+
// Returns an AArch64 binary's thunk symbols by reading from its symbol table.
121+
absl::btree_map<uint64_t, llvm::object::ELFSymbolRef> ReadAArch64ThunkSymbols(
122+
const BinaryContent &binary_content);
123+
124+
// Returns the binary's thunk symbols by reading from its symbol table.
125+
std::optional<absl::btree_map<uint64_t, llvm::object::ELFSymbolRef>>
126+
ReadThunkSymbols(const BinaryContent &binary_content);
127+
119128
// Returns the binary's `BBAddrMap`s by calling LLVM-side decoding function
120129
// `ELFObjectFileBase::readBBAddrMap`. Returns error if the call fails or if the
121130
// result is empty.

propeller/binary_content_test.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,23 @@ TEST(GetSymbolAddressTest, SymbolNotFound) {
6969
Not(IsOk()));
7070
}
7171

72+
TEST(ThunkSymbolsTest, AArch64Thunks) {
73+
const std::string binary = absl::StrCat(
74+
::testing::SrcDir(),
75+
"_main/propeller/testdata/fake_thunks.bin");
76+
ASSERT_OK_AND_ASSIGN(std::unique_ptr<BinaryContent> binary_content,
77+
GetBinaryContent(binary));
78+
EXPECT_THAT(ReadThunkSymbols(*binary_content), Optional(SizeIs(2)));
79+
}
80+
81+
TEST(ThunkSymbolsTest, x86NoThunks) {
82+
const std::string binary = absl::StrCat(
83+
::testing::SrcDir(),
84+
"_main/propeller/testdata/propeller_sample_1.bin");
85+
ASSERT_OK_AND_ASSIGN(std::unique_ptr<BinaryContent> binary_content,
86+
GetBinaryContent(binary));
87+
EXPECT_THAT(ReadThunkSymbols(*binary_content), Eq(std::nullopt));
88+
}
89+
7290
} // namespace
7391
} // namespace propeller

propeller/testdata/BUILD

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ exports_files([
3636
"bimodal_sample.x.bin",
3737
"call_from_simple_loop.protobuf",
3838
"clang_v0_labels.binary",
39+
"fake_thunks.bin",
3940
"hot_and_cold_landing_pads.protobuf",
4041
"libro_sample.so",
4142
"llvm_function_samples.binary",
@@ -352,3 +353,30 @@ genrule(
352353
target_compatible_with = ["//third_party/bazel_platforms/cpu:aarch64"],
353354
tools = [":sample"],
354355
)
356+
357+
cc_binary(
358+
name = "fake_thunks",
359+
srcs = ["fake_thunks.c"],
360+
)
361+
362+
# This rule can be used to manually generate fake_thunks.bin.
363+
# Do not make this into the deps of tests since regenerating the file every time is cumbersome.
364+
#
365+
# To build `fake_thunks_bin`, you need to target Arm. If building with Propeller
366+
# annotations, pass `--host_features=propeller_annotate` when building the genrule.
367+
genrule(
368+
name = "fake_thunks_bin",
369+
srcs = ["fake_thunks.c"],
370+
outs = [
371+
"fake_thunks.bin.gen",
372+
],
373+
cmd = "$(CC) $(CC_FLAGS) -g -O2 -Wl,-build-id -pie -fbasic-block-sections=labels $< -o " +
374+
"$(RULEDIR)/fake_thunks.bin.gen",
375+
exec_compatible_with = ["//third_party/bazel_platforms/cpu:aarch64"],
376+
tags = [
377+
"manual",
378+
"requires-arch:arm",
379+
],
380+
target_compatible_with = ["//third_party/bazel_platforms/cpu:aarch64"],
381+
toolchains = _LLVM_PROPELLER_TESTDATA_TOOLCHAINS,
382+
)

propeller/testdata/fake_thunks.bin

11.5 KB
Binary file not shown.

propeller/testdata/fake_thunks.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* fake_thunks.c */
2+
volatile int x = 1;
3+
4+
__attribute__((noinline)) int __AArch64ADRPThunk_test1(int i) {
5+
return x + i;
6+
}
7+
8+
__attribute__((noinline)) int __AArch64ADRPThunk_test2(int i) {
9+
return x + i + 1;
10+
}
11+
12+
int sample1_func() { return 13; }
13+
14+
int main(void) {
15+
__AArch64ADRPThunk_test1(x);
16+
__AArch64ADRPThunk_test2(x);
17+
18+
return 0;
19+
}

0 commit comments

Comments
 (0)