making your own malloc function in C - c

I need your help in this. I have an average knowledge of C and here is the problem. I am about to use some benchmarks to test some computer architecture stuff (branch misses, cache misses) on a new processor. The thing about it is that benchmarks are in C but I must not include any library calls. For example, I cannot use malloc because I am getting the error
"undefined reference to malloc"
even if I have included the library. So I have to write my own malloc. I do not want it to be super efficient - just do the basics. As I am thinking it I must have an address in memory and everytime a malloc happens, I return a pointer to that address and increment the counter by that size. Malloc happens twice in my program so I do not even need large memory.
Can you help me on that? I have designed a Verilog and do not have so much experience in C.
I have seen previous answers but all seem too complicated for me. Besides, I do not have access to K-R book.
Cheers!
EDIT: maybe this can help you more:
I am not using gcc but the sde-gcc compiler. Does it make any difference? Maybe that's why I am getting an undefined reference to malloc?
EDIT2:
I am testing a MIPS architecture:
I have included:
#include <stdlib.h>
and the errors are:
undefined reference to malloc
relocation truncated to fit: R_MIPS_26 against malloc
and the compiler command id:
test.o: test.c cap.h
sde-gcc -c -o test.s test.c -EB -march=mips64 -mabi=64 -G -O -ggdb -O2 -S
sde-as -o test.o test.s EB -march=mips64 -mabi=64 -G -O -ggdb
as_objects:=test.o init.o
EDIT 3:
ok, I used implementation above and it runs without any problems. The problem is that when doing embedded programming, you just have to define everything you are using so I defined my own malloc. sde-gcc didn't recognize the malloc function.

This is a very simple approach, which may get you past your 2 mallocs:
static unsigned char our_memory[1024 * 1024]; //reserve 1 MB for malloc
static size_t next_index = 0;
void *malloc(size_t sz)
{
void *mem;
if(sizeof our_memory - next_index < sz)
return NULL;
mem = &our_memory[next_index];
next_index += sz;
return mem;
}
void free(void *mem)
{
//we cheat, and don't free anything.
}
If required, you might need to align the memory piece you hand back, so e.g. you always
give back memory addresses that's on an address that's a multiple of 4, 8, 16 or whatever you require.

Trying a thread safe nos answer given above, I am referring his code with some changes as below:
static unsigned char our_memory[1024 * 1024]; //reserve 1 MB for malloc
static size_t next_index = 0;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void *malloc(size_t sz)
{
void *mem;
pthread_mutex_lock(&lock);
if(sizeof our_memory - next_index < sz){
pthread_mutex_unlock(&lock);
return NULL;
}
mem = &our_memory[next_index];
next_index += sz;
pthread_mutex_unlock(&lock);
return mem;
}
void free(void *mem)
{
//we cheat, and don't free anything.
}

You need to link against libc.a or the equivilent for your system. If you don't use the standard C lib you won't get any of the startup code that runs before the main function either. Your program will never run....
You could either allocate a block of static data and use that in the place of malloc, like:
// char* fred = malloc(10000);
// equals
static char [100000] fred;
or call the standard malloc for a large block of continuous memory on startup and write yr own malloc type function to divide that down. In the 2nd case you would start benchmarking after the calling the system's malloc as to not effect the benchmarks.

I am sharing the complete approach for Malloc and free it works on every scenario. This is complimented using array. We can also implement using link list for metadata.
There are three Scenarios We have to Cover
Continuous Memory allocation: Allocate memory in continuous manner
Allocated memory between two allocated memory: When Memory is free to allocate in between two allocated memory block. we have to use that memory chunk for allocation.
Allocated from Initial block When Initial block is free.
for detailed You can see in diagram. Diagram for allocating algo of memory
Source code for malloc
#define TRUE 1
#define FALSE 0
#define MAX_ALOCATION_ALLOWED 20
static unsigned char our_memory[1024 * 1024];
static int g_allocted_number = 0;
static int g_heap_base_address = 0;
typedef struct malloc_info
{
int address;
int size;
}malloc_info_t;
malloc_info_t metadata_info[MAX_ALOCATION_ALLOWED] ={0};
void* my_malloc(int size)
{
int j =0;
int index = 0 ;
int initial_gap =0;
int gap =0;
int flag = FALSE;
int initial_flag = FALSE;
void *address = NULL;
int heap_index = 0;
malloc_info_t temp_info = {0};
if(g_allocted_number >= MAX_ALOCATION_ALLOWED)
{
return NULL;
}
for(index = 0; index < g_allocted_number; index++)
{
if(metadata_info[index+1].address != 0 )
{
initial_gap = metadata_info[0].address - g_heap_base_address; /*Checked Initial Block (Case 3)*/
if(initial_gap >= size)
{
initial_flag = TRUE;
break;
}
else
{
gap = metadata_info[index+1].address - (metadata_info[index].address + metadata_info[index].size); /*Check Gap Between two allocated memory (Case 2)*/
if(gap >= size)
{
flag = TRUE;
break;
}
}
}
}
if(flag == TRUE) /*Get Index for allocating memory for case 2*/
{
heap_index = ((metadata_info[index].address + metadata_info[index].size) - g_heap_base_address);
for(j = MAX_ALOCATION_ALLOWED -1; j > index+1; j--)
{
memcpy(&metadata_info[j], &metadata_info[j-1], sizeof(malloc_info_t));
}
}
else if (initial_flag == TRUE) /*Get Index for allocating memory for case 3*/
{
heap_index = 0;
for(j = MAX_ALOCATION_ALLOWED -1; j > index+1; j--)
{
memcpy(&metadata_info[j], &metadata_info[j-1], sizeof(malloc_info_t));
}
}
else /*Get Index for allocating memory for case 1*/
{
if(g_allocted_number != 0)
{
heap_index = ((metadata_info[index -1].address + metadata_info[index-1].size) - g_heap_base_address);
}
else /* 0 th Location of Metadata for First time allocation*/
heap_index = 0;
}
address = &our_memory[heap_index];
metadata_info[index].address = g_heap_base_address + heap_index;
metadata_info[index].size = size;
g_allocted_number += 1;
return address;
}
Now Code for Free
void my_free(int address)
{
int i =0;
int copy_meta_data = FALSE;
for(i = 0; i < g_allocted_number; i++)
{
if(address == metadata_info[i].address)
{
// memset(&our_memory[metadata_info[i].address], 0, metadata_info[i].size);
g_allocted_number -= 1;
copy_meta_data = TRUE;
printf("g_allocted_number in free = %d %d\n", g_allocted_number, address);
break;
}
}
if(copy_meta_data == TRUE)
{
if(i == MAX_ALOCATION_ALLOWED -1)
{
metadata_info[i].address = 0;
metadata_info[i].size = 0;
}
else
memcpy(&metadata_info[i], &metadata_info[i+1], sizeof(malloc_info_t));
}
}
For testing Now Test code is
int main()
{
int *ptr =NULL;
int *ptr1 =NULL;
int *ptr2 =NULL;
int *ptr3 =NULL;
int *ptr4 =NULL;
int *ptr5 =NULL;
int *ptr6 =NULL;
g_heap_base_address = &our_memory[0];
ptr = my_malloc(20);
ptr1 = my_malloc(20);
ptr2 = my_malloc(20);
my_free(ptr);
ptr3 = my_malloc(10);
ptr4 = my_malloc(20);
ptr5 = my_malloc(20);
ptr6 = my_malloc(10);
printf("Addresses are: %d, %d, %d, %d, %d, %d, %d\n", ptr, ptr1, ptr2, ptr3, ptr4, ptr5, ptr6);
return 0;
}

Related

Why does this code segfault on one machine but run fine on another?

This code segfaults on line 97 (according to gdb) on one machine (Linode) yet runs just fine on a different machine (personal) and I haven't really been able to figure out why. I tried ensuring that the heap was extended properly via sbrk but that still didn't seem to fix the issue. If anyone wouldn't mind explaining what I did wrong, I'd really appreciate it.
`
#define _DEFAULT_SOURCE
#define _BSD_SOURCE
#include <stdio.h>
#include <math.h>
typedef struct block // This is a node in a linked list representing various sections of memory
{
int size; // size of the block of allocated memory
int free; // whether the block is free and can be reallocated
struct block* next; // pointer to the next block in the linked list
char end[1]; // end represents the end of the header block struct
} block_t;
#define STRUCT_SIZE sizeof(block_t)
// --- Global variables
block_t* head = NULL; // Pointer to head of the linked list
block_t* lastVisited = NULL; // Pointer to the last visited node
void* brkPoint = NULL; // This is a pointer to the empty space on heap
// findBlock: Iterates through all blocks of memory until it is able to return a block able to contain a node of size size.
// headptr: Head of the linked list of blocks
// size: Size of the memory section being claimed
block_t* findBlock(block_t* headptr, unsigned int size) {
block_t* ptr = headptr;
while (ptr != NULL) {
if (ptr->size >= (size + STRUCT_SIZE) && ptr->free == 1) {
return ptr;
}
lastVisited = ptr;
ptr = ptr->next;
}
return ptr;
}
// splitBlock: Given a block ptr, split it into two blocks of size of size and ptr->size - size
// ptr: Pointer to the block being split
// size: Size of the first one of the two blocks
void splitBlock(block_t* ptr, unsigned int size) {
block_t* newBlock;
newBlock = ptr->end + size;
newBlock->size = ptr->size - size - STRUCT_SIZE;
newBlock->free = 1;
newBlock->next = ptr->next;
ptr->size = size;
ptr->free = 0;
ptr->next = newBlock;
}
// Increase amount of memory the program uses from the heap
// lastVisitedPtr: Pointer to the beginning of free heap (end of the program heap)
// size: The amount that you want to increase
block_t* increaseAllocation(block_t* lastVisitedPtr, unsigned int size) {
brkPoint = sbrk(0);
block_t* curBreak = brkPoint; //Current breakpoint of the heap
if (sbrk(size + STRUCT_SIZE) == (void*)-1) {
return NULL;
}
curBreak->size = (size + STRUCT_SIZE) - STRUCT_SIZE;
curBreak->free = 0;
curBreak->next = NULL;
lastVisitedPtr->next = curBreak;
if (curBreak->size > size)
splitBlock(curBreak, size);
return curBreak;
}
// malloc: A custom implementation of malloc, behaves exactly as expected
// _size: the amount of memory to be allocated into a block
// returns void*, a pointer to the block
void* mymalloc(size_t _size) {
void* brkPoint1; // New heap breakpoint
unsigned int size = _size;
int memoryNeed = size + STRUCT_SIZE; // Total size needed, including metadata
block_t* ptr; // ptr to new block
brkPoint = sbrk(0); // Set breakpoint to heap
if (head == NULL) { // If being run for the first time
if (sbrk(memoryNeed) == (void*)-1) { // If you cannot allocate enough memory, return null
return NULL;
}
brkPoint1 = sbrk(0); // Set new breakpoint to heap
head = brkPoint; // Head is at heap
head->size = memoryNeed - STRUCT_SIZE;
head->free = 0; // Head is no longer free
head->next = NULL; // No next
ptr = head; // Return pointer is head
printf("Malloc %zu bytes\n", size);
return ptr->end; // Return end of the metadata pointer (AKA beginning of allocated memory)
}
else { // Head exists
block_t* freeBlock = NULL;
freeBlock = findBlock(head, size); // Find a block that can fit size
if (freeBlock == NULL) {
freeBlock = increaseAllocation(lastVisited, size); // Increase heap and create new block
if (freeBlock == NULL) {
return NULL;
}
printf("Malloc %zu bytes\n", size);
return freeBlock->end;
}
else { // Free block with size > _size exists, claim it
if (freeBlock->size > size) { // If block's size is > size, split it down to size
splitBlock(freeBlock, size);
}
}
printf("Malloc %zu bytes\n", size);
return freeBlock->end;
}
}
// myfree: Sets block referenced by pointer to be free and merges consecutive blocks
void myfree(void* ptr) {
block_t* toFree;
toFree = ptr - STRUCT_SIZE;
if (toFree >= head && toFree <= brkPoint) {
toFree->free = 1;
printf("Freed %zu bytes\n", toFree->size);
}
}
#define ARRAY_ELEMENTS 1024
int main() {
// Allocate some data
int *data = (int *) mymalloc(ARRAY_ELEMENTS * sizeof(int));
// Do something with the data
int i = 0;
for (i = 0; i < ARRAY_ELEMENTS; i++) {
data[i] = i;
}
// Free the data
myfree(data);
return 0;
}
`
As mentioned above, I tried debugging with gdb and expanding the heap with sbrk, but that didn't fix the issue. I have no idea why it would run fine on my personal machine but not on a machine hosted elsewhere. Thanks a lot for checking this out
There is a ton of warnings which you should fix.
This one in particular is likely to cause crashes:
t.c:61:16: warning: implicit declaration of function ‘sbrk’.
Why is this likely to cause a crash?
Without a prototype, the C compiler is required (by the standard) to assume that the function returns an int.
On 32-bit platforms, this typically doesn't cause a problem because sizeof(int) == sizeof(void*) == 4.
But on 64-bit platforms sizeof(int) == 4 and sizeof(void*) == 8. Thus assigning void *p = sbrk(0); without a prototype may result in the pointer having only the low 4 bytes of the returned address; and that is likely to produce a crash when that pointer is dereferenced.
When I add missing #include <unistd.h> (where the prototype for sbrk is), the crash goes away.
In general you should always compile with -Wall -Wextra and fix resulting warnings. The compiler will often tell you about bugs, and save you a lot of debugging time.

How is printf avoiding a segmentation fault?

--Important Edit--
Thanks for the tip on compiling with -fsanitize=address -g, it allowed me to track down the problem. I'm almost done and I've isolated the issue (which happens near the top of the cleanup function). To simplify things, why does the following (when compiled with the above flags) fail?
#include <stdio.h>
#include <stdlib.h>
struct pair {
char *left;
char *right;
};
int main() {
struct pair *pairs = malloc(100 * sizeof(*pairs));
for (int x = 0; x < 100; x++) {
printf("%i\n", x);
pairs->left = pairs->right = NULL;
pairs += sizeof(*pairs);
}
return 0;
}
After printing 0-7 on new lines, I get ==9803==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61b000000788 at pc 0x00010cb90d88 bp 0x7ffee306fa90 sp 0x7ffee306fa88...Address 0x61b000000788 is a wild pointer.
--Original Question--
I've been working on a brainfuck interpreter in C, but I keep inconsistently getting a segfault. While trying to debug this for a day, I've done many things which, rather than catching where the problem is, simply cause it not to happen. I think at this point I'm encountering undefined behavior, but after rereading my code multiple times I don't see where it could be happening. All of these things cause the program to work as intended:
Printing a variable amount of characters between the bottom of the function body of cleanup and the top of the function body of execute (including inside the main function), though this isn't always consistent
Compiling with the -g flag for debugging
At the top of the execute function
unsigned char *pointer = (unsigned char*) calloc(30000, 1);
unsigned char *leftbound = pointer, *rightbound = pointer;
rightbound += 29999;
changing 30000 to 1000 and 29999 to 999
I've read the documentation on malloc, realloc, and calloc, and browsed for other answers, and I still can't tell the problem. As far as I can tell, I have no memory leaks (even when I realloc a struct pair*, the memory at the pointers within each struct is not leaked because it is within the char *program block) or other issues. That's why I would provide the minimal answer to reproduce the problem, but I'm beginning to doubt that removing seemingly unrelated parts of my source code will have no effect on it (though I have stripped down my code a lot still).
I'm using Mac OS X 10.14, bash "gcc -o brainfc brainfc.c" OR "clang -o brainfc brainfc.c" to compile, "brainfc mandelbrot.b" to run program.
The mandelbrot.b file can be found here: http://esoteric.sange.fi/brainfuck/utils/mandelbrot/mandelbrot.b
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *program = NULL;
struct pair {
char *left;
char *right;
};
//Reads into global variable program from file
void fileinput(char *filename) {
FILE *fp;
fp = fopen(filename, "rb");
if (fp) {
size_t inputlen = 0;
fseek(fp, 0, SEEK_END);
int filesize = ftell(fp);
rewind(fp);
program = malloc(filesize + 1);
fread(program, filesize, 1, fp);
*(program + filesize) = 0;
fclose(fp);
}
}
//Removes unwanted characters from program, as well as compiling lookup table of pairs
//This happens in a single sweep through the program for efficiency,
//though again this problem might not occur if I optimized for readability
struct pair* cleanup() {
int pairsize = 200;
struct pair *pairs = calloc(pairsize, sizeof(*pairs));
char *src, *dest;
struct pair *buildptr = pairs;
int bracketlevel = 0;
for (src = dest = program; *src; dest += (strchr("<>+-[].,", *src++) != NULL)) {
*dest = *src;
if (*dest == '[') {
bracketlevel++;
while (buildptr->left) {
if (buildptr == pairs + (pairsize - 1) * sizeof(*pairs)) {
pairsize += 100;
pairs = realloc(pairs, pairsize * sizeof(*pairs));
for (int x = 0; x < 100; x++) {
buildptr += sizeof(*pairs);
buildptr->left = buildptr->right = NULL;
}
buildptr -= sizeof(*pairs) * 100;
}
buildptr += sizeof(*pairs);
}
buildptr->left = dest;
} else if (*dest == ']') {
bracketlevel--;
if (bracketlevel < 0) {
return NULL;
}
while (buildptr->right) {
buildptr -= sizeof(*pairs);
}
buildptr->right = dest;
}
}
if (bracketlevel != 0) {
return NULL;
}
*dest = 0;
program = realloc(program, strlen(program) + 1);
return pairs;
}
//Executes program
int execute(struct pair *pairs) {
unsigned char *pointer = (unsigned char*) calloc(30000, 1);
unsigned char *leftbound = pointer, *rightbound = pointer;
rightbound += 29999;
for (char *pc = program; *pc; pc++) {
switch (*pc) {
case '<':
if (pointer == leftbound) return 1;
pointer--;
break;
case '>':
if (pointer == rightbound) return 1;
pointer++;
break;
case '+':
(*pointer)++;
break;
case '-':
(*pointer)--;
break;
case '[':
while (pairs->left != pc) pairs += sizeof(*pairs);
if (!(*pointer)) pc = pairs->right;
break;
case ']':
while (pairs->right != pc) pairs -= sizeof(*pairs);
if (*pointer) pc = pairs->left;
break;
case '.':
printf("%c", *pointer);
break;
case ',':
printf("Inputting 10 (for now)\n");
*pointer = 10;
break;
}
}
return 0;
}
//Parses command line arguments, calls each function in order
int main(int argc, char *argv[]) {
if (argc > 0) {
char *filepath = argv[1];
fileinput(filepath);
}
if (program == NULL) {
printf("Error: File not found\n");
return 3;
}
struct pair *pairs = cleanup();
if (pairs == NULL) {
printf("Error: Invalid program\n");
return 4;
}
int execstatus = execute(pairs);
switch (execstatus) {
case 1:
printf("\nError: Pointer out-of-bounds\n");
return 1;
case 2:
printf("\nError: Byte overflow\n");
return 2;
default:
return 0;
}
}
Any help would be greatly appreciated.
pairs += sizeof(*pairs);
Pointer arithmetic in C is always in units of the type pointed to - here, it's in units of struct pairs. So if you want pairs to point to the next struct pair in the array, add 1. (The compiler will internally translate this into adding the appropriate number of bytes, or however pointers happen to work on your system.) This line should be pairs += 1; or pairs++; or ++pairs; according to your taste.
As it stands, if sizeof(*pairs) happens to be, say, 16 on your system, you are skipping past 15 more struct pairs every time you iterate. You will end up accessing the 0th, 16th, 32nd, ... 1584th struct pair in the array. Since it only contains 100, obviously most of these will be out of bounds. Hence your segfault.
As previously mentioned the usage of pointers is a bit messed up.
Instead of
pairs->left = pairs->right = NULL;
pairs += sizeof(*pairs);
Use
pairs[x].left = pairs[x].right = NULL;
As a bonus you have pairs still intact to do the clean up

C: using realloc for high performance with an array of structs

I am using realloc to adjust the size of an array of structs containing 3 points x, y and z. This struct is encapsulated inside another struct that contains the array, the length of the array and a "reserved" value that is used for a pre-allocation strategy for even faster performance when it is evident that more structs of points will be appended to the struct array.
I am compiling with a Makefile that looks like this:
CFLAGS = -g -Wall
LIBS = -lm
default: echo "You must specify a target, e.g. file1, file2"
file2:
gcc $(CFLAGS) -o $# test.c file2.c $(LIBS)
I have a function to initialize an empty array structure, one to reset the array to be empty and free any dynamically allocated memory, one to append a point to the end of the array and one to remove a point designated by the index value.
I am getting two errors that I cannot find the cause of. One is that my code returns a non-zero status code of 1 and the other is the length seems to be off by one when I append a few thousand points.
I am letting the append function do all the work but if I should be allocating dynamic memory in initialization, please tell me so. I am pretty sure that my reset and remove functions are working as they are supposed to. Please take a look at append as well.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <assert.h>
typedef struct point
{
int x, y, z;
} point_t;
typedef struct
{
// number of points in the array
size_t len;
// pointer to an array of point_t structs
point_t* points;
size_t reserved;
} point_array_t;
void point_array_initial( point_array_t* pa )
{
assert(pa);
pa->len = 0;
pa->reserved = 0;
pa->points=NULL;
}
void point_array_reset( point_array_t* pa )
{//just free the array and set pa to NULL
assert(pa);
pa->points = memset(pa->points, 0, sizeof(point_t)*(pa->len));
pa->len = 0;
pa->reserved=0;
free(pa->points);
pa->points=NULL;
}
int point_array_append( point_array_t* pa, point_t* p )
{
assert(pa);
assert(p);
if(pa == NULL)//something wrong with intialization or reset
{
return 1;
}
if(p == NULL)//nothing to append
{
return 1;
}
//append the first point
if(pa->len == 0)
{
pa->len=1;
pa->reserved=pa->len*2;
pa->points = malloc(sizeof(point_t)* (pa->reserved));
if(pa->points == NULL)//malloc failed
{
return 1;
}
pa->points[pa->len-1].x = p->x;
pa->points[pa->len-1].y = p->y;
pa->points[pa->len-1].z = p->z;
}
if (pa->reserved > pa->len )
{
pa->len+=1;
pa->points[pa->len-1].x = p->x;//insert at index 0
pa->points[pa->len-1].y = p->y;
pa->points[pa->len-1].z = p->z;
}
//when we run out of space in reserved (len has caught up)
else if(pa->reserved == pa->len)
{
pa->len+=1;
pa->reserved=pa->len*2;
pa->points=realloc(pa->points, sizeof(point_t)*(pa->reserved));//doubling size of array
pa->points[pa->len-1].x = p->x;//TODO: change formula to find insertion point
pa->points[pa->len-1].y = p->y;
pa->points[pa->len-1].z = p->z;
}
return 0;
}
int point_array_remove( point_array_t* pa, unsigned int i )
{
assert(pa);
if (i >= pa->len)//out of bounds
{
return 1;
}
if(pa->len==0)//0 elements trying to remove from empty array
{
//pa->len=0;
//free(pa->points);
//pa->points=NULL;
return 1;
}
else if(pa->len ==1)//remove only element
{
pa->len-=1;//no copying required, just shorten
pa->points=realloc(pa->points, sizeof(point_t)*(pa->len));
//free(pa->points);
//pa->points=NULL;
}
else//array size is longer than 1 or 0
{
pa->points[i].x = pa->points[pa->len-1].x;
pa->points[i].y = pa->points[pa->len-1].y;
pa->points[i].z = pa->points[pa->len-1].z;
pa->len-= 1;//shorten array size
pa->reserved = pa->len*2;
pa->points=realloc(pa->points, sizeof(point_t)*(pa->len));//could reallocate for reserve here to increase speed.
}
return 0;
}
an else is missing after the if(pa->len == 0) body in the append function: the first point is appended twice.
Note that you have too many special cases in this function. It can be simplified into just a one test: if the array is too small, reallocate it, and append the point.
Other simplifications are possible:
the test if (pa->len == 0)//0 elements trying to remove from empty array is redundant with the previous one.
take advantage of the fact that realloc(NULL, size) is equivalent to malloc(size) and realloc(p, 0) to free(p), and free(NULL) is OK.
beware that realloc() may fail, even when shrinking the block.
you should only shrink the array when it becomes too sparse, not for every call to point_array_remove.
Here is a simpler version:
#include <assert.h>
#include <stdlib.h>
typedef struct point {
int x, y, z;
} point_t;
typedef struct {
size_t len; // number of valid points in the array
size_t reserved; // allocated number of points in the array
point_t *points; // pointer to an array of point_t structs
} point_array_t;
void point_array_initial(point_array_t *pa) {
assert(pa);
pa->len = 0;
pa->reserved = 0;
pa->points = NULL;
}
void point_array_reset(point_array_t *pa) {
assert(pa);
free(pa->points);
pa->len = 0;
pa->reserved = 0;
pa->points = NULL;
}
int point_array_append(point_array_t *pa, const point_t *p) {
point_t *points;
assert(pa);
assert(p);
// no need to test pa nor p, asserts would already abort
points = pa->points;
if (pa->len >= pa->reserved || points == NULL) {
// reallocate of points array is too small
size_t newsize = pa->reserved;
if (newsize < pa->len)
newsize = pa->len;
if (newsize < 1)
newsize = 1;
newsize += newsize;
points = realloc(points, newsize * sizeof(*points);
if (points == NULL)
return 1;
pa->points = points;
pa->reserved = newsize;
}
// append point structure
points[pa->len++] = *p;
return 0;
}
int point_array_remove(point_array_t *pa, unsigned int i) {
point_t *points;
assert(pa);
if (i >= pa->len || pa->points == NULL) { //out of bounds or invalid array
return 1;
}
if (pa->len - i > 1) {
memmove(&pa->points + i, &pa->points + i + 1,
sizeof(*pa->points) * (pa->len - i - 1));
}
pa->len--;
if (pa->reserved >= pa->len * 3) {
size_t newsize = pa->len * 2;
// shorten the array with care.
// note that the array will be freed when it becomes empty
// no special case needed.
points = realloc(pa->points, sizeof(*points) * newsize);
if (points != NULL) {
pa->points = points;
pa->reserved = newsize;
}
}
return 0;
}
In addition to the error pointed out by chqrlie, here are a few additional thoughts on your code.
A better choice of CFLAGS for non-debug builds would be
-Wall -Wextra -O3
add -pedantic for a few additional warnings and you can use -Ofast with gcc >= 4.6.
Never realloc the pointer itself, If realloc fails, NULL is returned and you have lost the reference to your original memory block -- and created a memory leak because you no longer have the beginning address of the block to free. Don't increment len or reserved until you validate realloc succeeded. Instead, always use a temporary pointer and increment values only on success, e.g.
else if(pa->reserved == pa->len)
{
void *tmp = realloc(pa->points, sizeof(point_t)*(pa->len + 1) * 2);
if (!tmp) {
/* handle error - exit or return */
}
pa->points = tmp;
pa->len+=1;
pa->reserved=pa->len*2;
}
The following looks like a problem if you are simply wanting to shorten the array by one:
else if(pa->len ==1)//remove only element
{
pa->len-=1;//no copying required, just shorten
pa->points=realloc(pa->points, sizeof(point_t)*(pa->len));
//free(pa->points);
//pa->points=NULL;
}
else//array size is longer than 1 or 0
{
pa->points[i].x = pa->points[pa->len-1].x;
pa->points[i].y = pa->points[pa->len-1].y;
pa->points[i].z = pa->points[pa->len-1].z;
pa->len-= 1;//shorten array size
pa->reserved = pa->len*2;
pa->points=realloc(pa->points, sizeof(point_t)*(pa->len));//could reallocate for reserve here to increase speed.
}
In the else above you are assigning the previous point to the last, then chopping off the last -- either I don't understand what you are trying to accomplish, or it's not doing what you think it is. In either case, unless you have some compelling reason for wanting to realloc to shorten the array by one (I'd wait until all add/remove operations are done and then call a final realloc on len element to exactly size your memory use). Instead, I would replace the entirety of the above with:
else
pa->len -= 1;
No need to mess with anything else. You effectively ignore the data in the last row -- which isn't hurting anything, until your next add overwrites the values.

C assign value to double pointer

I am working on some C code on micro processor stm32f103. Since allocating memory from the heap isn't stable, I am not encouraged to use the C library functions malloc() and free() etc. Instead, I thought of declaring a large chunk of static memory in advance during compilation time, and reallocating the memory to suit my pseudo dynamic memory allocation purposes. My new malloc implementation works fine when testing on my computer, but crashes on the stm32 when I do malloc for double data type.
Here is my malloc implementation. I know it is not a real dynamic memory allocation, but i do it just to practice using pointers.
pk_malloc.c
#include "pk_malloc.h"
char pool[RESERVE];
void* alloc[RESERVE];
void mem_init()
{
for (int i = 0; i != RESERVE; i++)
{
alloc[i] = NULL;
}
}
void* mem_malloc(size_t size)
{
if (size > 0)
{
for (int i = 0; i != RESERVE; i++)
{
if (alloc[i] == NULL)
{
int end;
for (end = i; end != RESERVE; end++)
{
if (alloc[end] != NULL || end - i == size + 1)
{
break;
}
}
if (end - i == size + 1)
{
for (int k = i + 1; k != end; k++)
{
alloc[k] = &pool[k];
}
return alloc[i + 1];
}
}
}
}
return NULL;
}
void* mem_realloc(void* mem, size_t new_size)
{
if (mem == NULL)
{
return mem_malloc(new_size);
}
int old_size = 0;
void** alloc_t = &alloc[(char*)(mem) - pool];
while (*alloc_t != NULL)
{
old_size++;
alloc_t++;
}
if (new_size <= old_size)
{
mem_free((char*)mem + new_size);
return mem;
}
else
{
int i = alloc_t - alloc;
int size = new_size - old_size;
int end;
for (end = i; end != RESERVE; end++)
{
if (alloc[end] != NULL || end - i == size + 1)
{
break;
}
}
if (end - i == size + 1)
{
for (int k = i; k != end - 1; k++)
{
alloc[k] = &pool[k];
}
return alloc[i];
}
else
{
void* realloc_t = mem_malloc(new_size);
if (realloc_t == NULL)
{
return mem;
}
else
{
mem_copy(realloc_t, mem);
mem_free(mem);
return realloc_t;
}
}
}
}
void mem_copy(void* dest, void* source)
{
int dest_index = (char*)(dest) - pool;
int source_index = (char*)(source) - pool;
char* writer = (char*)(source);
while (alloc[source_index] != NULL && alloc[dest_index] != NULL)
{
pool[dest_index] = pool[source_index];
dest_index++;
source_index++;
}
}
void mem_free(void* mem)
{
if (mem != NULL)
{
void** alloc_t = &alloc[(char*)(mem) - pool];
while (*alloc_t != NULL)
{
*alloc_t = NULL;
alloc_t++;
}
}
}
pk_malloc.h
#ifndef _PK_MALLOC
#define _PK_MALLOC
#include <stdlib.h>
#define RESERVE 64
void mem_init();
void* mem_malloc(size_t size);
void* mem_realloc(void* mem, size_t new_size);
void mem_copy(void* dest, void* source);
void mem_free(void* mem);
#endif
main.c
int main()
{
mem_init();
int* hoho = (int*)(mem_malloc(sizeof(int)));
*hoho = 123;
printf("%d", *hoho);
mem_free(hoho);
}
The code works on my computer, and also works on the STM32. However when I change my datatype to a double:
int main()
{
mem_init();
double* hoho = (double*)(mem_malloc(sizeof(double)));
*hoho = 0.618;
printf("%f", *hoho);
mem_free(hoho);
}
It only works on my computer, while it crashed on the STM32.
I did some testing and debugs, I find that this line works, the pointer is not NULL and it has a valid address.
double* hoho = (double*)(mem_malloc(sizeof(double)));
However this line crashed.
*hoho = 0.618;
After more testing, I find any data types occupying more than 4 bytes crashes the same way, including long long etc.
Strangely, I made some user defined structs with lots of int, float data types etc, which definitely occupies more than 4 bytes, the code works fine on the STM32.
struct ABC
{
int a;
float b;
};
This line works no problems.
struct ABC* hoho = (struct ABC*)(mem_malloc(sizeof(struct ABC)));
The variable hoho can be assigned, and its members can be accessed without ease, working both on my computer and the STM32.
Please note that all the code works on my laptop, and most data types work for the STM32 too.
I have been stuck with the problem for hours, any help appreciated.
The core in STM32F1 is a Cortex-M3. This QA here points out that while unaligned word access is allowed by Cortex-M3 for simple instructions, not all instructions support unaligned access. In your case, the C compiler uses an instruction that doesn't support an unaligned address with double.
Notice that
the standard library malloc returns a pointer that is "suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated" (C11 7.22.3)
while a pointer may be converted to another pointer, "if the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined" (C11 6.3.2.3p7).
Thus the behaviour of a program is undefined already at these lines
int *hoho = mem_malloc(sizeof(int));
double *hoho = mem_malloc(sizeof(double));
if the pointer returned is not suitably aligned for int and double respectively.
To fix the code, change it so that it always returns pointers of proper alignment. The official ARM compilers maintain an 8-byte aligned heap.

C pointer segmentation cause when tries to walk symbol table of shared library

I was wondering if anyone can think of a reason how a read from a pointer could cause a segmentation when the pointer is:
1. non NULL pointer.
The thing I'm trying to do is walk the shared library and access their symbol table. The segmentation happens when I try to access the ELF hash table to get the amount of symbols within the symbol table.
This is not noticed in x86 platform.
It happens only when executing on MIPS64 platform.
The code are based on from this link:
How to interpret the dynamic symbol table in an ELF executable?
static void
btrace_dl_symtab_walk(struct dl_phdr_info *info,
btrace_dl_lib_t *ctx) {
ElfW(Dyn*) dyn;
ElfW(Sym*) sym = NULL;
ElfW(Word*) hash;
ElfW(Word) sym_cnt = 0;
char* strtab = NULL;
char* sym_name = NULL;
unsigned int i;
int j;
/*
* Make indicator to show all of them acomplished before going forward
*/
for (j = 0; j < info->dlpi_phnum; j++) {
if (info->dlpi_phdr[j].p_type == PT_DYNAMIC) {
dyn = (ElfW(Dyn)*)(info->dlpi_addr + info->dlpi_phdr[j].p_vaddr);
while(dyn->d_tag != DT_NULL) {
if (dyn->d_tag == DT_HASH) {
hash = (ElfW(Word*))dyn->d_un.d_ptr;
if (!hash) {
return;
}
/*
* SEGFAULT happens here
*/
printf("Before Seg Fault\n");
sym_cnt = *(hash + 1); //<=============== This line causes seg fault
printf("Never reached here\n");
} else if(dyn->d_tag == DT_GNU_HASH) {
/*
* Since there is no simply way to find entry count
* in GNU hash table, we have no choice but to
* count by hand
*/
uint32_t *buckets;
uint32_t *hashval;
hash = (ElfW(Word*))dyn->d_un.d_ptr;
buckets = hash + 4 + (hash[2]*sizeof(size_t)/4);
for (i = sym_cnt = 0; i < hash[0]; i++) {
if (buckets[i] > sym_cnt) {
sym_cnt = buckets[i];
}
}
if (sym_cnt) {
sym_cnt -= hash[1];
hashval = buckets + hash[0] + sym_cnt;
do {
sym_cnt++;
} while (!(*hashval++ & 1));
}
sym_cnt += hash[1];
}else if (dyn->d_tag == DT_STRTAB) {
strtab = (char*)dyn->d_un.d_ptr;
} else if (dyn->d_tag == DT_SYMTAB) {
sym = (ElfW(Sym*))dyn->d_un.d_ptr;
break;
}
dyn++;
}
break;
}
}
// Other acitivities
}
Any guidance are welcome. Thank you
I was wondering if anyone can think of a reason how a read from a pointer could cause a segmentation when the pointer is: 1. non NULL pointer.
The fact that pointer is not NULL does not imply that you can read from it. It could be invalid for any number of reasons, e.g.
char *p = mmap(...);
munmap(p, ...);
char c = p[0]; // p points into unmapped memory, SIGSEGV likely.
This is not noticed in x86 platform. It happens only when executing on MIPS64 platform.
Are you using the same version of GLIBC on both platforms?
If I recall correctly, older versions of GLIBC did not relocate DT_HASH, but newer versions do. This may also be architecture-specific.
You'll want to print the value of hash, and compare it to the value of dyn. If hash is small, you'll need to relocate it by info->dlpi_addr.

Resources