How compilers allocate identifers in the memory - c

i wanne to know ....now i'm trying to program an embedded system wich contain RAM ,ROM, Microprocessor ....
know i can't under stand when i write instructions like :
int x ;
x= 20 ;
When these program is compiled and Burn to the Rom memory it will be in the operation code ...
my Question :
know when the processor revieve the operation code corresponding to Int x ;
the microprocessor will save a memory location at a random address .
now ..when the processor receive the 2nd instruction x=20 ...it will do a move instruction ...but how can the microprocessor know where does it locate the x variable
.......................................................................
i mean ...how the processor knows the addresses of memory locations reserved for variables ?!!!!

Basically the instruction is just a label for the compiler. The compiler then replaces every occasion of x with an address or a register. This depends on the architecture and its optimization possibilities.

When the code is compiled, the variable x will be assigned a memory address. For example, lets say that the compiler decides to use memory address 0x16 for x. The instruction "x = 20;" will be translated into machine code that will say something equivalent to "put 0x14 into the memory byte at address 0x16" (0x14 being the hex value of 20).
The processor itself will have no knowledge of a variable called "x", it will just be told to look for a byte at address 0x16 (according to my example).

Related

Assigning a 2 byte variable to a 3 byte register?

My Watch dog timer has a default value of 0x0fffff and i want to write a 2 byte variable (u2 compare) in it. What happens when i assign the value simply like this
wdt_register = compare;
What happens to most significant byte of register?
Register definition. It's 3 bytes register containing H, M, L 8bit registers. 4 most significat bits of H are not used and then it's actually a 20 bit register. Datasheet named all of them as WDTCR_20.
My question is what happens when i assign a value to the register using this line (just an example of 2 byte value written to 3 byte register) :
WDTCR_20 = 0x1234;
Your WDT is a so-called special function register. In hardware, it may end up being three bytes, or it could be four bytes, some of which are fixed/read-only/unused. Your compiler's implementation of the write is itself implementation-dependent if the SFR is declared in a particular way that makes the compiler emit SFR-specific write instructions.
This effectively makes the result of the assignment implementation-dependent; the high eight bits might end up being discarded, might set some other microarchitectural flags, or might cause a trap/crash if they aren't set to a specific (likely all-zeros value). It depends on the processor's datasheet (since you didn't mention a processor/toolchain, we don't know exactly).
For example, the AVR-based atmega328p datasheet shows an example of such a register:
In this case, the one-byte register is actually only three bits, effectively (bits 7..3 are fixed to zero on read and ignored on write, and could very well have no physical flip-flop or SRAM cell associated with them).

gdb debugging what is the 8 byte gap for?

I'm debugging a function to learn more about the structure of a stack in memory. I am using gdb on the Ubuntu OS x86. My function consists of the following:
function func(){
long local1=0;
printf("address of swap is %p\n",&local1);
}
In gdb I set a breakpoint inside the function and print out the frame values using info frame. I am able to get the address of the saved registers of ebp and eip which I presume holds the saved base pointer and return address respectively. I also print out the address of local1. So using these addresses I constructed the following stack:
bffff03c --> eip (stores the return address)
bffff038 --> ebp (saved base pointer)
bffff02c --> local1 address
Now I must be missing something because there is a 8 byte gap between ebp and local1. I assumed local1 data type is 4 bytes which leaves the address between bffff030 -> bffff038 unaccounted for. Would really appreciate help with this one.
EDIT
here is the assembly code. the only anomaly i could think of is the SUB instruction following mov esp,ebp, though I'm not sure how it relates to the gap.
The 8 byte gap is because on x86 calling conventions require 16 byte stack alignment on entrance to a function, and a return pointer is only 8 bytes. So we get the 8 bytes of "wasted" space.

Stack memory layout in c

I have written a small program for learning about the stack pointer in C. Usually, the stack pointer points to the last address of RAM. For example, I run this code in Linux, which has the stack pointer in last address (0x7fffffffffff) but the variables are stored in these addresses (for this program):
c --> 0x7ffc67ca5c94
b --> 0x7ffc67ca5c98
a --> 0x7ffc67ca5c9c
Code:
void fun(int *ptr)
{
printf("c --> %p\n",ptr);
ptr++;
printf("b --> %p\n",ptr);
ptr++;
printf("a --> %p\n",ptr);
}
int main()
{
int a = 10,b = 20,c = 30;
printf("%p %p %p\n",&a,&b,&c);
fun(&c);
return 0;
}
Output for this program:
0x7ffc67ca5c9c 0x7ffc67ca5c98 0x7ffc67ca5c94
c --> 0x7ffc67ca5c94
b --> 0x7ffc67ca5c98
a --> 0x7ffc67ca5c9c
My questions are:
Why are the variables not stored in last section of the stack frame (0x7fffffffffff) which skips some memory locations and gets stored? Any valid reason for such behavior?
Why the stack pointer address has six bytes?
I am working on a 64-bit machine with a 32-bit GCC compiler.
which has the stack pointer in last address (0x7fffffffffff)
If running a 32bit process, the last address (minus the reserved area) would be 0x7fff ffff when doing the usual 2GB/2GB split. But 32bit Linux typically reserves only a 1GB kernel address area, so the stack would actually start at 0xc000 0000 (TASK_SIZE).
What you are seeing here, instead is the curious split of the x64 address space layout. Here, indeed the user address space ends at 0x0000 7fff ffff ffff, with 0xffff ff80 0000 0000 and above reserved for the kernel.
Current MMUs actually enforce this, 0xfffe ff80 0000 0000 or similar are not valid addresses, the bit 47-63 must be equal to form a Canonical Form Address
Why the stack pointer address has six bytes?
It does not look like it does from the output of your program.
You are printing the size each variable takes on the stack, not the pointer size. The six-byte addresses from printf() are actually 64bit-addresses with leading zeros cut off (thanks to #Jonathan Leffler for spotting this).
Indeed, sizeof(int *) == 8, but sizeof(int) == 4, because even 64bit Linux has 32bit ints (only long is 64 bit). It's quite easy to miss that, though.
Why are the variables not stored in last section of the stack frame (0x7fffffffffff) which skips some memory locations and gets stored? Any valid reason for such behavior?
If you look here, there is quite some stuff going into that address space before the user stack starts. Since you might need whole pages for this code for protection, there might be some overhead. Add library startup code and you might get quite a few bytes of memory.
Most of the code there is probably inherited copy-on-write (or even read-only) from the parent process.
Edit: On x64, any kernel-exported code probably go in the higher memory area. Don't claim what I have not verified ;-).
As a side note: When I compile and run your code on 64bit FreeBSD 10.2 I get this
0x7fffffffe5c8 0x7fffffffe5c4 0x7fffffffe5c0
c --> 0x7fffffffe5c0
b --> 0x7fffffffe5c4
a --> 0x7fffffffe5c8
which is similar to your output, even though FreeBSD seems to position the stack differently.
Running in 32bit mode, I get this:
0xffffd788 0xffffd784 0xffffd780
c --> 0xffffd780
b --> 0xffffd784
a --> 0xffffd788
The latter is probably really fun to explain (e.g. where is my kernel address space?).
variables can be stored at any address
that address contains more than 32 bits, so it is assigning 64 bit
addresses

How do I force the program to use unaligned addresses?

I've heard reads and writes of aligned int's are atomic and safe, I wonder when does the system make non malloc'd globals unaligned other than packed structures and casting/pointer arithmetic byte buffers?
[X86-64 linux] In all of my normal cases, the system always chooses integer locations that don't get word torn, for example, two byte on one word and the other two bytes on the other word. Can any one post a program/snip (C or assembly) that forces the global variable to unaligned address such that the integer gets torn and the system has to use two reads to load one integer value ?
When I print the below program, the addresses are close to each other such that multiple variables are within 64bits but never once word tearing is seen (smartness in the system or compiler ?)
#include <stdio.h>
int a;
char b;
char c;
int d;
int e = 0;
int isaligned(void *p, int N)
{
if (((int)p % N) == 0)
return 1;
else
return 0;
}
int main()
{
printf("processor is %d byte mode \n", sizeof(int *));
printf ( "a=%p/b=%p/c=%p/d=%p/f=%p\n", &a, &b, &c, &d, &e );
printf ( " check for 64bit alignment of test result of 0x80 = %d \n", isaligned( 0x80, 64 ));
printf ( " check for 64bit alignment of a result = %d \n", isaligned( &a, 64 ));
printf ( " check for 64bit alignment of d result = %d \n", isaligned( &e, 64 ));
return 0;}
Output:
processor is 8 byte mode
a=0x601038/b=0x60103c/c=0x60103d/d=0x601034/f=0x601030
check for 64bit alignment of test result of 0x80 = 1
check for 64bit alignment of a result = 0
check for 64bit alignment of d result = 0
How does a read of a char happen in the above case ? Does it read from 8 byte aligned boundary (in my case 0x601030 ) and then go to 0x60103c ?
Memory access granularity is always word size isn't it ?
Thx.
1) Yes, there is no guarantee that unaligned accesses are atomic, because [at least sometimes, on certain types of processors] the data may be written as two separate writes - for example if you cross over a memory page boundary [I'm not talking about 4KB pages for virtual memory, I'm talking about DDR2/3/4 pages, which is some fraction of the total memory size, typically 16Kbits times whatever the width is of the actual memory chip - which will vary depending on the memory stick itself]. Equally, on other processors than x86, you get a trap for reading unaligned memory, which would either cause the program to abort, or the read be emulated in software as multiple reads to "fix" the unaligned read.
2) You could always make an unaligned memory region by something like this:
char *ptr = malloc(sizeof(long long) * number+1);
long long *unaligned = (long long *)&ptr[2];
for(i = 0; i < number; i++)
temp = unaligned[i];
By the way, your alignment check checks if the address is aligned to 64 bytes, not 64 bits. You'll have to divide by 8 to check that it's aligned to 64 bits.
3) A char is a single byte read, and the address will be on the actual address of the byte itself. The actual memory read performed is probably for a full cache-line, starting at the target address, and then cycling around, so for example:
0x60103d is the target address, so the processor will read a cache line of 32 bytes, starting at the 64-bit word we want: 0x601038 (and as soon as that's completed the processor goes on to the next instruction - meanwhile the next read will be performed to fill the cacheline), then cacheline is filled with 0x601020, 0x601028, 0x601030. But should we turn the cache off [if you want your 3GHz latest x86 processor to be slightly slower than a 66MHz 486, disabling the cache is a good way to achieve that], the processor would just read one byte at 0x60103d.
4) Not on x86 processors, they have byte addressing - but for normal memory, reads are done on a cacheline basis, as explained above.
Note also that "may not be atomic" is not at all the same as "will not be atomic" - so you'll probably have a hard time making it go wrong by will - you really need to get all the timings of two different threads just right, and straddle cachelines, straddle memory page boundaries, and so on to make it go wrong - this will happen if you don't want it to happen, but trying to make it go wrong can be darn hard [trust me, I've been there, done that].
It probably doesn't, outside of those cases.
In assembly it's trivial. Something like:
.org 0x2
myglobal:
.word SOME_NUMBER
But on Intel, the processor can safely read unaligned memory. It might not be atomic, but that might not be apparent from the generated code.
Intel, right? The Intel ISA has single-byte read/write opcodes. Disassemble your program and see what it's using.
Not necessarily - you might have a mismatch between memory word size and processor word size.
1) This answer is platform-specific. In general, though, the compiler will align variables unless you force it to do otherwise.
2) The following will require two reads to load one variable when run on a 32-bit CPU:
uint64_t huge_variable;
The variable is larger than a register, so it will require multiple operations to access. You can also do something similar by using packed structures:
struct unaligned __attribute__ ((packed))
{
char buffer[2];
int unaligned;
char buffer2[2];
} sample_struct;
3) This answer is platform-specific. Some platforms may behave like you describe. Some platforms have instructions capable of fetching a half-register or quarter-register of data. I recommend examining the assembly emitted by your compiler for more details (make sure you turn off all compiler optimizations first).
4) The C language allows you to access memory with byte-sized granularity. How this is implemented under the hood and how much data your CPU fetches to read a single byte is platform-specific. For many CPUs, this is the same as the size of a general-purpose register.
The C standards guarantee that malloc(3) returns a memory area that complies to the strictest alignment requirements, so this just can't happen in that case. If there are unaligned data, it is probably read/written by pieces (that depends on the exact guarantees the architecture provides).
On some architectures unaligned access is allowed, on others it is a fatal error. When allowed, it is normally much slower than aligned access; when not allowed the compiler must take the pieces and splice them together, and that is even much slower.
Characters (really bytes) are normally allowed to have any byte address. The instructions working with bytes just get/store the individual byte in that case.
No, memory access is according to the width of the data. But real memory access is in terms of cache lines (read up on CPU cache for this).
Non-aligned objects can never come into existence without you invoking undefined behavior. In other words, there is no sequence of actions, all having well-defined behavior, which a program can take that will result in a non-aligned pointer coming into existence. In particular, there is no portable way to get the compiler to give you misaligned objects. The closest thing is the "packed structure" many compilers have, but that only applies to structure members, not independent objects.
Further, there is no way to test alignedness in portable C. You can use the implementation-defined conversions of pointers to integers and inspect the low bits, but there is no fundamental requirement that "aligned" pointers have zeros in the low bits, or that the low bits after conversion to integer even correspond to the "least significant" bits of the pointer, whatever that would mean. In other words, conversions between pointers and integers are not required to commute with arithmetic operations.
If you really want to make some misaligned pointers, the easiest way to do it, assuming alignof(int)>1, is something like:
char buf[2*sizeof(int)+1];
int *p1 = (int *)buf, *p2 = (int *)(buf+sizeof(int)+1);
It's impossible for both buf and buf+sizeof(int)+1 to be simultaneously aligned for int if alignof(int) is greater than 1. Thus at least one of the two (int *) casts gets applied to a misaligned pointer, invoking undefined behavior, and the typical result is a misaligned pointer.

If a pointer's address is 64 bits, why does it appear as 6 bytes when printed?

#include <stdio.h>
int main(void){
int *ptr;
printf("the value of ptr is %p",ptr);
}
This gives me 0x7fffbd8ce900, which is only 6 bytes. Should it be 8 bytes (64bit)?
Although a pointer is 64 bits, current processors actually only support 48 bits, so the upper two bytes of an address are always either 0000 or (due to sign-extension) FFFF.
In the future, if 48 bits is no longer enough, new processors can add support for 56-bit or 64-bit virtual addresses, and existing programs will be able to utilize the additional space since they're already using 64-bit pointers.
That just means the first two bytes are zero (which, incidentally, is currently guaranteed for x86-64 chips—but that doesn't mean anything in this case, since your pointer is not initialized). %p is allowed to truncate leading zeroes, just like any other numeric type. %016p, however, is not. This should work fine:
printf("the value of ptr is %016p", ptr);
Because the 6-bytes address is just the virtual address(offset of the actual physical address). In physical architecture(X86 for instance), memory is divided into portions that may be addressed by a single index register without changing a 16-bit segment selector. In real mode of X86-CPU, a segment is always using 16-bit(2-bytes) segment-selector, which will dynamically decided by the Operating-System at the very beginning when your program started to run(i.e. creating actual running process).
Hence, if your variable have the 48-bit address 0x7fffbd8ce900, and your program have the segment-selector offset 08af, and the real address of the variable is (0x08af<<48)+0x7fffbd8ce900 = 0x08af7fffbd8ce900, which is 64-bit.
further reading pls turn to:
x86 memory segmentation

Resources