zig: linking symbol can't find - linker

When try to write a freestanding program in zig, we have already defined a link script. (Actually os)
However, I can't get the address of the symbol I defined in script.
Some methods are tried but fails.
Method 1, but segmentation fault on compiling step.
const s = #extern(* fn () void , .{
.name = "symbol",
});
Method 2, relocation R_RISCV_HI20 out of range
extern fn symbol() void;
I think maybe the core problem is in the 'section'. The symbol doesn't in .data, .rodata or .text segment but .bss segment.
How to get the location of this symbol correctly?

Related

How does dynamic linker know which library to search for a symbol?

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.

relocation and symbol table value

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.

Address Of (&) To Get Value From Symbol Table

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.)

How to solve the error in linker script?

I created a memory linker script and saved it as memory.ld in the eclipse ide : Project : properties : gcc linker : miscellaneous : I added -M -T memory.ld
memory.ld :
MEMORY
{
ram (rw) : ORIGIN = 0x4000000 , LENGTH = 2M
}
SECTIONS
{
RAM : { *(.myvarloc)
} > ram }
In my c program : I made a global declaration as:
__attribute__ ((section(".myvarloc")))
uint8 measurements [30];
ERRORS:
/usr/bin/ld: FEBRUARY section `.text' will not fit in region `ram'
/usr/bin/ld: region `ram' overflowed by 20018 bytes
/usr/lib/i386-linux-gnu/libc_nonshared.a(elf-init.oS): In function `__libc_csu_init':
(.text+0x2b): undefined reference to `__init_array_end'
/usr/lib/i386-linux-gnu/libc_nonshared.a(elf-init.oS): In function `__libc_csu_init':
(.text+0x31): undefined reference to `__init_array_start'
/usr/lib/i386-linux-gnu/libc_nonshared.a(elf-init.oS): In function `__libc_csu_init':
(.text+0x57): undefined reference to `__init_array_start'
/usr/bin/ld: FEBRUARY: hidden symbol `__init_array_end' isn't defined
/usr/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status
Depending on the compiler you are using (GCC?) and the processor for which you are compiling (x86?), the compiler will generate several segment references in the object files. The most common ones are .text for code segments, .data for initialized data and .bss for uninitialized data.
You can see which segments your compiler generates by using the nm utility on your object files.
I assume that until you provided your own linker script, the environment has provided some default script automatically and/or implicitly. But now that you have decided to "roll your own", you have to take care of all details yourself.
I cannot verify the details, but you could start with the following SECTIONS:
SECTIONS
{
.bss : { *(.myvarloc) }
.bss : { *(.bss) }
.data : { *(.data) }
.text : { *(.text) }
}
I'm not sure if this is the exact syntax your GCC linker (it depends a little on the version), but you can find more information in the manual.

Replacing static function in kernel module

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

Resources