2

I have code that, when I compile on Linux, works fine, but fails on MinGW for Windows.

I'm compiling these four files together. a.h is the header file, b.c, c.c and main.c. Here are the contents.

└──> cat a.h
enum {
  BLACK,
  WHITE
} Colors;
└──> cat b.c
#include "a.h"
#include <stdio.h>

void foo() {
  printf("%d\n", BLACK);
}
└──> cat c.c
#include "a.h"
#include <stdio.h>

void bar() {
  printf("%d\n", WHITE);
}
└──> cat main.c
void foo();
void bar();

int main() {
  foo();
  bar();
  return 0;
}

I compile them with this command:

gcc -c b.c; gcc -c c.c; gcc -c main.c; gcc -o colors b.o c.o main.o

It works fine on my Linux desktop but it fails on a MinGW VM. The error is:

# gcc -c b.c; gcc -c c.c; gcc -c main.c; gcc -o colors b.o c.o main.o
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: c.o:c.c:(.bss+0x0): multiple definition of `Colors'; b.o:b.c:(.bss+0x0): first defined here
collect2.exe: error: ld returned 1 exit status

When I run nm on b.o on Linux, I see this:

└──> nm b.o
0000000000000004 C Colors
0000000000000000 T foo
                 U printf

But on MinGW, I see this:

0000000000000004 B Colors

Somehow gcc is compiling them differently on each system. The B there means that I can't have the same symbol twice, though on Linux I get C and it works fine. From the manpage for nm:

           "B"
           "b" The symbol is in the uninitialized data section (known as BSS).

           "C" The symbol is common.  Common symbols are uninitialized data.  When linking, multiple common symbols may appear with
               the same name.  If the symbol is defined anywhere, the common symbols are treated as undefined references.

How do I fix this so that I am able to compile on MinGW?

Linux uses GCC 9; MinGW probably uses GCC 10.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Eyal
  • 5,728
  • 7
  • 43
  • 70
  • 1
    First question I'd have is why you declare a `enum { BLACK, WHITE } Colors;` to be an object of anonymous enum type? – StoryTeller - Unslander Monica Dec 22 '20 at 17:07
  • This code isn't mine but it's failing to compile. It's here: http://git.geda-project.org/gerbv/tree/src/callbacks.h?id=b5f1eacd798f327ab319af939f89031db4b7c10a#n29 I think that the intent was to have each of the ALL_CAPS names inside the enum act as if #defines with sequential values. – Eyal Dec 23 '20 at 04:11

1 Answers1

6

You seem to have created a variable named Colors of untagged enum type:

enum {
  BLACK,
  WHITE
} Colors;

where you probably meant to just declare an enum type with tag Colors:

enum Colors {
  BLACK,
  WHITE
};

The former is invalid C if it's included in more than one source file since it produces a duplicate definition of Colors. This is allowed but not required to produce an error; implementation details of how different systems' object file formats represent uninitialized global variables cause it to be or not be detected as an error at link time. Modern GCC no longer uses commons even on targets that support them, so this will be an error on Linux too if you update to a new enough GCC.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • On linux I am using gcc-9. I'm not sure what version of gcc is on the MinGW but it might be version 10. When was this change introduced? (BTW, I am compiling code that I did not write so I'm not sure how easily I could modify it.) – Eyal Dec 22 '20 at 18:19
  • 3
    GCC 10 defaults to `-fno-common` but earlier versions default to `-fcommon`. Code abusing the C standard rules (exploiting a 'common extension' documented in the C standard) now fail to link under GCC 10. (See [GCC 10 Changes](https://gcc.gnu.org/gcc-10/changes.html) under the C section (after the C Family section): _GCC now defaults to `-fno-common`. As a result, global variable accesses are more efficient on various targets. In C, global variables with multiple tentative definitions now result in linker errors. With `-fcommon` such definitions are silently merged during linking._) – Jonathan Leffler Dec 22 '20 at 18:49
  • If the header used `typedef enum { BLACK, WHITE } Colors;`, you'd have a type definition for an untagged enumeration type, and would not have linkage errors, and could use `Colors black = WHITE;` in a variable definition, for example. Tagging the enumeration also works, but you'd have to use `enum Colors black = WHITE;` to define a variable. – Jonathan Leffler Dec 22 '20 at 18:55
  • See also [How do I use `extern` to share variables between source files?](https://stackoverflow.com/questions/1433204/how-do-i-use-extern-to-share-variables-between-source-files/1433387#1433387) for a discussion of the 'common extension' (C11 standard, Annex [J.5.11 Multiple external definitions](http://port70.net/~nsz/c/c11/n1570.html#J.5.11)). – Jonathan Leffler Dec 22 '20 at 18:59