How does linker know what to link with a system call? - linux

When I was trying to compile squid by hand on a RHEL 5.5 server, run configure and got
configure: WARNING: Eep! Cannot find epoll, kqueue, /dev/poll, poll or select!
configure: WARNING: Will try select and hope for the best.
configure: Using select for the IO loop.
Looks like the kernel is not configured with CONFIG_EPOLL. So I tried to compile this example epoll program to check whether it works.
On my gentoo box (which CONFIG_EPOLL is enabled.), it's compiled without any problem.
On the server, I got
/tmp/cc8PhJh0.o: In function 'main':
epoll-exmaple.c:(.text+0x262): undefined reference to 'epoll_create1'
collect2: ld returned 1 exit status
We all know for c program compiler looks for the definitions in *.h files and linker links them with *.so files.
My questions is, epoll_create1 is a system call to kernel. Which file exactly does linker search to locate the implementation to that system call?
Thanks.

It looks in the system C library (normally; a small handful of system calls are in other special libraries like librt). The C library provides a C API for userspace programs that handles making the system call for you. Sometimes this can be a very thin wrapper around the system call that just takes care of setting up and returning the arguments, but more frequently it has various glue that you don't want to have to worry about, such as differences in data sizes between userspace and the kernel, differences in implementation for the different architectures, backward or forward compatibility for changes in the kernel system call API, and so forth.
% readelf -s /lib/i386-linux-gnu/libc.so.6 | grep epoll_create1
1837: 000d5280 52 FUNC GLOBAL DEFAULT 12 epoll_create1##GLIBC_2.9
If you look at the C library as above, you can see the C function that the linker is linking code against.

Related

Does executable file of C++ program contain object code of system calls also

We use Linux system calls like fork(), pthread(), signal() and so on in C or C++ programs and compile the program to generate executable file (a.out). Now my doubt is whether the file a.out contain the object code of all linux system calls used, or whether the executable contain only the calls to system functions and the system call functions are linked during runtime? Suppose if I move my a.out file to some other Linux operating system which implements system calls in different syntax and try to compile it there will it work?
My doubt is whether system call function definitions part of a.out file?
User space binaries don't contain implementations of system calls. That would mean that any user could inject any code into kernel and take over system.
Instead they need to switch to kernel mode, by using processor interrupt or special instruction. Then processor can execute system call implementation from the kernel.
User space library, such as libc, is usually used, which provides stubs, which convert arguments of a syscall to a proper protocol and trigger jump to kernel mode. It is usually linked dynamically, so these stubs also don't appear in executable file.

How to invoke newly added system call by the function id without using syscall(__NR_mysyscall)

I am working with Linux-3.9.3 kernel in Ubuntu 10.04. I have added a basic system call in the kernel directory of the linux-3.9.3 source tree. I am able to use it with syscall() by passing my newly system call number in it as an argument. But I want to invoke it directly by using its method name as in the case of getpid() or open() system calls. Can any one help me to add it in GNU C library. I went through few documents but did not get any clear idea of how to accomplish it.
Thanks!!!
Assuming you are on a 64 bits Linux x86-64, the relevant ABI is the x86-64 ABI. Read also the x86 calling conventions wikipage and the linux assembly howto and syscalls(2)
So syscalls are using a different convention than ordinary function calls (e.g. all arguments are passed by registers, error condition could use the carry bit). Hence, you need a C wrapper to make your syscall available to C applications.
You could look into the source code of existing C libraries, like GNU libc or musl libc (so you'll need to make your own library for that syscall).
The MUSL libc source code is very readable, see e.g. its src/unistd/fsync.c as an example.
I would suggest wrapping your new syscall in your own library without patching libc. Notice that some uncommon syscalls are sitting in a different library, e.g. request_key(2) has its C wrapper in libkeyutils

How to build the elf interpreter (ld-linux.so.2/ld-2.17.so) as static library?

I apologize if my question is not precise because I don't have a lot
of Linux related experience. I'm currently building a Linux from
scratch (mostly following the guide at linuxfromscratch.org version
7.3). I ran into the following problem: when I build an executable it
gets a hardcoded path to something called ELF interpreter.
readelf -l program
shows something like
[Requesting program interpreter: /lib/ld-linux.so.2]
I traced this library ld-linux-so.2 to be part of glibc. I am not very
happy with this behaviour because it makes the binary very unportable
- if I change the location of /lib/ld-linux.so.2 the executable no
longer works and the only "fix" I found is to use the patchelf utility
from NixOS to change the hardcoded path to another hardcoded path. For
this reason I would like to link against a static version of the ld
library but such is not produced. And so this is my question, could
you please explain how could I build glibc so that it will produce a
static version of ld-linux.so.2 which I could later link to my
executables. I don't fully understand what this ld library does, but I
assume this is the part that loads other dynamic libraries (or at
least glibc.so). I would like to link my executables dynamically, but
I would like the dynamic linker itself to be statically built into
them, so they would not depend on hardcoded paths. Or alternatively I
would like to be able to set the path to the interpreter with
environment variable similar to LD_LIBRARY_PATH, maybe
LD_INTERPRETER_PATH. The goal is to be able to produce portable
binaries, that would run on any platform with the same ABI no matter
what the directory structure is.
Some background that may be relevant: I'm using Slackware 14 x86 to
build i686 compiler toolchain, so overall it is all x86 host and
target. I am using glibc 2.17 and gcc 4.7.x.
I would like to be able to set the path to the interpreter with environment variable similar to LD_LIBRARY_PATH, maybe LD_INTERPRETER_PATH.
This is simply not possible. Read carefully (and several times) the execve(2), elf(5) & ld.so(8) man pages and the Linux ABI & ELF specifications. And also the kernel code doing execve.
The ELF interpreter is responsible for dynamic linking. It has to be a file (technically a statically linked ELF shared library) at some fixed location in the file hierarchy (often /lib/ld.so.2 or /lib/ld-linux.so.2 or /lib64/ld-linux-x86-64.so.2)
The old a.out format from the 1990s had a builtin dynamic linker, partly implemented in old Linux 1.x kernel. It was much less flexible, and much less powerful.
The kernel enables, by such (in principle) arbitrary dynamic linker path, to have various dynamic linkers. But most systems have only one. This is a good way to parameterize the dynamic linker. If you want to try another one, install it in the file system and generate ELF executables mentioning that path.
With great pain and effort, you might make your own ld.so-like dynamic linker implementing your LD_INTERPRETER_PATH wish, but that linker still has to be an ELF shared library sitting at some fixed location in the file tree.
If you want a system not needing any files (at some predefined, and wired locations, like /lib/ld.so, /dev/null, /sbin/init ...), you'll need to build all its executable binaries statically. You may want (but current Linux distributions usually don't do that) to have a few statically linked executables (like /sbin/init, /bin/sash...) that will enable you to repair a system broken to the point of not having any dynamic linker.
BTW, the /sbin/init -or /bin/sh - path is wired inside the kernel itself. You may pass some argument to the kernel at boot load time -e.g. with GRUB- to overwrite the default. So even the kernel wants some files to be here!
As I commented, you might look into MUSL-Libc for an alternative Libc implementation (providing its own dynamic linker). Read also about VDSO and ASLR and initrd.
In practice, accept the fact that modern Linuxes and Unixes are expecting some non-empty file system ... Notice that dynamic linking and shared libraries are a huge progress (it was much more painful in the 1990s Linux kernels and distributions).
Alternatively, define your own binary format, then make a kernel module or a binfmt_misc entry to handle it.
BTW, most (or all) of Linux is free software, so you can improve it (but this will take months -or many years- of work to you). Please share your improvements by publishing them.
Read also Drepper's Hwo to Write Shared Libraries paper; and this question.
I ran into the same issue. In my case I want to bundle my application with a different GLIBC than comes system installed. Since ld-linux.so must match the GLIBC version I can't simply deploy my application with the according GLIBC. The problem is that I can't run my application on older installations that don't have the required GLIBC version.
The path to the loader interpreter can be modified with --dynamic-linker=/path/to/interp. However, this needs to be set at compile time and therefore would require my application to be installed in that location (or at least I would need to deploy the ld-linux.so that goes with my GLIBC in that location which goes against a simple xcopy deployment.
So what's needed is an $ORIGIN option equivalent to what the -rpath option can handle. That would allow for a fully dynamic deployment.
Given the lack of a dynamic interpreter path (at runtime) leaves two options:
a) Use patchelf to modify the path before the executable gets launched.
b) Invoke the ld-linux.so directly with the executable as an argument.
Both options are not as 'integrated' as a compiled $ORIGIN path in the executable itself.

What's the difference between /usr/include/linux and the include folder in linux kernel source?

On a freshly installed Ubuntu , i found kernel headers in both /usr/include/linux , and /usr/src/kernel-version-headers/include/linux
Are they mutually the same ?
They are very different; the /usr/include/linux headers are the headers that were used when compiling the system's standard C library. They are owned by the C library packaging, and updated in lockstep with the standard C library. They exist to provide the userland interface to the kernel, as understood and "brokered"1 by the C library.
The /usr/src/linux-headers-$(uname -r)/include/linux headers are used via the /lib/modules/$(uname -r)/build symbolic links. They are owned by the kernel headers packages and updated in lockstep with the kernel. These are a subset of the kernel headers and enough of the Kbuild system required to build out-of-tree kernel modules. These files represent the kernel internals -- modules must build against these if they are to properly understand in-memory objects. See the kernel's Documentation/kbuild/modules.txt file for some details.
1: "Mediated" was my first word choice, but it implies some sort of access controls, which isn't the case. "Brokered" implies a third-party process, but that is also not the case. Consider: when a C program calls _exit(), it is actually calling the Standard C library's _exit() wrapper, which calls the exit(2) system call. The select(2) interface has an upper limit on the number of file descriptors that can be tracked, and that limit is compiled into the standard C library. Even if the kernel's interface were extended, the C library would also need to be recompiled.

Why would the ELF header of a shared library specify Linux as the OSABI?

All the standard shared libraries on my Linux system (Fedora 9) specify ELFOSABI_NONE (0) as their OSABI.
This is fine - however I've received a shared library from a supplier where the OSABI given in the ELF header is ELFOSABI_LINUX (3).
This doesn't sound like an unreasonable value for a shared library intended for a Linux system, however it is a different value to that of all my other libraries - and so when I try to open this library, with dlopen(), from one of my other libraries this fails with the error "ELF file OS ABI invalid".
I compiled up the FreeBSD utility brandelf.c and used it to change the OSABI type to 0 and now the library seems to play fine with everything else.
I'm just wondering - why do you think this library is marked as ELFOSABI_LINUX? I'm guessing maybe they cross compiled on another system and specified some gcc flag that caused this value to be set into the ELF header? I tried to achieve something similar but couldn't determine the appropriate gcc flag or flags.
I'd like to know what the likely cause is as this particular supplier wont do anything without a lot of hand holding and I'd like to be able to say "you're probably doing X but this means we have to modify your libraries after we take delivery of them".
Possibly the vendor is cross compiling on FreeBSD or using a very recent Fedora system where anything using STT_GNU_IFUNC will be marked as ELFOSABI_LINUX. If you are trying to use it on Linux there should be no problems with changing it to ELFOSABI_NONE like you have done.

Resources