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.)
Related
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 am trying to understand how current macro retrieves struct task_struct of the process.
I am trying to understand for x86 architecture, and after exploring kernel source, struck at the following code:
#include <linux/compiler.h>
#include <asm/percpu.h>
#ifndef __ASSEMBLY__
struct task_struct;
DECLARE_PER_CPU(struct task_struct *, current_task);
static __always_inline struct task_struct *get_current(void)
{
return percpu_read_stable(current_task);
}
#define current get_current()
#endif /* __ASSEMBLY__ */
#endif /* _ASM_X86_CURRENT_H */
Where are the variables declared in DECLARE_PER_CPU stored in memory.
Are they at fixed location or in CPU Registers.
I am still unable to get, how this will give the task_struct pointer
Can anyone explain it. Thanks for your time and patience
from what i understood from the sources that i will mention below, the answers to your questions are such:
the variables are already defined by the DEFINE_PER_CPU macro, and the use of DECLARE_PER_CPU is there to tell the compiler that an external reference is being made.
the section in which current_struct variable is stored depends whether the CONFIG_SMP is defined on 32bit arch's, if its defined it will be at ".data.percpu" section otherwise, it will be at ".data" section.
In 64 bit arch it will always be at ".data.percpu".
the variables which are declared by DECLARE_PER_CPU will be stored on the stack.
let me qoute:
On boot up, an area is allocated by the size of the ".data.percpu" section +
PERCPU_ENOUGH_ROOM times NR_CPUS.
The __per_cpu_offset[] array holds the difference between
the ".data.percpu" section and the location where the data is actually
stored.
__per_cpu_offset[0] holds the difference for the variables
assigned to cpu 0, __per_cpu_offset[1] holds the difference for the
variables to cpu 1, and so on.
so, it depandes on the order of the other declarations of "per cpu" variables
the macro per_cpu_read_stable is used to read the "current_task" per-cpu variable.
https://0xax.gitbooks.io/linux-insides/content/Concepts/linux-cpu-1.html
https://lwn.net/Articles/180101
https://elixir.bootlin.com/linux/latest/ident/PER_CPU_BASE_SECTION
Unable to understand how the "current" macro works for x86 architecture
I am writing an intel x86 assembly program to compute logarithms using a log table. I call the assembly function in a c program. I don't want to move all the values in the log table to memory every time i call. I'm new to assembly on a non-simulated processor, so I'm not even sure where I'm allowed to store it. 20,000 32-bit integers.
How can I store a "large" amount of data once at the beginning of a c program, so that I can access it in an assembly routine? If i put it in the .data section, is it moved to memory every time i call the actual function?
Edit: this is how i call the function
#include <stdio.h>
extern int doIt(float) asm("doIt");
int main(){
printf("%d\n", doIt(7.0));
printf("%d\n", doIt(4.0));
... //more calls of the sort
}
Not sure if the c code is completely correct. In doIt i need to access the mentioned table repeatedly.
To give it an answer:
#include <stdint.h>
const int32_t table[10]; /* .rodata */
int32_t table[10]; /* .bss */
/*
* However, if you initialize with any (nonzero) values
* it goes to:
*/
int32_t table[10]={
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0xaaaaaaaa
}; /* .data */
const int32_t table[10]={
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0xaaaaaaaa
}; /* .rodata */
About the sections: .data and .rodata are stored in the object file and may not be loaded to RAM unless you need to use them, or anything contained into the same page -maybe you can change this behaviour with a linker script, I don't know-, and .bss section doesn't actually store any data, that is why once you initialize the variable, it moves to .data and gets it's image stored into the object file. Most compilers will ignore initialization to zero because the .bss variables do not have their image stored into the object file, so the loader fills their space to zero anyways when it loads the program.
Then, when you compile the object, you can import the symbol name from your ASM routine.
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.
I want the variables in a specific .c file should be stored in a particular RAM location,how can I achieve this.
I am using IAR IDE for ARM.
The reason I want to do so is when compiler stores variables in bit-band SRAM region of ARM core they cannot be read correctly.If I comment some section of code then compiler stores them in non-bitband SRAM region and then correct result is observed.
With EWARM, you can place variables with a few methods. You can place at a specific address using #. From IAR's support site:
const char RELEASEDATE[16] # 0x0000FF10 = __DATE__ ;
const char RELEASETIME[16] # 0x0000FF20 = __TIME__ ;
Alternatively, you can locate variables in a section (which sounds like what you want) using the location pragma. From the same source:
#pragma location = "ConstSection1"
__root const char RELEASEDATE[16] = __DATE__ ;
#pragma location = "ConstSection2"
__root const char RELEASETIME[16] = __TIME__ ;
And in your linker file (.icf) you would have:
place at address mem: 0x0000FF10 { readonly section ConstSection1 };
place at address mem: 0x0000FF20 { readonly section ConstSection2 };
I prefer the #pragma as the # operator looks out of place. Additionally, someone reading the code expects #pragma to be something compiler specific, while # hides itself in the code and doesn't stand out.