Files
llvm-project/compiler-rt/lib/interception/tests/interception_linux_test.cpp
Yaxun (Sam) Liu 43d6e3874e [compiler-rt] Expose shared DSO helpers for compiler-rt runtimes (#191098)
The motivation of this PR is to refactor and expose DSO helper functions
so
they can be used by all compiler-rt libraries, including the profile
library,
without duplicating dlopen/dlsym (non-Windows) or
LoadLibrary/GetProcAddress
(Windows) logic in each runtime.

Implement the helpers in namespace __interception in
interception_linux.cpp for
non-Windows targets and interception_win.cpp for Windows, and use them
from the
existing Linux interception path for RTLD_NEXT/RTLD_DEFAULT/dlvsym
lookups.

This is NFC for existing libraries that already use interception's
public APIs;
sanitizer and interception lit behavior is unchanged.
2026-04-10 09:30:07 -04:00

163 lines
4.6 KiB
C++

//===-- interception_linux_test.cpp ---------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
// Tests for interception_linux.h.
//
//===----------------------------------------------------------------------===//
// Do not declare functions in ctype.h.
#define __NO_CTYPE
#include "interception/interception.h"
#include <stdlib.h>
#include "gtest/gtest.h"
#if SANITIZER_LINUX
static int isdigit_called;
namespace __interception {
int isalpha_called;
int isalnum_called;
int islower_called;
} // namespace __interception
using namespace __interception;
DECLARE_REAL(int, isdigit, int);
DECLARE_REAL(int, isalpha, int);
DECLARE_REAL(int, isalnum, int);
DECLARE_REAL(int, islower, int);
INTERCEPTOR(void *, malloc, SIZE_T s) { return calloc(1, s); }
INTERCEPTOR(void, dummy_doesnt_exist__, ) { __builtin_trap(); }
INTERCEPTOR(int, isdigit, int d) {
++isdigit_called;
return d >= '0' && d <= '9';
}
INTERCEPTOR(int, isalpha, int d) {
// Use non-commutative arithmetic to verify order of calls.
isalpha_called = isalpha_called * 10 + 3;
return (d >= 'a' && d <= 'z') || (d >= 'A' && d <= 'Z');
}
INTERCEPTOR(int, isalnum, int d) {
isalnum_called = isalnum_called * 10 + 3;
return __interceptor_isalpha(d) || __interceptor_isdigit(d);
}
INTERCEPTOR(int, islower, int d) {
islower_called = islower_called * 10 + 3;
return d >= 'a' && d <= 'z';
}
namespace __interception {
TEST(Interception, InterceptFunction) {
uptr malloc_address = 0;
EXPECT_TRUE(InterceptFunction("malloc", &malloc_address, (uptr)&malloc,
(uptr)&TRAMPOLINE(malloc)));
EXPECT_NE(0U, malloc_address);
EXPECT_FALSE(InterceptFunction("malloc", &malloc_address, (uptr)&calloc,
(uptr)&TRAMPOLINE(malloc)));
uptr dummy_address = 0;
EXPECT_FALSE(InterceptFunction("dummy_doesnt_exist__", &dummy_address,
(uptr)&dummy_doesnt_exist__,
(uptr)&TRAMPOLINE(dummy_doesnt_exist__)));
EXPECT_EQ(0U, dummy_address);
}
TEST(Interception, Basic) {
EXPECT_TRUE(INTERCEPT_FUNCTION(isdigit));
// After interception, the counter should be incremented.
isdigit_called = 0;
EXPECT_NE(0, isdigit('1'));
EXPECT_EQ(1, isdigit_called);
EXPECT_EQ(0, isdigit('a'));
EXPECT_EQ(2, isdigit_called);
// Calling the REAL function should not affect the counter.
isdigit_called = 0;
EXPECT_NE(0, REAL(isdigit)('1'));
EXPECT_EQ(0, REAL(isdigit)('a'));
EXPECT_EQ(0, isdigit_called);
}
TEST(Interception, DsoHelpers) {
EXPECT_TRUE(DynamicLoaderAvailable());
void* self = OpenLibrary(nullptr);
EXPECT_NE(nullptr, self);
EXPECT_NE(nullptr, LookupSymbol(self, "malloc"));
EXPECT_NE(nullptr, LookupSymbolDefault("malloc"));
EXPECT_NE(nullptr, LookupSymbolNext("malloc"));
EXPECT_EQ(nullptr, LookupSymbol(self, "symbol_that_does_not_exist__"));
}
TEST(Interception, ForeignOverrideDirect) {
// Actual interceptor is overridden.
EXPECT_FALSE(INTERCEPT_FUNCTION(isalpha));
isalpha_called = 0;
EXPECT_NE(0, isalpha('a'));
EXPECT_EQ(13, isalpha_called);
isalpha_called = 0;
EXPECT_EQ(0, isalpha('_'));
EXPECT_EQ(13, isalpha_called);
isalpha_called = 0;
EXPECT_NE(0, REAL(isalpha)('a'));
EXPECT_EQ(0, REAL(isalpha)('_'));
EXPECT_EQ(0, isalpha_called);
}
#if ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT
TEST(Interception, ForeignOverrideIndirect) {
// Actual interceptor is _not_ overridden.
EXPECT_TRUE(INTERCEPT_FUNCTION(isalnum));
isalnum_called = 0;
EXPECT_NE(0, isalnum('a'));
EXPECT_EQ(13, isalnum_called);
isalnum_called = 0;
EXPECT_EQ(0, isalnum('_'));
EXPECT_EQ(13, isalnum_called);
isalnum_called = 0;
EXPECT_NE(0, REAL(isalnum)('a'));
EXPECT_EQ(0, REAL(isalnum)('_'));
EXPECT_EQ(0, isalnum_called);
}
TEST(Interception, ForeignOverrideThree) {
// Actual interceptor is overridden.
EXPECT_FALSE(INTERCEPT_FUNCTION(islower));
islower_called = 0;
EXPECT_NE(0, islower('a'));
EXPECT_EQ(123, islower_called);
islower_called = 0;
EXPECT_EQ(0, islower('A'));
EXPECT_EQ(123, islower_called);
islower_called = 0;
EXPECT_NE(0, REAL(islower)('a'));
EXPECT_EQ(0, REAL(islower)('A'));
EXPECT_EQ(0, islower_called);
}
#endif // ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT
} // namespace __interception
#endif // SANITIZER_LINUX