{"id":20435904,"url":"https://github.com/andyneff/diamond","last_synced_at":"2026-06-01T02:31:20.240Z","repository":{"id":42505886,"uuid":"466301172","full_name":"andyneff/diamond","owner":"andyneff","description":"Random diamond linking problem demonostration","archived":false,"fork":false,"pushed_at":"2022-04-02T13:01:24.000Z","size":20,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-05T06:43:43.080Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andyneff.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-03-04T23:44:28.000Z","updated_at":"2022-03-04T23:44:47.000Z","dependencies_parsed_at":"2022-09-08T22:12:18.233Z","dependency_job_id":null,"html_url":"https://github.com/andyneff/diamond","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/andyneff/diamond","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyneff%2Fdiamond","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyneff%2Fdiamond/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyneff%2Fdiamond/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyneff%2Fdiamond/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andyneff","download_url":"https://codeload.github.com/andyneff/diamond/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyneff%2Fdiamond/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33757790,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-15T08:38:21.866Z","updated_at":"2026-06-01T02:31:20.220Z","avatar_url":"https://github.com/andyneff.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Diamond linking example\n\nRevisiting the diamond linking problem and demonstrating the different \"orders\" that can occur.\n\n`./compile.bsh` to build all the executables. Run `./main_*` to see all the test scenarios.\n\n# Linking\n\nOn Linux, there are three times when symbols from multiple files will be linked together:\n\n1. at link time - After compiling `.c` files into `.o` and `.a` files, `ld` links references to symbols together with their definitions into an executable or a shared library (an `.so` file). However, not all symbols are resolved now; undefined symbols, as they are called, are not fully linked until load time (the next step).\n2. at load time - When the executable is launched, undefined symbols whose linkage was deferred at link time are resolved now. They are resolved against the dynamically-linked dependencies (the `.so` files listed by `ldd`) by `ld.so`. This linking occurs before `int main` even starts.\n3. during run time - `dlopen` can dynamically load shared libraries (`.so` files) during program execution. `ld.so` will link them (and their dependencies) into a local scope appended to the executable's global scope.\n\n## `ld` - Link-time resolution\n\nWhen linking (building) an executable or shared library (`.so` file), the rules are as follows:\n\n1. Search the `.o`'s first\n2. Always search the dynamic symbol table of the `.so`'s (the dynamically-linked dependencies)\n    - The dynamic symbol table (.dynsym) includes the symbol definitions (`nm -D --defined`) that can be used by the linker to resolve a symbol.\n    - If `ld` locates the definition of a needed symbol within a `.so` file, a corresponding _undefined_ symbol is added to the dynamic symbol table (`nm -u`) of the target---executable or shared library---being built. As a result, those symbols are not linked fully until load time (see Load-time resolution).\n3. Search the `.a`'s\n\nNOTE When an executable is built, all symbols that **it** defined are not, by default, added to the dynamic symbol table; i.e., they have local, not external, visibility, and hence, are not eligible to resolve undefined symbols. Conversely, symbols in a shared library are, by default, dynamic. This can, however, be fine-tuned with the `--export-dynamic` family of flags, the `--dynamic-list` flag or the `--version-script` flag in `ld`. [1]\n\nNOTE step 2 is always run, even if step 1 finds a match. As a consequence of this, there is an odd corner case where a symbol can (accidentally) become dynamic when building an executable without the use of the export-control flags mentioned in the previous note; see the  `_foo` examples below.\n\n### Duplicate symbols\n\n- Duplicate symbols that happen during step 1 will result in a linker error: `multiple definition of 'foo_c'`. This can be demonstrated via: `./compile.bsh clash`\n- Duplicate symbols that happen during steps 2 or 3 will be resolved with the first definition found winning. **No** warnings or errors are displayed.\n\n## `ld.so` - Load-time resolution\n\nWhen the executable is launched, its dynamically-linked dependencies (the `.so` files listed by `ldd`) are loaded and undefined symbols resolved/linked (i.e., their symbol definitions relocated). This starts with the executable and proceeds to the dynamic libraries linked directly to the executable in a breadth-first manner according to the order in which they were specified on the link line. The first definition found wins. The dependencies of these dependent libraries are resolved in a similar manner, and so on.\n\nSpecifically, each undefined symbol (the result of step 2 from `ld` - Link-time resolution) is resolved according to the following rules, where the first definition found in the dynamic symbol table wins:\n\n1. Search symbols in the executable. (However, as noted above, defined symbols are not, by default, found in an executable's dynamic symbol table.)\n2. Search the dynamically-linked libraries in a breadth-first manner.\n\n## `dlopen` - Run-time resolution\n\nOnce the executable is fully loaded and running, `dlopen` may be called to dynamically load a shared library. Undefined symbols are again resolved like those during Load-time resolution, albeit with a slight deviation (by default): a dynamically-loaded library creates a **local** scope which is searched after the global scope of the executable and its dynamically-linked dependencies.\n\nMuch like link-time resolution, each undefined symbol from the dynamically-loaded shared library (step 2 from `ld` - Link time resolution) is resolved according to the following rules, where the first definition found in the dynamic symbol table wins:\n\n1. Search symbols in the executable.\n2. Search the dynamically-linked dependencies of the executable in a breadth-first manner.\n3. Search the dynamically-linked dependencies of the dlopen'd shared library in a breadth-first manner. These symbols belong to a private link chain and cannot be used to bind symbols in other dlopen's.\n\nNOTE Although not recommended, if the `RTLD_GLOBAL` flag is set (in contrast to `RTLD_LOCAL`, which is the default), `dlopen` will load symbols into the global namespace. There are other `ld` flags as well that can affect linking like `RTLD_DEEPBIND`, but also flags that can be set in the shared object when linking it together like `DF_SYMBOLIC`. These flags are also not recommended. [2]\n\n# Tests\n\n## `main_1` and `main_2`\n\n```\n$ ./main_1\nok\nfoo_c version 1 100\nfoo_c version 1 200\n$ ./main_2\nok\nfoo_c version 2 100\nfoo_c version 2 200\n```\n\nExplanation: This demonstrates `ld` linking rule 3: first (static) library on the link line wins.\n\n## `main_3` and `main_4`\n\n```\n$ ./main_3\nok\nfoo_c version 1 100\nfoo_c version 1 200\n$ ./main_4\nok\nfoo_c version 2 100\nfoo_c version 2 200\n```\n\nExplanation: This demonstrates load-time resolution rule 2: first (dynamic) library on the link line wins.\n\n## `main_5` - `main_8`\n\n```\n$ ./main_5\nok\nfoo_c version 2 100\nfoo_c version 2 200\n$ ./main_6\nok\nfoo_c version 1 100\nfoo_c version 1 200\n$ ./main_7\nok\nfoo_c version 1 100\nfoo_c version 1 200\n$ ./main_8\nok\nfoo_c version 2 100\nfoo_c version 2 200\n```\n\nExplanation: This demonstrates `ld` linking rule 2: dynamic library always wins over a static library.\n\n## `main_foo_0` - `main_foo_8`\n\n```\n$ ./main_foo_*\nok\nfoo_c version 3 100\nfoo_c version 3 200\nfoo_c version 3 300\n```\n\nExplanation: This demonstrates `ld` linking rule 1.\n\nSpecial note: `nm -D` on `main_foo_3` through `main_foo_8` show that `foo_c` is in the dynamic symbol table and is defined (`T`). This shows that `ld`'s rule 1 takes precedence over rule 3.\n\n## `main_dyload_1`\n\n```\n$ ./main_dyload_1\ndynamic ok\nfoo_c version 1 100\nfoo_c version 2 200\nfoo_c version 1 100\nfoo_c version 1 200\nfoo_c version 1 100\nfoo_c version 1 200\n```\n\nExplanation: This demonstrates run-time linking rule 3.\n\n- The first two lines \"work\" as intended because `libmain.so` hasn't been loaded yet. Thus `liba.so` and `libb.so` are both loaded into separate private link chains.\n- The second two lines do not work as intended because `libmain.so` is loaded into one private link chain, thus the second lookup for `foo_c` exercises rule 3 of run-time linking and uses the first `foo_c` found.\n- The last two lines exercise a corner case where \"`.so` files are not reloaded if they were loaded in another's link chain\". So `libad.so` and `libbd.so` (in addition to `libc1d.so` and `libc2d.so`) are never reloaded, and use the version from `libmain.so`'s link chain.\n\n## `main_dyload_2`\n\n```\n$ ./main_dyload_2\ndynamic ok\nfoo_c version 4 100\nfoo_c version 4 200\nfoo_c version 4 100\nfoo_c version 4 200\nfoo_c version 4 100\nfoo_c version 4 200\n```\n\nExplanation: This demonstrates rule 2 of run-time linking. That is, `foo_c` is not in the dynamic symbol table of the main executable (`main_dyload_2`). `foo_c` exists in the dynamically-linked dependency, libc4d.so, of `main_dyload_2`, as well as the dynamically-linked dependency of the dlopen'd shared libraries libad.so and libbd.so. However, the symbol in libc4d.so is found first.\n\n## `main_foo_dyload_1`\n\n```\n$ ./main_foo_dyload_1\ndynamic ok\nfoo_c version 1 100\nfoo_c version 2 200\nfoo_c version 3 111\nfoo_c version 1 100\nfoo_c version 1 200\nfoo_c version 3 222\nfoo_c version 1 100\nfoo_c version 1 200\nfoo_c version 3 333\n```\n\nExplanation: This demonstrates rule 1 and rule 3 of `ld` linking. The third version of `foo_c` is linked statically from the main `.o`. Consequentially, it is not a dynamic symbol (`nm -D main_foo_dyload_1` does not include `foo_c`) and, therefore, does not trigger rule 1 during load-time or run-time linking. As a result, `foo_c` version 1 and 2 are linked according to the rules for `main_foo_1`\n\n## `main_foo_dyload_2`\n\n```\n$ ./main_foo_dyload_2\ndynamic ok\nfoo_c version 3 100\nfoo_c version 3 200\nfoo_c version 3 111\nfoo_c version 3 100\nfoo_c version 3 200\nfoo_c version 3 222\nfoo_c version 3 100\nfoo_c version 3 200\nfoo_c version 3 333\n```\n\nExplanation: This is an interesting chain of events:\n\n- During link time, according to rule 1: `foo_c` is linked statically from the main `.o`. But `foo_c` is also found in `libc4d.so`, so it is also added to the dynamic symbol list (`nm -D --defined main_foo_dyload_2`).\n- During run time, rule 1 of load-time linking is always triggered and the static version is always found.\n\n# Additional notes\n\n- To use clang, `export GCC=clang` before running the compile script\n- There is actually a connivent way to handle the diamond-linking problem: that is to pass the `--default-symver` flag to the linker. However this is believed to potentially contain bugs and might not be stable. `export CFLAGS=-Wl,--default-symver` and compile to test this out. `main_3` and `main_4` will now work perfectly\n\n# References\n\n[1] https://stackoverflow.com/questions/26294841/having-object-file-symbols-become-dynamic-symbols-in-executable\n\n[2] Ulrich Drepper, How To Write Shared Libraries. December 10, 2011. Available: https://www.akkadia.org/drepper/dsohowto.pdf\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandyneff%2Fdiamond","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandyneff%2Fdiamond","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandyneff%2Fdiamond/lists"}