ALPM-SONAMEV1(7)
okay so bestie, buckle up π
this is ALPM-SONAMEV1, section (7) of the Miscellaneous Information Manual β and heads up, we're in legacy territory here, so keep that in mind the whole way through π«Ά
πΈ NAME πΈ
soname β how soname data is represented and used in ALPM-based packaging. that's the whole vibe β¨
π DESCRIPTION π
okay so sonames[1] are literally the mechanism that keeps binary compatibility on lock between shared objects and the things that consume them (i.e. other ELF[2] files). more specifically, the soname[1] data gets encoded in the SONAME field of the dynamic section inside shared object files, and it usually denotes specific info about the version of that object file. meanwhile ELF[2] files can dynamically link against specific versions of shared objects, and they declare that dependency with the help of the NEEDED field in their dynamic section. no cap π§
so the strings that represent soname info can be used in alpm-package-relations of type provision and run-time dependency β which is exactly how you get strict package dependency setups based on ABI compatibility. slay. π₯
now, the alpm-soname format exists in multiple versions, and this doc is about version 1, which is a legacy version β it was actually removed with the release of pacman 6.1 on 2024-03-04 π. for the latest spec, go read alpm-soname instead.
π¦ FORMAT π¦
okay so how soname info is represented depends on which ALPM file format it shows up in. keep up π
file formats that represent static package source data (i.e. PKGBUILD and SRCINFO) are a whole different category from the ones that represent dynamic package build environment data (i.e. PKGINFO).
π static source data: SRCINFO / PKGBUILD π
the SRCINFO file format (and by extension PKGBUILD too) is used to represent static data about package sources, so it should only ever refer to the basic form of alpm-sonamev1. and the basic form is defined as the name of the shared object file that the soname data is encoded in (e.g. libexample.so). simple, iconic, done β¨
π
dynamic build data: PKGINFO π
the PKGINFO file format is where dynamic, build-environment-specific data is allowed to live. in these files, soname data may be referred to using the unversioned form or the explicit form (both of them contain information derived from the targeted shared object file).
the unversioned form
the unversioned form is defined as: the name of a shared object file, directly followed by an = sign, directly followed by a shared object's (unversioned) soname string, directly followed by a - sign, directly followed by the ELF architecture format of the shared object file (e.g. libexample.so=libexample.so-64). refer to readelf for further info on how to extract relevant fields such as SONAME or NEEDED from the dynamic section of an ELF[2] file π«‘
the explicit form
the explicit form is defined as: the name of a shared object file, directly followed by an = sign, directly followed by a version string, directly followed by a - sign, directly followed by the ELF architecture format (e.g. libexample.so=1-64). the version string represents the version info that the developer of the shared object encoded as part of the soname β which is everything following the .so. suffix (e.g. 1 in libexample.so.1, or 1.0 in libexample.so.1.0). usually this is the current interface number of the library interface version[3]. and again: refer to readelf for further info on how to extract relevant fields such as SONAME or NEEDED from the dynamic section of an ELF[2] file π
the ELF architecture format
the ELF architecture format is a number derived from the Class field of the ELF header of an ELF[2] file. for the 32-bit format it's 32, for the 64-bit format it's 64. refer to readelf for further info on how to extract the ELF header from an ELF[2] file π
π the least specific file name rule π
when you use the name of a shared object file, it always refers to the least specific file name. so if, for example, these files exist on a system:
/usr/lib/libexample.so -> libexample.so.1
/usr/lib/libexample.so.1 -> libexample.so.1.0.0
/usr/lib/libexample.so.1.0.0
then the symlink libexample.so is the least specific file name, while libexample.so.1.0.0 would refer to the actual ELF[2] file. ikr π
π the notes you should NOT skip π
Note: while it's technically possible, it is strongly discouraged to use the unversioned or explicit form inPKGBUILDorSRCINFO! that's because it requires manual handling of dynamically changing soname data in the static package source file formats β and those may end up out of sync with the actual, explicit data derived from the build environment π
Note: both the basic and the explicit forms of thealpm-sonamev1format overlap withalpm-package-nameand composite comparison expressions as defined in thealpm-comparisonformat, respectively. because of this ambiguity it is essential to use thealpm-sonamev1forms in their respective correct file formats β or, ideally, just switch to a later version of the alpm-soname format altogether π
π₯ USAGE π₯
a package can depend on a specific soname with the help of an alpm-package-relation of type run-time dependency, if another package provides a matching soname in their alpm-package-relation of type provision. matching follows the rules of alpm-comparison, which means the name of the shared object file is matched as alpm-package-name, and the remainder of the unversioned or explicit form is matched as alpm-package-version β and in there, the ELF architecture format is interpreted as the alpm-pkgrel. so:
- a run-time dependency on
libexample.so=1orlibexample.so>=1is matched by a provision oflibexample.so=1-64as well aslibexample.so=1-32β - while a run-time dependency on
libexample.so=1-32is only satisfied by an exact match againstlibexample.so=1-32π―
more specifically: a soname dependency of one package is based on the soname data of a shared object file provided by one of its dependency packages. that's the whole chain, bestie π
π what a build tool does automatically π
a package build tool (e.g. makepkg) automatically derives soname information for ELF[2] files in the build environment, based on the following rules:
- if the package that is built contains a shared object file, and the name of that shared object file (i.e. the basic form) is present as an alpm-package-relation of type provision in the package's
PKGBUILDfile (e.g.provides=(libexample.so)), then relevant data from theSONAMEfield in the dynamic section of the targeted ELF[2] file is extracted.- if the soname data contains a version, it is extracted together with the ELF architecture format of the file to construct the explicit form. it is added instead of the basic form to the alpm-package-relation of type provision in the
PKGINFOfile of the package (e.g.provides = libexample.so=1-64). π - if the soname data does not contain a version, the entire soname together with the ELF architecture format of the file is used to construct the unversioned form. it is added instead of the basic form to the alpm-package-relation of type provision in the
PKGINFOfile of the package (e.g.provides = libexample.so=libexample.so-64). π
- if the soname data contains a version, it is extracted together with the ELF architecture format of the file to construct the explicit form. it is added instead of the basic form to the alpm-package-relation of type provision in the
- if the package that is built contains an ELF[2] file that dynamically links against a shared object available in the build environment, and the name of that shared object file (i.e. the basic form) is present as an alpm-package-relation of type run-time dependency in the package's
PKGBUILDfile (e.g.depends=(libexample.so)), then relevant soname data from theNEEDEDfields in the dynamic section of the ELF[2] file is extracted for those specific shared objects.- all soname data that contains a version is extracted together with the ELF architecture format of the ELF[2] file and is used to construct the explicit form. it is matched against the alpm-package-relation of type provision in the
PKGINFOdata of the packages available in the build environment. if one matches, the explicit form instead of the basic form is added to the alpm-package-relation of type run-time dependency in thePKGINFOfile of the package that is being built (e.g.depend = libexample.so=1-64). π¦ - all soname data that does not contain a version is extracted in its entirety, together with the ELF architecture format of the ELF[2] file, and is used to construct the unversioned form. it is matched against the alpm-package-relation of type provision in the
PKGINFOdata of the packages available in the build environment. if one matches, the unversioned form instead of the basic form is added to the alpm-package-relation of type run-time dependency in thePKGINFOfile of the package that is being built (e.g.depend = libexample.so=1-64). π¦
- all soname data that contains a version is extracted together with the ELF architecture format of the ELF[2] file and is used to construct the explicit form. it is matched against the alpm-package-relation of type provision in the
π Providing a soname
Okay so picture this: you've got a PKGBUILD for a package called example, and it builds + installs an upstream project that ships a shared object named libexample.so. Iconic. Here's the whole thing, no cap:
pkgname=example
pkgver=1.0.0
pkgrel=1
pkgdesc="An example library"
arch=(x86_64)
url="https://example.org/library.html"
license=(MIT)
depends=(glibc)
provides=(libexample.so)
source=("https://example.org/$pkgname-$pkgver.tar.gz")
sha256sums=(7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730)
build() {
make -C $pkgname-$pkgver
}
package() {
make DESTDIR="$pkgdir" install -C $pkgname-$pkgver
}
The following SRCINFO file can be generated from the above PKGBUILD file:
pkgbase = example
pkgdesc = An example library
pkgver = 1.0.0
pkgrel = 1
arch = x86_64
url = https://example.org/library.html
license = MIT
depends = glibc
provides = libexample.so
source = https://example.org/example-1.0.0.tar.gz
sha256sums = 7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730
pkgname = example
This example assumes that the project results in installing the following files to the filesystem:
/usr/lib/libexample.so -> libexample.so.1
/usr/lib/libexample.so.1 -> libexample.so.1.0.0
/usr/lib/libexample.so.1.0.0
Here, the file /usr/lib/libexample.so.1.0.0 encodes the soname libexample.so.1. π
After building from source, the resulting package file for example contains the following PKGINFO file:
pkgname = example
pkgver = 1.0.0-1
pkgdesc = An example library
url = https://example.org/library.html
builddate = 1729181726
packager = Your Name <your.name@example.org>
size = 181849963
arch = x86_64
license = MIT
provides = libexample.so=1-64
depend = glibc
π¦ Depending on a soname
Now flip it π«Ά β here's a PKGBUILD for a package named application, and it builds an upstream project that depends on the example package from the previous example. More specifically, the resulting package depends on the shared object libexample.so, which is provided by the example package.
Real talk though, bestie, the placement here is not random. In the PKGBUILD, the run-time dependency on the libexample.so shared object gets added in the package function instead of in the global run-time dependency declaration, while example is declared as a global build dependency. Why? Because multiple sonames with the same name but different ELF architecture formats may exist (think the explicit forms libexample.so=1-32 and libexample.so=1-64). If libexample.so was exposed as a global run-time dependency, a build tool may accidentally pull in the wrong package containing a libexample.so β and that's because of the existing structural overlap between alpm-sonamev1 and alpm-package-relation! π So yeah, that's a whole thing to be aware of.
pkgname=application
pkgver=1.0.0
pkgrel=1
pkgdesc="An example application"
arch=(x86_64)
url="https://example.org/application.html"
license=(MIT)
depends=(glibc)
makedepends=(example)
source=("https://example.org/$pkgname-$pkgver.tar.gz")
sha256sums=(b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c)
build() {
make -C $pkgname-$pkgver
}
package() {
depends+=(example libexample.so)
make DESTDIR="$pkgdir" install -C $pkgname-$pkgver
}
The following SRCINFO file can be generated from the above PKGBUILD file:
pkgbase = application
pkgdesc = An example application
pkgver = 1.0.0
pkgrel = 1
arch = x86_64
url = https://example.org/application.html
license = MIT
depends = glibc
depends = example
depends = libexample.so
makedepends = example
source = https://example.org/application-1.0.0.tar.gz
sha256sums = b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c
pkgname = application
After building from source, the resulting package file for application contains the following PKGINFO file:
pkgname = application
pkgver = 1.0.0-1
pkgdesc = An example application
url = https://example.org/application.html
builddate = 1729181726
packager = Your Name <your@email.com>
size = 181849963
arch = x86_64
license = MIT
depend = glibc
depend = example
depend = libexample.so=1-64
π SEE ALSO π
Go read these next, bestie, they're literally the supporting cast:
readelf(1)PKGBUILD(5)PKGINFO(5)SRCINFO(5)makepkg.conf(5)alpm-comparison(7)alpm-package-name(7)alpm-package-relation(7)makepkg(8)
π NOTES π
- soname
https://en.wikipedia.org/wiki/Soname
- ELF
https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
- library interface version
https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
Aaand that's the whole page, bestie π β this was alpm-sonamev1, living in section (7) of the manual ((7) being the footer you'd normally see at the bottom, so consider this your cute sign-off instead β¨). Go forth and soname responsibly ππ¦
π¦ where this page comes from
- arch rolling: alpm-docs 0.13.0-1 Β· CC-BY-SA-4.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms