Assign a memory location with address 0x67AB and value 0x1234 using pointers.
i am trying to assign a fixed location in memory using pointers and not using malloc()
The following code writes 0x1234 to the address 0x67AB in the address space of the executing process, provided your C implementation supports this behavior (as it goes beyond what the C standard requires). You may need to use a type other than uint16_t, depending on your precise needs. If you do not know what you are doing, this code will likely fail or cause other problems:
#include <stdint.h>
…
* (uint16_t *) 0x67AB = 0x1234;
If your program is running under an operating system, you will not be able to write to a fix location. In order to do something like this your program would need to have root access and make a system call so that the operative system itself writes on that location.
However, if you're writing a program for a micro-controller or an embedded system this is how you do it:
char* p = (char*)0x67AB;
p[0] = 0x12; // You need to write each byte at a time.
p[1] = 0x34;
Assuming you know writing to a specific address if OK, as in a PIC or controller:
#include <stdint.h>
int16_t *ptr = (int16_t *) 0x67AB;
*ptr = 0x1234;
As the endian of integer is not specified, a byte-by-byte setting may not provided expected behavior.
Related
I have this code:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
int main (int argc, char** argv) {
*(volatile uint8_t*)0x12345678u = 1;
int var = *(volatile uint8_t*)0x12345678;
printf("%i", var);
printf("%i", &var);
return (EXIT_SUCCESS);
}
I want to see a 1 and the address of that int, which i specified previously. But when compiled by gcc in bash, only "command terminated" without any error will be shown. Does anyone know why so?
PS: I am newbie to C, so just experimenting.
What you are doing:
*(volatile uint8_t*)0x12345678u = 1;
int var = *(volatile uint8_t*)0x12345678;
is totally wrong.
You have no guarantee whatsoever that an arbitrary address like 0x12345678 will be accessible, not to mention writable by your program. In other words, you cannot set a value to an arbitrary address and expect it to work. It's undefined behavior to say the least, and will most likely crash your program due to the operating system stopping you from touching memory you don't own.
The "command terminated" that you get when trying to run your program happens exactly because the operating system is preventing your program from accessing a memory location it is not allowed to access. Your program gets killed before it can do anything.
If you are on Linux, you can use the mmap function to request a memory page at an (almost) arbitrary address before accessing it (see man mmap). Here's an example program which achieves what you want:
#include <sys/mman.h>
#include <stdio.h>
#define WANTED_ADDRESS (void *)0x12345000
#define WANTED_OFFSET 0x678 // 0x12345000 + 0x678 = 0x12345678
int main(void) {
// Request a memory page starting at 0x12345000 of 0x1000 (4096) bytes.
unsigned char *mem = mmap(WANTED_ADDRESS, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// Check if the OS correctly granted your program the requested page.
if (mem != WANTED_ADDRESS) {
perror("mmap failed");
return 1;
}
// Get a pointer inside that page.
int *ptr = (int *)(mem + WANTED_OFFSET); // 0x12345678
// Write to it.
*ptr = 123;
// Inspect the results.
printf("Value : %d\n", *ptr);
printf("Address: %p\n", ptr);
return 0;
}
The operating system and loader do not automatically make every possible address available to your program. The virtual address space of your process is constructed on demand by various operations of the program loader and of services inside the process. Although every address “exists” in the sense of being a potential address of memory, what happens when a process attempts to access an address is controlled by special data structures in the system. Those data structures control whether a process can read, write, or execute various portions of memory, whether the virtual addresses are currently mapped to physical memory, and whether the virtual addresses are not currently mapped to memory but will be provide with physical memory when needed. Initially, much of a process’ address space is marked not in use (or at least implicitly marked, in that none of the explicit records for the address space apply to it).
In the executions of your program you have attempted so far, the address 0x12345678 has not been mapped and marked available to your process, so, when your process attempted to use it, the system detected a fault and terminated your process.
(Some systems randomize the layout of the address space when a program is being loaded, to make it harder for an attacker to exploit bugs in a program. Because of this, it is possible that 0x12345678 will be accessible in some executions of your program and not others.)
The quote from C11 standard 6.5.3.2p4:
4 The unary * operator denotes indirection. [...] If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.
You use * operator on (volatile uint8_t*)0x12345678u pointer. Is this a valid pointer? Is it invalid pointer? What is an "invalid value" of a pointer?
There is no check that allows to find out which particilar pointer values are valid, which aren't. It is not implemented in C language. A random pointer may just happen to be a valid pointer. But most, most probably it is an invalid pointer. In which case - the behavior is undefined.
Dereferencing an invalid pointer is undefined behavior. But - outside of C scope and into operating system - on *unix systems trying to access memory that you are not allowed to, should raise a signal SIGSEGV on your program and terminate your program. Most probably this is what happens. Your program is not allowed to access memory location that is behind 0x12345678 value, the operating system specifically protects against that.
Also note, that systems use ASLR, so that pointer values within your program are indeed in some degree random. There are not linear, ie. *(char*)0x01 will not access the first byte in your ram. Operating system (or more exact, the underlying hardware as configured by the operating system) translates pointer values in your program to physical location in ram using what is called virtual memory. The same pointer values may just happen to be valid on the second run of your program. But most probably, because pointers can have so many values, most probably it isn't a valid pointer. Your operating system kills your program, as it detects an invalid memory access.
I thought it should be as simple as:
uint32_t getCrc(void)
{
uint32_t expectedCrc = *(uint32_t*)0x27FF0;
return expectedCrc;
}
And if the memory location has the following value:
Then the expectedCrc should equal to 0xECD8743D
But surprisingly, the value is: 0x82828282
I tried defining a pointer to uint32_t and assign the memory address to it as following:
uint32_t getCrc(void)
{
uint32_t *ptr = (uint32_t*)0x27FF0;
uint32_t expectedCrc = *ptr;
return expectedCrc;
}
But the value of the pointer itself was 0xFFFF and expectedCrc equal 0x82828282
I found these two values in a different memory address:
I also tried the same with char *ptr = (char*)0x27FF0 but it gave the same values.
Finally, I tried to check what is the size of a pointer to char in this controller using uint8_t size = sizeof(char*); and the answer was 0xb0 which equals 176.
I think it has something to do with the 24-bit memory address and the CPU architecture. I'm working on stm8 controller.
Could someone explain why does this happen?
UPDATE
I tried replacing the address 0x27FF0 with 0xFFF0 and it worked fine. So the problem is with the long address. I want to write the CRC value in the last address to avoid over-writing it with the code itself in case the program went bigger. How can I handle this?
From the Cosmic compiler datasheet https://www.cosmic-software.com/pdf/cxstm8_pd.pdf
cxstm8 provides 2 different memory models depending on the size of the application.
For applications smaller that 64k, the “section 0” memory model provides the best code density by defaulting function calls and pointers to 2 bytes.
For applications bigger than 64k, the standard memory model provides the best flexibility for using easily the linear addressing space. Each model comes with its own set of libraries.
This may be the cause for your problem. If you want to access the memory location of above 16 bit address directly you need to use the correct memory model.
As #Raje answered that it is about the memory models, I did further reading in the COSMIC user's guide and I found the following:
The STM8 compiler supports two memory models for application
larger than 64K, allowing you to choose the most efficient behavior
depending on your processor configuration and your application. All
these models allow the code to be larger than 64K and then function
pointers are defaulted to #far pointers (3 bytes). Data pointers are
defaulted to #near pointers (2 bytes) unless explicitly declared with
the #far modifier.
Therefore, the solution was to add #far to the pointer's type as following:
uint32_t calculatedCrc = 0;
expectedCrc = *(#far uint32_t*)0x27FF0;
This got the problem solved.
I'm trying to emulate a system that needs to copy data to a peripheral, bare metal, no OS.
The convention states that the copy function is a C-function which takes as the address of the peripheral an 8-bit address that is written to a certain register. The peripheral uses that internally. However, I'm simulating the thing in C and to test the full functionality I am doing something like the following MWE:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char *argv[]){
//stack-array to copy
char array[4] = {3,32,-12,99};
//naive emulation of the peripheral memory space
char peripheral_array[4];
//herein lies the address send as a value
char address_reg = (char*)(peripheral_array);
//assume this is the peripheral side
//set ptr to the address of the peripheral_array
char *ptr = (char*) address_reg;
memcpy((void*)ptr,array,sizeof(char)*4);
return EXIT_SUCCESS;
}
I get segmentation fault.
What's the issue at hand here?
How can I store the pointer of array as a value, send it, recast it successfully as the address to an array and perform the memcpy?
You want to emulate something with a 8-bit address space in an environment with a 32-bit or 64-bit address space, and thus you have some difficulties, because it doesn't convert trivially. Specifically, this line char address_reg = (char*)(peripheral_array) cast a wide pointer to a 8-bit value, and lose most of the pointer, which means you won't be able to convert back.
The solution is to push further your simulation and emulate your destination 8-bit address space :
Typedef a 8-bit pointer (cleaner) : typedef uint8_t ptr8;
Declare your 8-bit destination address space : uint8_t my_destination_memory[256]
Define you own memcpy to copy to this address space : void my_memcpy( ptr8 addr_dest, const void * src, int len ) {
memcpy( my_destination_memory + addr_dest, src, len );
}
This way you can pass around your 8-bit pointer of type ptr8 (or whatever you name it) and copy to it without problem.
Note that I assumed that your source address space is not of importance, buti f it is you can also emulate it. You should be able to emulate 16-bit or even 24-bit address space in the same way (You can use the native pointers if you need 32-bit).
why storing it into char? the variable that can hold an address to a char is a char* - On Both machines - your PC and your embedded MCU!
On your MCU sizeof(char*) may be 1 or 2, and on your PC it may be 4 or 8;
If you want to write platform compatible code, use a char*.
If you want to go a few steps further to simulating the machines address space then you have to provide own implementations of the standard-lib functions. So that they interpret addresses in machines address space as indices to some some memory array that you defined.
However the better approach is mostly to provide some Hardware Abstraction Layer (HAL) that encapsulates system specific tasks, rather than using machine specifica in your business logic.
First of all, please note that storing integer values in the char type is dangerous, since it is a type with implementation-defined signedness. It should never be used for anything but strings.
Instead, always use either int8_t (signed) or uint8_t (unsigned).
The cause of the problem is that a char (or an int8_t for that matter) is not large enough to hold an address.
There is however the uintptr_t in stdint.h which is guaranteed to be large enough to contain an address.
Simply replace
char address_reg = (char*)(peripheral_array);
with
uintptr_t address_reg = (uintptr_t)peripheral_array;
What is wrong with the following code:
unsigned int *ptr;
ptr=(unsigned int*)0x1234;
*ptr=10;
/*do someting*/
*ptr=100;
Imp point, I have read the memory map of the microcontroller, and this location can be very much used by the programmer, so the case of not-owning-the-memory is ruled out.
All I want to find out is, can I use the above method to directly access a memory location?
Thanks in advance.
Yes it is fine, unless (as you said) you are within accessible memory range.
Also I would like to add, since you mentioned microcontroller, if this is address of a SFR or a GPR then its alias (using #define)must already be defined in the header for the controller, so it is better you use that alias, for improved readability/understandability and portability.
EDIT : (As Vlad pointed out in the comment)
Using volatile will make your program more safe & reliable, as compiler will not optimize out immediate memory write, and you can be sure that the value you write is immediately written to the location you want.
Yes you can use it, but be aware of the alignment issues. Depending in your microcontroller following might happen:
uint16_t * p1 = (uint16_t *)0x8;
uint16_t * p2 = (uint16_t *)0x9;
*p1 = 1; // Ok
*p2 = 1; // Unaligned access -> Crash or something else
ARM for example requires that 16-bit variables are aligned on even addresses (divisible by 2).
Is it possible to assign a variable the address you want, in the memory?
I tried to do so but I am getting an error as "Lvalue required as left operand of assignment".
int main() {
int i = 10;
&i = 7200;
printf("i=%d address=%u", i, &i);
}
What is wrong with my approach?
Is there any way in C in which we can assign an address we want, to a variable?
Not directly.
You can do this though : int* i = 7200;
.. and then use i (ie. *i = 10) but you will most likely get a crash. This is only meaningful when doing low level development - device drivers, etc... with known memory addreses.
Assuming you are on an x86-type processer on a modern operating system, it is not possible to write to aribtray memory locations; the CPU works in concert with the OS to protect memory so that one process cannot accidentally (or intentionally) overwrite another processes' memory. Allowing this would be a security risk (see: buffer overflow). If you try to anyway, you get the 'Segmentation fault' error as the OS/CPU prevents you from doing this.
For technical details on this, you want to start with 1, 2, and 3.
Instead, you ask the OS to give you a memory location you can write to, using malloc. In this case, the OS kernel (which is generally the only process that is allowed to write to arbitrary memory locations) finds a free area of memory and allocates it to your process. The allocation process also marks that area of memory as belonging to your process, so that you can read it and write it.
However, a different OS/processor architecture/configuration could allow you to write to an arbitrary location. In that case, this code would work:
#include <stdio.h>
void main() {
int *ptr;
ptr = (int*)7000;
*ptr = 10;
printf("Value: %i", *ptr);
}
C language provides you with no means for "attaching" a name to a specific memory address. I.e. you cannot tell the language that a specific variable name is supposed to refer to a lvalue located at a specific address. So, the answer to your question, as stated, is "no". End of story.
Moreover, formally speaking, there's no alternative portable way to work with specific numerical addresses in C. The language itself defines no features that would help you do that.
However, a specific implementation might provide you with means to access specific addresses. In a typical implementation, converting an integral value Ato a pointer type creates a pointer that points to address A. By dereferencing such pointer you can access that memory location.
Not portably. But some compilers (usually for the embedded world) have extensions to do it.
For example on IAR compiler (here for MSP430), you can do this:
static const char version[] # 0x1000 = "v1.0";
This will put object version at memory address 0x1000.
You can do in the windows system with mingw64 setup in visual studio code tool, here is my code
#include<stdio.h>
int main()
{
int *c;
c = (int *)0x000000000061fe14; // Allocating the address 8-bit with respect to your CPU arch.
*c = NULL; // Initializing the null pointer for allocated address
*c = 0x10; // Assign a hex value (you can assign integer also without '0x')
printf("%p\n",c); // Prints the address of the c pointer variable
printf("%x\n",*c); // Prints the assigned value 0x10 -hex
}
It is tested with mentioned environment. Hope this helps Happy coding !!!
No.
Even if you could, 7200 is not a pointer (memory address), it's an int, so that wouldn't work anyway.
There's probably no way to determine which address a variable will have. But as a last hope for you, there is something called "pointer", so you can modify a value on address 7200 (although this address will probably be inaccessible):
int *i = (int *)7200;
*i = 10;
Use ldscript/linker command file. This will however, assign at link time, not run time.
Linker command file syntax depends largely on specific compiler. So you will need to google for linker command file, for your compiler.
Approximate pseudo syntax would be somewhat like this:
In linker command file:
.section start=0x1000 lenth=0x100 myVariables
In C file:
#pragma section myVariables
int myVar=10;
It's not possible, maybe possible with compiler extensions. You could however access memory at an address you want (if the address is accessible to your process):
int addr = 7200;
*((int*)addr) = newVal;
I think '&' in &a evaluates the address of i at the compile time which i think is a virtual address .So it is not a Lvalue according to your compiler. Use pointer instead