Undefined symbols in hello world C++ kernel module - linux

I have added C++ support to the Linux kernel version 4.14.41, compiled it and booted using the kernel successfully. I can check the correctness of the C++ module by inserting a LKM. This is the module that I am trying to load:
#include<c++/begin_include.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<c++/end_include.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LKM in c++");
MODULE_AUTHOR("MOOL");
class hello
{
public:
hello();
void hi();
};
void hello::hi()
{
printk("Hello world!! \n");
}
hello::hello()
{
printk("Constructor is being called \n");
}
extern "C"
{
static int __init test_classes_init()
{
class hello obj;
obj.hi();
printk("Module inserted:\n");
return 0;
}
static void __exit test_classes_fini()
{
printk("Module removed:\n");
}
module_init(test_classes_init);
module_exit(test_classes_fini);
}
The Makefile:
obj-m = helloworld.o
KVERSION=$(shell uname -r)
all:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
When I enter the make command, the helloworld.ko is generated with the warnings
WARNING: "begin_fini" [/home/jai/Downloads/helloworld/helloworld.ko] undefined !
WARNING: "end_init" [/home/jai/Downloads/helloworld/helloworld.ko] undefined !
WARNING: "begin_init" [/home/jai/Downloads/helloworld/helloworld.ko] undefined !
But when I try to insert it using insmod helloworld.ko, the undefined symbol error occurs.
dmesg:
loading out-of-tree module taints kernel
Unknown symbol begin_init (err 0)
Unknown symbol end_init (err 0)
Unknown symbol begin_fini (err 0)
These begin_init, end_init and begin_fini are defined in lib/gcc/crtstuff.c (which was ported into the kernel). These functions are declared as extern in both crtstuff.c and linux/module.h. This module.h is being included in the helloworld module above, but still, those symbols become undefined. So, How can I make those functions defined?

Your kernel C++ implementation is incomplete. You will have to implement global constructor and destructor support (processing of .init_array and .fini_array sections properly), or stop using these C++ features in the source code. This needs cooperation from the kernel module loader. Changes to the startup code will not work because the startup code is not linked into kernel modules.

Related

Error inserting kernel module on target device

I have written a basic kernel module which creates a thread and then starts the thread.
I am compiling the kernel module for Beagle bone black board and i have the kernel source code for Beagle bone black.
The kernel module source code(kmodule.c) and Makefile is given below.
/**kmodule.c**/
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/kthread.h>
#include<linux/types.h>
#include<linux/sched.h> //For wake_up_process()
static struct task_struct *th_handler = NULL;
static s32 thread_function(void *pdata);
static s32 __init thread_module_init(void)
{
th_handler = kthread_create(thread_function,NULL,"test_thread");
if(!IS_ERR_OR_NULL(th_handler))
{
wake_up_process(th_handler);
}
pr_info("%s done.",__FUNCTION__);
return 0;
}
static void __exit thread_module_deinit(void)
{
pr_info("%s done.",__FUNCTION__);
}
static s32 thread_function(void *pdata)
{
pr_info("Inside thread function.");
}
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1");
MODULE_AUTHOR("Author name");
MODULE_DESCRIPTION("Description");
/**Makefile**/
export ARCH=arm
export CROSS_COMPILE=~/cross_compiler/arm-linux-gnueabi- #Cross compiler for Beagle Bone black
BBB_KERNEL_SRC_PATH=~/bbb_kernelsrc #Kernel source path for Beagle bone black.
obj-m += threadmodule.o
threadmodule-objs := kmodule.o
all:
make -C $(BBB_KERNEL_SRC_PATH) M=$(PWD) modules
clean:
make -C $(BBB_KERNEL_SRC_PATH) M=$(PWD) clean
The kernel module builds fine without errors and .ko file is generated.
When i insert the kernel module on Beagle bone board black i get the following error.
insmod: ERROR: could not insert module threadmodule.ko: Invalid
parameters
Also dmesg|tail -20 output:
threadmodule: disagrees about version version of symbol wake_up_process
threadmodule: Unknown symbol wake_up_process(err -22)
Any idea why this error is coming.
Beagle bone black kernel source version : 4.19.94

Thread safety of std::cout insertion operator

I've always thought that using std::cout << something was thread safe.
For this little example
#include <iostream>
#include <thread>
void f()
{
std::cout << "Hello from f\n";
}
void g()
{
std::cout << "Hello from g\n";
}
int main()
{
std::thread t1(f);
std::thread t2(g);
t1.join();
t2.join();
}
my expectation was that the order of the two outputs would be undefined (and indeed that is what I observe in practice), but that the calls to operator<< are thread safe.
However, ThreadSanitizer, DRD and Helgrind all seem to give various errors regarding access to std::__1::ios_base::width(long) and std::__1::basic_ios<char, std::__1::char_traits >::fill()
On Compiler Explorer I don't see any errors.
On FreeBSD 13, ThreadSanitizer gives me 3 warnings, the two listed above plus the malloc/memcpy to the underlying i/o buffer.
Again in FreeBSD 13, DRD gives 4 errors, width() and fill() times two for the two threads.
Finally FreeBSD 13 Helgrind gives one known false positive related to TLS in thread creation, fill()and width() twice.
On Fedora 34
No errors with g++ 11.2.1 and ThreadSanitizer
DRD complains about malloc/memcpy in fwrite with g++ compiled exe
Helgrind also complains about fwrite and also for the construction of cout, again with the g++ compiled exe
clang++ 12 ThreadSanitizer complains about fill() and width()
DRD with the clang++ compiler exe complains about fill(), width(), fwrite and one other in start_thread
Helgrind with the clang++ exe complains about some TLS, fill(), width(), fwrite
macOS XCode clang++ ThreadSanitizer generates warnings as well (which will be libc++).
Looking at the libc++ and libstdc++ code I don't see anything at all that protects width(). So I don't understand why there are no complaints on compiler explorer.
I tried running with TSAN_OPTIONS=print_suppressions=1 and there was no more output (g++ Fedora ThreadSanitizer)
There does seem to be some consensus over the width() and fill() calls.
Looking more closely at the libstdc++ source I see that there is
(with some trimming and comments):
// ostream_insert.h
// __n is the length of the string pointed to by __s
template<typename _CharT, typename _Traits>
basic_ostream<_CharT, _Traits>&
__ostream_insert(basic_ostream<_CharT, _Traits>& __out,
const _CharT* __s, streamsize __n)
{
typedef basic_ostream<_CharT, _Traits> __ostream_type;
typedef typename __ostream_type::ios_base __ios_base;
typename __ostream_type::sentry __cerb(__out);
if (__cerb)
{
__try
{
const streamsize __w = __out.width();
if (__w > __n)
{
// snipped
// handle padding
}
else
__ostream_write(__out, __s, __n);
// why no hazard here?
__out.width(0);
}
__out is the stream object, global cout in this case. I don't see anything like locks or atomics.
Any suggestions as to how ThreadSanitizer/g++ is getting a "clean" output?
There is this somewhat cryptic comment
template<typename _CharT, typename _Traits>
basic_ostream<_CharT, _Traits>::sentry::
sentry(basic_ostream<_CharT, _Traits>& __os)
: _M_ok(false), _M_os(__os)
{
// XXX MT
if (__os.tie() && __os.good())
__os.tie()->flush();
The libc++ code looks similar. In iostream
template<class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>&
__put_character_sequence(basic_ostream<_CharT, _Traits>& __os,
const _CharT* __str, size_t __len)
{
#ifndef _LIBCPP_NO_EXCEPTIONS
try
{
#endif // _LIBCPP_NO_EXCEPTIONS
typename basic_ostream<_CharT, _Traits>::sentry __s(__os);
if (__s)
{
typedef ostreambuf_iterator<_CharT, _Traits> _Ip;
if (__pad_and_output(_Ip(__os),
__str,
(__os.flags() & ios_base::adjustfield) == ios_base::left ?
__str + __len :
__str,
__str + __len,
__os,
__os.fill()).failed())
__os.setstate(ios_base::badbit | ios_base::failbit);
and in locale
template <class _CharT, class _OutputIterator>
_LIBCPP_HIDDEN
_OutputIterator
__pad_and_output(_OutputIterator __s,
const _CharT* __ob, const _CharT* __op, const _CharT* __oe,
ios_base& __iob, _CharT __fl)
{
streamsize __sz = __oe - __ob;
streamsize __ns = __iob.width();
if (__ns > __sz)
__ns -= __sz;
else
__ns = 0;
for (;__ob < __op; ++__ob, ++__s)
*__s = *__ob;
for (; __ns; --__ns, ++__s)
*__s = __fl;
for (; __ob < __oe; ++__ob, ++__s)
*__s = *__ob;
__iob.width(0);
return __s;
}
Again I see no thread protection, but also this time the tools detect a hazard.
Are these real issues? For plain calls to operator<< the value of width doesn't change, and is always 0.
libstdc++ does not produce the error while libc++ does.
iostream.objects.overview Concurrent access to a synchronized ([ios.members.static]) standard iostream object's formatted and unformatted input ([istream]) and output ([ostream]) functions or a standard C stream by multiple threads does not result in a data race ([intro.multithread]).
So this looks like a libc++ bug to me.
I got the answer from Jonathan Wakely. Makes me feel rather stupid.
The difference is that on Fedora, libstdc++.so contains an explicit instantiation of the iostream classes. libstdc++.so isn't instrumented for ThreadSanitizer so it cannot detect any hazards related to it.

Running LLVM passes on Windows 10 gives no output in terminal?

I've the sample pass code from LLVM.org:
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
namespace {
struct Hello : public FunctionPass {
static char ID;
Hello() : FunctionPass(ID) {}
bool runOnFunction(Function &F) override {
errs() << "Hello: ";
errs().write_escaped(F.getName()) << '\n';
return false;
}
}; // end of struct Hello
} // end of anonymous namespace
char Hello::ID = 0;
static RegisterPass<Hello> X("hello", "Hello World Pass",
false /* Only looks at CFG */,
false /* Analysis Pass */);
The project builds fine and creates a SkeletonPass.dll.
When I execute the command:
C:\Users\nlykkei\Projects\llvm-pass-tutorial\build>opt -load skeleton\Debug\SkeletonPass.dll -hello foo.bc
opt: Unknown command line argument '-hello'. Try: 'opt -help'
opt: Did you mean '-help'?
opt doesn't recognize -hello option, even thus everything works fine on Ubuntu 16.04.
In addition, if I execute:
clang -Xclang -load -Xclang skeleton\Debug\SkeletonPass.dll foo.bc
nothing is printed out on Visual Studio terminal (Native Tools Command Prompt x86). On Linux, the function names are printed nicely for the same bitcode file.
What can be the reason for my experience? I do exactly the same on Windows 10 as I do on Ubuntu, but very different results.
Plugins are special beasts on Windows, because the latter does not support proper dynamic linking, so, your pass simply does not register itself in the PassRegistry. So you'd either need to compile all the LLVM into .dll or link your pass statically into opt / clang.

GoogleTest CMake doesn't recognize TEST_F: Like it's not recognizing GTest something

OK, I admit it, this is a unique case. When we build our application we are using make, so I've included my tests in a test folder under src. Then at the same level as our release folder we have created a unit-test folder that includes all of our source files and our test source files.
But my IDE is CLion, which uses CMake. In my CMakeLists.txt file, I have included:
enable_testing()
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})
add_executable(TestProject ${SOURCE_FILES})
target_link_libraries(TestProject ${GTEST_BOTH_LIBRARIES})
I am creating my first Test Fixture. Here is the code:
#include "OPProperties.h"
#include "gtest/gtest.h"
namespace {
// The fixture for testing class OPPropertiesTestTest.
class OPPropertiesTestTest : public ::testing::Test {
protected:
// You can remove any or all of the following functions if its body
// is empty.
OPPropertiesTestTest() {
// You can do set-up work for each test here.
}
virtual ~OPPropertiesTestTest() {
// You can do clean-up work that doesn't throw exceptions here.
}
// If the constructor and destructor are not enough for setting up
// and cleaning up each test, you can define the following methods:
virtual void SetUp() {
// Code here will be called immediately after the constructor (right
// before each test).
}
virtual void TearDown() {
// Code here will be called immediately after each test (right
// before the destructor).
}
// Objects declared here can be used by all tests in the test case for OPPropertiesTestTest.
};
TEST_F(OPPropertiesTestTest, ThisTestWillFail) {
EXPECT_EQ(0, 2);
}
} // namespace
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Here is an image capture:
Notice the syntax checker errors in my TEST_F function. When I started to type TEST_F code completion is trying to find a Boost Test Function.
Can someone tell me what else I need to add to the CMakeLists.txt file or what I am not doing that GTest functions are not being recognized?
As πάντα ῥεῖ pointed out, I hadn't actually tried to build the code. When I did I first received a linker error for pthread, so we added the following line the the CMakeLists.txt file:
target_link_libraries(OnePrint pthread)
Then I tried again to build and received these errors:
/home/user/gtest-1.7.0/lib/.libs/libgtest.so: undefined reference to `pthread_key_create'
/home/user/gtest-1.7.0/lib/.libs/libgtest.so: undefined reference to `pthread_getspecific'
/home/user/gtest-1.7.0/lib/.libs/libgtest.so: undefined reference to `pthread_key_delete'
/home/user/gtest-1.7.0/lib/.libs/libgtest.so: undefined reference to `pthread_setspecific'
collect2: error: ld returned 1 exit status
So, I ran a search on these errors and found this question.
The answer that worked for me was here.

Create a shared lib using another shared lib

I have a shared library "libwiston.so". I am using this to create another shared library called "libAnimation.so", which will be used by another project. Now, the second library "libAnimation.so" can't be used in test code correctly. So I doubt that the creation of the second lib "libAnimation.so" is right. The gcc command to create this lib is
g++ -g -shared -Wl,-soname,libwiston.so -o libAnimation.so $(objs) -lc".
Has someone come across this problem?
That looks like a weird link line - you are creating libAnimation.so, but its internal DT_SONAME name is libwiston.so.
I don't think that what you wanted to do. Don't you want to link libAnimation.so against libwiston.so (-lwiston)?
g++ -g -shared -o libAnimation.so $(objs) -lc -lwiston
I think it would be easier to wrap your build in automake/autoconf and rely on libtool to get the shared library creation correct.
I'll do a humble review on the process of creating shared libraries.
Let's begin by creating libwiston.so. First we implement the function we would like to export and then define it on a header so other programs knows how to call it.
/* file libwiston.cpp
* Implementation of hello_wiston(), called by libAnimation.so
*/
#include "libwiston.h"
#include <iostream>
int hello_wiston(std::string& msg)
{
std::cout << msg << std::endl;
return 0;
}
and:
/* file libwiston.h
* Exports hello_wiston() as a C symbol.
*/
#include <string>
extern "C" {
int hello_wiston(std::string& msg);
};
This code can be compiled with: g++ libwiston.cpp -o libwiston.so -shared
Now we implement the second shared library, named libAnimation.so that calls the function exported by the first library.
/* file libAnimation.cpp
* Implementation of call_wiston().
* This function is a simple wrapper around hello_wiston().
*/
#include "libAnimation.h"
#include "libwiston.h"
#include <iostream>
int call_wiston(std::string& param)
{
hello_wiston(param);
return 0;
}
and header:
/* file libAnimation.h
* Exports call_wiston() as a C symbol.
*/
#include <string>
extern "C" {
int call_wiston(std::string& param);
};
Compile it with: g++ libAnimation.cpp -o libAnimation.so -shared -L. -lwiston
Finally, we create a small application to test libAnimation.
/* file demo.cpp
* Implementation of the test application.
*/
#include "libAnimation.h"
int main()
{
std::string msg = "hello stackoverflow!";
call_wiston(msg);
}
And compile it with: g++ demo.cpp -o demo -L. -lAnimation
There's an interesting tool named nm that you can use to list the symbols exported by your shared library. Using these examples, you could execute the following commands to check for the symbols:
nm libAnimation.so | grep call_wiston
outputs:
00000634 t _GLOBAL__I_call_wiston
000005dc T call_wiston
and also:
nm libwiston.so | grep hello_wiston
outputs:
0000076c t _GLOBAL__I_hello_wiston
000006fc T hello_wiston

Resources