9

So I have these three files

Main.c

#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include "support.h"

int main( void ) {
    int* num1 = malloc(100);
    printf("num1: %p", &num1);
}

Support.c

#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "support.h"

void *malloc(size_t size) {
    struct block_meta *block;
    if (size <= 0) {
        return NULL;
    }
    if (!global_base) { // First call.
    block = request_space(NULL, size);
        if (!block) {
            return NULL;
        }
        global_base = block;
    } else {
        struct block_meta *last = global_base;
        block = find_free_block(&last, size);
        if (!block) { // Failed to find free block.
            block = request_space(last, size);
            if (!block) {
                return NULL;
            }
        } else { // Found free block
            block->free = 0;
            block->magic = 0x77777777;
        }
    }
    return(block+1);
}


void free(void *ptr) {
    if (!ptr) {
        return;
    }
    struct block_meta* block_ptr = get_block_ptr(ptr);
    assert(block_ptr->free == 0);
    assert(block_ptr->magic == 0x77777777 || block_ptr->magic == 0x12345678);
    block_ptr->free = 1;
    block_ptr->magic = 0x55555555;
}

void *realloc(void *ptr, size_t size) {
    if (!ptr) {
        // NULL ptr. realloc should act like malloc.
        return malloc(size);
    }
    struct block_meta* block_ptr = get_block_ptr(ptr);
    if (block_ptr->size >= size) {
        // We have enough space. Could free some once we implement split.
        return ptr;
    }
    // Need to really realloc. Malloc new space and free old space.
    // Then copy old data to new space.
    void *new_ptr;
    new_ptr = malloc(size);
    if (!new_ptr) {
        return NULL; // TODO: set errno on failure.
    }
    memcpy(new_ptr, ptr, block_ptr->size);
    free(ptr);
    return new_ptr;
}

void *calloc(size_t nelem, size_t elsize) {
    size_t size = nelem * elsize; // TODO: check for overflow.
    void *ptr = malloc(size);
    memset(ptr, 0, size);
    return ptr;
}

Support.h

#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

void *malloc(size_t size);
void free(void *ptr);
void *realloc(void *ptr, size_t size);


struct block_meta {
    size_t size;
    struct block_meta *next;
    int free;
    int magic; // For debugging only. TODO: remove this in non-debug mode.
};

#define META_SIZE sizeof(struct block_meta)

void *global_base = NULL;

struct block_meta *find_free_block(struct block_meta **last, size_t size) {
    struct block_meta *current = global_base;
    while (current && !(current->free && current->size >= size)) {
        *last = current;
        current = current->next;
    }
    return current;
}

struct block_meta *request_space(struct block_meta* last, size_t size) {
    struct block_meta *block;
    block = sbrk(0);
    void *request = sbrk(size + META_SIZE);
    assert((void*)block == request); // Not thread safe.
    if (request == (void*) -1) {
        return NULL; // sbrk failed.
    }
    if (last) { // NULL on first request.
        last->next = block;
    }
    block->size = size;
    block->next = NULL;
    block->free = 0;
    block->magic = 0x12345678;
    return block;
}

struct block_meta *get_block_ptr(void *ptr) {
    return (struct block_meta*)ptr - 1;
}

However when I attempt to compile using

gcc -o asgn2 main.c support.c

I get the error

/tmp/ccscmcbS.o:(.bss+0x0): multiple definition of `global_base'
/tmp/ccyjhjQC.o:(.bss+0x0): first defined here
/tmp/ccscmcbS.o: In function `find_free_block':
support.c:(.text+0x0): multiple definition of `find_free_block'
/tmp/ccyjhjQC.o:main.c:(.text+0x0): first defined here
/tmp/ccscmcbS.o: In function `request_space':
support.c:(.text+0x55): multiple definition of `request_space'
/tmp/ccyjhjQC.o:main.c:(.text+0x55): first defined here
/tmp/ccscmcbS.o: In function `get_block_ptr':
support.c:(.text+0xfe): multiple definition of `get_block_ptr'
/tmp/ccyjhjQC.o:main.c:(.text+0xfe): first defined here
collect2: error: ld returned 1 exit status

I dont believe that I declared those methods more than once, also it is in a much different format than I am usually given. Not quite sure what it means.

Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
Hunter Tipton
  • 313
  • 1
  • 4
  • 11
  • 1
    Duplicate of http://stackoverflow.com/questions/34748913/lnk2005-error-in-clr-windows-form. That one is on _Win_, but it's the same thing: Definitions inside a header file (included in multiple source files). – CristiFati Mar 24 '16 at 20:56
  • Search for "C header include guards" on google for more info. – Harry Mar 24 '16 at 23:26

5 Answers5

24

The problem is that you have functions and globals defined (as opposed to declared) in your header file. Therefore, those functions are pulled into both main.c and support.c when they are compiled. Then during the linking phase, the linker sees multiple definitions.

Even if you had include guards, it wouldn't help in this case because that only defends against multiple definitions in a single compilation unit, not across multiple units.

Take the definitions of those function out of the header file, replace them with declarations, and put them either in support.c or in a separate .c file.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • Same thing for global variables, `global_base` is defined (declaration with an initial value) in the header, and so it is defined twice. – jdarthenay Mar 24 '16 at 21:23
  • 1
    I'm curios whether this complaint came in with an update of gcc or anything related to it. Because i have a project which used to work 10 years ago. Today (10 years later ) tried to compile it again if failed with this message. I'm pretty sure this project worked back in the day. – KoKlA Jun 22 '20 at 15:30
  • 4
    @KoKlA gcc 10 defaults to `-fno-common` instead of `-fcommon`. (See, e.g., https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85678) – kopecs Jul 26 '21 at 16:06
8

You can use the -fcommon option for gcc.

Beercandyman
  • 81
  • 1
  • 2
  • I don't know why it works, but it works... (my code would compile just fine on mac with clang but not on linux with gcc-11, and this fixed it). – valerio Oct 04 '22 at 15:29
  • @valerio the behavior of gcc has reversed itself in the way it handles uninitialized global variables. It used to be -fcommon, and now behaves as if the -fno-common option has been used. See the gcc man page for details. – Thagomizer Jul 11 '23 at 00:32
1

Make sure that the header is included only once, so add something like the following to the headers source code:

#ifndef _HAVE_SUPPORT_H
#define _HAVE_SUPPORT_H

// ...
// YOUR HEADER SOURCE CODE
// ...


#endif //_HAVE_SUPPORT_H

As I said this makes sure that the header is included only once, because then it defines _HAVE_SUPPORT_H. If now another source tries to include it, it will not do anything because _HAVE_SUPPRORT_H is already defined.

It also helps if you have only function declarations in the header and your 'real' functions will be in another *.c file.

Edit: The second parts is the most important for your problem as @kaylum noticed

mame98
  • 1,271
  • 12
  • 26
  • 3
    Note that include guards do not solve the OP's problem since the header is included in two different .c files. "If now another source tries to include it, it will not do anything" that's not actually correct. Another source file can include the same header and get the same definitions resulting in multiple definitions. The last point is actually the crucial point and hence needs to be worded more strongly (not "might" it has to be a "must" in the OP's case). – kaylum Mar 24 '16 at 21:14
1

For me the solution was simple, downgrade to previous GCC version. Here is comparison gcc installed on two different Ubuntu version.

Because on my case, code was legacy code from about 20 years ago, then it makes sense for me to keep using old compiler.

Deadelina
  • 41
  • 4
1

One more possible solution to make function static.

Fairoz
  • 1,616
  • 13
  • 16