Implementing a syscall on real-time Debian Wheezy - linux-kernel

For educational purposes, I want to implement a system call in Debian Wheezy. I wish to implement it on the kernel that comes in the linux-image-3.2.0--rt-amd64 package. Here is an overview of what I have tried:
To get the kernel source:
apt-get source linux-image-3.2.0-4-rt-amd64
From that, I get the following files/directories the directory I executed in:
linux_3.2.41.orig.tar.xz
linux_3.2.41-2+deb7u2.dsc
linux_3.2.41-2+deb7u2.debian.tar.xz
as well as:
linux_3.2.41
which contains the source code for the kernel.
Then, to make the necessary changes in order to add the system call, I basically followed this page:
How to write system calls on debian/ubuntu
The following is a condensed version of the instructions given there modified to reflect the changes I made.
+File 1: linux-x.x.x/vpart_syscalls/vpart_syscalls.c
#include <linux/linkage.h>
#include <linux/kernel.h>
asmlinkage long insert_partition(char*dest, const char* src)
{
printk("<--- the syscall has been called!");
return 0;
}
File 2: linux-x.x.x/vpart_syscalls/Makefile. Create a Makefile within the same test directory you created above and put this line in it:
obj-y := vpart_syscalls.o
File 3: linux-x.x.x/arch/x86/kernel/syscall_table_32.S. Now, you have to add your system call to the system call table. Append to the file the following line:
.long insert_partition
File 4: linux-x.x.x/arch/x86/include/asm/unistd_32.h
In this file, the names of all the system calls will be associated with a unique number. After the last system call-number pair, add a line
#define __NR_insert_partition 349
Then replace NR_syscalls value, stating total number of system calls with (the existing number incremented by 1) i.e. in this case the NR_syscalls should've been 338 and the new value is 339.
#define NR_syscalls 350
File 5: linux-x.x.x/include/linux/syscalls.h
Append to the file the prototype of our function.
asmlinkage long insert_partition(int lenTicks, int vpid);
just before the #endif line in the file.
File 6: Makefile at the root of source directory.
Open Makefile and find the line where core-y is defined and add the directory test to the end of that line.
core-y += kernel/ mm/ fs/ test/ vpart_syscalls/
I then proceeded to build the kernel in a different fashion than is described there:
make localmodconfig
make menuconfig (making no changes)
make-kpkg clean
fakeroot make-kpkg --initrd --append-to-version=+tm kernel_image kernel_headers
cd ..
dpkg -i linux-image-3.8.*
dpkg -i linux-headers-3.8.*
The kernel that is installed boots fine. I made the following c program to test the syscall:
#include <stdio.h>
#include <linux/unistd.h>
#include <sys/syscall.h>
int main(){
printk("Calling the new syscall!\n");
int ret = 100;
ret = syscall(349, 1, 2);
printf("call return value: %i\n", ret);
return 0;
}
When I compile and run this program, I get a return value of -1. I check the messages using dmesg and there is no evidence of my printk being called..
If anyone knows where my problem is I would be really really happy! I should say I am not too experienced at changing and building the kernel, but I have learned a lot about it. I read Robert Loves book - linux kernel development and several guides on the webs.

I think, the steps 3 and 4 may be incorrect for 64-bit kernels:
File 3: linux-x.x.x/arch/x86/kernel/syscall_table_32.S.
File 4: linux-x.x.x/arch/x86/include/asm/unistd_32.h
There are two files here: http://lxr.linux.no/linux+v3.2.41/arch/x86/kernel/
syscall_64.c 668 2008-12-24 14:26:58 -0800
syscall_table_32.S 8659 2012-01-04 14:55:50 -0800
First one defines syscall table contents for 64-bit mode using C file and macro-cheating with unistd_64.h
#define __SYSCALL(nr, sym) [nr] = sym,
const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
....
#include <asm/unistd_64.h>
};
Where asm/unistd_64.h is
#define __NR_read 0
__SYSCALL(__NR_read, sys_read)
and so on.
And second one, which you changed - is for 32-bit mode and written using asm file and labels (.long sys_call_name).
So, you defined syscall for 32-bit mode and you are using linux-image-3.2.0-4-rt-amd64 which is basically for " 64-bit PCs".
I think you compiled your test program as gcc test.c, which defaults to 64-bit mode. You can try -m32 option of gcc: gcc -m32 test.c to get 32-bit application (this will only work if you have correct cross environment for 32-bit builds) or compile this test on some 32-bit linux.
Or the other choice is to make step "4a": edit arch/x86/include/asm/unistd_64.h to add two lines:
#define __NR_insert_partition YOUR_NUMBER
__SYSCALL(__NR_insert_partition, insert_partition)
I'm not sure where and how NR_syscalls for 64bit is defined. It may be generated during build.

Related

DPI-C and SystemVerilog External Compilation flow issue

ModelSim User's manual (v10.1c), in page 660, talks about the default autocompile flow (using vlog) and external compilation flow to get the the DPI-C to work in ModelSim. I'm able to get the auto-compile flow to work. I'm stuck with the external compilation flow.
Problem Summary: I get an "undefined reference" error in when I try to create the .dll file in-spite of using the right export and import statements in my system verilog file.
Here are the files that make up this project:
file 1: mytest.cpp
#include<stdio.h>
#include "experiment3.h"
int mymain() {
printf("---starting test in c-domain---\n");
PrintHelloWorld();
return 0;
}
file 2: experiment3.h
#ifndef INCLUDED_EXPERIMENT3
#define INCLUDED_EXPERIMENT3
#ifdef __cplusplus
#define DPI_LINK_DECL extern "C"
#else
#define DPI_LINK_DECL
#endif
#include "svdpi.h"
DPI_LINK_DECL DPI_DLLESPEC
int
mymain();
DPI_LINK_DECL void
PrintHelloWorld();
#endif
file 3: mytb.sv
module mytb;
timeunit 1ns/1ps;
export "DPI-C" function PrintHelloWorld;
import "DPI-C" context task mymain();
function void PrintHelloWorld();
$display("HelloWorld\n");
endfunction
//start test
initial begin
#10ns;
mymain();
end
endmodule
Here are the command that I am using:
command 1 :g++ -c -IC:\intelFPGA\17.0\modelsim_ase\include -o ./mytest.o ./mytest.cpp
comments :command 1 executes without any problem
key-words :MinGW, GCC
command 2 :g++ -shared -Bsymbolic -o ./mytest.dll ./mytest.o -LC:\intelFPGA\17.0\modelsim_ase\win32aloem
comments :[1] command 2 fails when I use the mytest.cpp showed above
[2] command 2 passes when I comment out "PrintHelloWorld()" in mytest.cpp
error :c:/mingw/bin/../lib/gcc/mingw32/8.2.0/../../../../mingw32/bin/ld.exe:
./mytest.o:mytest.cpp:(.text+0x2d): undefined
reference to '`PrintHelloWorld' collect2.exe:error:ld
returned 1 exit status
key-words :MinGW, GCC, dll
command 3 : vsim -sv_lib ../src_cpp/mytest work.mytb
comments : [1] executed in console in ModelSim
[2] works when I don't have "PrintHelloWorld()" in mytest.cpp
Most of the online DPI-C examples deal with running (CPP and .SV) everything in ModelSim. I don't want that. I want to keep the HW and SW flow separate. And, this separation does work to some extent (I have no issues with calling C functions from SV (import works fine). The roadblock is with trying to call SystemVerilog function from the C function (something seems to be wrong with the export).
Any thoughts on how I can get past this hurdle ?
Based on looking at examples, try adding -fPIC to your command 1. Then command 2 should work as is.
From my experience the final file should be a shared object (.so); not a dynamic link library (.dll). I run SystemVerilog on unix based systems, so maybe windows is different. If you run into an issue, that could be something to try.
Try changing
DPI_LINK_DECL void
PrintHelloWorld();
to
DPI_LINK_DECL DPI_DLLISPEC void
PrintHelloWorld();
(if DPI_DLLISPEC doesn't work replace it directly with __declspec(dllimport))

Capture vDSO in strace

I was wondering if there is a way to capture (in other words observe) vDSO calls like gettimeofday in strace.
Also, is there a way to execute a binary without loading linux-vdso.so.1 (a flag or env variable)?
And lastly, what if I write a program that delete the linux-vdso.so.1 address from the auxiliary vector and then execve my program? Has anyone ever tried that?
You can capture calls to system calls which have been implemented via the vDSO by using ltrace instead of strace. This is because calls to system calls implemented via the vDSO work differently than "normal" system calls and the method strace uses to trace system calls does not work with vDSO-implemented system calls. To learn more about how strace works, check out this blog post I wrote about strace. And, to learn more about how ltrace works, check out this other blog post I wrote about ltrace.
No, it is not possible to execute a binary without loading linux-vdso.so.1. At least, not on my version of libc on Ubuntu precise. It is certainly possible that newer versions of libc/eglibc/etc have added this as a feature but it seems very unlikely. See the next answer for why.
If you delete the address from the auxillary vector, your binary will probably crash. libc has a piece of code which will first attempt to walk the vDSO ELF object, and if this fails, will fall back to a hardcoded vsyscall address. The only way it will avoid this is if you've compiled glibc with the vDSO disabled.
There is another workaround, though, if you really, really don't want to use the vDSO. You can try using glibc's syscall function and pass in the syscall number for gettimeofday. This will force glibc to call gettimeofday via the kernel instead of the vDSO.
I've included a sample program below illustrating this. You can read more about how system calls work by reading my syscall blog post.
#include <sys/time.h>
#include <stdio.h>
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
int
main(int argc, char *argv[]) {
struct timeval tv;
syscall(SYS_gettimeofday, &tv);
return 0;
}
Compile with gcc -o test test.c and strace with strace -ttTf ./test 2>&1 | grep gettimeofday:
09:57:32.651876 gettimeofday({1467305852, 651888}, {420, 140735905220705}) = 0 <0.000006>

Creating multiple segment for section using linker script

Could anyone please point me out what is the limitation to the alignment value which makes the creation of multiple segment for a section.
With The test case mentioned below:
#include <stdio.h>
#define SIZE (1 << 11)
int Buffer[SIZE] __attribute__ ((aligned (SIZE * sizeof(int)))) ;
int main (int argc, char * argv[])
{
printf("Test\n");
return 0;
}
And here if i change the macro as:
#define SIZE (1 << 11) to #define SIZE (1 << 12)
Without the above changes we see only two loadable segment while with the above changes we observed three loadable segment. As the alignment of BSS changes from 8K to 16K for GCC 4.8.1 which creates three loadable segments.
So can anyone please tell me what changes need to be done in linker script to make creation of only one loadable segment for data.
There are two ways of creating a linker script:
1. Under ld/emulparam directory there are shell script which creates linker script.
2. While other part of linker script came from the actual source.
Now in the source part there depends which linker you are using ie. GNU linker or gold linker.
GNU Linker script is build based on:
Under directory ~/binutils-2013.11/ld/emulparams/, there are different architecture specifics shell script based on different ELF type and platform like for i386/Vxworks
elf_i386_vxworks.sh
While still the rest of generic contains in the script came from the ld/elf sources.
While about segment creation then please look into procedure **bfd_boolean
_bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info)**
under source "bfd/elf.c"

gcc recompile "make" result no changes

i'm using cs50 appliance.
i've tried to write a new file test.c , found as long as i include int i line, it doesn't generate a new file test, if i remove that line and make again, it can generate test file. then i made changes on the test file, it still output the original file result, no reflect the new changes.
#include <stdio.h>
#include <cs50.h>
int
main (void)
{
printf("Number: \n");
int i = GetInt();
}
it was running properly before though... anyone can help please?
Apparently your default rules for make run the compiler on test.c.
The compiler notices that you are assigning a value to variable i, but you never use that value in any way; it would normally report this as a warning.
Apparently either your compiler or make are configured in such a way that this warning becomes a fatal error to make.
The remedy is to use the variable. It looks as though you need to pick up a book on the C programming language, or follow a course, if that's not what you're doing already.

sys_call_table in linux kernel 2.6.18

I am trying to set the sys exit call to a variable by
extern void *sys_call_table[];
real_sys_exit = sys_call_table[__NR_exit]
however, when I try to make, the console gives me the error
error: ‘__NR_exit’ undeclared (first use in this function)
Any tips would be appreciated :) Thank you
Since you are in kernel 2.6.x , sys_call_table isnt exported any more.
If you want to avoid the compilation error try this include
#include<linux/unistd.h>
however, It will not work. So the work around to "play" with the sys_call_table is to find the address of sys_call_table in SystemXXXX.map (located at /boot) with this command:
grep sys_call System.map-2.6.X -i
this will give the addres, then this code should allow you to modify the table:
unsigned long *sys_call_table;
sys_call_table = (unsigned long *) simple_strtoul("0xc0318500",NULL,16);
original_mkdir = sys_call_table[__NR_mkdir];
sys_call_table[__NR_mkdir] = mkdir_modificado;
Hope it works for you, I have just tested it under kernel 2.6.24, so should work for 2.6.18
also check here, Its a very good
http://commons.oreilly.com/wiki/index.php/Network_Security_Tools/Modifying_and_Hacking_Security_Tools/Fun_with_Linux_Kernel_Modules
If you haven't included the file syscall.h, you should do that ahead of the reference to __NR_exit. For example,
#include <syscall.h>
#include <stdio.h>
int main()
{
printf("%d\n", __NR_exit);
return 0;
}
which returns:
$ cc t.c
$ ./a.out
60
Some other observations:
If you've already included the file, the usual reasons __NR_exit wouldn't be defined are that the definition was being ignored due to conditional compilation (#ifdef or #ifndef at work somewhere) or because it's being removed elsewhere through a #undef.
If you're writing the code for kernel space, you have a completely different set of headers to use. LXR (http://lxr.linux.no/linux) searchable, browsable archive of the kernel source is a helpful resource.

Resources