I have main.c file which contains call to external function fun()
int main()
{
fun();
}
and result of readelf -r is as follows
Relocation section '.rela.text' at offset 0x298 contains 3 entries:
Offset Info Type Sym. Value Sym. Name +Addend
00000000000a 000b00000002 R_X86_64_PC32 0000000000000000 fun - 4
I just want to know that how info field(which is symbol table entry) is mapped with symbol fun and why sym.value is 0000??
Keep in mind that the C standard doesn't actually specify how this works under the covers, the description that follows is of a very common implementation method.
With a single translation unit holding the code:
int main() { fun(); }
the information available from that compiled (not yet linked) object file is basically:
symbol status value
------ ------ -----
main defined pointer to main within object
fun needed zero
That's because it knows where main is but has no information on fun - it will need to be found later. So reading the object file will naturally return an unknown value for fun.
Of course, you will need some code to define fun as well, such as in another translation unit:
void fun(void) { puts("Hello, world."); }
Compiling this would result in the following information:
symbol status value
------ ------ -----
fun defined pointer to fun within object
puts needed zero
It's the link stage that ties these together. It takes both object files (and the object/library files for the any other dependencies, such as the C run-time library containing puts) and binds them together, making adjustments to all code that uses undefined symbols.
So what you end up with an executable file format where all symbols are known and all references are resolved.
Related
I'm experimenting with LD_PRELOAD/dlopen and faced a confusion regarding symbol lookup. Consider the following 2 libraries:
libshar
shared.h
int sum(int a, int b);
shared.c
int sum(int a, int b){
return a + b;
}
libshar2
shared.h
int sum(int a, int b);
shared.c
int sum(int a, int b){
return a + b + 10000;
}
and executable bin_shared:
#include <dlfcn.h>
#include "shared.h"
int main(void){
void *handle = dlopen("/home/me/c/build/libshar2.so", RTLD_NOW | RTLD_GLOBAL);
int s = sum(2 + 3);
printf("s = %d", s);
}
linking the binary with libshar and libdl I considered the following 2 cases:
LD_PRELOAD is empty
The program prints 5.
Why does the dynamic linker decide to lookup the sum function in the libshar, not libshar2? Both of them are loaded and contain the needed symbol:
0x7ffff73dc000 0x7ffff73dd000 0x1000 0x0 /home/me/c/build/libshar2.so
0x7ffff73dd000 0x7ffff75dc000 0x1ff000 0x1000 /home/me/c/build/libshar2.so
0x7ffff75dc000 0x7ffff75dd000 0x1000 0x0 /home/me/c/build/libshar2.so
0x7ffff75dd000 0x7ffff75de000 0x1000 0x1000 /home/me/c/build/libshar2.so
#...
0x7ffff7bd3000 0x7ffff7bd4000 0x1000 0x0 /home/me/c/build/libshar.so
0x7ffff7bd4000 0x7ffff7dd3000 0x1ff000 0x1000 /home/me/c/build/libshar.so
0x7ffff7dd3000 0x7ffff7dd4000 0x1000 0x0 /home/me/c/build/libshar.so
0x7ffff7dd4000 0x7ffff7dd5000 0x1000 0x1000 /home/me/c/build/libshar.so
LD_PRELOAD = /path/to/libshar2.so
The program prints 10005. This is expected, but again I noticed that both libshar.so and libshar2.so are loaded:
0x7ffff79d1000 0x7ffff79d2000 0x1000 0x0 /home/me/c/build/libshar.so
0x7ffff79d2000 0x7ffff7bd1000 0x1ff000 0x1000 /home/me/c/build/libshar.so
0x7ffff7bd1000 0x7ffff7bd2000 0x1000 0x0 /home/me/c/build/libshar.so
0x7ffff7bd2000 0x7ffff7bd3000 0x1000 0x1000 /home/me/c/build/libshar.so
0x7ffff7bd3000 0x7ffff7bd4000 0x1000 0x0 /home/me/c/build/libshar2.so
0x7ffff7bd4000 0x7ffff7dd3000 0x1ff000 0x1000 /home/me/c/build/libshar2.so
0x7ffff7dd3000 0x7ffff7dd4000 0x1000 0x0 /home/me/c/build/libshar2.so
0x7ffff7dd4000 0x7ffff7dd5000 0x1000 0x1000 /home/me/c/build/libshar2.so
The LD_PRELOAD case seems to be explained in ld.so(8):
LD_PRELOAD
A list of additional, user-specified, ELF shared objects to be loaded
before all others. The items of the list can be separated by spaces
or colons. This can be used to selectively override functions in
other shared objects. The objects are searched for using the rules
given under DESCRIPTION.
Why does the dynamic linker decide to lookup the sum function in the libshar, not libshar2?
Dynamic linkers on UNIX attempt to emulate what would have happened if you linked with archive libraries.
In the case of empty LD_PRELOAD, the symbol search order is (when the symbol is referenced by the main binary; rules get more complicated when the symbol is referenced by the DSO): the main binary, directly linked DSOs in the order they are listed on the link line, dlopened DSOs in the order they were dlopened.
LD_PRELOAD = /path/to/libshar2.so
The program prints 10005. This is expected,
Non-empty LD_PRELOAD modifies the search order by inserting any libraries listed after the main executable, and before any directly linked DSOs.
but again I noticed that both libshar.so and libshar2.so are loaded:
Why is that a surprise? The dynamic linker loads all libraries listed in LD_PRELOAD, and then all libraries that you directly linked against (as explained before).
dlopen can't (nor can anything else) change the definition of (global) symbols already present at the time of the call. It can only make available new ones that did not exist before.
The (sloppy) formalization of this is in the specification for dlopen:
Symbols introduced into the process image through calls to dlopen() may be used in relocation activities. Symbols so introduced may duplicate symbols already defined by the program or previous dlopen() operations. To resolve the ambiguities such a situation might present, the resolution of a symbol reference to symbol definition is based on a symbol resolution order. Two such resolution orders are defined: load order and dependency order. Load order establishes an ordering among symbol definitions, such that the first definition loaded (including definitions from the process image file and any dependent executable object files loaded with it) has priority over executable object files added later (by dlopen()). Load ordering is used in relocation processing. Dependency ordering uses a breadth-first order starting with a given executable object file, then all of its dependencies, then any dependents of those, iterating until all dependencies are satisfied. With the exception of the global symbol table handle obtained via a dlopen() operation with a null pointer as the file argument, dependency ordering is used by the dlsym() function. Load ordering is used in dlsym() operations upon the global symbol table handle.
Note that LD_PRELOAD is nonstandard functionality and thus not described here, but on implementations that offer it, LD_PRELOAD acts with load order after the main program but before any shared libraries loaded as dependencies.
At runtime, are global variables in a loaded shared library guaranteed to occupy a contiguous memory region? If so, is it possible to find out that address range?
Context: we want to have multiple "instances" of a shared library (e.g. a protocol stack implementation) in memory for simulation purposes (e.g. to simulate a network with multiple hosts/routers). One of the approaches we are trying is to load the library only once, but emulate additional instances by creating and maintaining "shadow" sets of global variables, and switch between instances by memcpy()'ing the appropriate shadow set in/out of the memory area occupied by the global variables of the library. (Alternative approaches like using dlmopen() to load the library multiple times, or introducing indirection inside the shared lib to access global vars have their limitations and difficulties too.)
Things we tried:
Using dl_iterate_phdr() to find the data segment of the shared lib. The resulting address range was not too useful, because (1) it did not point to an area containing the actual global variables but to the segment as loaded from the ELF file (in readonly memory), and (2) it contained not only the global vars but also additional internal data structures.
Added start/end guard variables in C to the library code, and ensured (via linker script) that they are placed at the start and end of the .data section in the shared object. (We verified that with objdump -t.) The idea was that at runtime, all global variables would be located in the address range between the two guard variables. However, our observation was that the relative order of the actual variables in memory was quite different than what would follow from the addresses in the shared object. A typical output was:
$ objdump -t libx.so | grep '\.data'
0000000000601020 l d .data 0000000000000000 .data
0000000000601020 l O .data 0000000000000000 __dso_handle
0000000000601038 l O .data 0000000000000000 __TMC_END__
0000000000601030 g O .data 0000000000000004 custom_data_end_marker
0000000000601028 g O .data 0000000000000004 custom_data_begin_marker
0000000000601034 g .data 0000000000000000 _edata
000000000060102c g O .data 0000000000000004 global_var
$ ./prog
# output from dl_iterate_phdr()
name=./libx.so (7 segments)
header 0: type=1 flags=5 start=0x7fab69fb0000 end=0x7fab69fb07ac size=1964
header 1: type=1 flags=6 start=0x7fab6a1b0e08 end=0x7fab6a1b1038 size=560 <--- data segment
header 2: type=2 flags=6 start=0x7fab6a1b0e18 end=0x7fab6a1b0fd8 size=448
header 3: type=4 flags=4 start=0x7fab69fb01c8 end=0x7fab69fb01ec size=36
header 4: type=1685382480 flags=4 start=0x7fab69fb0708 end=0x7fab69fb072c size=36
header 5: type=1685382481 flags=6 start=0x7fab69bb0000 end=0x7fab69bb0000 size=0
header 6: type=1685382482 flags=4 start=0x7fab6a1b0e08 end=0x7fab6a1b1000 size=504
# addresses obtained via dlsym() are consistent with the objdump output:
dlsym('custom_data_begin_marker') = 0x7fab6a1b1028
dlsym('custom_data_end_marker') = 0x7fab6a1b1030 <-- between the begin and end markers
# actual addresses: at completely different address range, AND in completely different order!
&custom_data_begin_marker = 0x55d613f8e018
&custom_data_end_marker = 0x55d613f8e010 <-- end marker precedes begin marker!
&global_var = 0x55d613f8e01c <-- after both markers!
Which means the "guard variables" approach does not work.
Maybe we should iterate over the Global Offset Table (GOT) and collect the addresses of global variables from there? However, there doesn't seem to be an official way for doing that, if it's possible at all.
Is there something we overlooked? I'll be happy to clarify or post our test code if needed.
EDIT: To clarify, the shared library in question is a 3rd party library whose source code we prefer not to modify, hence the quest for the above general solution.
EDIT2: As further clarification, the following code outlines what I would like to be able to do:
// x.c -- source for the shared library
#include <stdio.h>
int global_var = 10;
void bar() {
global_var++;
printf("global_var=%d\n", global_var);
}
// a.c -- main program
#include <stdlib.h>
#include <dlfcn.h>
#include <memory.h>
struct memrange {
void *ptr;
size_t size;
};
extern int global_var;
void bar();
struct memrange query_globals_address_range(const char *so_file)
{
struct memrange result;
// TODO what generic solution can we use here instead of the next two specific lines?
result.ptr = &global_var;
result.size = sizeof(int);
return result;
}
struct memrange g_range;
void *allocGlobals()
{
// allocate shadow set and initialize it with actual global vars
void *globals = malloc(g_range.size);
memcpy(globals, g_range.ptr, g_range.size);
return globals;
}
void callBar(void *globals) {
memcpy(g_range.ptr, globals, g_range.size); // overwrite globals from shadow set
bar();
memcpy(globals, g_range.ptr, g_range.size); // save changes into shadow set
}
int main(int argc, char *argv[])
{
g_range = query_globals_address_range("./libx.so");
// allocate two shadow sets of global vars
void *globals1 = allocGlobals();
void *globals2 = allocGlobals();
// call bar() in the library with a few times with each
callBar(globals1);
callBar(globals2);
callBar(globals2);
callBar(globals1);
callBar(globals1);
return 0;
}
Build+run script:
#! /bin/sh
gcc -c -g -fPIC x.c -shared -o libx.so
gcc a.c -g -L. -lx -ldl -o prog
LD_LIBRARY_PATH=. ./prog
EDIT3: Added dl_iterate_phdr() output
Shared libraries are compiled as Position-Independent Code. That means that unlike executables, addresses are not fixed, but are rather decided during dynamic linkage.
From a software engineering standpoint, the best approach is to use objects (structs) to represent all your data and avoid global variables (such data structures are typically called "contexts"). All API functions then take a context argument, which allows you to have multiple contexts in the same process.
At runtime, are global variables in a loaded shared library guaranteed to occupy a contiguous memory region?
Yes: on any ELF platform (such as Linux) all writable globals are typically grouped into a single writable PT_LOAD segment, and that segment is located at a fixed address (determined at the library load time).
If so, is it possible to find out that address range?
Certainly. You can find the library load address using dl_iterate_phdr, and iterate over the program segments that it gives you. One of the program headers will have .p_type == PT_LOAD, .p_flags == PF_R|PF_W. The address range you want is [dlpi_addr + phdr->p_vaddr, dlpi_addr + phdr->p_vaddr + phdr->p_memsz).
Here:
# actual addresses: completely different order:
you are actually looking at the address of the GOT entries in the main executable, and not the addresses of the variables themselves.
I have a question regarding how symbols are placed in the symbol table and then how you reference those symbols from within C.
So let's say I have a startup assembly file where some basic stuff is done before calling main(). One of the things it does is EXPORT a couple symbols which define the stack.
St_Length EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
EXPORT St_Length
EXPORT ra_Stack
ra_Stack SPACE St_Length
Then in a source.c file in which I want to reference those symbols I have something like:
extern uint32_t St_Length; /* exported from asm.s */
void func( void )
{
uint32_t i;
for(i = 0; i < (uint32_t)(&St_Length); ++i)
\\do something
}
This seems to work correctly, i.e. it will actually perform that loop 0x400 times. My question is why do I have to use the ampersand to get what seems to me like it should be the value of the symbol, not the address of the symbol?
NOTE: this is for an ARM Cortex-M3 using the toolchain provided with Keil.
Thanks for the help.
EDIT: Here is the relevant portion of my symbol table from the generated map file:
Global Symbols
Symbol Name Value Ov Type Size Object(Section)
St_Length 0x00000400 Number 0 startup_stm32f10x_md.o ABSOLUTE
Any explanation of the symbol table 'type' column would be very helpful too.
Because using EQU you have defined St_Length as a symbol for address 0x400. To define it as a variable at some appropriate address, you should try something along the lines of:
St_Length DCD 0x400
(Put this in your data area.)
I wonder if the number that follows a local static variable name in assembler is random or if there is a meaning.
I compiled a sample C source with gcc v. 4.7.2 in Debian wheezy; the assembler listing shows a row containing:
.comm i.1705,4,4
Where does 1705 come from?
Thank you in advance.
Here is the source:
int main() { static int i=0; return i; }
It is probably some counter of all identifiers encountered during the compilation. If I put another declaration just behind, it gives me the next number. If I put another, non-static, object in between the difference is 2.
Folks,
I'm trying to hack a kernel module by modifying its symbol. The basic idea is to replace the original function with new function by overwriting its address in the symtab. However, I found when declaring the function as static, the hacking fails. But it works with non-static function. My example code is below:
filename: orig.c
int fun(void) {
printk(KERN_ALERT "calling fun!\n");
return 0;
}
int evil(void) {
printk(KERN_ALERT "===== EVIL ====\n");
return 0;
}
static int init(void) {
printk(KERN_ALERT "Init Original!");
fun();
return 0;
}
void clean(void) {
printk(KERN_ALERT "Exit Original!");
return;
}
module_init(init);
module_exit(clean);
Then I follow the styx's article to replace the original function "fun" in symtab to call function "evil", http://www.phrack.org/issues.html?issue=68&id=11
>objdump -t orig.ko
...
000000000000001b g F .text 000000000000001b evil
0000000000000056 g F .text 0000000000000019 cleanup_module
0000000000000036 g F .text 0000000000000020 init_module
0000000000000000 g F .text 000000000000001b fun
...
By executing the elfchger
>./elfchger -s fun -v 1b orig.ko
[+] Opening orig.ko file...
[+] Reading Elf header...
>> Done!
[+] Finding ".symtab" section...
>> Found at 0xc630
[+] Finding ".strtab" section...
>> Found at 0xc670
[+] Getting symbol' infos:
>> Symbol found at 0x159f8
>> Index in symbol table: 0x1d
[+] Replacing 0x00000000 with 0x0000001b... done!
I can successfully change the fun's symbol table to be equal to evil and inserting the module see the effects:
000000000000001b g F .text 000000000000001b evil
...
000000000000001b g F .text 000000000000001b fun
> insmod ./orig.ko
> dmesg
[ 7687.797211] Init Original!
[ 7687.797215] ===== EVIL ====
While this works fine. When I change the declaration of fun to be "static int fun(void)" and follows the same steps as mentioned above, I found the evil does not get called. Could anyone give me some suggestion?
Thanks,
William
Short version: Declaring a function as 'static' makes it local and prevents the symbol to be exported. Thus, the call is linked statically, and the dynamic linker does not effect the call in any way at load time.
Long Version
Declaring a symbol as 'static' prevents the compiler from exporting the symbol, making it local instead of global. You can verify this by looking for the (missing) 'g' in your objdump output, or at the lower-case 't' (instead of 'T') in the output of 'nm'. The compiler might also inline the local function, in which case the symbol table wouldn't contain it at all.
Local symbols have to be unique only for the translation unit in which they are defined. If your module consisted of multiple translation units, you could have a static fun() in each of them. An nm or objdump of the finished .ko may then contain multiple local symbols called fun.
This also implies that local symbols are valid only in their respective translation unit, and also can be referred (in your case: called) only from inside this unit. Otherwise, the linker just would not now, which one you mean. Thus, the call to static fun() is already linked at compile time, before the module is loaded.
At load time, the dynamic linker won't tamper with the local symbol fun or references (in particular: calls) to it, since:
its local linkage already done
there are potentially more symbols named 'fun' throughout and the dynamic linker would not be able to tell, which one you meant