I'm following Derek Molloys guide to building loadable kernel modules, but get stuct at some points.
I have the kernel code in a .c-file:
hello.c
#include <linux/init.h> // Macros used to mark up functions e.g., __init __exit
#include <linux/module.h> // Core header for loading LKMs into the kernel
#include <linux/kernel.h> // Contains types, macros, functions for the kernel
MODULE_LICENSE("GPL"); ///< The license type -- this affects runtime behavior
MODULE_AUTHOR("Derek Molloy"); ///< The author -- visible when you use modinfo
MODULE_DESCRIPTION("A simple Linux driver for the BBB."); ///< The description -- see modinfo
MODULE_VERSION("0.1"); ///< The version of the module
static char *name = "world"; ///< An example LKM argument -- default value is "world"
module_param(name, charp, S_IRUGO); ///< Param desc. charp = char ptr, S_IRUGO can be read/not changed
MODULE_PARM_DESC(name, "The name to display in /var/log/kern.log"); ///< parameter description
/** #brief The LKM initialization function
* The static keyword restricts the visibility of the function to within this C file. The __init
* macro means that for a built-in driver (not a LKM) the function is only used at initialization
* time and that it can be discarded and its memory freed up after that point.
* #return returns 0 if successful
*/
static int __init helloBBB_init(void){
printk(KERN_INFO "EBB: Hello %s from the BBB LKM!\n", name);
return 0;
}
/** #brief The LKM cleanup function
* Similar to the initialization function, it is static. The __exit macro notifies that if this
* code is used for a built-in driver (not a LKM) that this function is not required.
*/
static void __exit helloBBB_exit(void){
printk(KERN_INFO "EBB: Goodbye %s from the BBB LKM!\n", name);
}
/** #brief A module must use the module_init() module_exit() macros from linux/init.h, which
* identify the initialization function at insertion time and the cleanup function (as
* listed above)
*/
module_init(helloBBB_init);
module_exit(helloBBB_exit);
and the makefile as this:
Makefile
obj-m+=hello.o
all:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean
when I try to run make in a directory with onl the two above files, i get
Make: Nothing to be done for all
I'm running 3.8.13-bone47, but I wasn't able to find the exact header files matching on this link that Derek recommended, so I downloaded 3.8.13-bone71 instead. Could that be the problem? Do I have to download the headers, when I'm compiling in directly on the BeagleBone? I have also tried change the lines in the Makefile to a hardcoded distribution name that matches mine (3.8.13-bone47), doesn't work either.
Thank you very much guys!
I solved my question. I had two problems:
Missing Tabs in Makefile
I added a tab to the beginning of each line with a make statement. It has to be an actually tab, the <\t> didn't work for me.
Wrong Header files
It turns out that the proper version of the header files is quite important :) I got the ones from http://rcn-ee.net/deb/trusty-armhf/v3.8.13-bone47/ and added the mach/timex.h file, and was the able to follow Derek's guide from then on.
Related
There is a large number of questions on SO about how to execute a library or dynamically load an executable. As far as I can tell, all the answers come down to: compile your executable as position-independent code and load it with dlopen. This worked great --- and still works great on macOS --- until a recent change in glibc, which explicitly disabled dlopening PIEs. This change is now in the current version of glibc (2.30) on ArchLinux, for example, and trying to dlopen a position-independent executable gives an error: "cannot dynamically load position-independent executable".
It's difficult to guess what prompted such a radical change that breaks so much code and useful use cases. (The explanations on Patchwork and Bugzilla don't make much sense to me.) But there is now a question: what to do if you want to create an executable that's also a dynamic library, or vice versa?
A solution was linked from one of the comments. Reproducing it here for posterity:
#include <stdio.h>
#include <unistd.h>
const char service_interp[] __attribute__((section(".interp"))) = "/lib/ld-linux-x86-64.so.2";
extern "C" {
void lib_entry(void)
{
printf("Entry point of the service library\n");
_exit(0);
}
}
Compiling with g++ -shared test-no-pie.cpp -o test-no-pie -Wl,-e,lib_entry produces a shared object (dynamic library) that can also be executed on Linux.
I have two questions:
What if I want to pass command-line arguments? How to modify this solution so it accepts arc,argv?
Are there other alternatives?
It's difficult to guess what prompted such a radical change
Not really: it never worked correctly.
that breaks so much code
That code was broken already in subtle ways. Now you get a clear indication that it will not work.
Are there other alternatives?
Don't do that?
What problem does dlopening an executable solve?
If it's a real problem, open a GLIBC bugzilla feature request, explaining that problem and requesting a supported mechanism to achieve desired result.
Update:
at least say why "it never worked correctly". Is it some triviality like potentially clashing globals between the executables, or something real?
Thread-local variables is an example that doesn't work correctly. Whether you think they are "real" or not I have no idea.
Here is the code:
// foo.c
#include <stdio.h>
__thread int var;
__attribute__((constructor))
static void init()
{
var = 42;
printf("foo.c init: %d %p\n", var, &var);
}
int bar() {
printf("foo.c bar: %d %p\n", var, &var);
return var;
}
int main()
{
printf("foo.c main: %d %p bar()=%d\n", var, &var, bar());
return 0;
}
gcc -g foo.c -o foo -Wl,-E -fpie -pie && ./foo
foo.c init: 42 0x7fb5dfd7d4fc
foo.c bar: 42 0x7fb5dfd7d4fc
foo.c main: 42 0x7fb5dfd7d4fc bar()=42
// main.c
// Error checking omitted for brevity
#include <dlfcn.h>
#include <stdio.h>
int main()
{
void *h1 = dlopen("./foo", RTLD_LOCAL|RTLD_LAZY);
int (*bar)(void) = dlsym(h1, "bar");
printf("main.c: %d\n", bar());
return 0;
}
gcc -g main.c -ldl && ./a.out
foo.c init: 42 0x7fb7305da73c
foo.c bar: 0 0x7fb7305da73c <<< what?
main.c: 0 <<< what?
This is using GNU C Library (Debian GLIBC 2.28-10) stable release version 2.28.
Bottom line: this was never designed to work, and you just happened to not step on many of the land-mines, so you thought it is working, when in fact you were exercising undefined behavior.
Please see this answer:
https://stackoverflow.com/a/68339111/14760867
The argc, argv question is not answered there, but when I found I needed one, I hacked something together to parse /proc/self/cmdline at runtime for pam_cap.so use.
There is a large number of questions on SO about how to execute a library or dynamically load an executable. As far as I can tell, all the answers come down to: compile your executable as position-independent code and load it with dlopen. This worked great --- and still works great on macOS --- until a recent change in glibc, which explicitly disabled dlopening PIEs. This change is now in the current version of glibc (2.30) on ArchLinux, for example, and trying to dlopen a position-independent executable gives an error: "cannot dynamically load position-independent executable".
It's difficult to guess what prompted such a radical change that breaks so much code and useful use cases. (The explanations on Patchwork and Bugzilla don't make much sense to me.) But there is now a question: what to do if you want to create an executable that's also a dynamic library, or vice versa?
A solution was linked from one of the comments. Reproducing it here for posterity:
#include <stdio.h>
#include <unistd.h>
const char service_interp[] __attribute__((section(".interp"))) = "/lib/ld-linux-x86-64.so.2";
extern "C" {
void lib_entry(void)
{
printf("Entry point of the service library\n");
_exit(0);
}
}
Compiling with g++ -shared test-no-pie.cpp -o test-no-pie -Wl,-e,lib_entry produces a shared object (dynamic library) that can also be executed on Linux.
I have two questions:
What if I want to pass command-line arguments? How to modify this solution so it accepts arc,argv?
Are there other alternatives?
It's difficult to guess what prompted such a radical change
Not really: it never worked correctly.
that breaks so much code
That code was broken already in subtle ways. Now you get a clear indication that it will not work.
Are there other alternatives?
Don't do that?
What problem does dlopening an executable solve?
If it's a real problem, open a GLIBC bugzilla feature request, explaining that problem and requesting a supported mechanism to achieve desired result.
Update:
at least say why "it never worked correctly". Is it some triviality like potentially clashing globals between the executables, or something real?
Thread-local variables is an example that doesn't work correctly. Whether you think they are "real" or not I have no idea.
Here is the code:
// foo.c
#include <stdio.h>
__thread int var;
__attribute__((constructor))
static void init()
{
var = 42;
printf("foo.c init: %d %p\n", var, &var);
}
int bar() {
printf("foo.c bar: %d %p\n", var, &var);
return var;
}
int main()
{
printf("foo.c main: %d %p bar()=%d\n", var, &var, bar());
return 0;
}
gcc -g foo.c -o foo -Wl,-E -fpie -pie && ./foo
foo.c init: 42 0x7fb5dfd7d4fc
foo.c bar: 42 0x7fb5dfd7d4fc
foo.c main: 42 0x7fb5dfd7d4fc bar()=42
// main.c
// Error checking omitted for brevity
#include <dlfcn.h>
#include <stdio.h>
int main()
{
void *h1 = dlopen("./foo", RTLD_LOCAL|RTLD_LAZY);
int (*bar)(void) = dlsym(h1, "bar");
printf("main.c: %d\n", bar());
return 0;
}
gcc -g main.c -ldl && ./a.out
foo.c init: 42 0x7fb7305da73c
foo.c bar: 0 0x7fb7305da73c <<< what?
main.c: 0 <<< what?
This is using GNU C Library (Debian GLIBC 2.28-10) stable release version 2.28.
Bottom line: this was never designed to work, and you just happened to not step on many of the land-mines, so you thought it is working, when in fact you were exercising undefined behavior.
Please see this answer:
https://stackoverflow.com/a/68339111/14760867
The argc, argv question is not answered there, but when I found I needed one, I hacked something together to parse /proc/self/cmdline at runtime for pam_cap.so use.
The GCC manual describes -fabi-compat-version=n, which is used to handle variations in C++ name mangling in the slightly variant C++ ABIs of GCC 3.4 to 9.2, and probably later. It has an important caveat:
On targets that support strong aliases, G++ works around mangling changes by
creating an alias with the correct mangled name when defining a symbol with
an incorrect mangled name. This switch specifies which ABI version to use for
the alias.
However, it's not immediately obvious how you find out if your platform does support strong aliases.
The easy way to find out is to write a small program that uses __attribute__ to create an alias, and then use nm to see if the alias exists. Here's sample code, which is a variant on the classic "Hello, World" program:
/* strong_alias.c
compile with 'gcc strong_alias.c'
run ./a.out to check it works, and then run 'nm ./a.out'
to check that strong_alias() has the same address as main() */
#include <stdio.h>
int main( int argc, char *argv[])
{
printf( "Hello, World\n");
return 0;
}
int strong_alias( int argc, char *argv[]) __attribute__ (( alias ("main")));
Compile it and check that it runs, then use nm strong_alias to look at its symbol table. Here's a version that was compiled on CentOS 7 for x86-64:
nm ./a.out | egrep ' (main|strong_alias)'
000000000040052d T main
000000000040052d T strong_alias
We can see that main and strong_alias have the same address, and are thus aliases.
I read the following manual:
http://linux.die.net/man/3/clock_gettime
and I wrote the following code:
#include <time.h>
int main() {
struct timespec clk;
clock_gettime(CLOCK_REALTIME, &clk);
return 0;
}
Surprisingly, I get the following errors:
Symbol CLOCK_REALTIME could not be resolved
undefined reference to clock_gettime
I still don't understand what is the problem. I included the header, and these names show in this header.
maybe you should use#define _POSIX_TIMERS,#define _REENTRANT
besides, when you compile the code, make sure to link the real-time library which is cc filename.c -o filename -lrt
Update 1.0:
sometimes in windows or mac os, C ide may not include real-time library automatically, or we may not used the posix directly without _POSIX_TIMES, therefore you have to link the real-time library manually. In Linux, you can just type in cc filename.c -o filename -lrt to compile the c file.
I created the make file
obj-m += hello.o
all:
make -C /home/developer/Desktop/xukr-20120201-omap3/linux-2.6.37-tn M=/home/developer/Desktop/module_test modules
clean:
make -C /home/developer/Desktop/xukr-20120201-omap3/linux-2.6.37-tn M=/home/developer/Desktop/module_test clean
Then, i found a simple hello program
#define __KERNEL__ /* We're part of the kernel */
#define MODULE /* Not a permanent part, though. */
/* Standard headers for LKMs */
#include <linux/modversions.h>
#include <linux/module.h>
#include <linux/tty.h> /* console_print() interface */
/* Initialize the LKM */
int init_module()
{
console_print("Hello, world - this is the kernel speaking\n");
/* More normal is printk(), but there's less that can go wrong with
console_print(), so let's start simple.
*/
/* If we return a non zero value, it means that
* init_module failed and the LKM can't be loaded
*/
return 0;
}
/* Cleanup - undo whatever init_module did */
void cleanup_module()
{
console_print("Short is the life of an LKM\n");
}
And i tried to compile on command line with this
make ARCH=arm CROSS_COMPILE=angstrom-linux-gnueabi-
And i get this error
/bin/sh: angstrom-linux-gnueabi-gcc: not found
What is wrong with this? i am really new at this.
Thanks in advance,
You could use remake to debug and understand your Makefile. Invoke it as remake -x -d it will give you a lot of debugging output, while behaving otherwise as GNU make.
As I commented, don't forget to use $(MAKE) instead of make inside your Makefile.
Regarding the error: angstrom-linux-gnueabi-gcc: not found you need to install the appropriate cross-compiler (and cross-linker) toolchain on your system (or perhaps set appropriately your PATH environment variable, so that it would be found).
All this won't solve your problem, but will help you understanding it