Does Linux kernel have main function? - c

I am learning Device Driver and Kernel programming.According to Jonathan Corbet book we do not have main() function in device drivers.
#include <linux/init.h>
#include <linux/module.h>
static int my_init(void)
{
return 0;
}
static void my_exit(void)
{
return;
}
module_init(my_init);
module_exit(my_exit);
Here I have two questions :
Why we do not need main() function in Device Drivers?
Does Kernel have main() function?

start_kernel
On 4.2, start_kernel from init/main.c is a considerable initialization process and could be compared to a main function.
It is the first arch independent code to run, and sets up a large part of the kernel. So much like main, start_kernel is preceded by some lower level setup code (done in the crt* objects in userland main), after which the "main" generic C code runs.
How start_kernel gets called in x86_64
arch/x86/kernel/vmlinux.lds.S, a linker script, sets:
ENTRY(phys_startup_64)
and
phys_startup_64 = startup_64 - LOAD_OFFSET;
and:
#define LOAD_OFFSET __START_KERNEL_map
arch/x86/include/asm/page_64_types.h defines __START_KERNEL_map as:
#define __START_KERNEL_map _AC(0xffffffff80000000, UL)
which is the kernel entry address. TODO how is that address reached exactly? I have to understand the interface Linux exposes to bootloaders.
arch/x86/kernel/vmlinux.lds.S sets the very first bootloader section as:
.text : AT(ADDR(.text) - LOAD_OFFSET) {
_text = .;
/* bootstrapping code */
HEAD_TEXT
include/asm-generic/vmlinux.lds.h defines HEAD_TEXT:
#define HEAD_TEXT *(.head.text)
arch/x86/kernel/head_64.S defines startup_64. That is the very first x86 kernel code that runs. It does a lot of low level setup, including segmentation and paging.
That is then the first thing that runs because the file starts with:
.text
__HEAD
.code64
.globl startup_64
and include/linux/init.h defines __HEAD as:
#define __HEAD .section ".head.text","ax"
so the same as the very first thing of the linker script.
At the end it calls x86_64_start_kernel a bit awkwardly with and lretq:
movq initial_code(%rip),%rax
pushq $0 # fake return address to stop unwinder
pushq $__KERNEL_CS # set correct cs
pushq %rax # target address in negative space
lretq
and:
.balign 8
GLOBAL(initial_code)
.quad x86_64_start_kernel
arch/x86/kernel/head64.c defines x86_64_start_kernel which calls x86_64_start_reservations which calls start_kernel.
arm64 entry point
The very first arm64 that runs on an v5.7 uncompressed kernel is defined at https://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L72 so either the add x13, x18, #0x16 or b stext depending on CONFIG_EFI:
__HEAD
_head:
/*
* DO NOT MODIFY. Image header expected by Linux boot-loaders.
*/
#ifdef CONFIG_EFI
/*
* This add instruction has no meaningful effect except that
* its opcode forms the magic "MZ" signature required by UEFI.
*/
add x13, x18, #0x16
b stext
#else
b stext // branch to kernel start, magic
.long 0 // reserved
#endif
le64sym _kernel_offset_le // Image load offset from start of RAM, little-endian
le64sym _kernel_size_le // Effective size of kernel image, little-endian
le64sym _kernel_flags_le // Informative flags, little-endian
.quad 0 // reserved
.quad 0 // reserved
.quad 0 // reserved
.ascii ARM64_IMAGE_MAGIC // Magic number
#ifdef CONFIG_EFI
.long pe_header - _head // Offset to the PE header.
This is also the very first byte of an uncompressed kernel image.
Both of those cases jump to stext which starts the "real" action.
As mentioned in the comment, these two instructions are the first 64 bytes of a documented header described at: https://github.com/cirosantilli/linux/blob/v5.7/Documentation/arm64/booting.rst#4-call-the-kernel-image
arm64 first MMU enabled instruction: __primary_switched
I think it is __primary_switched in head.S:
/*
* The following fragment of code is executed with the MMU enabled.
*
* x0 = __PHYS_OFFSET
*/
__primary_switched:
At this point, the kernel appears to create page tables + maybe relocate itself such that the PC addresses match the symbols of the vmlinux ELF file. Therefore at this point you should be able to see meaningful function names in GDB without extra magic.
arm64 secondary CPU entry point
secondary_holding_pen defined at: https://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L691
Entry procedure further described at: https://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L691

Fundamentally, there is nothing special about a routine being named main(). As alluded to above, main() serves as the entry point for an executable load module. However, you can define different entry points for a load module. In fact, you can define more than one entry point, for example, refer to your favorite dll.
From the operating system's (OS) point of view, all it really needs is the address of the entry point of the code that will function as a device driver. The OS will pass control to that entry point when the device driver is required to perform I/O to the device.
A system programmer defines (each OS has its own method) the connection between a device, a load module that functions as the device's driver, and the name of the entry point in the load module.
Each OS has its own kernel (obviously) and some might/maybe start with main() but I would be surprised to find a kernel that used main() other than in a simple one, such as UNIX! By the time you are writing kernel code you have long moved past the requirement to name every module you write as main().
Hope this helps?
Found this code snippet from the kernel for Unix Version 6. As you can see main() is just another program, trying to get started!
main()
{
extern schar;
register i, *p;
/*
* zero and free all of core
*/
updlock = 0;
i = *ka6 + USIZE;
UISD->r[0] = 077406;
for(;;) {
if(fuibyte(0) < 0) break;
clearsig(i);
maxmem++;
mfree(coremap, 1, i);
i++;
}
if(cputype == 70)
for(i=0; i<62; i=+2) {
UBMAP->r[i] = i<<12;
UBMAP->r[i+1] = 0;
}
// etc. etc. etc.

Several ways to look at it:
Device drivers are not programs. They are modules that are loaded into another program (the kernel). As such, they do not have a main() function.
The fact that all programs must have a main() function is only true for userspace applications. It does not apply to the kernel, nor to device drivers.

With main() you propably mean what main() is to a program, namely its "entry point".
For a module that is init_module().
From Linux Device Driver's 2nd Edition:
Whereas an application performs a single task from beginning to end, a module registers itself in order to serve future requests, and its "main" function terminates immediately. In other words, the task of the function init_module (the module's entry point) is to prepare for later invocation of the module's functions; it's as though the module were saying, "Here I am, and this is what I can do." The second entry point of a module, cleanup_module, gets invoked just before the module is unloaded. It should tell the kernel, "I'm not there anymore; don't ask me to do anything else."

Yes, the Linux kernel has a main function, it is located in arch/x86/boot/main.c file. But Kernel execution starts from arch/x86/boot/header.S assembly file and the main() function is called from there by "calll main" instruction.
Here is that main function:
void main(void)
{
/* First, copy the boot header into the "zeropage" */
copy_boot_params();
/* Initialize the early-boot console */
console_init();
if (cmdline_find_option_bool("debug"))
puts("early console in setup code.\n");
/* End of heap check */
init_heap();
/* Make sure we have all the proper CPU support */
if (validate_cpu()) {
puts("Unable to boot - please use a kernel appropriate "
"for your CPU.\n");
die();
}
/* Tell the BIOS what CPU mode we intend to run in. */
set_bios_mode();
/* Detect memory layout */
detect_memory();
/* Set keyboard repeat rate (why?) and query the lock flags */
keyboard_init();
/* Query Intel SpeedStep (IST) information */
query_ist();
/* Query APM information */
#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
query_apm_bios();
#endif
/* Query EDD information */
#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
query_edd();
#endif
/* Set the video mode */
set_video();
/* Do the last things and invoke protected mode */
go_to_protected_mode();
}

While the function name main() is just a common convention (there is no real reason to use it in kernel mode) the linux kernel does have a main() function for many architectures, and of course usermode linux has a main function.
Note the OS runtime loads the main() function to start an app, when an operating system boots there is no runtime, the kernel is simply loaded to a address by the boot loader which is loaded by the MBR which is loaded by the hardware. So while a kernel may contain a function called main it need not be the entry point.
See Also:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633559%28v=vs.85%29.aspx
Linux kernel source:
x86: linux-3.10-rc6/arch/x86/boot/main.c
arm64: linux-3.10-rc6/arch/arm64/kernel/asm-offsets.c

Related

How do i properly implement threads in Windows Kernel Driver?

I am trying to learn how to code windows kernel drivers.
In my driver i have 2 threads which are created at some point with PsCreateSystemThread
I have a global variable called Kill which signals the threads to terminate like this.
VOID AThread(IN PVOID Context)
{
for (;;)
{
if(Kill == True)
break;
KmWriteProcessMemory(rProcess, &NewValue, dwAAddr, sizeof(NewValue));
}
PsTerminateSystemThread(STATUS_SUCCESS);
}
In my unload function i am setting Kill = TRUE
VOID f_DriverUnload(PDRIVER_OBJECT pDriverObject)
{
Kill = TRUE;
IoDeleteSymbolicLink(&SymLinkName);
IoDeleteDevice(pDeviceObject);
DbgPrint("Driver Unloaded successfully..\r\n");
}
Most of the time there's no problem, but sometimes the machine will crash when i try to unload the driver. It happens more frequently when i have some kind of sleep function being used in the threads, so i'm assuming it's crashing because the threads have not yet terminated before the driver tries to unload.
I'm not too sure how to use synchronisation and such, and there's not a lot of clear information out there that i can find. So how do i properly implement threads and ensure they're terminated before the driver is unloaded?
Once the thread is created, you have HANDLE threadHandle result. Then you need to convert this handle to PETHREAD ThreadObject; :
ObReferenceObjectByHandle(threadHandle,
THREAD_ALL_ACCESS,
NULL,
KernelMode,
&ThreadObject,
NULL );
and close threadHandle:
ZwClose(threadHandle);
When you want to stop the thread, set the flag and wait for thread completion:
Kill = TRUE;
KeWaitForSingleObject(ThreadObject,
Executive,
KernelMode,
FALSE,
NULL );
ObDereferenceObject(ThreadObject);
Then f_DriverUnload function may exit.
You can see all this stuff here: https://github.com/Microsoft/Windows-driver-samples/tree/master/general/cancel/sys
See cancel.h and cancel.c files. Additionally, this code uses semaphore instead of global flag to stop the thread.
when you create thread which used your driver, the driver of course must not be unloaded, until thread not exit. for do this need call ObfReferenceObject for your driver object, before create thread. if create thread fail - call ObfDereferenceObject. and when thread exit - need call ObfDereferenceObject. but here is problem - how / from where call this ? call ObfDereferenceObject from the end of thread routine no sense - the driver can be unloaded inside ObfDereferenceObject and we return from call to not existing memory place. ideally will be if external code (windows itself) call this, just after thread return.
look for IoAllocateWorkItem for good example. work item - like thread, and driver must not be unloaded, until WorkerRoutine not return. and here system care about this - for this we pass DeviceObject to IoAllocateWorkItem: Pointer to the caller's driver object or to one of the caller's device objects. - the system reference this object (device or driver) when we call IoQueueWorkItem and this is guarantee that driver will be not unloaded during WorkerRoutine execution. when it return - windows call ObfDereferenceObject for passed device or driver object. and here all ok, because we return to system kernel code (not to driver) after this. but unfortunately PsCreateSystemThread not take pointer to driver object and not implement such functional.
another good example FreeLibraryAndExitThread - the driver is kernel mode dll by fact, which can be loaded and unloaded. and FreeLibraryAndExitThread exactly implement functional which we need, but for user mode dlls only. again no such api in kernel mode.
but anyway solution is possible. possible yourself jump (not call) to ObfDereferenceObject at the end of thread execution, but for this need use assembler code. not possible do this trick in c/c++.
first of all let declare pointer to driver object in global variable - we initialize it to valid value in driver entry point.
extern "C" PVOID g_DriverObject;
than some macros for get mangled c++ names, this need for use it in asm file:
#if 0
#define __ASM_FUNCTION __pragma(message(__FUNCDNAME__" proc\r\n" __FUNCDNAME__ " endp"))
#define _ASM_FUNCTION {__ASM_FUNCTION;}
#define ASM_FUNCTION {__ASM_FUNCTION;return 0;}
#define CPP_FUNCTION __pragma(message("extern " __FUNCDNAME__ " : PROC ; " __FUNCSIG__))
#else
#define _ASM_FUNCTION
#define ASM_FUNCTION
#define CPP_FUNCTION
#endif
in c++ we declare 2 functions for thread:
VOID _AThread(IN PVOID Context)_ASM_FUNCTION;
VOID __fastcall AThread(IN PVOID Context)
{
CPP_FUNCTION;
// some code here
// but not call PsTerminateSystemThread !!
}
(don't forget __fastcall on AThread - for x86 this need)
now we create thread with next code:
ObfReferenceObject(g_DriverObject);
HANDLE hThread;
if (0 > PsCreateSystemThread(&hThread, 0, 0, 0, 0, _AThread, ctx))
{
ObfDereferenceObject(g_DriverObject);
}
else
{
NtClose(hThread);
}
so you set thread entry point to _AThread which will be implemented in asm file. at begin you call ObfReferenceObject(g_DriverObject);. the _AThread will call you actual thread implementation AThread in c++. finally it return back to _AThread (because this you must not call PsTerminateSystemThread. anyway call this api is optional at all - when thread routine return control to system - this will be auto called). and _AThread at the end de-reference g_DriverObject and return to system.
so main trick in asm files. here 2 asm for x86 and x64:
x86:
.686p
extern _g_DriverObject:DWORD
extern __imp_#ObfDereferenceObject#4:DWORD
extern ?AThread##YIXPAX#Z : PROC ; void __fastcall AThread(void *)
_TEXT segment
?_AThread##YGXPAX#Z proc
pop ecx
xchg ecx,[esp]
call ?AThread##YIXPAX#Z
mov ecx,_g_DriverObject
jmp __imp_#ObfDereferenceObject#4
?_AThread##YGXPAX#Z endp
_TEXT ends
END
x64:
extern g_DriverObject:QWORD
extern __imp_ObfDereferenceObject:QWORD
extern ?AThread##YAXPEAX#Z : PROC ; void __cdecl AThread(void *)
_TEXT segment 'CODE'
?_AThread##YAXPEAX#Z proc
sub rsp,28h
call ?AThread##YAXPEAX#Z
add rsp,28h
mov rcx,g_DriverObject
jmp __imp_ObfDereferenceObject
?_AThread##YAXPEAX#Z endp
_TEXT ENDS
END

SPARC LEON error: IU exception (tt = 0x2B, data store error)

Good morning, I need an help because I'm stuck and I cannot find any solution looking at the manuals.
I want to use EDAC on Leon3. I'm programming in C using the BCC compiler. In particular, I have a GR-UT699 board. I'm using GRMON to flash my elf file in the RAM. My program is a short test where I want to use the EDAC. To enable the EDAC I simple bitbang the registers in this way (I can say that I checked the register and they are correctly wroted):
#define MCFG2_RMW_bit_set 0x00000040 //enable read-modify-write cycles on sub-word writes to 16 and 32bit areas with common write strobe
#define MCFG2_DE_bit_set 0x00004000 //SDRAM controller (1 en, 0 dis)
#define MCFG3_R_bit_set 0x00000200 //enable EDAC checking of the SDRAM or SRAM (1 en, 0 dis)
#define MCFG1_IE_bit_set 0x00080000 //enable access to mapped I/O memory.
...
edac->MCFG1 = edac->MCFG1 | MCFG1_IE_bit_set;
edac->MCFG2 = edac->MCFG2 | MCFG2_RMW_bit_set | MCFG2_DE_bit_set;
edac->MCFG3 = edac->MCFG3 | MCFG3_R_bit_set;
...
return 0;
}
these instructions are executed inside a init function which returns 0. I just set the bits which you can see in the previous defines.
When the function returns, I just want to call a printf() to show a message. The latter (the printf) output is never showed. So the program crashes after having set the register and before the printf. I think it crashes during the init function return.
these is the grmon console output:
grmon2> run
IU exception (tt = 0x2B, data store error)
0x40009acc: 81c3e008 retl <memmove+484>
grmon2> inst
TIME ADDRESS INSTRUCTION RESULT SYMBOL
2608062 40009978 andcc %g1, %g3, %g0 [00000000] memmove+0x90
2608065 4000997C be 0x40009AB0 [00000000] memmove+0x94
2608066 40009980 or %g2, %o1, %g1 [40013FA0] memmove+0x98
2608067 40009AB0 mov 0, %g1 [00000000] memmove+0x1c8
2608068 40009AB4 ldub [%o1 + %g1], %g3 [0000002E] memmove+0x1cc
2608070 40009AB8 stb %g3, [%g2 + %g1] [40012EA0 2E2E2E2E] memmove+0x1d0
2608072 40009ABC add %g1, 1, %g1 [00000001] memmove+0x1d4
2608073 40009AC0 cmp %g1, %o2 [00000000] memmove+0x1d8
2608076 40009AC4 bne,a 0x40009AB8 [00000000] memmove+0x1dc
2608078 40009ACC retl [ TRAP ] memmove+0x1e4
I saw that I needed to set the IE bit in the MCFG1 reg, and so I did. But the program still crashes. What is wrong here?
thanks in advance for your patience.
-Lorenzo
I found at least one solution which does not produces a crash of the program.
If you want to use EDAC you have to initialize the memory controller registers (from GRMON using "mcfgx 0xvalue etc" OR using -edac option when starting GRMON).
Then a wash of the RAM shall be performed (use of the wash command from GRMON).
It is important launch the wash command (or generally wash the memory from a firmware) after the EDAC has been enabled. In fact, if you wash the memory after the ENAC has been enabled the checkbits are generated. Otherwise you'll perform a simple memory clean.
Then you can finally load a program into the RAM (from grmon using "load").
It is important to notice that also IU/FPU register shall be cleared at reset, this can be done from MKPROM (if necessary).
This solution works for programs that are loaded in the RAM through GRMON.
If is necessary to flash the programs into the flash ROM similar operation shall be performed by means of MKPROM. I have not done this yet but I hope is something really similar.
Lorenzo.

linux memory management - how to get "Random xxx offset"?

I am studying process memory management.
I read a post about Process address space layout.
I referenced the following URL.
In linux, start_data, end_data, start_brk, brk, etc are member variable of struct mm_struct.
However I want to know how to calculate Random brk, stack, mmap offset.
It seems that those three values(Random xxx offset) are't defined in struct mm_struct.
Is there any function or MACRO to calculate those values?
I am using linux kernel version 4.4 and x86-64 architecture.
Thank you.
The OS already implements /proc/< pid >/maps which shows all VMAs of that process, including the stack,heap and of course the mmap-ed ones.
If you want to check from where all these information fill you can check kernel source code, the relevant code (to look up VMAs of a given PID) seems to be here: fs/proc/task_mmu.c .
And, yes indeed, the "[heap]" is marked by this code snippet from the above src file (kernel ver 3.10.24):
fs/proc/task_mmu.c:show_map_vma()
...
if (vma->vm_start <= mm->brk && vma->vm_end >= mm->start_brk)
{
name = "[heap]"; goto done; }
...
And one more thing if you want to check start-end address of particular segment, Do check The mm_struct is defined in . you will get following thing :-
struct mm_struct{
......
unsigned long start_code, end_code, start_data, end_data;
unsigned long start_brk, brk, start_stack;
......
}
start_code, end_code The start and end address of the code section;
start_data, end_data The start and end address of the data section;
start_brk, brk The start and end address of the heap;
start_stack Predictably enough, the start of the stack region;

Strange load instructions produced by mipsel-gcc when compiling glibc

I'm trying to get a small piece of hello-world MIPS program running in Gem 5 simulator. The program was compiled with gcc 4.9.2 and glibc 2.19 (built by crosstool-ng) and runs well in qemu, but it crashed with a page fault (trying to access address 0) in gem5.
Code is rather simple:
#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}
file ./test result:
./test: ELF 32-bit LSB executable, MIPS, MIPS-I version 1, statically
linked, for GNU/Linux 3.15.4, not stripped
After some debugging with gdb, I figured out that the page fault is triggered by _dl_setup_stack_chk_guard function in glibc. It accepts a void pointer called _dl_random passed by __libc_start_main function, which happens to be NULL. However, as far as I know, these functions never dereference the pointer, but instructions were generated to load values from the memory _dl_random pointer points to. Some code pieces might help understanding:
in function __libc_start_main (macro THREAD_SET_STACK_GUARD is not set):
/* Initialize the thread library at least a bit since the libgcc
functions are using thread functions if these are available and
we need to setup errno. */
__pthread_initialize_minimal ();
/* Set up the stack checker's canary. */
uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random);
# ifdef THREAD_SET_STACK_GUARD
THREAD_SET_STACK_GUARD (stack_chk_guard);
# else
__stack_chk_guard = stack_chk_guard;
# endif
in function _dl_setup_stack_chk_guard (always inlined):
static inline uintptr_t __attribute__ ((always_inline))
_dl_setup_stack_chk_guard (void *dl_random)
{
union
{
uintptr_t num;
unsigned char bytes[sizeof (uintptr_t)];
} ret = { 0 };
if (dl_random == NULL)
{
ret.bytes[sizeof (ret) - 1] = 255;
ret.bytes[sizeof (ret) - 2] = '\n';
}
else
{
memcpy (ret.bytes, dl_random, sizeof (ret));
#if BYTE_ORDER == LITTLE_ENDIAN
ret.num &= ~(uintptr_t) 0xff;
#elif BYTE_ORDER == BIG_ENDIAN
ret.num &= ~((uintptr_t) 0xff << (8 * (sizeof (ret) - 1)));
#else
# error "BYTE_ORDER unknown"
#endif
}
return ret.num;
}
disassembly code:
0x00400ea4 <+228>: jal 0x4014b4 <__pthread_initialize_minimal>
0x00400ea8 <+232>: nop
0x00400eac <+236>: lui v0,0x4a
0x00400eb0 <+240>: lw v0,6232(v0)
0x00400eb4 <+244>: li a0,-256
0x00400eb8 <+248>: lwl v1,3(v0)
0x00400ebc <+252>: lwr v1,0(v0)
0x00400ec0 <+256>: addiu v0,v0,4
0x00400ec4 <+260>: and v1,v1,a0
0x00400ec8 <+264>: lui a0,0x4a
0x00400ecc <+268>: sw v1,6228(a0)
0x4a1858 (0x4a0000 + 6232) is the address of _dl_random
0x4a1854 (0x4a0000 + 6228) is the address of __stack_chk_guard
Page fault occurs at 0x00400eb8. I don't quite get it how instruction 0x00400eb8 and 0x00400ebc are generated. Could someone shed some light on it please? Thanks.
Here is how I find the root of this problem and my suggestion for solution.
I think it helpful to dive into the Glibc source code to see what really happens. Starting from _dl_random or __libc_start_main are both OK.
As the value of _dl_random is unexpectedly NULL, we need to find how this variable initialize and where it is assigned. With the help of code analysing tools, we can find _dl_random in Glibc is only assigned with meaningful value in function _dl_aux_init, and this function is called by __libc_start_min.
_dl_aux_init iterates on its parameter -- auxvec -- and acts corresponding to auxvec[i].at_type. AT_RANDOM is the case for the assignment of _dl_random. So the problem is that there isn't an AT_RANDOM element to make _dl_random assigned.
As the program runs well in user mode qemu, the root of this problem resides in system environment provider, say, gem5, which has the responsibility to construct auxvec. Having that keyword, we can find that the auxv is constructed in gem5/src/arch/<arch-name>/process.cc.
The current auxv for MIPS is constructed as below:
// Set the system page size
auxv.push_back(auxv_t(M5_AT_PAGESZ, MipsISA::PageBytes));
// Set the frequency at which time() increments
auxv.push_back(auxv_t(M5_AT_CLKTCK, 100));
// For statically linked executables, this is the virtual
// address of the program header tables if they appear in the
// executable image.
auxv.push_back(auxv_t(M5_AT_PHDR, elfObject->programHeaderTable()));
DPRINTF(Loader, "auxv at PHDR %08p\n", elfObject->programHeaderTable());
// This is the size of a program header entry from the elf file.
auxv.push_back(auxv_t(M5_AT_PHENT, elfObject->programHeaderSize()));
// This is the number of program headers from the original elf file.
auxv.push_back(auxv_t(M5_AT_PHNUM, elfObject->programHeaderCount()));
//The entry point to the program
auxv.push_back(auxv_t(M5_AT_ENTRY, objFile->entryPoint()));
//Different user and group IDs
auxv.push_back(auxv_t(M5_AT_UID, uid()));
auxv.push_back(auxv_t(M5_AT_EUID, euid()));
auxv.push_back(auxv_t(M5_AT_GID, gid()));
auxv.push_back(auxv_t(M5_AT_EGID, egid()));
Now we know what to do. We just need to provide an accessible address value to _dl_random tagged by MT_AT_RANDOM. Gem5's ARM arch implements this already (code). Maybe we can take it as an example.

How to place variables in the access bank - PIC 18 MPASM linker script

I have a linker script which starts
INCLUDE 18f14K50_g.lkr
I want my interrupt service variables to go into the ACCESS bank. (My program's so small at the moment the whole lot can, but maybe in future...). So
SECTION NAME=VarsModemISR RAM=accessram
which results in:
MPLINK 4.39, Linker
Device Database Version 1.1
Copyright (c) 1998-2011 Microchip Technology Inc.
Error - section 'VarsModemISR' has a memory 'accessram' which is not defined in the linker command file.
Errors : 1
Examining the included file I believe it is. Either that or I'm working in extended mode and "gpre" is. I can use an #IFDEF to check, which I tried. The result, it was trying to use "accessram" not "gpre".
Maybe if I try defining the access bank explicitly by copying the line from the include file:
ACCESSBANK NAME=accessram START=0x0 END=0x5F
SECTION NAME=VarsModemISR RAM=accessram
This results in the error
MPLINK 4.39, Linker
Device Database Version 1.1
Copyright (c) 1998-2011 Microchip Technology Inc.
Error - duplicate definition of memory 'accessram'
Errors : 1
Which has me confused. According to the Assembler/Linker documentation I use SECTION with the RAM option, where RAM has previously been declared using ACCESSBANK, SHAREBANK or DATABANK. It should work.
Thanks
- Richard
There is really no need to change linker script, use default one!
Accessed file registers are available at any moment under PIC18 MCPUs.
Just declare variables in appropriate memory databank named ACCESSBANK which start at 0x00 and end at 0x60 address.
If you are using MPLAB than declare:
_Shared udata_acs 0 ;Shared memory file registers
IntReg1 res 1
IntReg2 res 1
;...
_UpperBank0 udata 060h ;Banked file memory registers
RegA res 1
;...
_Bank1 udata 0100h ;Banked file memory registers
N res 1
;...
Linker should automatically set the 'a' bit in code instruction for file register addresses, which are declared in ACCESSBANK.
I am using UDATA_ACS to declare the variables I want in access, so in modem.asm I have
; Variables for the interrupt handler - Access RAM
VarsModemISR UDATA_ACS
wave_index res 1 ; Index into the wave table for current sample
sample_period res 1 ; Sample period in use, TMR0 ticks
sample_count res 1 ; Amount of samples output since last bit boundary
fsrtmpl res 1 ; Temporary store for FSR
fsrtmph res 1 ; Temporary store for FSR
; Variables for the modem code - GPR0, non-Access
VarsModem UDATA
flag res 1 ; Counter for transmitting AX25 flags
bit res 1 ; Bit counter when transmitting a character
ch res 1 ; Current character being transmitted
...
My current linker script uses the supplied script, but defines my segments. I note that there's only one program page defined in the script, unlike on the PIC16s. No more PAGESEL?
INCLUDE 18f14K50_g.lkr
SECTION NAME=CodeModemISR ROM=page
SECTION NAME=CodeModem ROM=page
SECTION NAME=CodeWaveTable ROM=page
SECTION NAME=CodeEepromUtil ROM=page
SECTION NAME=VarsModem RAM=gpr0
SECTION NAME=VarsGPSState RAM=gpr0
SECTION NAME=CodeConfigEEPROM ROM=eedata
The resulting map contains the mappings I expect:
Hard coded locations as expected:
HighInterruptVector code 0x000008 program 0x000004
LowInterruptVector code 0x000018 program 0x000002
Movable locations packed in:
CodeModemISR code 0x00001a program 0x000028
CodeModem code 0x000042 program 0x0000fe
CodeWaveTable code 0x000140 program 0x000040
CodeMain code 0x000180 program 0x000054
EEPROM in the right place
CodeConfigEEPROM code 0xf00000 program 0x000044
And variables in ACCESSRAM and GP0
VarsModemISR udata 0x000000 data 0x000005
VarsModem udata 0x000060 data 0x000027
VarsGPSState udata 0x000087 data 0x00000e
There are more problems to solve, but they may be in other posts. I note that CodeWaveTable is taking 64 bytes so it's not closely packed. Solution - use CODE_PACK and now it's 32 bytes.

Resources