{% set rfcid = “RFC-0135” %} {% include “docs/contribute/governance/rfcs/_common/_rfc_header.md” %}
Design for encoding the system ABI revision into packages.
RFC-0002 introduces the concepts of API level and ABI revision. The API level is a monotonically increasing human-understandable number that corresponds to a set of APIs that are available when building applications. The ABI revision corresponds to the specific semantics exposed by the Fuchsia System Interface the application expects the platform to provide. An API level corresponds to a specific ABI revision, but multiple API levels can refer to the same ABI revision. This functionality allows for the Fuchsia platform to evolve over time while continuing to support older applications.
Implementing platform versioning requires that packages encode the ABI level they wish to target, so that the platform can know whether a package is compatible with the running system.
Facilitator: abarth@google.com
Reviewers:
The following stakeholders were chosen as this RFC touches Fuchsia Tools, Software Delivery, and will eventually impact how Component Framework will select the component system interface to present to applications.
Consulted:
Socialization:
This RFC went through a design review with the Software Delivery and Fuchsia Tools teams.
A Fuchsia package is the unit of distribution for distributing and organizing files for Fuchsia programs, components, and services.
The meta.far is the package metadata archive. It contains the mapping of user-facing file names to the underlying content addressed blobs. This is used by the packaging system to download package contents, and construct a namespace hierarchy. It also contains custom user-provided files.
This design introduces the notion of a package ABI revision, which is the target ABI revision of the Fuchsia platform interface used by a package. This value will be written as an unsigned little endian 64-bit integer written into the meta.far
in the file meta/fuchsia.abi/abi-revision
.
All package generation tooling will be updated to require either the API level or the ABI revision to be specified during package building. When the API level is specified, the tooling should encode the corresponding ABI revision found in the SDK version history, or error out. Likewise, the tooling should enforce that the specified ABI revision is supported by the SDK, or error out. This should avoid the risk of creating a component with an older SDK, but specifying an ABI only present in a newer SDK. At best this would result in a component that failed to run. At worst this could lead the component to strange or dangerous bugs.
This proposal only covers how the ABI revision is encoded into a package. Users of the package ABI revision will be defined in future designs. However, here are some potential use cases to help illustrate how this may be used:
This proposal indends to make the API level or ABI revision a required argument when building a package, but this will be optional during the initial rollout of this feature. With that in mind, end-developers should parameterize their build rules for the target API level or ABI revision. This should make it easy to target new releases. It may even be possible to set up automatic testing of new API levels by setting up a test roller that experimentally advances the target API level and sees if anything fails. This could feed back into platform bug reports if these failures were unexpected.
In order for a Fuchsia device to update from one release to another, the system updater resolves a special package known as the update package. This package describes all the base packages and other artifacts necessary for the new Fuchsia version to run. These new packages are not intended to be used on the current system, but instead would be used once the device is rebooted into the new system.
In light of this, if we decided to use the package ABI revision to reject downloading a package, we need to be careful to avoid breaking the OTA process. This can be seen through the following example:
Consider two Fuchsia builds A
, which only supports ABI-1
and C
, which supports ABI-2
. If we filter packages based off ABI, the update package for C
would need to be ABI-1
for it to download. However, the C
version base packages would have to be ABI-2
to use the new ABI, but these would be rejected by the version A
package resolver.
One way to avoid this is to introduce new ABI revisions through a stepping stone release. Fuchsia is designed to allow a device to skip multiple releases in order to update to the latest version. A stepping stone release is a special release that cannot be skipped. It is designed to enable graceful transitions through platform interfaces.
Redoing the prior example, instead lets have 3 sequential Fuchsia releases:
A
: supports ABI-1
, base packages at ABI-1
B
: supports ABI-1
and ABI-2
, base packages at ABI-1
.C
: supports ABI-2
, base packages at ABI-2
.If we mark B
as a stepping stone release, then a device running A
would first update to B
, then update to C
.
This idea can be taken even further by decoupling how packages are defined from the ABI revision. Since we do not intend to frequently make backwards incompatible changes to package layout, we can version our package layout, and then state which package layout versions are supported by a given ABI revision. This would allow us to install a greater range of update packages without needing to create stepping stone releases.
The meta.far
currently contains two package metadata files, meta/package
and meta/contents
, as well as arbitrary user specified files. This means that there is a possibility of collision between user files and any new metadata files we introduce into a package. To avoid this possibility, the package ABI revision will be written in a directory meta/fuchsia.abi
. This directory name follows the convention used elsewhere in Fuchsia, such as the platform FIDL namespaces. The package building tools will be further updated to prevent users from defining custom files in meta/fuchsia.abi
.
Since this is a directory controlled by the platform, it's possible to use this location to store ABI-related files without colliding with user files. This would allow us to evolve our notion of a package ABI revision. For example, if we wanted to support multiple ABI revisions in a package, we could add meta/fuchsia.abi/abi-revision-set
to contain all the supported revisions. We could drop meta/fuchsia.abi/abi-revision
once all users have migrated to the new file.
The overhead of adding the package ABI revision to the meta adds 8KiB uncompressed to the meta.far in the worst case, where the first 4KiB comes from the FAR data chunk, and the second if the DIRNAMES section causes the first content chunk to be pushed out to the next 4KiB aligned offset. However, this shouldn't be a significant impact in practice, because blobfs has an 8KiB block alignment. Furthermore, it compresses very well with the default compressor zstd. According to the following example, this only adds 47 bytes to the meta.far:
# Create an empty package. % mkdir empty && cd empty % pm init && pm build % ls -l meta.far -rw-r--r-- 1 etryzelaar primarygroup 12288 Oct 5 10:21 meta.far % fx chunked-compress c meta.far meta.far.compressed Wrote 447 bytes (97% compression) # Then create a meta.far that contain's the abi-revision. % cd .. && mkdir with-abi && cd with-abi % pm init % mkdir meta/fuchsia.abi % python3 -c " import struct; abi_revision = int('0xC7003BF9', 16) f = open('meta/fuchsia.abi/abi-revision', 'wb') f.write(struct.pack('<Q', abi_revision)) " % pm build % ls -l meta.far -rw-r--r-- 1 etryzelaar primarygroup 16384 Oct 5 10:22 meta.far % fx chunked-compress c meta.far meta.far.compressed Wrote 494 bytes (97% compression)
All package building tools will be extended to support specifying the API level or the ABI revision on the command line.
The pm
CLI will be extended to allow the ABI to be specified during package initialization and package building.
Example invocations:
# Create a package with an API level. Under the covers this will look up the # ABI revision from the SDK. % pm init --api-level 5 # Create a package with an ABI revision. These commands are equivalent. % pm init --abi-revision 0xC7003BF9 % pm init --abi-revision 3338681337 # Build a package with an ABI revision. % pm build --abi-revision 0xC7003BF9
Options:
--api-level LEVEL
: The API level to encode into the package. This value must be supported by the SDK. This option conflicts with the --abi-revision
flag.--abi-revision REVISION
: The ABI revision which will be baked into the package. This may be a decimal or hexadecimal integer, which must be supported by the SDK. This option conflicts with the --api-level
flag.Initially the --api-level
and --abi-revision
flags will be optional to allow for petals and end-developers to implement support over a period of time. Eventually this will be a required argument once the ecosystem has transitioned over to specifying the flags.
This RFC introduces a trivial amount of additional work when building packages, and a small overhead to the storage, so the performance implications should be negligible.
By itself this proposal does not significantly change the ergonomics of Fuchsia. However, this will eventually enable product developers to target a specific system interface for their components. This should provide the stability they need while not blocking Fuchsia's ability to evolve.
This change is backwards compatible, since this design introduces a new file with no consumers. However, this functionality will eventually allow the system to deprecate and eventually remove support for old ABI revisions.
The meta/fuchsia.abi/abi-revision
file will need to be parsed. However, this format is simple to parse, and should be easy to validate that parsers are correct.
See RFC-0002 Security Considerations for more details.
This proposal has no meaningful impact on privacy.
The packaging tooling will be extended to verify that packages are generated with the expected ABI to be set. Since there will be a transition period, the package resolver tests will be extended to verify that it can work with packages that contain or not contain the meta/fuchsia.abi/abi-revision
file.
The packaging documentation will be updated to discuss how the ABI is expressed in packages, and how users can select the API level or the ABI revision when building packages.
Rather than encoding the ABI revision as a little endian integer, we could instead use:
A little endian integer was selected because:
RFC-0002 Applications states that packages can include multiple components, each of which targets a different ABI revision. To support this, rather than encoding a single ABI revision in the package, the package ABI could be a set of all the component ABI revisions.
However, as of writing there are no use cases that require a package that contains components that target different system ABIs. A single number simplifies the initial implemation for ABI support. It would not be difficult to extend this design if this scenario becomes important.
The meta.far could directly embed the ABI revision, by either:
Since the storage overhead is minimal, especially with compression, we do not think the space savings are worth defining a new chunk type.
Rather than directly encoding the ABI into packages, we could instead add ABI revision to the package metadata in the package repository. This has the following cons:
Software Delivery is writing an RFC that proposes a new persistent-FIDL packaging metadata. We could instead store the ABI revision in this metadata by updating the proposed schema to this:
flexible union Contents { 1: ContentsV1 v1; }; table ContentsV1 { 1: vector<fuchsia.io.Path>:MAX paths; 2: vector<fuchsia.pkg.BlobId>:MAX hashes; 3: vector<uint64>:MAX blob_sizes; 4: uint64 abi_revision; };
Advantages:
The disadvantages of using FIDL:
Android applications target an SDK version by specifying the uses-sdk element in the app manifest.
Windows applications target the OS version by specifying the SupportedOS GUID listed in the application manifest.
macOS applications target the OS version by specifying the LSMinimumSystemVersion in the application bundle's Info.plist
file.
iOS applications target the OS version by specifying the MinimumOSVersion in the application bundle's Info.plist
file.