Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multiple definition of in Nim generated static libraries #19830

Closed
JDragan opened this issue May 25, 2022 · 13 comments
Closed

multiple definition of in Nim generated static libraries #19830

JDragan opened this issue May 25, 2022 · 13 comments

Comments

@JDragan
Copy link

JDragan commented May 25, 2022

Example

lib_a.nim

proc foo(): int32 {.exportc.} = return 24

lib_b.nim

proc bar(): int32 {.exportc.} = return 42

app.c

#include "stdio.h"
int main() {
  printf("%i\n", foo());
  printf("%i\n", bar());
}
nim c -d:release --mm:arc --app:staticlib --noMain --nimMainPrefix:libA --nimcache:ncache --out:libA.a lib_a.nim
nim c -d:release --mm:arc --app:staticlib --noMain --nimMainPrefix:libB --nimcache:ncache --out:libB.a lib_b.nim
gcc app.c libA.a libB.a -o app

Current Output

/usr/bin/ld: libB.a(@mlib_b.nim.c.o): in function `PreMainInner':
@mlib_b.nim.c:(.text+0x10): multiple definition of `PreMainInner'; libA.a(@mlib_a.nim.c.o):@mlib_a.nim.c:(.text+0x10): first defined here
/usr/bin/ld: libB.a(@mlib_b.nim.c.o): in function `PreMain':
@mlib_b.nim.c:(.text+0x20): multiple definition of `PreMain'; libA.a(@mlib_a.nim.c.o):@mlib_a.nim.c:(.text+0x20): first defined here
/usr/bin/ld: libB.a(@mlib_b.nim.c.o): in function `NimMainInner':
@mlib_b.nim.c:(.text+0x40): multiple definition of `NimMainInner'; libA.a(@mlib_a.nim.c.o):@mlib_a.nim.c:(.text+0x40): first defined here
/usr/bin/ld: libB.a(@mlib_b.nim.c.o): in function `NimMainModule':
@mlib_b.nim.c:(.text+0x70): multiple definition of `NimMainModule'; libA.a(@mlib_a.nim.c.o):@mlib_a.nim.c:(.text+0x70): first defined here
/usr/bin/ld: libB.a(@mlib_b.nim.c.o):(.bss+0x0): multiple definition of `gEnv'; libA.a(@mlib_a.nim.c.o):(.bss+0x0): first defined here
/usr/bin/ld: libB.a(@mlib_b.nim.c.o):(.bss+0x8): multiple definition of `cmdLine'; libA.a(@mlib_a.nim.c.o):(.bss+0x8): first defined here
/usr/bin/ld: libB.a(@mlib_b.nim.c.o):(.bss+0x10): multiple definition of `cmdCount'; libA.a(@mlib_a.nim.c.o):(.bss+0x10): first defined here
collect2: error: ld returned 1 exit status

Expected Output

24
42

Possible Solution

  • Emit NimMainModule_source_name_qwerty12345() instead of NimMainModule()
  • Emit cmdCount_source_name_qwerty12345() instead of cmdCount()
  • etc, etc...

Additional Information

Tested with --mm:arc --mm:orc --mm:refc --mm:none + with the --header option

$ nim -v
Nim Compiler Version 1.7.1 [Linux: amd64]
git hash: 19001c070bbe7645ff45fcbd66ab221235715302
@JDragan JDragan changed the title multiple definition of Nim generated static libraries multiple definition of in Nim generated static libraries May 25, 2022
@ghost
Copy link

ghost commented May 26, 2022

You have to use the --nimMainPrefix switch 7ff43d0 so that each static library has it's own prefixes.

@JDragan
Copy link
Author

JDragan commented May 26, 2022

The thing is that only NimMain() is renamed to libANimMain().
NimMainInner(), PreMain(), PreMainInner() are not prefixed with libA/libB, so their symbols clash...

@deech
Copy link
Contributor

deech commented May 31, 2022

This issue is currently blocking progress for me, any way to get it prioritized?

@JDragan
Copy link
Author

JDragan commented May 31, 2022

Maybe this will help, works on Windows. On Linux however, gEnv, cmdCount and cmdLine are still in the generated code, which shouldn't happen since the --noMain switch is used

diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index ad59b759f..bb91b8624 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -1322,7 +1322,7 @@ proc getSomeInitName(m: BModule, suffix: string): Rope =
 proc getInitName(m: BModule): Rope =
   if sfMainModule in m.module.flags:
     # generate constant name for main module, for "easy" debugging.
-    result = rope"NimMainModule"
+    result = rope m.config.nimMainPrefix & "NimMainModule"
   else:
     result = getSomeInitName(m, "Init000")
 
@@ -1351,11 +1351,11 @@ proc genMainProc(m: BModule) =
     preMainCode.add("\tvoid* rtl_handle;\L")
     preMainCode.add(loadLib("rtl_handle", "nimGC_setStackBottom"))
     preMainCode.add(hcrGetProcLoadCode(m, "nimGC_setStackBottom", "nimrtl_", "rtl_handle", "nimGetProcAddr"))
-    preMainCode.add("\tinner = PreMain;\L")
+    preMainCode.add("\tinner = " & m.config.nimMainPrefix & "PreMain;\L")
     preMainCode.add("\tinitStackBottomWith_actual((void *)&inner);\L")
     preMainCode.add("\t(*inner)();\L")
   else:
-    preMainCode.add("\tPreMain();\L")
+    preMainCode.add("\t" & m.config.nimMainPrefix & "PreMain();\L")
 
   const
     # not a big deal if we always compile these 3 global vars... makes the HCR code easier
@@ -1370,20 +1370,20 @@ proc genMainProc(m: BModule) =
 
     PreMainVolatileBody =
       "\tvoid (*volatile inner)(void);$N" &
-      "\tinner = PreMainInner;$N" &
+      "\tinner = $3PreMainInner;$N" &
       "$1" &
       "\t(*inner)();$N"
 
     PreMainNonVolatileBody =
       "$1" &
-      "\tPreMainInner();$N"
+      "\t$3PreMainInner();$N"
 
     PreMainBodyStart = "$N" &
-      "N_LIB_PRIVATE void PreMainInner(void) {$N" &
+      "N_LIB_PRIVATE void $3PreMainInner(void) {$N" &
       "$2" &
       "}$N$N" &
       PosixCmdLine &
-      "N_LIB_PRIVATE void PreMain(void) {$N"
+      "N_LIB_PRIVATE void $3PreMain(void) {$N"
 
     PreMainBodyEnd =
       "}$N$N"
@@ -1394,21 +1394,21 @@ proc genMainProc(m: BModule) =
     MainProcsWithResult =
       MainProcs & ("\treturn $1nim_program_result;$N")
 
-    NimMainInner = "N_LIB_PRIVATE N_CDECL(void, NimMainInner)(void) {$N" &
+    NimMainInner = "N_LIB_PRIVATE N_CDECL(void, $5NimMainInner)(void) {$N" &
         "$1" &
       "}$N$N"
 
     NimMainVolatileBody =
       "\tvoid (*volatile inner)(void);$N" &
       "$4" &
-      "\tinner = NimMainInner;$N" &
+      "\tinner = $5NimMainInner;$N" &
       "$2" &
       "\t(*inner)();$N"
 
     NimMainNonVolatileBody =
       "$4" &
       "$2" &
-      "\tNimMainInner();$N"
+      "\t$5NimMainInner();$N"
 
     NimMainProcStart =
       "N_CDECL(void, $5NimMain)(void) {$N"
@@ -1488,9 +1488,9 @@ proc genMainProc(m: BModule) =
     else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", [])
   inc(m.labels)
   if m.config.selectedGC notin {gcNone, gcArc, gcOrc}:
-    appcg(m, m.s[cfsProcs], PreMainBodyStart & PreMainVolatileBody & PreMainBodyEnd, [m.g.mainDatInit, m.g.otherModsInit])
+    appcg(m, m.s[cfsProcs], PreMainBodyStart & PreMainVolatileBody & PreMainBodyEnd, [m.g.mainDatInit, m.g.otherModsInit, m.config.nimMainPrefix])
   else:
-    appcg(m, m.s[cfsProcs], PreMainBodyStart & PreMainNonVolatileBody & PreMainBodyEnd, [m.g.mainDatInit, m.g.otherModsInit])
+    appcg(m, m.s[cfsProcs], PreMainBodyStart & PreMainNonVolatileBody & PreMainBodyEnd, [m.g.mainDatInit, m.g.otherModsInit, m.config.nimMainPrefix])
 
   if m.config.target.targetOS == osWindows and
       m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}:

@deech
Copy link
Contributor

deech commented May 31, 2022

This is nice for now but we really need symbol isolation otherwise it's impossible to create C ABIs for Nim libraries because any two will clash on any common parts of their respective dependency tree including the standard library. It might be worth generalizing --nimMainPrefix=... to something like --nimPrefix=... that will do to all other visible symbols what nimMainPrefix does for NimMain, there is still potential for unpleasant overlap if two people pick the same prefix and the binary that uses them will be a little bloated with some redundant symbols but IMO it's worth it to be able to use Nim from other languages.

@deech
Copy link
Contributor

deech commented Jun 1, 2022

This also seems promising, I had no idea you could use objcopy this way. https://stackoverflow.com/questions/6940384/how-to-deal-with-symbol-collisions-between-statically-linked-libraries/6940389#6940389

@JDragan
Copy link
Author

JDragan commented Jun 1, 2022

imo --nimMainPrefix shouldn't exist.

When using --app:staticlib, --noMain should be activated and the prefix for these functions should be taken from the --out:SomeLib.a switch (instead NimMain(), one will call the initialization with SomeLibMain()) and this would be general rule no matter if you compile 1 or 1000 libraries.

@deech
Copy link
Contributor

deech commented Jun 4, 2022

I discovered that if you can use the cpp backend the --cppCompileToNamespace:myLib flag will wrap all the generated C++ code in a namespace and prevent symbol clashes.

Araq pushed a commit that referenced this issue Jun 27, 2022
…19934)

* ref #19830; multiple definition of in Nim generated static libraries

* fix compile errors
quantimnot added a commit to quantimnot/Nim that referenced this issue Jun 30, 2022
Expand `--nimMainPrefix` semantics to include prefixing these symbols
for the C, C++ and ObjC backends:
* `cmdCount`
* `cmdLine`
* `gEnv`
* `PreMain`
* `PreMainInner`
* `NimMainInner`
* `NimMainModule`

Fixes nim-lang#15955
Fixes nim-lang#19830
ringabout added a commit that referenced this issue Sep 23, 2022
@Araq Araq removed the Showstopper label Oct 8, 2022
@Araq
Copy link
Member

Araq commented Oct 8, 2022

This works now.

@Araq Araq closed this as completed Oct 8, 2022
@dmknght
Copy link

dmknght commented Dec 20, 2022

Hello! I'm having similar issue trying to build a static lib in Nim and then use an other Nim script to import the lib. (It's for testing only). I got similar issue and add --passL:-Wl,--allow-multiple-definition to Nim worked for me.

capocasa pushed a commit to capocasa/Nim that referenced this issue Mar 31, 2023
…braries (nim-lang#19934)

* ref nim-lang#19830; multiple definition of in Nim generated static libraries

* fix compile errors
narimiran pushed a commit that referenced this issue Apr 24, 2023
…19934)

* ref #19830; multiple definition of in Nim generated static libraries

* fix compile errors

(cherry picked from commit 0189122)
@waruqi
Copy link

waruqi commented Aug 6, 2023

Hello! I'm having similar issue trying to build a static lib in Nim and then use an other Nim script to import the lib. (It's for testing only). I got similar issue and add --passL:-Wl,--allow-multiple-definition to Nim worked for me.

It does not work now.

ld: unknown option: --allow-multiple-definition
clang: error: linker command failed with exit code 1 (use -v to see invocati
on)

xmake-io/xmake#4042 (comment)

@dmknght
Copy link

dmknght commented Aug 7, 2023

ld: unknown option: --allow-multiple-definition
clang: error: linker command failed with exit code 1 (use -v to see invocati
on)

This is NOT the error mentioned in the issue. The error log showed the compiler was clang.

@waruqi
Copy link

waruqi commented Aug 7, 2023

ld: unknown option: --allow-multiple-definition
clang: error: linker command failed with exit code 1 (use -v to see invocati
on)

This is NOT the error mentioned in the issue. The error log showed the compiler was clang.

I know, but earlier I used this method to solve this issue as well, but now it doesn't work.

Also, nim always calls clang as a linker on my machine. if this flag is not available, how should I resolve this issue. multiple definition of NimMainModule

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants