This is the function from u-boot:
static void boot_jump_linux(bootm_headers_t *images, int flag)
{
#ifdef CONFIG_ARM64
void (*kernel_entry)(void *fdt_addr);
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
kernel_entry = (void (*)(void *fdt_addr))images->ep;
debug("## Transferring control to Linux (at address %lx)...\n",
(ulong) kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
announce_and_cleanup(fake);
if (!fake)
kernel_entry(images->ft_addr);
#else
unsigned long machid = gd->bd->bi_arch_number;
char *s;
void (*kernel_entry)(int zero, int arch, uint params);
unsigned long r2;
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
kernel_entry = (void (*)(int, int, uint))images->ep;
s = getenv("machid");
if (s) {
strict_strtoul(s, 16, &machid);
printf("Using machid 0x%lx from environment\n", machid);
}
debug("## Transferring control to Linux (at address %08lx)" \
"...\n", (ulong) kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
announce_and_cleanup(fake);
if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
r2 = (unsigned long)images->ft_addr;
else
r2 = gd->bd->bi_boot_params;
if (!fake)
kernel_entry(0, machid, r2);
#endif
}
I understood from the related question: Trying to understand the usage of function pointer that kernel_entryis a pointer to a function. Can someone help me understand where that function is defined? I don't even know the name of this function so I failed to grepit.
NOTE: The entire u-boot source code is here.
Indeed kernel_entry is a function pointer. It is initialized from the ep field of the piece of data passed in called images, of type bootm_header_t. The definition of that struct is in include/image.h. This is the definition of a bootable image header, ie the header of a kernel image which contain the basic info to boot that image from the boot loader. Obviously, to start it, you need a program entry point, similarly to the main function in regular C programs.
In that structure, the entry point is simply defined as a memory address (unsigned long), which the code you listed cast into that function pointer.
That structure as been obtained from loading the first blocks of the image file on disk, whose location is known already by the boot loader.
Hence the actual code pointed by that function pointer belongs to a different binary, and the definition of the function must be located in a different source code. For a linux kernel, this entry point is an assembly hand coded function, whose source is in head.S. This function being highly arch dependent, you will find many files of that name implementing it accross the kernel tree.
Related
I created a small unit test library in C.
Its main feature is the fact that you don't need to register your test functions, they are identified as test functions because they have a predefined prefix (test_).
For example, if you want to create a test function, you can write something like this:
int test_abc(void *t)
{
...
}
Yes, just like in Go.
To find the test functions, the runner:
takes the name of the executable from argv[0];
parses the ELF sections to find the symbol table;
from the symbol table, takes all the functions named test_*;
treats the addresses from the symbol table as function pointers;
invoke the test functions.
For PIE binaries, there is one additional step. To find the load address for the test functions, I assume there is a common offset that applies to all functions. To figure out the offset, I subtract the address of main (runtime, function pointer) from the address of main read from the symbol table.
All the things described above are working fine: https://github.com/rodrigo-dc/testprefix
However, as far as I understood, function pointer arithmetic is not allowed by the C99 standard.
Given that I have the address from the symbol table - Is there a reliable way to get the runtime address of functions (in case of PIE binaries)?
I was hoping for some linker variable, some base address, or anything like that.
Is there a reliable way to get the runtime address of functions (in case of PIE binaries)?
Yes: see this answer, and also the comment about using dladdr().
P.S. Note that taking address of main in C++ is not allowed.
Because you have an ELF executable, this probably precludes "funny" architectures (e.g. Intel 8051, PIC, etc.) that might have segmented or non-linear, non-contiguous address spaces.
So, you [probably] can use the method you've described with main to get the actual address. You just need to convert to/from either char * or uintptr_t types so you are using byte offsets/differences.
But, you can also create a unified table of pointers to the various functions using by creating descriptor structs that are placed in a special linker section of your choosing using (e.g.) __attribute__((section("mysection"))
Here is some code that shows what I mean:
#include <stdio.h>
typedef struct {
int (*test_func)(void *); // pointer to test function
const char *test_name; // name of the test
int test_retval; // test return value
// more data ...
int test_xtra;
} testctl_t;
// define a struct instance for a given test
#define ATTACH_TEST(_func) \
testctl_t _func##_ctl __attribute__((section("testctl"))) = { \
.test_func = _func, \
.test_name = #_func \
}
// advance to next struct (must be 16 byte aligned)
#define TESTNEXT(_test) \
(testctl_t *) (((char *) _test) + asiz)
int
test_abc(void *t)
{
printf("test_abc: hello\n");
return 1;
}
ATTACH_TEST(test_abc);
int
test_def(void *t)
{
printf("test_def: hello\n");
return 2;
}
ATTACH_TEST(test_def);
int
main(void)
{
// these are special symbols defined by the linker for our special linker
// section that denote the start/end of the section (similar to
// _etext/_edata)
extern testctl_t __start_testctl;
extern testctl_t __stop_testctl;
size_t rsiz = sizeof(testctl_t);
size_t asiz;
testctl_t *test;
// align the size to a 16 byte boundary
asiz = rsiz;
asiz += 15;
asiz /= 16;
asiz *= 16;
// show the struct sizes
printf("main: sizeof(testctl_t)=%zx/%zx\n",rsiz,asiz);
// section start and stop symbol addresses
printf("main: start=%p stop=%p\n",&__start_testctl,&__stop_testctl);
// cross check of expected pointer values
printf("main: test_abc=%p test_abc_ctl=%p\n",test_abc,&test_abc_ctl);
printf("main: test_def=%p test_def_ctl=%p\n",test_def,&test_def_ctl);
for (test = &__start_testctl; test < &__stop_testctl;
test = TESTNEXT(test)) {
printf("\n");
// show the address of our test descriptor struct and the pointer to
// the function
printf("main: test=%p test_func=%p\n",test,test->test_func);
printf("main: calling %s ...\n",test->test_name);
test->test_retval = test->test_func(test);
printf("main: return is %d\n",test->test_retval);
}
return 0;
}
Here is the program output:
main: sizeof(testctl_t)=18/20
main: start=0x404040 stop=0x404078
main: test_abc=0x401146 test_abc_ctl=0x404040
main: test_def=0x401163 test_def_ctl=0x404060
main: test=0x404040 test_func=0x401146
main: calling test_abc ...
test_abc: hello
main: return is 1
main: test=0x404060 test_func=0x401163
main: calling test_def ...
test_def: hello
main: return is 2
So I am trying to use this built-in UART function (from the Vitis SDK from Xilinix) to determine if there is a valid byte to read over UART. I created this function to return 1 if there was a byte to read or 0 if there wasn't
u32 UartHasMessage(void){
if(XUartPs_IsReceiveData(&XUartPs_Main)){
return 1;
}
else{
return 0;
}
}
However, even when there is a byte to read over UART, this function always returns false.
The weird behavior I am experiencing is when I step through the code using the debugger, I call UartHasMessage() to check if there is a byte to read, and it returns false, but in the next line I call a function to read a byte over UART and that contains the correct byte I sent over the host.
u32 test - UartHasMessage();
UartGetByte(&HostReply);
How come this UartHasMessage always returns false, but then in the next line I am able to read the byte correctly?
Caveat: Without some more information, this is a bit speculative and might be a comment, but it is too large for that.
The information below comes from the Xilinx documentation on various pages ...
XUartPs_RecvByte will block until a byte is ready. So, no need to call XUartPs_IsReceiveData directly (I think that XUartPS_RecvByte calls it internally).
A web search on XUartPs_Main came up with nothing, so we'd need to see the definition you have.
Most Xilinx documentation uses UART_BASEADDRESS:
#define UART_BASEADDR XPAR_XUARTPS_0_BASEADDR
I found a definition:
#define XPAR_XUARTPS_0_BASEADDR 0xE0001000
You might be better off using a more standard method, such as calling the XUartPs_LookupConfig function to get the configuration table entry which has all relevant values.
I'm guessing that you created the XUartPS_Main definition.
But, based on what you posted, (needing &XUartPS_Main instead of XUartPS_Main), it is linked/loaded at the exact address of the UART register bank. Let's assume that address is (e.g.) 0x10000. So, we might have:
u32 XUartPS_Main __attribute__(at(0x10000));
The at is an extension that some build systems support (e.g. arm) that forces the variable to be loaded at a given address. So, let's assume we have that (even if the mechanism is slightly different (e.g.):
__attribute__((section(".ARM.__at_0x10000")))
The definition of XUARTPS_SR_OFFSET is:
#define XUARTPS_SR_OFFSET 0x002CU
Offsets are [typically] byte offsets.
Given:
#define XUartPs_IsReceiveData(BaseAddress) \
!((Xil_In32((BaseAddress) + XUARTPS_SR_OFFSET) & \
(u32)XUARTPS_SR_RXEMPTY) == (u32)XUARTPS_SR_RXEMPTY)
Now if the definition of XUartPS_Main uses u32 [as above], we may have a problem because XUARTPS_SR_OFFSET will be treated as a u32 index and not a byte offset. So, it will access the wrong address.
So, try:
XUartPs_IsReceiveData((unsigned char *) &XUartPs_Main)
But, if it were me, I'd rework things to use Xilinx's standard definitions.
UPDATE:
Hi so XUartPs_main is defined as static XUartPs XUartPs_Main; I use it in a variety of functions such as a function to send bytes over uart and I call it by its address like I did with this function, all my other functions work as expected except this one. Is it possible it is something to do with the way the fifo works? –
29belgrade29
No, not all the API functions are the same.
The struct definition is [I synthesized this from the API doc]:
typedef struct {
u16 DeviceId; // Unique ID of device.
u32 BaseAddress; // Base address of device (IPIF)
u32 InputClockHz;
} XUartPs;
Somewhere in your code you had to initialize this with:
XUartPs_Main = XUartPs_ConfigTable[my_device_id];
Or, with:
XUartPs_Main = *XUartPs_LookupConfig(my_device_id);
If an API function is defined as (e.g.):
void api_dosomething(XUartPs_Config *cfg,...)
Then, you call it with:
api_dosomething(&XUartPs_Main,...);
So, most functions probably take such a pointer.
But, XUartPs_IsReceiveData does not want a pointer to a XUartPs_Config struct. It wants a base address. This is:
XUartPs_Main.BaseAddress
So, you want:
XUartPs_IsReceiveData(XUartPs_Main.BaseAddress)
Suppose having the following code elements working on a fifo buffer:
static uint_fast32_t buffer_start;
static uint_fast32_t buffer_end;
static mutex_t buffer_guard;
(...)
void buffer_write(uint8_t* data, uint_fast32_t len)
{
uint_fast32_t pos;
mutex_lock(buffer_guard);
pos = buffer_end;
buffer_end = buffer_end + len;
(...) /* Wrap around buffer_end, fill in data */
mutex_unlock(buffer_guard);
}
bool buffer_isempty(void)
{
bool ret;
mutex_lock(buffer_guard);
ret = (buffer_start == buffer_end);
mutex_unlock(buffer_guard);
return ret;
}
This code might be running on an embedded system, with a RTOS, with the buffer_write() and buffer_isempty() functions called from different threads. The compiler has no means to know that the mutex_lock() and mutex_unlock() functions provided by the RTOS are working with a critical sections.
As the code is above, due to buffer_end being a static variable (local to the compilation unit), the compiler might choose to reorder accesses to it around function calls (at least as far as I understand the C standard, this seems possible to happen). So potentially the code performing buffer_end = buffer_end + len line have a chance to end up before the call to mutex_lock().
Using volatile on these variables (like static volatile uint_fast32_t buffer_end;) seems to resolve this as then they would be constrained by sequence points (which a mutex_lock() call is, due to being a function call).
Is my understanding right on these?
Is there a more appropriate means (than using volatile) of dealing with this type of problem?
I have been trying to port a driver from 2.6 to 4.X without support from the original board manufacturer (and very limited Linux experience).
The original driver uses init_timer() and passes in a pointer to the timer_list structure. That timer_list structure's data element was set to a pointer to another memory structure and the function element set to the callback. Inside the callback function the data element was used to access other bits of stuff.
The current timer init-method uses:
timer_setup( timer_list *, callback, (unsigned int) flags);
and the timer_list structure was changed to eliminate the data field.
I'm not sure what is the best/proper way to inform the callback function of the equivalent data element. Can anyone provide some guidance?
Here is a snippet of the old driver:
myDevice * dev;
dev->getIntrTimer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);
init_timer(dev->getIntrTimer);
dev->getIntrTimer->data = (unsigned long) dev;
dev->getIntrTimer->function = GetIntrTimerCallback;
The callback function starts off like this:
void GetIntrTimerCallback(unsigned long devAddr)
{
myDevice *dev = (myDevice *) devAddr;
dev->blahBlah++; // etc.
So the old code gets passed the pointer to myDevice so inside the callback that structure can be accessed.
But with the new timer method only has available an int that is 4 bytes but a pointer is 8 (or whatever).
What I'd like to do is this:
dev->getIntrTimer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);
timer_setup(dev->getIntrTimer, GetIntrTimerCallback, dev);
but of course that generates compile errors because dev is a pointer to type myDevice, which does not fit in an int.
The timer_setup() with three args is present since 4.14 Linux kernel (FYI there was setup_timer() in slightly earlier versions). If you maintain some code which should be relevant up to recent kernels - you have to change it in appropriate way every time the API changes. Now you can access your data through the special function from_timer() based on container_of().
timer_list is normally used not as pointer inside struct, so the example implies normal usage and could be something like:
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
init_timer(&dev->getIntrTimer);
dev->getIntrTimer.data = (unsigned long) dev;
dev->getIntrTimer.function = GetIntrTimerCallback;
/* ... */
add_timer(&dev->getIntrTimer);
#else
timer_setup(&dev->getIntrTimer, GetIntrTimerCallback, 0);
/* the third argument may include TIMER_* flags */
/* ... */
#endif
The callback function:
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
void GetIntrTimerCallback(unsigned long devAddr)
{
myDevice *dev = (myDevice *) devAddr;
#else
void GetIntrTimerCallback(struct timer_list *t)
{
myDevice *dev = from_timer(dev, t, getIntrTimer);
#endif
/* Do something with "dev" */
Read also:
Linux kernel timers new API
Example
Linux kernel versions
This is the function in u-boot's bootm.c from where the kernel is launched:
/* Subcommand: GO */
static void boot_jump_linux(bootm_headers_t *images, int flag)
{
#ifdef CONFIG_ARM64
void (*kernel_entry)(void *fdt_addr);
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
kernel_entry = (void (*)(void *fdt_addr))images->ep;
debug("## Transferring control to Linux (at address %lx)...\n",
(ulong) kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
announce_and_cleanup(fake);
if (!fake)
kernel_entry(images->ft_addr);
#else
unsigned long machid = gd->bd->bi_arch_number;
char *s;
void (*kernel_entry)(int zero, int arch, uint params);
unsigned long r2;
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
kernel_entry = (void (*)(int, int, uint))images->ep;
s = getenv("machid");
if (s) {
strict_strtoul(s, 16, &machid);
printf("Using machid 0x%lx from environment\n", machid);
}
debug("## Transferring control to Linux (at address %08lx)" \
"...\n", (ulong) kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
announce_and_cleanup(fake);
if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
r2 = (unsigned long)images->ft_addr;
else
r2 = gd->bd->bi_boot_params;
if (!fake)
kernel_entry(0, machid, r2);
#endif
}
I am facing difficulty in understanding how kernel_entry is working here. Especially in the second-last line it is being used as:
kernel_entry(0, machid, r2);
So where is the definition of kernel_entry()? I failed to find in entire u-boot and kernel source code.
Update
I am rephrasing my question here:
Suppose kernel_entry is a pointer to a function and is being defined as:
bootm_headers_t *images
kernel_entry = (void (*)(int, int, uint))images->ep;
Then somewhere in the program it is being called as:
kernel_entry(0, machid, r2);
I understand being a pointer, kernel_entry should store an address of a function. But I want to understand what operations will be performed on the three arguments. Why do we have those arguments?
The declaration of kernel_entry variable and its type, which is a pointer to a function taking int, int, uint and returning void (probably the most confusing part), is here:
void (*kernel_entry)(int zero, int arch, uint params);
Assignment, images->ep is cast into desired signature function pointer and put into the variable:
kernel_entry = (void (*)(int, int, uint))images->ep;
Finally, the function is called:
kernel_entry(0, machid, r2);
Please note that if CONFIG_ARM64 is defined, then the function kernel_entry points to has different signature:
void (*kernel_entry)(void *fdt_addr); //takes one void* param and returns void
U-Boot has the kernel image in its addressable memory space, reads an address contained in that image (at images->ep), and branches to that entry point address.
The "definition of kernel_entry()" is actually in kernel source code, the label "start" at arch/arm/boot/compressed/head.S is what you are looking for.
To understand the kernel boot process, IMO the definitive tutorial is chapter 5 of Hallinan "Embedded Linux Primer".