[{"data":1,"prerenderedAt":1541},["ShallowReactive",2],{"NoscriptNav_XrRK2e2e8meJ0jKVGkb5ULGQDVi3UiFQ9nupAr7Yns":3,"\u002Freports\u002Fhow-binary-dependencies-work\u002F":8},["Island",4],{"key":5,"result":6},"NoscriptNav_XrRK2e2e8meJ0jKVGkb5ULGQDVi3UiFQ9nupAr7Yns",{"head":7},{},{"id":9,"title":10,"authors":11,"body":13,"canonicalUrl":1524,"canonicalWebsiteName":1525,"category":1526,"date":1527,"description":1528,"extension":1529,"featured":1530,"fullWidthLayout":1530,"image":1531,"imageAlt":1532,"location":1533,"meta":1534,"metaImage":1535,"navigation":1536,"path":1537,"seo":1538,"stem":1539,"venue":1533,"venueUrl":1533,"__hash__":1540},"reports\u002Freports\u002Fhow-binary-dependencies-work.md","How Binary Dependencies Work Across Different Languages",[12],"vlad",{"type":14,"value":15,"toc":1491},"minimark",[16,20,28,47,88,91,101,104,107,126,129,134,149,152,161,164,180,183,197,200,212,221,224,236,247,259,262,272,297,307,319,322,326,336,348,361,378,393,396,420,433,443,452,463,469,472,486,488,491,515,518,522,525,549,552,575,578,596,599,608,611,614,649,653,662,667,676,679,681,684,694,700,703,706,712,715,721,731,740,749,778,780,787,857,860,864,871,918,921,951,958,967,970,978,982,991,994,997,1014,1016,1023,1064,1067,1073,1091,1093,1096,1123,1126,1132,1135,1141,1144,1150,1155,1161,1190,1193,1201,1208,1284,1290,1318,1321,1330,1339,1345,1348,1391,1394,1397,1400,1402,1433,1436,1444,1452,1456],[17,18,19],"p",{},"Sometimes, your code depends on a library, but you don't want to compile that library's source code alongside your\nprogram. Rather, you want to directly call into a compiled binary of the library. This most frequently happens when you\nwant to call a C library from languages like Python, JavaScript or Rust.",[17,21,22,23,27],{},"In this situation, the library you're calling is a ",[24,25,26],"em",{},"binary dependency"," of your program.",[17,29,30,31,35,36,39,40,43,44,46],{},"For example, under the hood, Python's\n",[32,33],"binding",{"value":34},"\u003C link href=\"https:\u002F\u002Fnumpy.org\u002F\" class=\"suplink\" target=\"_blank\" >","NumPy",[32,37],{"value":38},"\u003C \u002Flink >","\nlibrary calls functions from C library\n",[32,41],{"value":42},"\u003C link href=\"https:\u002F\u002Fgithub.com\u002FOpenMathLib\u002FOpenBLAS\" class=\"suplink\" target=\"_blank\" >","OpenBLAS",[32,45],{"value":38},"\nto do some of its computation.",[17,48,49,50,59,60,63,64,66,67,70,71,73,74,81,82,87],{},"Binary dependencies are interesting because they most frequently occur as\n",[24,51,52,55,56,58],{},[32,53],{"value":54},"\u003C link href=\"https:\u002F\u002Fwww.endorlabs.com\u002Flearn\u002Fdependency-resolution-in-python-beware-the-phantom-dependency\" class=\"suplink\" target=\"_blank\" >","phantom dependencies",[32,57],{"value":38}," —\n","\ndependencies that we don't know about, because they are never recorded in a place such as a\n",[32,61],{"value":62},"\u003C link href=\"https:\u002F\u002Fnesbitt.io\u002F2026\u002F01\u002F13\u002Fpackage-manager-glossary.html#manifest\" class=\"suplink\" target=\"_blank\" >","package manifest",[32,65],{"value":38},".\nThis has serious implications for security, and for the\n",[32,68],{"value":69},"\u003C link href=\"https:\u002F\u002Fopenpath.quest\u002F2024\u002Fthe-open-source-sustainability-crisis\u002F\" class=\"suplink\" target=\"_blank\" >","financial sustainability",[32,72],{"value":38},"\nof Open Source. I've spoken about these topics\n",[75,76,80],"a",{"href":77,"rel":78},"https:\u002F\u002Ffosdem.org\u002F2026\u002Fschedule\u002Fevent\u002F7NQJNU-binary_dependencies_identifying_the_hidden_packages_we_all_depend_on\u002F",[79],"nofollow","at FOSDEM 2026",",\nand you can learn more about this in\n",[75,83,86],{"href":84,"rel":85},"https:\u002F\u002Fhackmd.io\u002F@vladh\u002Fbinary-dependencies",[79],"my research proposal",".",[17,89,90],{},"But before we can conduct this research, we need to understand how binary dependencies work to begin with. There are\nsome interesting questions to ask:",[92,93,94,98],"ul",{},[95,96,97],"li",{},"How does calling a precompiled (eg, C) library work under the hood?",[95,99,100],{},"What are the different ways to do this in different programming languages?",[17,102,103],{},"I'll try to answer these questions. I'll provide examples from a few different programming languages, but you shouldn't\nrely on the code in this post, because my aim is not to provide accurate documentation, but to show the similarities\nbetween these various languages, and how they share the same ideas.",[17,105,106],{},"In this post, I'll cover three methods of calling code located in precompiled binaries:",[92,108,109,114,120],{},[95,110,111],{},[24,112,113],{},"dynamic linking,",[95,115,116,119],{},[24,117,118],{},"dynamic loading",", and",[95,121,122,125],{},[24,123,124],{},"dynamic linking with extension modules",", where I'll cover Python's extension modules, Ruby's extension libraries, and\nNode.js's C++ addons.",[17,127,128],{},"But for now, let's start with the basic ideas.",[130,131,133],"h2",{"id":132},"general-principles","General Principles",[17,135,136,137,140,141,143,144,148],{},"Say you have a precompiled\n",[32,138],{"value":139},"\u003C link href=\"https:\u002F\u002Fdeveloper.ibm.com\u002Ftutorials\u002Fl-dynamic-libraries\u002F\" class=\"suplink\" target=\"_blank\" >","dynamic library",[32,142],{"value":38},",\noriginally written in C, in a file called ",[145,146,147],"code",{},"c_lib.so",". How can we reach into this file, find the function we want to\ncall, and actually run the code inside it? Let's start by looking at the general steps any solution must cover.",[17,150,151],{},"I'm assuming we're on Linux, but the steps are analogous on other platforms. I'll start with hand-wavy pseudocode, then\nmove into some real code.",[153,154,155,156],"figure",{},"\n\t",[157,158],"img",{"src":159,"alt":160},"https:\u002F\u002Fvlad.website\u002Fillus\u002Fbinary-dependencies\u002Fgraph-demo.svg","An abstract illustration of a C function call",[17,162,163],{},"The first step is obvious. Whatever tool we're using to do the calling, we need to let it know about the file that has\nour dynamic library, so we need something with this general shape:",[165,166,155,169,155,174],"div",{"className":167},[168],"alert",[170,171,173],"h3",{"id":172},"linking-declaration","Linking declaration",[175,176,177],"pre",{},[145,178,179],{},"link(\"c_lib.so\")",[17,181,182],{},"Then, we need some details about the function we want to call. We need to know",[92,184,185,188,191,194],{},[95,186,187],{},"the function's name,",[95,189,190],{},"the function's return type,",[95,192,193],{},"the number of arguments,",[95,195,196],{},"the arguments' types.",[17,198,199],{},"Putting this together, we'll need a signature approximately like this:",[165,201,155,203,155,207],{"className":202},[168],[170,204,206],{"id":205},"type-definitions","Type definitions",[175,208,209],{},[145,210,211],{},"foo(const char*, i64, i64) -> str",[17,213,214,215,218,219,87],{},"This is where the complications start. The C function we're calling takes a string as its first argument. C strings are\nrepresented as null-terminated character arrays. However, strings in our host language might be represented differently\n— maybe a struct containing (1) a pointer to some bytes, (2) a length, and (3) a capacity, like\n",[32,216],{"value":217},"\u003C link href=\"https:\u002F\u002Fdoc.rust-lang.org\u002Fstd\u002Fstring\u002Fstruct.String.html#representation\" class=\"suplink\" target=\"_blank\" >","in Rust",[32,220],{"value":38},[17,222,223],{},"So we might need to take some of the arguments we intend to pass to the function, then convert them to the appropriate C\ntypes, so that the function we're calling receives its data in its expected format. For example:",[165,225,155,227,155,231],{"className":226},[168],[170,228,230],{"id":229},"data-conversion","Data conversion",[175,232,233],{},[145,234,235],{},"x = foo(\"hi\".to_cstr(), 1, 2)",[17,237,238,239,242,243,246],{},"Now that we have the symbol of the function we're looking for — which is basically its name, so ",[145,240,241],{},"foo"," — we need to\nfind the code for this function at its specific address inside our ",[145,244,245],{},".so"," file. The result\nmight be an address like this:",[165,248,155,250,155,254],{"className":249},[168],[170,251,253],{"id":252},"symbol-finding","Symbol finding",[175,255,256],{},[145,257,258],{},"\u002Fusr\u002Flib\u002Fc_lib.so\n0x1234: foo()",[17,260,261],{},"To complete the call, we'll somehow need to generate the assembly code that actually calls into the function on our\narchitecture. Fortunately, we know the number of arguments and their types, as well as the function's address, and\nthat's all the information we need.",[17,263,264,265,268,269,271],{},"So we can follow our platform's\n",[32,266],{"value":267},"\u003C link href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCalling_convention\" class=\"suplink\" target=\"_blank\" >","calling convention",[32,270],{"value":38},"\nto put the appropriate arguments in the appropriate CPU registers, and get the returned value out of the appropriate\nregister. Of course, we won't need to do this manually, but let's consider what the result might look like.",[17,273,274,275,278,279,281,282,285,286,289,290,293,294,87],{},"I'm using Linux on an x86_64 machine, so I need to follow the\n",[32,276],{"value":277},"\u003C link href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FX86_calling_conventions#System_V_AMD64_ABI\" class=\"suplink\" target=\"_blank\" >","System V ABI",[32,280],{"value":38},",\nwhich means that I will put my arguments into registers ",[145,283,284],{},"rdi",", ",[145,287,288],{},"rsi"," and ",[145,291,292],{},"rdx"," (in that order), and I'll get my return\nvalue in register ",[145,295,296],{},"rax",[17,298,299,300,303,304,306],{},"In\n",[32,301],{"value":302},"\u003C link href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FX86_assembly_language\" class=\"suplink\" target=\"_blank\" >","x86 assembly language",[32,305],{"value":38},",\nthis would look something vaguely like:",[165,308,155,310,155,314],{"className":309},[168],[170,311,313],{"id":312},"call-generation","Call generation",[175,315,316],{},[145,317,318],{},"mov rdi, \"hi\\0\"\nmov rsi, 1\nmov rdx, 2\ncall foo\n; result in rax",[17,320,321],{},"These are the general steps we need to follow. Let's look at several concrete approaches to implementing such a function\ncall.",[130,323,325],{"id":324},"dynamic-linking","Dynamic Linking",[153,327,155,328,155,332],{},[157,329],{"src":330,"alt":331},"https:\u002F\u002Fvlad.website\u002Fillus\u002Fbinary-dependencies\u002Fgraph-dynlink.svg","An illustration of dynamic linking",[333,334,335],"figcaption",{},"\n\t\tCalling a function using dynamic linking. The diamonds represent data. We need to provide data to our shared\n\t\tlibrary using the C types it expects, and we will receive returned data in the same format.\n\t",[17,337,338,339,342,343,58,345,87],{},"Are you writing your code in a compiled language like Rust? If so, you can take advantage of the first method, which is\nthe simplest because of the help you get from your operating system's ",[32,340],{"value":341},"\u003C link\nhref=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FKernel_(operating_system)\" class=\"suplink\" target=\"_blank\" >","kernel",[32,344],{"value":38},[24,346,347],{},"dynamic linking",[17,349,350,351,347,354,356,357,360],{},"When using\n",[32,352],{"value":353},"\u003C link href=\"https:\u002F\u002Fdeveloper.ibm.com\u002Ftutorials\u002Fl-dynamic-libraries\u002F#dynamic-linking-with-linux0\" class=\"suplink\" target=\"_blank\" >",[32,355],{"value":38},",\nyour compiler will insert calls to the C function ",[145,358,359],{},"foo()"," just like any other function call. Here's the catch, though —\nthat function will remain undefined. On execution, the kernel will see that the function is undefined, and use the\nruntime dynamic linker to attempt to find the function's definition within the dynamic libraries specified as being\nrequired by the program during compilation.",[17,362,363,364,366,367,370,371,373,374,377],{},"The linker will know how to find the ",[145,365,147],{}," file in\n",[32,368],{"value":369},"\u003C link href=\"https:\u002F\u002Fman7.org\u002Flinux\u002Fman-pages\u002Fman8\u002Fld.so.8.html\" class=\"suplink\" target=\"_blank\" >","standard operating system paths",[32,372],{"value":38},"\nfor dynamic libraries such as ",[145,375,376],{},"\u002Fusr\u002Flocal\u002Flib\u002F",". This means that it's enough to just specify the dynamic library's\nfilename.",[17,379,380,381,383,384,386,387,390,391,87],{},"Do note, though, that we never specify which symbols are found in which libraries — so we never say that ",[145,382,241],{}," can be\nfound in ",[145,385,147],{},". The kernel will just search all the dynamic libraries our program depends on to find our desired\nsymbol, and take the\n",[32,388],{"value":389},"\u003C link href=\"https:\u002F\u002Fstackoverflow.com\u002Fa\u002F58028619\" class=\"suplink\" target=\"_blank\" >","first definition",[32,392],{"value":38},[394,395],"hr",{},[17,397,398,399,402,403,406,407,409,412,413,416,417,87],{},"Here's a more in-depth look at what the linker is doing. Let's say I compile a program that uses ",[145,400,401],{},"curl_easy_init()"," from\n",[32,404],{"value":405},"\u003C link href=\"https:\u002F\u002Fcurl.se\u002Flibcurl\u002F\" class=\"suplink\" target=\"_blank\" >","CURL's",[32,408],{"value":38},[145,410,411],{},"libcurl.so",", which you almost certainly have on your computer. Of course, when compiling, I'll use the ",[145,414,415],{},"-lcurl"," flag,\nto specify that my program depends on ",[145,418,419],{},"libcurl",[17,421,422,423,426,427,429,430,87],{},"It doesn't really matter what language we write this program in, as long as we can compile it to a binary. Because I'm\non Linux, the compiler outputs an\n",[32,424],{"value":425},"\u003C link href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FExecutable_and_Linkable_Format\" class=\"suplink\" target=\"_blank\" >","ELF executable",[32,428],{"value":38},"\nthat I've called ",[145,431,432],{},"myprog.bin",[17,434,435,436,439,440,442],{},"If we use ",[145,437,438],{},"ldd"," to inspect which dynamic libraries our program depends on, we can see that it does indeed depend on\n",[145,441,419],{},":",[175,444,449],{"className":445,"code":447,"language":448},[446],"language-text","$ ldd myprog.bin\n    linux-vdso.so.1 (0x00007f6ff7fec000)\n    libcurl.so.4 => \u002Fusr\u002Flib\u002Flibcurl.so.4 (0x00007f6ff7eb7000)\n    libc.so.6 => \u002Fusr\u002Flib\u002Flibc.so.6 (0x00007f6ff7c00000)\n    ...\n","text",[145,450,447],{"__ignoreMap":451},"",[17,453,454,455,458,459,462],{},"Let's now use ",[145,456,457],{},"readelf"," to check what ",[24,460,461],{},"symbols"," our program requires but has not yet defined, by grepping for “UND”\n(which stands for “undefined”):",[175,464,467],{"className":465,"code":466,"language":448},[446],"$ readelf -WCs myprog.bin | grep -i 'UND '\n    ...\n    81: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND curl_easy_init@CURL_OPENSSL_4\n    ...\n",[145,468,466],{"__ignoreMap":451},[17,470,471],{},"So the following information is built into our compiled binary, and can be used by the runtime linker when executing our\nprogram:",[92,473,474,479],{},[95,475,476,477,87],{},"Our program depends on the dynamic library ",[145,478,411],{},[95,480,481,482,485],{},"Our program requires the symbol ",[145,483,484],{},"curl_easy_init"," to be defined among any of our depended-upon dynamic libraries.",[394,487],{},[17,489,490],{},"We can now more concretely look at how we might call a C function from a different language, such as Rust. This section\nonly includes Rust examples, but the principles apply to other languages too.",[165,492,155,494,155,497,155,502],{"className":493},[168],[170,495,173],{"id":496},"linking-declaration-1",[498,499,501],"h4",{"id":500},"rust","Rust",[165,503,506,507,506,510,155],{"className":504},[505],"indent","\n\t\t",[17,508,509],{},"\n\t\t\tIn Rust, you can do this as part of your code using an attribute:\n\t\t",[175,511,512],{},[145,513,514],{},"#[link(name = \"c_lib\")]",[17,516,517],{},"Next, we want to define the function and its relevant types in our host language. It's worth pausing to look at the\nprevious diagram once again.",[153,519,155,520],{},[157,521],{"src":330,"alt":331},[17,523,524],{},"The diamonds represent data, which means that both our arguments and the return value use C data structures. So we need\nto use C types from within our host language.",[165,526,155,528,155,531,155,534,155,541],{"className":527},[168],[170,529,206],{"id":530},"type-definitions-1",[498,532,501],{"id":533},"rust-1",[17,535,536,537,540],{},"\n\t\tRust has built-in types such as ",[145,538,539],{},"c_char",".\n\t",[165,542,506,544,155],{"className":543},[505],[175,545,546],{},[145,547,548],{},"unsafe extern \"C\" {\n\tfn foo(a: ∗const c_char, b: i64, c: i64) -> ∗const c_char;\n}",[17,550,551],{},"At this point, we run into the data conversion issue we talked about before. Our host language needs to know about C's\ntypes, so that it can convert arguments into data structures C can understand:",[165,553,155,555,155,558,155,561],{"className":554},[168],[170,556,230],{"id":557},"data-conversion-1",[498,559,501],{"id":560},"rust-2",[165,562,506,564,506,570,155],{"className":563},[505],[17,565,566,567,442],{},"We convert our arguments to Rust's built-in C types, such as ",[145,568,569],{},"CString",[175,571,572],{},[145,573,574],{},"unsafe extern \"C\" {\n\tfoo(CString::new(\"hi\").unwrap().as_ptr(), 1, 2);\n}",[17,576,577],{},"Next, we need to actually find the address of the function's code within our dynamic library, using the function's\nsymbol (which is approximately the same as the function's name). Fortunately, we don't have to do anything here! The\nkernel will do this for us.",[165,579,155,581,155,584],{"className":580},[168],[170,582,253],{"id":583},"symbol-finding-1",[17,585,586,587,540],{},"\n\t\tThe OS handles this. On Linux, this is done in\n\t\t",[75,588,593],{"href":589,"className":590,"target":592},"https:\u002F\u002Fwww.man7.org\u002Flinux\u002Fman-pages\u002Fman8\u002Fld.so.8.html",[591],"suplink","_blank",[145,594,595],{},"ld-linux.so",[17,597,598],{},"Last, we need the assembly instructions that will actually call into our dynamic library. The compiler will\nautomatically handle this when compiling our host language's function call. So we're done.",[165,600,155,602,155,605],{"className":601},[168],[170,603,313],{"id":604},"call-generation-1",[17,606,607],{},"The compiler handles this.",[17,609,610],{},"I hope you can see why dynamic linking is easier than some other methods — the compiler and the kernel handled\nthe symbol finding and call generation for us. We just had to specify the function definitions and types we're using,\nand convert our data to those types.",[17,612,613],{},"At the same time, if we're trying to call C code from an interpreted language like Python, Ruby or JavaScript, this\napproach isn't helpful.",[615,616,155,617,155,625,155,646],"details",{},[618,619,620,621,624],"summary",{},"But sometimes, you ",[24,622,623],{},"can"," use dynamic linking with interpreted languages…kind of.",[17,626,627,628,633,634,639,640,645],{},"\n\t\tTools like\n\t\t",[75,629,632],{"href":630,"className":631,"target":592},"https:\u002F\u002Fcython.org\u002F",[591],"Cython",",\n\t\t",[75,635,638],{"href":636,"className":637,"target":592},"https:\u002F\u002Fnumba.pydata.org\u002F",[591],"Numba","\n\t\tand\n\t\t",[75,641,644],{"href":642,"className":643,"target":592},"https:\u002F\u002Fwww.swig.org\u002F",[591],"SWIG","\n\t\taugment interpreted languages like Python into something that can be compiled. This means that you can write\n\t\tcode that's 90% Python, but still use dynamic linking.\n\t",[17,647,648],{},"\n\t\tWhen using these tools, though, you end up with a full-blown compiled language, so the binary dependency\n\t\tsituation is functionally the same as in the Rust example. For this reason, I won't talk about this scenario in\n\t\tany more detail.\n\t",[130,650,652],{"id":651},"dynamic-loading","Dynamic Loading",[153,654,155,655,155,659],{},[157,656],{"src":657,"alt":658},"https:\u002F\u002Fvlad.website\u002Fillus\u002Fbinary-dependencies\u002Fgraph-dynload.svg","An illustration of dynamic loading",[333,660,661],{},"\n\t\tCalling a function using dynamic loading. The dashed arrows indicate that the actual function calls are\n\t\tgenerated at runtime, not compile time. Notice the blue diamonds — we're still using C data structures.\n\t",[17,663,664,665,87],{},"Are you calling C code from an interpreted language, like Python, Ruby or JavaScript? If so, dynamic linking is not an\noption. The alternative approach that is easiest to understand is ",[24,666,118],{},[17,668,669,672,673,675],{},[32,670],{"value":671},"\u003C link href=\"https:\u002F\u002Fdeveloper.ibm.com\u002Ftutorials\u002Fl-dynamic-libraries\u002F#dynamic-loading-with-linux1\" class=\"suplink\" target=\"_blank\" >","Dynamic loading",[32,674],{"value":38},"\nis fundamentally similar to dynamic linking. The main difference is that, because our code is not being compiled, we\nneed our linking to happen at runtime, so we can't take advantage of all of the information the compiler baked into our\nbinary in the “dynamic linking” section — since there's no compiler!",[17,677,678],{},"I'll provide code examples for Python, Ruby and JavaScript. But for now, let's look at the technical details.",[394,680],{},[17,682,683],{},"The first things we need to do when doing dynamic loading are to locate the dynamic library, and locate our desired\nsymbol within it.",[17,685,686,687,690,691,693],{},"Fortunately, Linux's helpful\n",[32,688],{"value":689},"\u003C link href=\"https:\u002F\u002Fwww.man7.org\u002Flinux\u002Fman-pages\u002Fman3\u002Fdlopen.3.html\" class=\"suplink\" target=\"_blank\" >","libdl",[32,692],{"value":38},",\nwhich has equivalents in other operating systems, allows us to do both of these tasks:",[175,695,698],{"className":696,"code":697,"language":448},[446],"\u002F\u002F Open dynamic library\nlib = dlopen(\"c_lib.so\");\n\u002F\u002F Locate symbol “foo” within it\nfoo = dlsym(lib, \"foo\");\n",[145,699,697],{"__ignoreMap":451},[17,701,702],{},"Here's where we run into trouble, though. How do we actually call this function?",[17,704,705],{},"When we were compiling our code, the compiler was able to generate the assembly code to correctly call our desired\nfunction according to our platform's calling convention. The compiler turned this:",[175,707,710],{"className":708,"code":709,"language":448},[446],"foo(\"hi\", 1, 2)\n",[145,711,709],{"__ignoreMap":451},[17,713,714],{},"into this:",[175,716,719],{"className":717,"code":718,"language":448},[446],"mov rdi, \"hi\"\nmov rsi, 1\nmov rdx, 2\ncall foo\n",[145,720,718],{"__ignoreMap":451},[17,722,723,724,285,726,289,728,730],{},"The compiler can do this because it's aware of our platform's calling conventions. For example, if we were on another\nprocessor architecture, putting our arguments in ",[145,725,284],{},[145,727,288],{},[145,729,292],{}," wouldn't be the right thing to do — we would\nneed to use other registers.",[17,732,733,734,737,738,87],{},"Without the compiler, we don't know how to do this. But fortunately, there's a library that does —\n",[32,735],{"value":736},"\u003C link href=\"https:\u002F\u002Fgithub.com\u002Flibffi\u002Flibffi\" class=\"suplink\" target=\"_blank\" >","libffi",[32,739],{"value":38},[17,741,742,743,745,746,748],{},"Because we can't call into our ",[145,744,147],{}," directly, we can call into libffi first. libffi will then know how to call\n",[145,747,147],{}," with the appropriate calling conventions. Neat!",[615,750,155,751,155,754,155,761],{},[618,752,753],{},"But…if we can't call external libraries…how can we call libffi, which is written in C? 😳",[17,755,756,757,760],{},"\n\t\tWe can't — at least not directly! We have to rely on some help from our language's interpreter, which, of course,\n\t\t",[24,758,759],{},"is"," a compiled program.\n\t",[17,762,763,764,769,770,777],{},"\n\t\tFor example, Python's\n\t\t",[75,765,768],{"href":766,"className":767,"target":592},"https:\u002F\u002Fgithub.com\u002Fpython\u002Fcpython\u002Ftree\u002F3.14\u002FLib\u002Fctypes",[591],"ctypes","\n\t\tlibrary, which wraps libffi, is written in Python. But it depends on Python's\n\t\t",[75,771,774],{"href":772,"className":773,"target":592},"https:\u002F\u002Fgithub.com\u002Fpython\u002Fcpython\u002Ftree\u002F3.14\u002FModules\u002F_ctypes",[591],[145,775,776],{},"_ctypes","\n\t\tmodule, which is part of the CPython interpreter, which is written in C.\n\t",[394,779],{},[17,781,782,783,786],{},"Putting this all together, we now use some version of ",[145,784,785],{},"dlopen"," to find and open our dynamic library. The specifics vary\nbetween different languages:",[165,788,155,790,155,793,155,797,155,816,155,821,155,825,155,834,155,839,155,843,155,852],{"className":789},[168],[170,791,173],{"id":792},"linking-declaration-2",[498,794,796],{"id":795},"python","Python",[17,798,799,800,804,805,810,811,540],{},"\n\t\tIn Python, this used to be accomplished by\n\t\t",[75,801,768],{"href":802,"className":803,"target":592},"https:\u002F\u002Fdocs.python.org\u002F3\u002Flibrary\u002Fctypes.html",[591],",\n\t\tbut the better way is to use\n\t\t",[75,806,809],{"href":807,"className":808,"target":592},"https:\u002F\u002Fcffi.readthedocs.io\u002Fen\u002Fstable\u002F",[591],"cffi","\n\t\tin its\n\t\t",[75,812,815],{"href":813,"className":814,"target":592},"https:\u002F\u002Fcffi.readthedocs.io\u002Fen\u002Fstable\u002Foverview.html#simple-example-abi-level-in-line",[591],"ABI mode",[175,817,818],{},[145,819,820],{},"from cffi import FFI\nC = ffi.dlopen(\"c_lib.so\")",[498,822,824],{"id":823},"ruby","Ruby",[17,826,827,828,833],{},"\n\t\tIn Ruby, this is accomplished with the\n\t\t",[75,829,832],{"href":830,"className":831,"target":592},"https:\u002F\u002Fgithub.com\u002Fffi\u002Fffi?tab=readme-ov-file",[591],"Ruby-FFI","\n\t\tgem.\n\t",[175,835,836],{},[145,837,838],{},"require 'ffi'\nmodule CLib\n\textend FFI::Library\n\tffi_lib 'c_lib'\nend",[498,840,842],{"id":841},"javascript","JavaScript",[17,844,845,846,851],{},"\n\t\tIn Node.js, use\n\t\t",[75,847,850],{"href":848,"className":849,"target":592},"https:\u002F\u002Fgithub.com\u002Fnode-ffi\u002Fnode-ffi",[591],"node-ffi",":\n\t",[175,853,854],{},[145,855,856],{},"const ffi = require('ffi');\nconst c_lib = ffi.Library('c_lib');",[17,858,859],{},"Of course, we also need to provide our type definitions. Let's double-check our diagram:",[153,861,155,862],{},[157,863],{"src":657,"alt":658},[17,865,866,867,870],{},"We are ",[24,868,869],{},"still"," using C types to talk to our dynamic library, so we need to use the C types built into whatever FFI\nlibrary we're using.",[165,872,155,874,155,877,155,880,155,883,155,888,155,891,155,902,155,907,155,910,155,913],{"className":873},[168],[170,875,206],{"id":876},"type-definitions-2",[498,878,796],{"id":879},"python-1",[17,881,882],{},"\n\t\tPython's cffi allows us to copy-paste C function declarations directly, which it will then parse, which saves us\n\t\tsome work.\n\t",[175,884,885],{},[145,886,887],{},"ffi.cdef(\"\"\"\n\tconst char* foo(const char*, int64_t, int64_t);\n\"\"\")",[498,889,824],{"id":890},"ruby-1",[17,892,893,894,897,898,901],{},"\n\t\tRuby-FFI offers some slightly unintuitively-named C types\n\t\t(",[145,895,896],{},":pointer"," is used for ",[145,899,900],{},"const char*","):\n\t",[175,903,904],{},[145,905,906],{},"require 'ffi'\nmodule CLib\n\textend FFI::Library\n\tffi_lib 'c_lib'\n\tattach_function :foo, [ :pointer, :int64, :int64 ], :pointer\nend",[498,908,842],{"id":909},"javascript-1",[17,911,912],{},"\n\t\tnode-ffi is similar:\n\t",[175,914,915],{},[145,916,917],{},"const ffi = require('ffi');\nconst c_lib = ffi.Library('c_lib', {\n\t'foo': [ 'string', [ 'string', 'int64', 'int64' ] ]\n});",[17,919,920],{},"Most libraries handle most type conversions automatically (except that first string for Python's cffi):",[165,922,155,924,155,927,155,930,155,935,155,938,155,943,155,946],{"className":923},[168],[170,925,230],{"id":926},"data-conversion-2",[498,928,796],{"id":929},"python-2",[175,931,932],{},[145,933,934],{},"foo(ffi.new(\"char[]\", b\"hello\"), 1, 2)",[498,936,824],{"id":937},"ruby-2",[175,939,940],{},[145,941,942],{},"CLib.foo(\"hello\", 1, 2)",[498,944,842],{"id":945},"javascript-2",[175,947,948],{},[145,949,950],{},"lib.foo(\"hello\", 1, 2)",[17,952,953,954,957],{},"And that's it! libffi will call ",[145,955,956],{},"dlsym"," to find the appropriate symbol in our dynamic library…",[165,959,155,961,155,964],{"className":960},[168],[170,962,253],{"id":963},"symbol-finding-2",[17,965,966],{},"Handled by libffi.",[17,968,969],{},"…and libffi will also generate our function call.",[165,971,155,973,155,976],{"className":972},[168],[170,974,313],{"id":975},"call-generation-2",[17,977,966],{},[130,979,981],{"id":980},"dynamic-linking-with-extension-modules","Dynamic Linking with Extension Modules",[153,983,155,984,155,988],{},[157,985],{"src":986,"alt":987},"https:\u002F\u002Fvlad.website\u002Fillus\u002Fbinary-dependencies\u002Fgraph-extmod.svg","An illustration of dynamic linking with extension modules",[333,989,990],{},"\n\t\tCalling a function using dynamic linking together with extension modules. The extension module in the center\n\t\tworks with the host language's data types (eg Python data types) on one side, and C data types on the other\n\t\tside. This allows the extension module to perform type conversions whenever would be best for performance.\n\t",[17,992,993],{},"The aforementioned solutions have one big limitation. Say our host language is Python. We can call C functions from\nPython as much as we want, which is great. But every time we do this, we have to convert Python data structures into C\ndata structures, and the other way around for the return values. This means we're doing a lot of extra work, which could\nresult in a big performance hit.",[17,995,996],{},"More generally, it would be helpful to be able to decide which work to do in Python, and which work to do in C. This\nwould also give us full flexibility to decide which code should use Python data structures, and which code should use C\ndata structures, so that we can decide to perform conversions at the best possible time.",[17,998,999,1000,1003,1004,1006,1007,1010,1011,1013],{},"And having a way to do some work in C means that we could call the ",[145,1001,1002],{},"c_lib"," dynamic library…from our own C code! We could\nnatively use ",[145,1005,1002],{},"'s API by doing ",[145,1008,1009],{},"#include \u003Cc_lib.h>"," in our C code. This would allow us to take the simpler\ndynamic linking approach when calling ",[145,1012,1002],{},"'s code, taking advantage of the kernel and the runtime dynamic linker —\nbetter performance, and less work.",[394,1015],{},[17,1017,1018,1019,1022],{},"To achieve this, we can use ",[24,1020,1021],{},"extension modules",". Extension modules are C programs that also know about the types of our\nhost language. So a Python extension module is a C program that knows how to construct Python data structures. Extension\nmodules gain this ability by including some C definitions provided by the language creators, such as:",[92,1024,1025,1044,1054],{},[95,1026,1027,1030,1033,1035,1036,1039,1042],{},[32,1028],{"value":1029},"\u003C link href=\"https:\u002F\u002Fgithub.com\u002Fpython\u002Fcpython\u002Fblob\u002Fmain\u002FInclude\u002FPython.h\" class=\"suplink\" target=\"_blank\" >",[145,1031,1032],{},"Python.h",[32,1034],{"value":38},"\nor ",[32,1037],{"value":1038},"\u003C link href=\"https:\u002F\u002Fhpyproject.org\u002F\" class=\"suplink\" target=\"_blank\" >",[145,1040,1041],{},"hpy.h",[32,1043],{"value":38},[95,1045,1046,1049,1052],{},[32,1047],{"value":1048},"\u003C link href=\"https:\u002F\u002Fgithub.com\u002Fruby\u002Fruby\u002Fblob\u002Fmaster\u002Finclude\u002Fruby\u002Fruby.h\" class=\"suplink\" target=\"_blank\" >",[145,1050,1051],{},"ruby.h",[32,1053],{"value":38},[95,1055,1056,1059,1062,87],{},[32,1057],{"value":1058},"\u003C link href=\"https:\u002F\u002Fgithub.com\u002Fnodejs\u002Fnode\u002Fblob\u002Fmain\u002Fsrc\u002Fnode.h\" class=\"suplink\" target=\"_blank\" >",[145,1060,1061],{},"node.h",[32,1063],{"value":38},[17,1065,1066],{},"Of course, we still need to call our extension module's C code from our host language. But because we're writing both\nthe host (eg Python) code, and the extension module's code, we can use whatever calling mechanism is most convenient for\nus, giving us a lot more flexibility.",[17,1068,1069,1070,87],{},"Because our Python\u002FRuby\u002FJavaScript interpreter will have support for native modules built in, we don't even need to do\nanything special to have our interpreter load our compiled C module — we can just do something like ",[145,1071,1072],{},"import ext",[17,1074,1075,1076,1079,1080,1083,1084,1086,1087,1090],{},"That's right, even though our ",[145,1077,1078],{},"ext.so"," dynamic library was written in C, Python\u002FRuby\u002FJavaScript can ",[24,1081,1082],{},"directly import","\nthis C dynamic module with a simple ",[145,1085,1072],{}," or equivalent, by special-casing ",[145,1088,1089],{},"import"," to recognise extension\nmodules. This is really convenient.",[394,1092],{},[17,1094,1095],{},"Extension modules are available via:",[92,1097,1098,1107,1115],{},[95,1099,1100,1103,1104,1106],{},[32,1101],{"value":1102},"\u003C link href=\"https:\u002F\u002Fdocs.python.org\u002F3\u002Fextending\u002Fextending.html\" class=\"suplink\" target=\"_blank\" >","Python's extension modules",[32,1105],{"value":38},",",[95,1108,1109,1112,1113,119],{},[32,1110],{"value":1111},"\u003C link href=\"https:\u002F\u002Fdocs.ruby-lang.org\u002Fen\u002Fmaster\u002Fextension_rdoc.html\" class=\"suplink\" target=\"_blank\" >","Ruby's extension libraries",[32,1114],{"value":38},[95,1116,1117,1120,1121,87],{},[32,1118],{"value":1119},"\u003C link href=\"https:\u002F\u002Fnodejs.org\u002Fapi\u002Faddons.html\" class=\"suplink\" target=\"_blank\" >","Node.js's C++ addons",[32,1122],{"value":38},[17,1124,1125],{},"In Python, an extension module might look vaguely like this:",[175,1127,1130],{"className":1128,"code":1129,"language":448},[446],"#define PY_SSIZE_T_CLEAN\n#include \u003CPython.h>\n\nstatic PyObject *\nfoo(PyObject *self, PyObject *args) {\n    ...\n}\n\nstatic PyMethodDef methods[] = {\n    { \"foo\", foo, METH_VARARGS, \"\" },\n    { NULL, NULL, 0, NULL }\n};\n\nstatic struct PyModuleDef module = {\n    PyModuleDef_HEAD_INIT, \"foo\", NULL, -1, methods\n};\n\nPyMODINIT_FUNC PyInit_ext(void) {\n    return PyModule_Create(&module);\n}\n",[145,1131,1129],{"__ignoreMap":451},[17,1133,1134],{},"In Ruby, it might look something like this:",[175,1136,1139],{"className":1137,"code":1138,"language":448},[446],"#include \"ruby\u002Fruby.h\"\n\nstatic VALUE\nfoo(VALUE self, VALUE a, VALUE b, VALUE c) {\n    ...\n}\n\nvoid Init_extension(void) {\n    VALUE Ext = rb_define_module(\"Ext\");\n    VALUE NativeHelpers = rb_define_class_under(Ext, \"NativeHelpers\", rb_cObject);\n    rb_define_singleton_method(NativeHelpers, \"foo\", foo, 3);\n}\n",[145,1140,1138],{"__ignoreMap":451},[17,1142,1143],{},"And in Node.js, something like this:",[175,1145,1148],{"className":1146,"code":1147,"language":448},[446],"#include \u003Cnode.h>\n\nnamespace ext {\n    ...\n\n    void Method(const FunctionCallbackInfo\u003CValue>& args) {\n        ...\n    }\n\n    void Initialize(Local\u003CObject> exports) {\n        NODE_SET_METHOD(exports, \"foo\", Method);\n    }\n\n    NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)\n}\n",[145,1149,1147],{"__ignoreMap":451},[17,1151,1152,1153,87],{},"Each of these files will get compiled with a C compiler — let's say the compiled module gets put in a file called\n",[145,1154,1078],{},[17,1156,1157,1158,1160],{},"Thanks to special-casing ",[145,1159,1089],{}," statements, these modules get loaded very easily:",[165,1162,155,1164,155,1167,155,1170,155,1174,155,1177,155,1182,155,1185],{"className":1163},[168],[170,1165,173],{"id":1166},"linking-declaration-3",[498,1168,796],{"id":1169},"python-3",[175,1171,1172],{},[145,1173,1072],{},[498,1175,824],{"id":1176},"ruby-3",[175,1178,1179],{},[145,1180,1181],{},"require \"ext\"",[498,1183,842],{"id":1184},"javascript-3",[175,1186,1187],{},[145,1188,1189],{},"require(\"ext\")",[17,1191,1192],{},"Things are also simpler when it comes to type definitions.",[17,1194,1195,1196,1198,1199,87],{},"You can trivially get ",[145,1197,1002],{},"'s type definitions in your extension module's C code by just doing ",[145,1200,1009],{},[17,1202,1203,1204,1207],{},"And when it comes to calls between your host language and your C extension module, there's no need to tediously\ntranslate C definitions into the equivalent Python\u002FRuby\u002FNode.js data structures. Various libraries can automate this\nprocess, by taking in your C definitions, and generating ",[24,1205,1206],{},"bindings"," — Python\u002FRuby\u002FNode.js code that allows you to\ndirectly call your C functions.",[165,1209,155,1211,155,1214,155,1217,155,1236,155,1241,155,1248,155,1256,155,1259,155,1273,155,1276],{"className":1210},[168],[170,1212,206],{"id":1213},"type-definitions-3",[498,1215,796],{"id":1216},"python-4",[17,1218,627,1219,633,1224,1229,1230,1235],{},[75,1220,1223],{"href":1221,"className":1222,"target":592},"https:\u002F\u002Fgithub.com\u002Fwjakob\u002Fnanobind",[591],"nanobind",[75,1225,1228],{"href":1226,"className":1227,"target":592},"https:\u002F\u002Fpybind11.readthedocs.io\u002Fen\u002Fstable\u002F",[591],"pybind11"," and\n\t\t",[75,1231,1234],{"href":1232,"className":1233,"target":592},"https:\u002F\u002Fwww.boost.org\u002Fdoc\u002Flibs\u002Flatest\u002Flibs\u002Fpython\u002Fdoc\u002Fhtml\u002Findex.html",[591],"Boost.Python","\n\t\tcan generate type definitions automatically from your C code. Here's a quick preview of how they work:\n\t",[175,1237,1238],{},[145,1239,1240],{},"#include \u003Cnanobind\u002Fnanobind.h>\nconst char* foo(const char *a, int64_t b, int64_t c) { ... }\nNB_MODULE(ext, m) {\n\tm.def(\"foo\", &foo);\n}",[17,1242,1243,1244,1247],{},"\n\t\tAlongside your own C code, you include C++ code from one of these binding generation libraries. This C++ code\n\t\ttakes advantage of C++'s introspection abilities — when you call ",[145,1245,1246],{},"NB_MODULE",", nanobind runs a\n\t\tbunch of C++ code that examines all of your function definitions, then creates Python glue code based on\n\t\tthem. This is really convenient.\n\t",[17,1249,1250,1251,540],{},"\n\t\tIf you're looking for something more manual, you can use\n\t\t",[75,1252,1255],{"href":1253,"className":1254,"target":592},"https:\u002F\u002Fcffi.readthedocs.io\u002Fen\u002Fstable\u002Foverview.html",[591],"cffi in API mode",[498,1257,824],{"id":1258},"ruby-4",[17,1260,1261,1262,633,1267,1272],{},"\n\t\tType definitions can be automated using\n\t\t",[75,1263,1266],{"href":1264,"className":1265,"target":592},"https:\u002F\u002Fgithub.com\u002Fruby-rice\u002Fruby-bindgen",[591],"ruby-bindgen",[75,1268,1271],{"href":1269,"className":1270,"target":592},"https:\u002F\u002Fgithub.com\u002Fruby-rice\u002Frice",[591],"rice"," etc.\n\t",[498,1274,842],{"id":1275},"javascript-4",[17,1277,506,1278,1283],{},[75,1279,1282],{"href":1280,"className":1281,"target":592},"https:\u002F\u002Fgithub.com\u002Fcharto\u002Fnbind#readme",[591],"nbind","\n\t\tis very similar to nanobind.\n\t",[17,1285,1286,1287,1289],{},"There is no need for any data conversion between your C extension module and the ",[145,1288,1002],{}," code you're calling, since they\nboth use C data structures. And with respect to conversion for calls between your host code and your C extension\nmodule, you now have more flexibility than in previous solutions.",[165,1291,155,1293,155,1296,155,1299,155,1308],{"className":1292},[168],[170,1294,230],{"id":1295},"data-conversion-3",[17,1297,1298],{},"\n\t\tBecause your extension module can create both Python\u002FRuby\u002FNode.js data structures and C data structures, you can\n\t\tperform data conversion whenever is most performant.\n\t",[17,1300,1301,1302,1304,1305,1307],{},"\n\t\tFor example, say you need to call ",[145,1303,1002],{},"'s function ",[145,1306,359],{}," 500 times. If you generate\n\t\t500 different function calls from your host language, you'll have to convert data back and forth 500 times.\n\t",[17,1309,1310,1311,1313,1314,1317],{},"\n\t\tBut if you call ",[145,1312,359],{}," 500 times from your extension module, keeping the data in the existing C\n\t\tstructures, you'll only have to convert that data to Python\u002FRuby\u002FNode.js data structures ",[24,1315,1316],{},"once"," — much\n\t\tbetter!\n\t",[17,1319,1320],{},"Symbol finding is also addressed by the aforementioned generation libraries, since you tell them about the functions in\nyour extension module using their own custom syntax.",[17,1322,1323,1324,1326,1327,1329],{},"Of course, when you call code in ",[145,1325,1002],{}," from your extension module, you don't have to do anything special — the runtime\ndynamic linker will do the work to find symbols like for our example ",[145,1328,241],{}," function, just like in our first dynamic\nlinking example.",[165,1331,155,1333,155,1336],{"className":1332},[168],[170,1334,253],{"id":1335},"symbol-finding-3",[17,1337,1338],{},"\n\t\tAddressed by binding generation libraries.\n\t",[17,1340,1341,1342,1344],{},"Call generation between your C extension module and ",[145,1343,1002],{}," is not a problem, since it's handled by the C compiler.",[17,1346,1347],{},"To generate the calls from your host code to your C extension module, each language has its own convention. I'm\nincluding these for completeness, though you don't need to worry about them at all:",[165,1349,155,1351,155,1354,155,1357,155,1373,155,1376,155,1385,155,1388],{"className":1350},[168],[170,1352,313],{"id":1353},"call-generation-3",[498,1355,796],{"id":1356},"python-5",[17,1358,506,1359,633,1366,540],{},[75,1360,1363],{"href":1361,"className":1362,"target":592},"https:\u002F\u002Fpeps.python.org\u002Fpep-0590\u002F",[591],[145,1364,1365],{},"vectorcall",[75,1367,1370],{"href":1368,"className":1369,"target":592},"https:\u002F\u002Fdocs.python.org\u002F3\u002Fc-api\u002Fcall.html",[591],[145,1371,1372],{},"tp_call",[498,1374,824],{"id":1375},"ruby-5",[17,1377,506,1378,540],{},[75,1379,1382],{"href":1380,"className":1381,"target":592},"https:\u002F\u002Fgithub.com\u002Fruby\u002Fruby\u002Fblob\u002F8a586af33b59cae93a1bee13c39e87dd087a4a6b\u002Fvm.c#L3096",[591],[145,1383,1384],{},"rb_vm_call_cfunc",[498,1386,842],{"id":1387},"javascript-5",[17,1389,1390],{},"\n\t\tI don't know! Do you? If so, let me know.\n\t",[17,1392,1393],{},"That's all for this particular method — dynamic linking with extension modules.",[17,1395,1396],{},"We've seen that extension modules have plenty of advantages. Are there any disadvantages?",[17,1398,1399],{},"Well, you have to write a bunch of C code, and either write quite tiresome bindings yourself, or import a lot of\nautogeneration machinery. It's up to you to decide whether that's worth it.",[394,1401],{},[615,1403,155,1404,155,1407,155,1410,155,1418,155,1421],{},[618,1405,1406],{},"A bonus fourth strategy: what does Go do?",[17,1408,1409],{},"\n\t\tGo is a compiled language, but it does not allow direct dynamic linking, as far as I can tell.\n\t",[17,1411,1412,1413,540],{},"\n\t\tInstead, Go uses a lightweight extension module system, implemented as part of\n\t\t",[75,1414,1417],{"href":1415,"className":1416,"target":592},"https:\u002F\u002Fpkg.go.dev\u002Fcmd\u002Fcgo",[591],"cgo",[17,1419,1420],{},"\n\t\tHowever, Go goes beyond simpler extension module implementations in that it bundles its own FFI implementation,\n\t\teffectively implementing a small libffi from scratch. We can think of cgo as a complete, lightweight,\n\t\tself-contained and optimised extension module system — not surprising, given Go's design philosophy.\n\t",[17,1422,1423,1424,1428,1429,540],{},"\n\t\tI haven't covered Go specifically above, but you can read more about cgo\n\t\t",[75,1425,1427],{"href":1426,"target":592},"https:\u002F\u002Fgo.dev\u002Fblog\u002Fcgo","here",",\n\t\tand if you're feeling brave, you can also read the full implementation details in\n\t\t",[75,1430,1432],{"href":1431,"target":592},"https:\u002F\u002Fgo.dev\u002Fsrc\u002Fruntime\u002Fcgocall.go","cgocall.go",[17,1434,1435],{},"And that's all I wanted to cover.",[17,1437,1438,1439,1443],{},"This is a living document, so your ",[75,1440,1442],{"href":1441},"\u002F#contact","feedback is welcome",". Given the complexity of the topic, I'm sure my\nunderstanding could be improved.",[17,1445,1446,1447],{},"Still, I hope you found this overview helpful. ",[1448,1449],"span",{"className":1450},[1451],"ender",[130,1453,1455],{"id":1454},"additional-resources","Additional Resources",[92,1457,1458,1468],{},[95,1459,1460,1467],{},[24,1461,1462],{},[75,1463,1466],{"href":1464,"rel":1465},"https:\u002F\u002Fgithub.com\u002Fsony\u002Fesstra",[79],"ESSTRA",", a tool developed at Sony that embeds metadata into binaries at compile time, so that they can later\nbe traced.",[95,1469,1470,1477,1478,1481,1482,1485,1486,87],{},[24,1471,1472],{},[75,1473,1476],{"href":1474,"rel":1475},"https:\u002F\u002Fgithub.com\u002Fpypa\u002Fauditwheel",[79],"auditwheel",", a Python tool that can analyse the binary dependency of wheels, using ",[145,1479,1480],{},"DT_NEEDED"," records\nfrom the ",[145,1483,1484],{},".dynamic"," section found in ELF files. See particularly ",[75,1487,1490],{"href":1488,"rel":1489},"https:\u002F\u002Fgithub.com\u002Fpypa\u002Fauditwheel\u002Fpull\u002F577",[79],"PR #577",{"title":451,"searchDepth":1492,"depth":1492,"links":1493},2,[1494,1502,1509,1516,1523],{"id":132,"depth":1492,"text":133,"children":1495},[1496,1498,1499,1500,1501],{"id":172,"depth":1497,"text":173},3,{"id":205,"depth":1497,"text":206},{"id":229,"depth":1497,"text":230},{"id":252,"depth":1497,"text":253},{"id":312,"depth":1497,"text":313},{"id":324,"depth":1492,"text":325,"children":1503},[1504,1505,1506,1507,1508],{"id":496,"depth":1497,"text":173},{"id":530,"depth":1497,"text":206},{"id":557,"depth":1497,"text":230},{"id":583,"depth":1497,"text":253},{"id":604,"depth":1497,"text":313},{"id":651,"depth":1492,"text":652,"children":1510},[1511,1512,1513,1514,1515],{"id":792,"depth":1497,"text":173},{"id":876,"depth":1497,"text":206},{"id":926,"depth":1497,"text":230},{"id":963,"depth":1497,"text":253},{"id":975,"depth":1497,"text":313},{"id":980,"depth":1492,"text":981,"children":1517},[1518,1519,1520,1521,1522],{"id":1166,"depth":1497,"text":173},{"id":1213,"depth":1497,"text":206},{"id":1295,"depth":1497,"text":230},{"id":1335,"depth":1497,"text":253},{"id":1353,"depth":1497,"text":313},{"id":1454,"depth":1492,"text":1455},"https:\u002F\u002Fvlad.website\u002Fhow-binary-dependencies-work\u002F","vlad.website","software-supply-chains","2026-01-16","Have you ever wondered how it's possible to call C code from other languages like Python? Here are the technical details — and also why this topic is important for security and sustainability.","md",false,"https:\u002F\u002Fvlad.website\u002Fstatic\u002Fhow-binary-dependencies-work-header.webp","A graph showing the different kinds of binary dependencies, all jumbled together",null,{},"https:\u002F\u002Fvlad.website\u002Fimages\u002Fopengraph\u002Fhow-binary-dependencies-work.png",true,"\u002Freports\u002Fhow-binary-dependencies-work",{"title":10,"description":1528},"reports\u002Fhow-binary-dependencies-work","Xr4rKyqU8xC9E41sv5Z4sLp4TutXVhGJob5fHMoFbqk",1780596105091]