# How to debug linking errors and module API conflicts

**URL:** <https://discuss.ocaml.org/t/how-to-debug-linking-errors-and-module-api-conflicts/5840>\
**Category:** Learning\
**Tags:** linking, base64, mrmime, gapi-ocaml, build\
**Created:** [May 26, 2020, 5:30am UTC](https://discuss.ocaml.org/t/how-to-debug-linking-errors-and-module-api-conflicts/5840 "2020-05-26T05:30:54Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![shonfeder](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/shonfeder/32/424_2.png) [@shonfeder](https://discuss.ocaml.org/u/shonfeder)\
**Post date:** [May 26, 2020, 5:30am UTC](https://discuss.ocaml.org/t/how-to-debug-linking-errors-and-module-api-conflicts/5840/1 "2020-05-26T05:30:54Z")

</div>

I’m at the stage in my OCaml journey where I think I should be able to troubleshoot linking problems and module conflicts, but they are still pretty much an opaque mystery to me. I was hoping some(ones) might help me untangle my current knot, and perhaps in a way that helps teach how to escape such binds in principle.

In this particular case, I am hitting an unbuildable project due to a conflict between the `mrmime` and `gapi-ocaml` packages. It’s clear that they depend on conflicting `Base64` modules, but I don’t know how to dig deeper in resolve this (perhaps by way of a shim, or patch, or PR, or whatever).

Here is a minimal reproducing case:

Assuming you’ve installed the two packages:

```auto
opam install mrmime gapi-ocaml

```

in `./_build`:

```auto
cat: _build: Is a directory

```

in `./dune`:

```auto
(executable
 (public_name main)
 (libraries threads
            mrmime
            gapi-ocaml))

```

in `./dune-project`:

```auto
(lang dune 2.5)

```

in `./main.ml`:

```auto
let _ = Mrmime.Mail.mail
let _ = GapiGmailV1Model.Message.empty

let () = print_endline "foo"

```

in `./main.opam`:

```auto

```

Then run

```auto
dune build
    ocamlopt main.exe (exit 2)
(cd _build/default && /home/sf/.opam/4.10.0/bin/ocamlopt.opt -w @1..3@5..28@30..39@43@46..47@49..57@61..62-40 -strict-sequence -strict-formats -short-paths -keep-locs -g -o main.exe /home/sf/.opam/4.10.0/lib/ocaml/unix.cmxa -I /home/sf/.opam/4.10.0/lib/ocaml /home/sf/.opam/4.10.0/lib/ocaml/threads/threads.cmxa -I /home/sf/.opam/4.10.0/lib/ocaml /home/sf/.opam/4.10.0/lib/mrmime/butils/butils.cmxa -I /home/sf/.opam/4.10.0/lib/mrmime/butils /home/sf/.opam/4.10.0/lib/stdlib-shims/stdlib_shims.cmxa /home/sf/.opam/4.10.0/lib/fmt/fmt.cmxa /home/sf/.opam/4.10.0/lib/bigarray-compat/bigarray_compat.cmxa /home/sf/.opam/4.10.0/lib/ke/ke.cmxa /home/sf/.opam/4.10.0/lib/bigstringaf/bigstringaf.cmxa -I /home/sf/.opam/4.10.0/lib/bigstringaf /home/sf/.opam/4.10.0/lib/mrmime/encoder/encoder.cmxa /home/sf/.opam/4.10.0/lib/result/result.cmxa /home/sf/.opam/4.10.0/lib/ptime/ptime.cmxa /home/sf/.opam/4.10.0/lib/macaddr/macaddr.cmxa /home/sf/.opam/4.10.0/lib/astring/astring.cmxa /home/sf/.opam/4.10.0/lib/domain-name/domain_name.cmxa /home/sf/.opam/4.10.0/lib/ipaddr/ipaddr.cmxa /home/sf/.opam/4.10.0/lib/base64/base64.cmxa /home/sf/.opam/4.10.0/lib/base64/rfc2045/base64_rfc2045.cmxa /home/sf/.opam/4.10.0/lib/uutf/uutf.cmxa /home/sf/.opam/4.10.0/lib/uuuu/uuuu.cmxa /home/sf/.opam/4.10.0/lib/yuscii/yuscii.cmxa /home/sf/.opam/4.10.0/lib/coin/coin.cmxa /home/sf/.opam/4.10.0/lib/rosetta/rosetta.cmxa /home/sf/.opam/4.10.0/lib/pecu/pecu.cmxa /home/sf/.opam/4.10.0/lib/rresult/rresult.cmxa /home/sf/.opam/4.10.0/lib/angstrom/angstrom.cmxa /home/sf/.opam/4.10.0/lib/mrmime/mrmime.cmxa /home/sf/.opam/4.10.0/lib/extlib/extLib.cmxa /home/sf/.opam/4.10.0/lib/ocaml/bigarray.cmxa -I /home/sf/.opam/4.10.0/lib/ocaml /home/sf/.opam/4.10.0/lib/netsys/netsys_oothr_mt.cmxa /home/sf/.opam/4.10.0/lib/netsys/netsys_oothr_mt_init.cmx /home/sf/.opam/4.10.0/lib/netsys/netsys.cmxa -I /home/sf/.opam/4.10.0/lib/netsys /home/sf/.opam/4.10.0/lib/ocaml/str.cmxa -I /home/sf/.opam/4.10.0/lib/ocaml /home/sf/.opam/4.10.0/lib/netstring/netstring.cmxa -I /home/sf/.opam/4.10.0/lib/netstring /home/sf/.opam/4.10.0/lib/curl/curl.cmxa -I /home/sf/.opam/4.10.0/lib/curl /home/sf/.opam/4.10.0/lib/zarith/zarith.cmxa -I /home/sf/.opam/4.10.0/lib/zarith /home/sf/.opam/4.10.0/lib/cryptokit/cryptokit.cmxa -I /home/sf/.opam/4.10.0/lib/cryptokit /home/sf/.opam/4.10.0/lib/easy-format/easy_format.cmxa /home/sf/.opam/4.10.0/lib/biniou/biniou.cmxa /home/sf/.opam/4.10.0/lib/yojson/yojson.cmxa /home/sf/.opam/4.10.0/lib/gapi-ocaml/gapi_ocaml.cmxa .main.eobjs/native/dune __exe__ Main.cmx)
/usr/bin/ld: /home/sf/.opam/4.10.0/lib/base64/base64.a(base64.o): in function `camlBase64__data_begin':
:(.data+0x0): multiple definition of `camlBase64__data_begin'; /home/sf/.opam/4.10.0/lib/extlib/extLib.a(base64.o)::(.data+0x0): first defined here
/usr/bin/ld: /home/sf/.opam/4.10.0/lib/base64/base64.a(base64.o): in function `camlBase64__$2f$2f_85':
/home/sf/.opam/4.10.0/.opam-switch/build/base64.3.4.0/_build/default/src/base64.ml:28: multiple definition of `camlBase64__code_begin'; /home/sf/.opam/4.10.0/lib/extlib/extLib.a(base64.o):/home/sf/.opam/4.10.0/.opam-switch/build/extlib.1.7.7/src/base64.ml:39: first defined here
/usr/bin/ld: /home/sf/.opam/4.10.0/lib/base64/base64.a(base64.o): in function `camlBase64':
:(.data+0x2e0): multiple definition of `camlBase64'; /home/sf/.opam/4.10.0/lib/extlib/extLib.a(base64.o)::(.data+0x1a0): first defined here
/usr/bin/ld: /home/sf/.opam/4.10.0/lib/base64/base64.a(base64.o): in function `camlBase64__gc_roots':
:(.data+0x3a8): multiple definition of `camlBase64__gc_roots'; /home/sf/.opam/4.10.0/lib/extlib/extLib.a(base64.o)::(.data+0x1f8): first defined here
/usr/bin/ld: /home/sf/.opam/4.10.0/lib/base64/base64.a(base64.o): in function `camlBase64__entry':
/home/sf/.opam/4.10.0/.opam-switch/build/base64.3.4.0/_build/default/src/base64.ml:280: multiple definition of `camlBase64__entry'; /home/sf/.opam/4.10.0/lib/extlib/extLib.a(base64.o):/home/sf/.opam/4.10.0/.opam-switch/build/extlib.1.7.7/src/base64.ml:128: first defined here
/usr/bin/ld: /home/sf/.opam/4.10.0/lib/base64/base64.a(base64.o): in function `camlBase64__code_end':
:(.text+0x181c): multiple definition of `camlBase64__code_end'; /home/sf/.opam/4.10.0/lib/extlib/extLib.a(base64.o)::(.text+0x99d): first defined here
/usr/bin/ld: /home/sf/.opam/4.10.0/lib/base64/base64.a(base64.o): in function `camlBase64__data_end':
:(.data+0x658): multiple definition of `camlBase64__data_end'; /home/sf/.opam/4.10.0/lib/extlib/extLib.a(base64.o)::(.data+0x458): first defined here
/usr/bin/ld: /home/sf/.opam/4.10.0/lib/base64/base64.a(base64.o): in function `camlBase64__frametable':
:(.data+0x660): multiple definition of `camlBase64__frametable'; /home/sf/.opam/4.10.0/lib/extlib/extLib.a(base64.o)::(.data+0x460): first defined here
collect2: error: ld returned 1 exit status
File "caml_startup", line 1:
Error: Error during linking

```

What should be my first step in figuring out what’s responsible for this conflict? What are my options in resolving it?

---

<div class="post-metadata">

**Author:** ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)\
**Post date:** [May 26, 2020, 5:49am UTC](https://discuss.ocaml.org/t/how-to-debug-linking-errors-and-module-api-conflicts/5840/2 "2020-05-26T05:49:26Z")

</div>

It is actually all there if you squint hard enough : )

I think it is due to both the `base64` and `extLib` defining a module called `Base64`. You need to let go of one of them.

Best wishes,  
Nicolás

---

<div class="post-metadata">

**Author:** ![shonfeder](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/shonfeder/32/424_2.png) [@shonfeder](https://discuss.ocaml.org/u/shonfeder)\
**Post date:** [May 26, 2020, 6:20am UTC](https://discuss.ocaml.org/t/how-to-debug-linking-errors-and-module-api-conflicts/5840/3 "2020-05-26T06:20:01Z")

</div>

Thank @nojb. This helps clarify and narrow down the conflict a bit. I’m not requiring either of these directly, So, assuming a user like myself (with some help 😉 ) has identified which modules are conflicting, is there a standard way of figuring out which dependencies introduce the conflict? In this case, it is fairly easy, as I only have two deps, so I can run something like

```auto
[sf@comp leila]$ opam info mrmime | rg base64\|extlib
              "base64" {>= "3.1.0"}
[sf@comp leila]$ opam info gapi-ocaml | rg base64\|extlib
              ("extlib" | "extlib-compat")

```

but with a longer dependency chain, this could be burried in a non obvious way. How would one diagnose the packages introducing the conflicting modules in such a case?

More important, what can be done to make this kind of situation better, for myself and for other users? E.g., should the `base64` and `exlib` packages both include a [`conflicts`](https://opam.ocaml.org/doc/Manual.html#opamfield-post-messages) field in their opam manifests to help users detect this conflict earlier? Would that be a helpful PR?

What if I _had_ to make these packages work together? Iiuc, this is just a nominal conflict, and if extlib didn’t dump it’s modules into the top-level, then the problem wouldn’t exist at all. So perhaps I should consider a patch, or vendoring one or the other package to fix this problem? Is that a reasonable course of action? If not, what other approaches do serious OCaml hackers take?

---

<div class="post-metadata">

**Author:** ![Chet\_Murthy](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/chet_murthy/32/1501_2.png) [@Chet\_Murthy](https://discuss.ocaml.org/u/Chet_Murthy)\
**Post date:** [May 26, 2020, 6:31am UTC](https://discuss.ocaml.org/t/how-to-debug-linking-errors-and-module-api-conflicts/5840/4 "2020-05-26T06:31:47Z")

</div>

One thing to note, is that opam isn’t responsible for linking: that’s handled by dune and ocamlfind/findlib. When I have problems like this, I always take the ocamlfind link command, and find out what the list of libraries is, that it’s trying to link. Then I can figure out if there’s a conflict: at least once I recall using ocamlobjinfo to discover which the exported symbols of each library were, and thereby narrowing it down … but I might be misremembering.

Your situation is analogous to a C programmer finding weird conflicts between DLLs: the tools are there to figure out the conflict, but the first step is get down to the list of DLLs.

I could be unfair here, but … `dune` hides that information pretty effectively (the logfile is … no easy to decipher). OTOH, the information -is- there in the logfile.

---

<div class="post-metadata">

**Author:** ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)\
**Post date:** [May 26, 2020, 7:22am UTC](https://discuss.ocaml.org/t/how-to-debug-linking-errors-and-module-api-conflicts/5840/5 "2020-05-26T07:22:37Z")

</div>

> Thank @nojb. This helps clarify and narrow down the conflict a bit. I’m not requiring either of these directly, So, assuming a user like myself (with some help 😉 ) has identified which modules are conflicting, is there a standard way of figuring out which dependencies introduce the conflict?

Well, if you read the linking error, you can see that the repeated symbol `camlBase64__*`, is mentioned to appear in both `.../base64/base64.*` and `.../exitlib/extLib.*`. That should give it to you.

> More important, what can be done to make this kind of situation better, for myself and for other users? E.g., should the `base64` and `exlib` packages both include a [`conflicts`](https://opam.ocaml.org/doc/Manual.html#opamfield-post-messages) field in their opam manifests to help users detect this conflict earlier? Would that be a helpful PR?

I don’t think so, because there is no problem having both installed in your opam universe. It is only a problem if you try to use both _at the same time._

> What if I _had_ to make these packages work together? Iiuc, this is just a nominal conflict, and if extlib didn’t dump it’s modules into the top-level, then the problem wouldn’t exist at all. So perhaps I should consider a patch, or vendoring one or the other package to fix this problem? Is that a reasonable course of action?

Yes, wrapping the library in a top-level module is the standard way to solve this issue. But old-style (unwrapped) libraries cannot easily change to the new style because it breaks every piece of code that uses them out there. Having said that, `dune` offers a way to handle the migration in steps by first building both the wrapped and unwrapped libraries with deprecation notice.

Cheers,  
Nicolás
