Search code examples
cclangwebassembly

How do I fix "error: Linking globals named 'absv': symbol multiply defined!"


I am trying to build a game with C and WASM, and I am running into a very nondescript error.

Result of running make -s:

error: Linking globals named 'absv': symbol multiply defined!

(absv is my custom abs() function (I renamed it from abs to absv in an attempt to fix the problem), because I can't use the standard library)

I would appreciate any help, because I have been at this for way too long.

This might be a lot...

Makefile:

all: main

CC = clang
LLCC = llc
LINK = llvm-link
WASMLD = wasm-ld
override CLINKFLAGS += -nostdinc -nostdlib -fno-stack-protector -O3 --target=wasm32 -emit-llvm -c -S -Wno-pragma-once-outside-header
override LLCCFLAGS += -exception-model=wasm -march=wasm32 -filetype=obj
override LINKFLAGS += -S -v -o out.ll *.ll
override WASMLDFLAGS += --no-entry --export-all --allow-undefined --lto-O3 -O3 -z stack-size=8192

SRCS = $(shell find . -name '.ccls-cache' -type d -prune -o -type f -name '*.c' -print)

main: $(SRCS)
    rm -rf *.ll
    $(CC) $(CLINKFLAGS) $(SRCS)
    $(LINK) $(LINKFLAGS)
    $(LLCC) $(LLCCFLAGS) out.ll
    $(WASMLD) $(WASMLDFLAGS) -o out.wasm out.o

clean:
    rm -rf *.o *.s *.ll out.wasm

script.js:

(async () => {
  let buffer = [];
  const game = document.getElementById("game");
  const gamectx = game.getContext("2d");
    const width = window.innerWidth;
  const height = window.innerHeight;
  const screen = game.getContext("2d").getImageData(0, 0, game.width, game.height);
  const assembly = await WebAssembly.instantiateStreaming(fetch("./out.wasm"), {
    env: {
      init: (none) => {},
      getWidth: (none) => {
        return game.width;
      },
      getHeight: (none) => {
        return game.height;
      },
        drawPixelToBuffer: (x, y, color) => {
              buffer.push([(color & 0b000000000000000011111111), (color >> 8) & 0b000000000000000011111111, (color >> 16) & 0b000000000000000011111111]);
      },
      drawFrame: (none) => {
        for (let i = 0; i < buffer.length; i++) {
          screen.data[i * 4] = buffer[i][0];
          screen.data[i * 4 + 1] = buffer[i][2];
          screen.data[i * 4 + 2] = buffer[i][3];
          screen.data[i * 4 + 3] = 255;
        }
        gamectx.putImageData(screen, 0, 0);
        buffer = [];
      }
      }
    });
    const c = assembly.instance.exports;
    c.start();
})();

main.c:

#include "types.h"
#include "js.h"

#include "render2d.c"

void start() {
  init();
  for (i32 y = 0; y < getHeight(); y++) {
    for (i32 x = 0; x < getWidth(); x++) {
      drawPixelToBuffer((color) {0, 0, 0});
    }
  }
  drawFrame();
  return;
}

types.h:

#pragma once

// Primatives
typedef unsigned char b;
typedef char i8;
typedef short int i16;
typedef int i32;
typedef long int i64;
typedef unsigned char u8;
typedef unsigned short int u16;
typedef unsigned int u32;
typedef unsigned long int u64;
typedef float f32;
typedef double f64;

// Customs
typedef struct {
  u8 red;
  u8 green;
  u8 blue;
} color;
typedef color texture[64][64];
typedef struct {
  u16 x;
  u16 y;
} pt2;
typedef struct {
  f64 x;
  f64 y;
  f64 z;
} pt3;
typedef struct {
  pt2 p1;
  pt2 p2;
  pt3 p3;
  texture texture;
} tri2;
typedef struct {
  pt3 p1;
  pt3 p2;
  pt3 p3;
  texture texture;
} tri3;

js.h:

#pragma once

#include "types.h"

extern void init();
extern i32 getWidth();
extern i32 getHeight();
extern void drawPixelToBuffer(color color);
extern void drawFrame();

render2d.c:

#pragma once

#include "types.h"
#include "js.h"
#include "math/sqrt.c"
#include "math/expn.c"
#include "math/absv.c"

void drawTri2(tri2 tri) {
  // Get all points on a line
  pt2 line1[(i32) sqrt(expn(absv(tri.p1.x - tri.p2.x), 2) + expn(absv(tri.p1.y - tri.p2.y), 2))];
  pt2 line2[(i32) sqrt(expn(absv(tri.p2.x - tri.p3.x), 2) + expn(absv(tri.p2.y - tri.p3.y), 2))];
  pt2 line3[(i32) sqrt(expn(absv(tri.p1.x - tri.p3.x), 2) + expn(absv(tri.p1.y - tri.p3.y), 2))];
}

math/absv.c:

#pragma once

#include "../types.h"

f64 absv(f64 n) {
  return n < 0 ? -n : n;
}

math/eexp.c:

#pragma once

#include "../types.h"
#include "absv.c"

f64 eexp(f64 n) {
  f64 a = 1.0;
  f64 o = 0;
  for (i32 i = 1; o != o + a ; i++) {
    o += a;
    a = a * absv(n) / i;
  }
  return n < 0 ? 1 / o : o;
}

math/expn.c:

#pragma once

#include "../types.h"
#include "nthroot.c"

f64 expn(f64 n, f64 p) {
  return nthroot(p, n);
}

math/nlog.c:

#pragma once

#include "../types.h"

f64 nlog(f64 n) {
    f64 oldSum = 0.0;
    f64 denom = 1.0;
    f64 frac = (n - 1) / (n + 1);
    f64 term = frac;
    f64 sum = term;

    while (sum != oldSum) {
        oldSum = sum;
        denom += 2.0;
        frac *= ((n - 1) / (n + 1)) * ((n - 1) / (n + 1));
        sum += frac / denom;
    }
    return sum * 2.0;
  }

math/nthroot.c:

#pragma once

#include "../types.h"
#include "eexp.c"
#include "nlog.c"

f64 nthroot(f64 r, f64 n) {
  return eexp(nlog(n) / r);
}

math/sqrt.c:

#pragma once

#include "../types.h"
#include "nthroot.c"

f64 sqrt(f64 n) {
  return nthroot(2.0, n);
}

Solution

  • Retired Ninja had it right: Don't do #include "absv.c"

    You do that in two .c files:

    1. render2d.c
    2. math/eexp.c

    They both define absv so you get the "multiply defined" error.

    Don't have any .c file include other .c files. You've already tried to fix this with [numerous] #pragma once. Get rid of them.

    What you want is a .h file that defines prototypes for functions. You already do this in js.h

    So, as an example, create math/math.h:

    #include "../types.h"
    
    f64 absv(f64 n);
    f64 eexp(f64 n);
    f64 expn(f64 n, f64 p);
    f64 nlog(f64 n);
    f64 nthroot(f64 r, f64 n);
    f64 sqrt(f64 n);
    

    Then, every .c file should do just:

    #include "math/math.h"
    

    instead of the various:

    #include "math/sqrt.c"
    

    Of course, now you have to compile each .c file separately (e.g. in the math directory):

    cc -c sqrt.c
    cc -c absv.c
    ...
    

    Then, you link together the various .o files you've created:

    cc -o render2d render2d.o math/sqrt.o math/absv.o ...
    

    I may not have got the directory hierarchies strictly right. Adjust to suit ;-)