Can we assign a value to a given memory location?

2020-06-03 03:06发布

问题:

I want to assign some value (say 2345) to a memory location(say 0X12AED567). Can this be done?

In other words, how can I implement the following function?

void AssignValToPointer(uint32_t pointer, int value)
{

}

回答1:

The answer depends on some factors. Is your program running within a modern operating system?

If yes, trying to access a memory area that is not mapped will cause a SIGSEGV. To accomplish that, you have to use a system specific function to map the region of memory that contains this exact address before trying to access it.



回答2:

The fact that you are asking this question kind of indicates that you're in over your head. But here you go:

*(int *)0x12AED567 = 2345;


回答3:

Just treat the memory location as a pointer

int* pMemory =  OX12AED567;
*pMemory = 2345;

Note: This will only work if that memory location is accessible and writable by your program. Writing to an arbitrary memory location like this is inherently dangerous.



回答4:

As far as C is concerned, that's undefined behaviour. My following suggestion is also undefined behaviour, but avoids all the type-based and aliasing-based problems: Use chars.

int a = get_value();
char const * const p = (const char * const)&a;
char * q = (char *)0x12345;

memcpy(q, p, sizeof(int));

Alternatively, you can access bytes q[i] directly. (This is the part that is UB: the pointer q was not obtained as the address-of an actual object or as the result of an allocation function. Sometimes this is OK; for instance if you're writing a free-standing program that runs in real mode and accesses the graphics hardware, you can write to the graphics memory directly at a well-known, hard-coded address.)



回答5:

You've indicated, that the address is a physical address, and that your code is running in a process.

So if you're

  1. in kind of high level operating system, e.g. Linux, you'd have to get a mapping into the physical address space. In Linux, /dev/mem does that for you.

  2. within the kernel or without operating system and with a MMU, you have to translate the physical address into a virtual address. In the Linux kernel, phys_to_virt() does that for you. In the kernel, I assume, this address is always mapped.

  3. within the kernel or without operating system and without MMU, you write directly to that address. There's no mapping to consider at all.

Now, you have a valid mapping or the physical address itself, that you pass to your function.

void AssignValToPointer(uint32_t pointer, int value)
{
    * ((volatile int *) pointer) = value;
}

You might want to add the volatile keyword as the compiler might optimize the write-operation away if you do not read from that location afterwards (likely case when writing to a register of a memory-mapped hardware).

You might also want to use the uintptr_t data type instead of uint32_t for the pointer.



回答6:

C99 standard draft

This is likely not possible without implementation defined behavior.

About casts like:

*(uint32_t *)0x12AED567 = 2345;

the C99 N1256 standard draft "6.3.2.3 Pointers" says:

5 An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation. 56)

GCC implementation

GCC documents its int to pointer implementation at: https://gcc.gnu.org/onlinedocs/gcc-5.4.0/gcc/Arrays-and-pointers-implementation.html#Arrays-and-pointers-implementation

A cast from integer to pointer discards most-significant bits if the pointer representation is smaller than the integer type, extends according to the signedness of the integer type if the pointer representation is larger than the integer type, otherwise the bits are unchanged.

so the cast will work as expected for this implementation. I expect other compilers to do similar things.



回答7:

With the proviso that it's not portable or safe (at all):

*((int *)0x12AED567) = 2345;