Skip to content

Commit dbc79ee

Browse files
committed
Fix ExtractGolangInterface to handle go 1.20 binaries and later
Signed-off-by: Dom Del Nano <ddelnano@gmail.com>
1 parent 685ed42 commit dbc79ee

File tree

3 files changed

+94
-29
lines changed

3 files changed

+94
-29
lines changed

src/stirling/obj_tools/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ pl_cc_test(
137137
"//src/stirling/obj_tools/testdata/go:test_binaries",
138138
"//src/stirling/obj_tools/testdata/go:test_go_1_17_binary",
139139
"//src/stirling/obj_tools/testdata/go:test_go_1_19_binary",
140+
"//src/stirling/obj_tools/testdata/go:test_go_1_21_binary",
140141
],
141142
deps = [
142143
":cc_library",

src/stirling/obj_tools/go_syms.cc

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,20 @@ StatusOr<absl::flat_hash_map<std::string, std::vector<IntfImplTypeInfo>>> Extrac
148148
ElfReader* elf_reader) {
149149
absl::flat_hash_map<std::string, std::vector<IntfImplTypeInfo>> interface_types;
150150

151-
// All itable objects in the symbols are prefixed with this string.
152-
const std::string_view kITablePrefix("go.itab.");
151+
// Go 1.20 binaries and later use go:itab.<type_name>,<interface_name> as the symbol name.
152+
// Go 1.19 binaries and earlier use go.itab.<type_name>,<interface_name> as the symbol name.
153+
// Optimistically try the newer format first.
154+
std::string_view kITablePrefix("go:itab.");
153155

154156
PX_ASSIGN_OR_RETURN(std::vector<ElfReader::SymbolInfo> itable_symbols,
155157
elf_reader->SearchSymbols(kITablePrefix, SymbolMatchType::kPrefix,
156158
/*symbol_type*/ ELFIO::STT_OBJECT));
157-
159+
if (itable_symbols.empty()) {
160+
kITablePrefix = "go.itab.";
161+
PX_ASSIGN_OR_RETURN(itable_symbols,
162+
elf_reader->SearchSymbols(kITablePrefix, SymbolMatchType::kPrefix,
163+
/*symbol_type*/ ELFIO::STT_OBJECT));
164+
}
158165
for (const auto& sym : itable_symbols) {
159166
// Expected format is:
160167
// go.itab.<type_name>,<interface_name>

src/stirling/obj_tools/go_syms_test.cc

Lines changed: 83 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include "src/stirling/obj_tools/go_syms.h"
2020

2121
#include <memory>
22+
#include <tuple>
23+
#include <utility>
2224

2325
#include "src/common/testing/testing.h"
2426

@@ -27,7 +29,9 @@ namespace stirling {
2729
namespace obj_tools {
2830

2931
using ::testing::Field;
32+
using ::testing::Matcher;
3033
using ::testing::StrEq;
34+
using ::testing::UnorderedElementsAre;
3135

3236
constexpr std::string_view kTestGoLittleEndiani386BinaryPath =
3337
"src/stirling/obj_tools/testdata/go/test_go1_13_i386_binary";
@@ -37,6 +41,8 @@ constexpr std::string_view kTestGoLittleEndianBinaryPath =
3741

3842
constexpr std::string_view kTestGoBinaryPath =
3943
"src/stirling/obj_tools/testdata/go/test_go_1_19_binary";
44+
constexpr std::string_view kTestGo1_21BinaryPath =
45+
"src/stirling/obj_tools/testdata/go/test_go_1_21_binary";
4046

4147
// The "endian agnostic" case refers to where the Go version data is varint encoded
4248
// directly within the buildinfo header. See the following reference for more details.
@@ -68,44 +74,95 @@ TEST(IsGoExecutableTest, WorkingOnBasicGoBinary) {
6874
EXPECT_TRUE(IsGoExecutable(elf_reader.get()));
6975
}
7076

71-
TEST(ElfGolangItableTest, ExtractInterfaceTypes) {
72-
const std::string kPath = px::testing::BazelRunfilePath(kTestGoBinaryPath);
77+
class ElfGolangItableTest
78+
: public ::testing::TestWithParam<std::tuple<
79+
std::string,
80+
Matcher<const std::vector<std::pair<std::string, std::vector<IntfImplTypeInfo>>>>>> {};
81+
82+
INSTANTIATE_TEST_SUITE_P(
83+
ElfGolangItableTestSuite, ElfGolangItableTest,
84+
::testing::Values(
85+
std::make_tuple(
86+
kTestGo1_21BinaryPath,
87+
UnorderedElementsAre(
88+
Pair("fmt.State",
89+
UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name, "*fmt.pp"))),
90+
Pair("internal/bisect.Writer",
91+
UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name,
92+
"*internal/godebug.runtimeStderr"))),
93+
Pair("internal/reflectlite.Type",
94+
UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name,
95+
"internal/reflectlite.rtype"))),
96+
Pair("error",
97+
UnorderedElementsAre(
98+
Field(&IntfImplTypeInfo::type_name, "main.IntStruct"),
99+
Field(&IntfImplTypeInfo::type_name, "*errors.errorString"),
100+
Field(&IntfImplTypeInfo::type_name, "syscall.Errno"),
101+
Field(&IntfImplTypeInfo::type_name, "*io/fs.PathError"),
102+
Field(&IntfImplTypeInfo::type_name, "runtime.errorString"),
103+
Field(&IntfImplTypeInfo::type_name, "internal/poll.errNetClosing"),
104+
Field(&IntfImplTypeInfo::type_name,
105+
"*internal/poll.DeadlineExceededError"),
106+
Field(&IntfImplTypeInfo::type_name, "*internal/bisect.parseError"))),
107+
Pair("io.Writer",
108+
UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name, "*os.File"))),
109+
Pair("sort.Interface", UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name,
110+
"*internal/fmtsort.SortedMap"))),
111+
Pair("reflect.Type",
112+
UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name, "*reflect.rtype"))),
113+
Pair("math/rand.Source64", UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name,
114+
"*math/rand.fastSource"))),
115+
Pair("math/rand.Source", UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name,
116+
"*math/rand.lockedSource"),
117+
Field(&IntfImplTypeInfo::type_name,
118+
"*math/rand.fastSource"))))),
119+
std::make_tuple(
120+
kTestGoBinaryPath,
121+
UnorderedElementsAre(
122+
Pair("error",
123+
UnorderedElementsAre(
124+
Field(&IntfImplTypeInfo::type_name, "main.IntStruct"),
125+
Field(&IntfImplTypeInfo::type_name, "*errors.errorString"),
126+
Field(&IntfImplTypeInfo::type_name, "*io/fs.PathError"),
127+
Field(&IntfImplTypeInfo::type_name,
128+
"*internal/poll.DeadlineExceededError"),
129+
Field(&IntfImplTypeInfo::type_name, "internal/poll.errNetClosing"),
130+
Field(&IntfImplTypeInfo::type_name, "runtime.errorString"),
131+
Field(&IntfImplTypeInfo::type_name, "syscall.Errno"))),
132+
Pair("sort.Interface", UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name,
133+
"*internal/fmtsort.SortedMap"))),
134+
Pair("math/rand.Source", UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name,
135+
"*math/rand.lockedSource"))),
136+
Pair("io.Writer",
137+
UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name, "*os.File"))),
138+
Pair("internal/reflectlite.Type",
139+
UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name,
140+
"*internal/reflectlite.rtype"))),
141+
Pair("reflect.Type",
142+
UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name, "*reflect.rtype"))),
143+
Pair("fmt.State",
144+
UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name, "*fmt.pp")))))));
145+
146+
TEST_P(ElfGolangItableTest, ExtractInterfaceTypes) {
147+
const std::string kPath = px::testing::BazelRunfilePath(std::get<0>(GetParam()));
73148

74149
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ElfReader> elf_reader, ElfReader::Create(kPath));
75-
ASSERT_OK_AND_ASSIGN(const auto interfaces, ExtractGolangInterfaces(elf_reader.get()));
150+
151+
ASSERT_OK_AND_ASSIGN(const auto interfaces_map, ExtractGolangInterfaces(elf_reader.get()));
152+
std::vector<std::pair<std::string, std::vector<IntfImplTypeInfo>>> interfaces(
153+
interfaces_map.begin(), interfaces_map.end());
76154

77155
// Check for `bazel coverage` so we can bypass the final checks.
78156
// Note that we still get accurate coverage metrics, because this only skips the final check.
79157
// Ideally, we'd get bazel to deterministically build test_go_binary,
80158
// but it's not easy to tell bazel to use a different config for just one target.
159+
81160
#ifdef PL_COVERAGE
82161
LOG(INFO) << "Whoa...`bazel coverage` is messaging with test_go_binary. Shame on you bazel. "
83162
"Ending this test early.";
84163
return;
85164
#else
86-
EXPECT_THAT(
87-
interfaces,
88-
UnorderedElementsAre(
89-
Pair("error",
90-
UnorderedElementsAre(
91-
Field(&IntfImplTypeInfo::type_name, "main.IntStruct"),
92-
Field(&IntfImplTypeInfo::type_name, "*errors.errorString"),
93-
Field(&IntfImplTypeInfo::type_name, "*io/fs.PathError"),
94-
Field(&IntfImplTypeInfo::type_name, "*internal/poll.DeadlineExceededError"),
95-
Field(&IntfImplTypeInfo::type_name, "internal/poll.errNetClosing"),
96-
Field(&IntfImplTypeInfo::type_name, "runtime.errorString"),
97-
Field(&IntfImplTypeInfo::type_name, "syscall.Errno"))),
98-
Pair("sort.Interface", UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name,
99-
"*internal/fmtsort.SortedMap"))),
100-
Pair("math/rand.Source", UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name,
101-
"*math/rand.lockedSource"))),
102-
Pair("io.Writer", UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name, "*os.File"))),
103-
Pair("internal/reflectlite.Type",
104-
UnorderedElementsAre(
105-
Field(&IntfImplTypeInfo::type_name, "*internal/reflectlite.rtype"))),
106-
Pair("reflect.Type",
107-
UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name, "*reflect.rtype"))),
108-
Pair("fmt.State", UnorderedElementsAre(Field(&IntfImplTypeInfo::type_name, "*fmt.pp")))));
165+
EXPECT_THAT(interfaces, std::get<1>(GetParam()));
109166
#endif
110167
}
111168

0 commit comments

Comments
 (0)