Hi everyone !
Lastly, I've looked for a way of determining wether a pointer points to some dynamically allocated memory, or global or automatic variables. And that without simply registering all allocations and checking each time if the pointer is an allocation, which is obviously extremely inefficient. The reason I've been looking for that kind of thing is also pretty obvious, for example, testing a pointer to know if it needs to be freed or not, or anything alike.
And all that, because nowadays, everyone and me first tend to allocate pretty much everything dynamically, which is fine up to some point. As a matter of fact, there are many things that could simply be declared instead of allocated, for instance, a list of all available extensions to some program could be declared statically, or at least, it's header. Anyway.
So this morning I thought of something that works, and is surprisingly simple. For a given pointer, given the layout of a process (at least a Linux process), you just need to compare it to the address of the break before you do any allocation (which you can obtain using sbrk (0), which is declared in <unistd.h>), and also to the address of the stack, which (at least on Linux) grows downward from the highest virtual memory address.
I don't actually have a very particular question, I would just like to have your opinion on wether it is good or bad practice, if there are situations in which it wouldn't work, this kind of things. It obviously isn't really portable, because the layout of a process changes accross operating systems, but it is usually similar and concceptually equivalent, so adapting the code isn't really complicated. Here is the code, by the way :
Code:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
void *my_break = NULL;
extern unsigned int IsStatic (void *ptr);
static unsigned int initialize (void);
static unsigned int finalize (void *ptr, ...);
unsigned int global_static = 0;
unsigned int *global_dynamic = NULL;
static unsigned int initialize (void) {
my_break = sbrk (0);
return 0;
}
static unsigned int finalize (void *ptr, ...) {
va_list arguments;
void *current = NULL;
va_start (arguments, ptr);
for (current = ptr; current != NULL; current = va_arg (arguments, void *))
free (current);
va_end (arguments);
}
int main (int argc, char *argv []) {
unsigned int ret = 0;
ret = initialize ();
if (ret) return finalize (NULL);
global_static = 17;
global_dynamic = calloc (1, sizeof (unsigned int));
if (global_dynamic == NULL) return finalize (NULL);
*global_dynamic = 34;
unsigned int local_static = 18;
unsigned int *local_dynamic = NULL;
local_dynamic = calloc (1, sizeof (unsigned int));
if (local_dynamic == NULL) return finalize (global_dynamic, NULL);
printf ("global_static: %d\n", IsStatic (&global_static));
printf ("global_dynamic: %d\n", IsStatic (global_dynamic));
printf ("local_static: %d\n", IsStatic (&local_static));
printf ("local_dynamic: %d\n", IsStatic (local_dynamic));
ret = finalize (global_dynamic, local_dynamic, NULL);
return ret;
}
And because I don't like inline assembly, and because the function to determine if an address is static or not mostly does things you can only do in assembly, I wrote it directly in assembly, nothing really complicated anyway. Notice it uses System V 64 bits calling convention, which is nearly standard accross Unix-like operating systems.
Code:
[BITS 64]
SECTION .text
GLOBAL IsStatic
EXTERN my_break
IsStatic:
push rbp
mov rbp, rsp
push rdi
mov rax, [my_break]
cmp rdi, rax
jge .L01
mov rax, 0
jmp .L_end
.L01:
cmp rdi, rsp
jbe .L02
mov rax, 0
jmp .L_end
.L02:
mov rax, 1
.L_end:
pop rdi
mov rsp, rbp
pop rbp
ret
Oh and it's for NASM. Just in case, here's how you compile :
Code:
$ nasm -felf64 isstatic.asm
$ gcc -o main main.c isstatic.o
And again, it works, but tell me what you think of it