summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/man/man7/zpool-features.71420
-rw-r--r--usr/src/pkg/manifests/developer-build-onbld.p5m3
-rw-r--r--usr/src/tools/Makefile2
-rw-r--r--usr/src/tools/find_elf/Makefile68
-rw-r--r--usr/src/tools/find_elf/find_elf.1onbld (renamed from usr/src/tools/scripts/find_elf.1onbld)21
-rw-r--r--usr/src/tools/find_elf/find_elf.c863
-rw-r--r--usr/src/tools/scripts/Makefile3
-rw-r--r--usr/src/tools/scripts/find_elf.pl457
8 files changed, 1585 insertions, 1252 deletions
diff --git a/usr/src/man/man7/zpool-features.7 b/usr/src/man/man7/zpool-features.7
index 146bfc5262..aabc9bc4ad 100644
--- a/usr/src/man/man7/zpool-features.7
+++ b/usr/src/man/man7/zpool-features.7
@@ -1,4 +1,4 @@
-'\" te
+.\"
.\" Copyright (c) 2013, 2017 by Delphix. All rights reserved.
.\" Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
.\" Copyright (c) 2014, Joyent, Inc. All rights reserved.
@@ -15,821 +15,661 @@
.\" CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your
.\" own identifying information:
.\" Portions Copyright [yyyy] [name of copyright owner]
-.TH ZPOOL-FEATURES 7 "May 15, 2019"
-.SH NAME
-zpool\-features \- ZFS pool feature descriptions
-.SH DESCRIPTION
-.LP
-ZFS pool on\-disk format versions are specified via "features" which replace
-the old on\-disk format numbers (the last supported on\-disk format number is
-28). To enable a feature on a pool use the \fBupgrade\fR subcommand of the
-\fBzpool\fR(8) command, or set the \fBfeature@\fR\fIfeature_name\fR property
-to \fBenabled\fR.
-.sp
-.LP
+.\" Copyright (c) 2019, Klara Inc.
+.\" Copyright (c) 2019, Allan Jude
+.Dd June 21, 2022
+.Dt ZPOOL-FEATURES 7
+.Os
+.
+.Sh NAME
+.Nm zpool-features
+.Nd description of ZFS pool features
+.
+.Sh DESCRIPTION
+ZFS pool on-disk format versions are specified via
+.Dq features
+which replace the old on-disk format numbers
+.Pq the last supported on-disk format number is 28 .
+To enable a feature on a pool use the
+.Nm zpool Cm upgrade ,
+or set the
+.Sy feature Ns @ Ns Ar feature-name
+property to
+.Sy enabled .
+.Pp
The pool format does not affect file system version compatibility or the ability
to send file systems between pools.
-.sp
-.LP
-Since most features can be enabled independently of each other the on\-disk
+.Pp
+Since most features can be enabled independently of each other, the on-disk
format of the pool is specified by the set of all features marked as
-\fBactive\fR on the pool. If the pool was created by another software version
+.Sy active
+on the pool.
+If the pool was created by another software version
this set may include unsupported features.
-.SS "Identifying features"
-.LP
-Every feature has a guid of the form \fIcom.example:feature_name\fR. The reverse
-DNS name ensures that the feature's guid is unique across all ZFS
-implementations. When unsupported features are encountered on a pool they will
-be identified by their guids. Refer to the documentation for the ZFS
+.
+.Ss Identifying features
+Every feature has a GUID of the form
+.Ar com.example : Ns Ar feature-name .
+The reversed DNS name ensures that the feature's GUID is unique across all ZFS
+implementations.
+When unsupported features are encountered on a pool they will
+be identified by their GUIDs.
+Refer to the documentation for the ZFS
implementation that created the pool for information about those features.
-.sp
-.LP
-Each supported feature also has a short name. By convention a feature's short
-name is the portion of its guid which follows the ':' (e.g.
-\fIcom.example:feature_name\fR would have the short name \fIfeature_name\fR),
+.Pp
+Each supported feature also has a short name.
+By convention a feature's short name is the portion of its GUID which follows the
+.Sq \&:
+.Po
+i.e.
+.Ar com.example : Ns Ar feature-name
+would have the short name
+.Ar feature-name
+.Pc ,
however a feature's short name may differ across ZFS implementations if
following the convention would result in name conflicts.
-.SS "Feature states"
-.LP
+.
+.Ss Feature states
Features can be in one of three states:
-.sp
-.ne 2
-.na
-\fB\fBactive\fR\fR
-.ad
-.RS 12n
-This feature's on\-disk format changes are in effect on the pool. Support for
-this feature is required to import the pool in read\-write mode. If this
-feature is not read-only compatible, support is also required to import the pool
-in read\-only mode (see "Read\-only compatibility").
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBenabled\fR\fR
-.ad
-.RS 12n
+.Bl -tag -width "disabled"
+.It Sy active
+This feature's on-disk format changes are in effect on the pool.
+Support for this feature is required to import the pool in read-write mode.
+If this feature is not read-only compatible,
+support is also required to import the pool in read-only mode
+.Pq see Sx Read-only compatibility .
+.It Sy enabled
An administrator has marked this feature as enabled on the pool, but the
-feature's on\-disk format changes have not been made yet. The pool can still be
-imported by software that does not support this feature, but changes may be made
-to the on\-disk format at any time which will move the feature to the
-\fBactive\fR state. Some features may support returning to the \fBenabled\fR
-state after becoming \fBactive\fR. See feature\-specific documentation for
-details.
-.RE
-
-.sp
-.ne 2
-.na
-\fBdisabled\fR
-.ad
-.RS 12n
-This feature's on\-disk format changes have not been made and will not be made
-unless an administrator moves the feature to the \fBenabled\fR state. Features
-cannot be disabled once they have been enabled.
-.RE
-
-.sp
-.LP
+feature's on-disk format changes have not been made yet.
+The pool can still be imported by software that does not support this feature,
+but changes may be made to the on-disk format at any time
+which will move the feature to the
+.Sy active
+state.
+Some features may support returning to the
+.Sy enabled
+state after becoming
+.Sy active .
+See feature-specific documentation for details.
+.It Sy disabled
+This feature's on-disk format changes have not been made and will not be made
+unless an administrator moves the feature to the
+.Sy enabled
+state.
+Features cannot be disabled once they have been enabled.
+.El
+.Pp
The state of supported features is exposed through pool properties of the form
-\fIfeature@short_name\fR.
-.SS "Read\-only compatibility"
-.LP
-Some features may make on\-disk format changes that do not interfere with other
-software's ability to read from the pool. These features are referred to as
-"read\-only compatible". If all unsupported features on a pool are read\-only
-compatible, the pool can be imported in read\-only mode by setting the
-\fBreadonly\fR property during import (see \fBzpool\fR(8) for details on
-importing pools).
-.SS "Unsupported features"
-.LP
-For each unsupported feature enabled on an imported pool a pool property
-named \fIunsupported@feature_guid\fR will indicate why the import was allowed
-despite the unsupported feature. Possible values for this property are:
-
-.sp
-.ne 2
-.na
-\fB\fBinactive\fR\fR
-.ad
-.RS 12n
-The feature is in the \fBenabled\fR state and therefore the pool's on\-disk
+.Sy feature Ns @ Ns Ar short-name .
+.
+.Ss Read-only compatibility
+Some features may make on-disk format changes that do not interfere with other
+software's ability to read from the pool.
+These features are referred to as
+.Dq read-only compatible .
+If all unsupported features on a pool are read-only compatible,
+the pool can be imported in read-only mode by setting the
+.Sy readonly
+property during import
+.Po see
+.Xr zpool 8
+for details on importing pools
+.Pc .
+.
+.Ss Unsupported features
+For each unsupported feature enabled on an imported pool, a pool property
+named
+.Sy unsupported Ns @ Ns Ar feature-name
+will indicate why the import was allowed despite the unsupported feature.
+Possible values for this property are:
+.Bl -tag -width "readonly"
+.It Sy inactive
+The feature is in the
+.Sy enabled
+state and therefore the pool's on-disk
format is still compatible with software that does not support this feature.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBreadonly\fR\fR
-.ad
-.RS 12n
-The feature is read\-only compatible and the pool has been imported in
-read\-only mode.
-.RE
-
-.SS "Feature dependencies"
-.LP
-Some features depend on other features being enabled in order to function
-properly. Enabling a feature will automatically enable any features it
-depends on.
-.SH FEATURES
-.LP
+.It Sy readonly
+The feature is read-only compatible and the pool has been imported in
+read-only mode.
+.El
+.
+.Ss Feature dependencies
+Some features depend on other features being enabled in order to function.
+Enabling a feature will automatically enable any features it depends on.
+.
+.de feature
+.It Sy \\$2
+.Bl -tag -compact -width "READ-ONLY COMPATIBLE"
+.It GUID
+.Sy \\$1:\\$2
+.if !"\\$4"" \{\
+.It DEPENDENCIES
+\fB\\$4\fP\c
+.if !"\\$5"" , \fB\\$5\fP\c
+.if !"\\$6"" , \fB\\$6\fP\c
+.if !"\\$7"" , \fB\\$7\fP\c
+.if !"\\$8"" , \fB\\$8\fP\c
+.if !"\\$9"" , \fB\\$9\fP\c
+.\}
+.It READ-ONLY COMPATIBLE
+\\$3
+.El
+.Pp
+..
+.
+.ds instant-never \
+.No This feature becomes Sy active No as soon as it is enabled \
+and will never return to being Sy enabled .
+.
+.ds remount-upgrade \
+.No Each filesystem will be upgraded automatically when remounted, \
+or when a new file is created under that filesystem. \
+The upgrade can also be triggered on filesystems via \
+Nm zfs Cm set Sy version Ns = Ns Sy current Ar fs . \
+No The upgrade process runs in the background and may take a while to complete \
+for filesystems containing large amounts of files.
+.
+.de checksum-spiel
+When the
+.Sy \\$1
+feature is set to
+.Sy enabled ,
+the administrator can turn on the
+.Sy \\$1
+checksum on any dataset using
+.Nm zfs Cm set Sy checksum Ns = Ns Sy \\$1 Ar dset
+.Po see Xr zfs 8 Pc .
+This feature becomes
+.Sy active
+once a
+.Sy checksum
+property has been set to
+.Sy \\$1 ,
+and will return to being
+.Sy enabled
+once all filesystems that have ever had their checksum set to
+.Sy \\$1
+are destroyed.
+..
+.
+.Sh FEATURES
The following features are supported on this system:
-.sp
-.ne 2
-.na
-\fB\fBasync_destroy\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.delphix:async_destroy
-READ\-ONLY COMPATIBLE yes
-DEPENDENCIES none
-.TE
-
+.Bl -tag -width Ds
+.feature org.zfsonlinux allocation_classes yes
+This feature enables support for separate allocation classes.
+.Pp
+This feature becomes
+.Sy active
+when a dedicated allocation class vdev
+.Pq dedup or special
+is created with the
+.Nm zpool Cm create No or Nm zpool Cm add No commands .
+With device removal, it can be returned to the
+.Sy enabled
+state if all the dedicated allocation class vdevs are removed.
+.
+.feature com.delphix async_destroy yes
Destroying a file system requires traversing all of its data in order to
-return its used space to the pool. Without \fBasync_destroy\fR the file system
-is not fully removed until all space has been reclaimed. If the destroy
-operation is interrupted by a reboot or power outage the next attempt to open
-the pool will need to complete the destroy operation synchronously.
-
-When \fBasync_destroy\fR is enabled the file system's data will be reclaimed
-by a background process, allowing the destroy operation to complete without
-traversing the entire file system. The background process is able to resume
+return its used space to the pool.
+Without
+.Sy async_destroy ,
+the file system is not fully removed until all space has been reclaimed.
+If the destroy operation is interrupted by a reboot or power outage,
+the next attempt to open the pool will need to complete the destroy
+operation synchronously.
+.Pp
+When
+.Sy async_destroy
+is enabled, the file system's data will be reclaimed by a background process,
+allowing the destroy operation to complete
+without traversing the entire file system.
+The background process is able to resume
interrupted destroys after the pool has been opened, eliminating the need
-to finish interrupted destroys as part of the open operation. The amount
-of space remaining to be reclaimed by the background process is available
-through the \fBfreeing\fR property.
-
-This feature is only \fBactive\fR while \fBfreeing\fR is non\-zero.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBempty_bpobj\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.delphix:empty_bpobj
-READ\-ONLY COMPATIBLE yes
-DEPENDENCIES none
-.TE
-
+to finish interrupted destroys as part of the open operation.
+The amount of space remaining to be reclaimed by the background process
+is available through the
+.Sy freeing
+property.
+.Pp
+This feature is only
+.Sy active
+while
+.Sy freeing
+is non-zero.
+.
+.feature com.delphix bookmarks yes extensible_dataset
+This feature enables use of the
+.Nm zfs Cm bookmark
+command.
+.Pp
+This feature is
+.Sy active
+while any bookmarks exist in the pool.
+All bookmarks in the pool can be listed by running
+.Nm zfs Cm list Fl t Sy bookmark Fl r Ar poolname .
+.
+.feature com.datto bookmark_v2 no bookmark extensible_dataset
+This feature enables the creation and management of larger bookmarks which are
+needed for other features in ZFS.
+.Pp
+This feature becomes
+.Sy active
+when a v2 bookmark is created and will be returned to the
+.Sy enabled
+state when all v2 bookmarks are destroyed.
+.
+.feature com.delphix device_removal no
+This feature enables the
+.Nm zpool Cm remove
+command to remove top-level vdevs,
+evacuating them to reduce the total size of the pool.
+.Pp
+This feature becomes
+.Sy active
+when the
+.Nm zpool Cm remove
+command is used
+on a top-level vdev, and will never return to being
+.Sy enabled .
+.
+.feature org.illumos edonr no extensible_dataset
+This feature enables the use of the Edon-R hash algorithm for checksum,
+including for nopwrite
+.Po if compression is also enabled, an overwrite of
+a block whose checksum matches the data being written will be ignored
+.Pc .
+In an abundance of caution, Edon-R requires verification when used with
+dedup:
+.Nm zfs Cm set Sy dedup Ns = Ns Sy edonr , Ns Sy verify
+.Po see Xr zfs 8 Pc .
+.Pp
+Edon-R is a very high-performance hash algorithm that was part
+of the NIST SHA-3 competition.
+It provides extremely high hash performance
+.Pq over 350% faster than SHA-256 ,
+but was not selected because of its unsuitability
+as a general purpose secure hash algorithm.
+This implementation utilizes the new salted checksumming functionality
+in ZFS, which means that the checksum is pre-seeded with a secret
+256-bit random key
+.Pq stored on the pool
+before being fed the data block to be checksummed.
+Thus the produced checksums are unique to a given pool,
+preventing hash collision attacks on systems with dedup.
+.Pp
+.checksum-spiel edonr
+.
+.feature com.delphix embedded_data no
+This feature improves the performance and compression ratio of
+highly-compressible blocks.
+Blocks whose contents can compress to 112 bytes
+or smaller can take advantage of this feature.
+.Pp
+When this feature is enabled, the contents of highly-compressible blocks are
+stored in the block
+.Dq pointer
+itself
+.Po a misnomer in this case, as it contains
+the compressed data, rather than a pointer to its location on disk
+.Pc .
+Thus the space of the block
+.Pq one sector, typically 512 B or 4 KiB
+is saved, and no additional I/O is needed to read and write the data block.
+.
+\*[instant-never]
+.
+.feature com.delphix empty_bpobj yes
This feature increases the performance of creating and using a large
number of snapshots of a single filesystem or volume, and also reduces
the disk space required.
-
+.Pp
When there are many snapshots, each snapshot uses many Block Pointer
-Objects (bpobj's) to track blocks associated with that snapshot.
-However, in common use cases, most of these bpobj's are empty. This
-feature allows us to create each bpobj on-demand, thus eliminating the
-empty bpobjs.
-
-This feature is \fBactive\fR while there are any filesystems, volumes,
+Objects
+.Pq bpobjs
+to track blocks associated with that snapshot.
+However, in common use cases, most of these bpobjs are empty.
+This feature allows us to create each bpobj on-demand,
+thus eliminating the empty bpobjs.
+.Pp
+This feature is
+.Sy active
+while there are any filesystems, volumes,
or snapshots which were created after enabling this feature.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBfilesystem_limits\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.joyent:filesystem_limits
-READ\-ONLY COMPATIBLE yes
-DEPENDENCIES extensible_dataset
-.TE
-
-This feature enables filesystem and snapshot limits. These limits can be used
-to control how many filesystems and/or snapshots can be created at the point in
-the tree on which the limits are set.
-
-This feature is \fBactive\fR once either of the limit properties has been
-set on a dataset. Once activated the feature is never deactivated.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBlz4_compress\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID org.illumos:lz4_compress
-READ\-ONLY COMPATIBLE no
-DEPENDENCIES none
-.TE
-
-\fBlz4\fR is a high-performance real-time compression algorithm that
-features significantly faster compression and decompression as well as a
-higher compression ratio than the older \fBlzjb\fR compression.
-Typically, \fBlz4\fR compression is approximately 50% faster on
-compressible data and 200% faster on incompressible data than
-\fBlzjb\fR. It is also approximately 80% faster on decompression, while
-giving approximately 10% better compression ratio.
-
-When the \fBlz4_compress\fR feature is set to \fBenabled\fR, the
-administrator can turn on \fBlz4\fR compression on any dataset on the
-pool using the \fBzfs\fR(8) command. Also, all newly written metadata
-will be compressed with \fBlz4\fR algorithm. Since this feature is not
-read-only compatible, this operation will render the pool unimportable
-on systems without support for the \fBlz4_compress\fR feature. Booting
-off of \fBlz4\fR-compressed root pools is supported.
-
-This feature becomes \fBactive\fR as soon as it is enabled and will
-never return to being \fBenabled\fR.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBspacemap_histogram\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.delphix:spacemap_histogram
-READ\-ONLY COMPATIBLE yes
-DEPENDENCIES none
-.TE
-
-This features allows ZFS to maintain more information about how free space
-is organized within the pool. If this feature is \fBenabled\fR, ZFS will
-set this feature to \fBactive\fR when a new space map object is created or
-an existing space map is upgraded to the new format. Once the feature is
-\fBactive\fR, it will remain in that state until the pool is destroyed.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBmulti_vdev_crash_dump\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.joyent:multi_vdev_crash_dump
-READ\-ONLY COMPATIBLE no
-DEPENDENCIES none
-.TE
-
-This feature allows a dump device to be configured with a pool comprised
-of multiple vdevs. Those vdevs may be arranged in any mirrored or raidz
-configuration.
-
-When the \fBmulti_vdev_crash_dump\fR feature is set to \fBenabled\fR,
-the administrator can use the \fBdumpadm\fR(8) command to configure a
-dump device on a pool comprised of multiple vdevs.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBextensible_dataset\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.delphix:extensible_dataset
-READ\-ONLY COMPATIBLE no
-DEPENDENCIES none
-.TE
-
+.
+.feature com.delphix enabled_txg yes
+Once this feature is enabled, ZFS records the transaction group number
+in which new features are enabled.
+This has no user-visible impact, but other features may depend on this feature.
+.Pp
+This feature becomes
+.Sy active
+as soon as it is enabled and will never return to being
+.Sy enabled .
+.
+.feature com.datto encryption no bookmark_v2 extensible_dataset
+This feature enables the creation and management of natively encrypted datasets.
+.Pp
+This feature becomes
+.Sy active
+when an encrypted dataset is created and will be returned to the
+.Sy enabled
+state when all datasets that use this feature are destroyed.
+.
+.feature com.delphix extensible_dataset no
This feature allows more flexible use of internal ZFS data structures,
and exists for other features to depend on.
-
-This feature will be \fBactive\fR when the first dependent feature uses it,
-and will be returned to the \fBenabled\fR state when all datasets that use
-this feature are destroyed.
-
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBbookmarks\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.delphix:bookmarks
-READ\-ONLY COMPATIBLE yes
-DEPENDENCIES extensible_dataset
-.TE
-
-This feature enables use of the \fBzfs bookmark\fR subcommand.
-
-This feature is \fBactive\fR while any bookmarks exist in the pool.
-All bookmarks in the pool can be listed by running
-\fBzfs list -t bookmark -r \fIpoolname\fR\fR.
-
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBenabled_txg\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.delphix:enabled_txg
-READ\-ONLY COMPATIBLE yes
-DEPENDENCIES none
-.TE
-
-Once this feature is enabled ZFS records the transaction group number
-in which new features are enabled. This has no user-visible impact,
-but other features may depend on this feature.
-
-This feature becomes \fBactive\fR as soon as it is enabled and will
-never return to being \fBenabled\fR.
-
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBhole_birth\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.delphix:hole_birth
-READ\-ONLY COMPATIBLE no
-DEPENDENCIES enabled_txg
-.TE
-
-This feature improves performance of incremental sends ("zfs send -i")
-and receives for objects with many holes. The most common case of
-hole-filled objects is zvols.
-
-An incremental send stream from snapshot \fBA\fR to snapshot \fBB\fR
-contains information about every block that changed between \fBA\fR and
-\fBB\fR. Blocks which did not change between those snapshots can be
+.Pp
+This feature will be
+.Sy active
+when the first dependent feature uses it, and will be returned to the
+.Sy enabled
+state when all datasets that use this feature are destroyed.
+.
+.feature com.joyent filesystem_limits yes extensible_dataset
+This feature enables filesystem and snapshot limits.
+These limits can be used to control how many filesystems and/or snapshots
+can be created at the point in the tree on which the limits are set.
+.Pp
+This feature is
+.Sy active
+once either of the limit properties has been set on a dataset
+and will never return to being
+.Sy enabled .
+.
+.feature com.delphix hole_birth no enabled_txg
+This feature has/had bugs, the result of which is that, if you do a
+.Nm zfs Cm send Fl i
+.Pq or Fl R , No since it uses Fl i
+from an affected dataset, the receiving party will not see any checksum
+or other errors, but the resulting destination snapshot
+will not match the source.
+Its use by
+.Nm zfs Cm send Fl i
+has been disabled by default
+.Po
+see
+.Sy send_holes_without_birth_time
+in
+.Xr zfs 4
+.Pc .
+.Pp
+This feature improves performance of incremental sends
+.Pq Nm zfs Cm send Fl i
+and receives for objects with many holes.
+The most common case of hole-filled objects is zvols.
+.Pp
+An incremental send stream from snapshot
+.Sy A No to snapshot Sy B
+contains information about every block that changed between
+.Sy A No and Sy B .
+Blocks which did not change between those snapshots can be
identified and omitted from the stream using a piece of metadata called
-the 'block birth time', but birth times are not recorded for holes (blocks
-filled only with zeroes). Since holes created after \fBA\fR cannot be
-distinguished from holes created before \fBA\fR, information about every
-hole in the entire filesystem or zvol is included in the send stream.
-
-For workloads where holes are rare this is not a problem. However, when
-incrementally replicating filesystems or zvols with many holes (for
-example a zvol formatted with another filesystem) a lot of time will
-be spent sending and receiving unnecessary information about holes that
+the
+.Dq block birth time ,
+but birth times are not recorded for holes
+.Pq blocks filled only with zeroes .
+Since holes created after
+.Sy A No cannot be distinguished from holes created before Sy A ,
+information about every hole in the entire filesystem or zvol
+is included in the send stream.
+.Pp
+For workloads where holes are rare this is not a problem.
+However, when incrementally replicating filesystems or zvols with many holes
+.Pq for example a zvol formatted with another filesystem
+a lot of time will be spent sending and receiving unnecessary information
+about holes that already exist on the receiving side.
+.Pp
+Once the
+.Sy hole_birth
+feature has been enabled the block birth times
+of all new holes will be recorded.
+Incremental sends between snapshots created after this feature is enabled
+will use this new metadata to avoid sending information about holes that
already exist on the receiving side.
-
-Once the \fBhole_birth\fR feature has been enabled the block birth times
-of all new holes will be recorded. Incremental sends between snapshots
-created after this feature is enabled will use this new metadata to avoid
-sending information about holes that already exist on the receiving side.
-
-This feature becomes \fBactive\fR as soon as it is enabled and will
-never return to being \fBenabled\fR.
-
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBembedded_data\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.delphix:embedded_data
-READ\-ONLY COMPATIBLE no
-DEPENDENCIES none
-.TE
-
-This feature improves the performance and compression ratio of
-highly-compressible blocks. Blocks whose contents can compress to 112 bytes
-or smaller can take advantage of this feature.
-
-When this feature is enabled, the contents of highly-compressible blocks are
-stored in the block "pointer" itself (a misnomer in this case, as it contains
-the compresseed data, rather than a pointer to its location on disk). Thus
-the space of the block (one sector, typically 512 bytes or 4KB) is saved,
-and no additional i/o is needed to read and write the data block.
-
-This feature becomes \fBactive\fR as soon as it is enabled and will
-never return to being \fBenabled\fR.
-
-.RE
-.sp
-.ne 2
-.na
-\fB\fBdevice_removal\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.delphix:device_removal
-READ\-ONLY COMPATIBLE no
-DEPENDENCIES none
-.TE
-
-This feature enables the "zpool remove" subcommand to remove top-level
-vdevs, evacuating them to reduce the total size of the pool.
-
-This feature becomes \fBactive\fR when the "zpool remove" command is used
-on a top-level vdev, and will never return to being \fBenabled\fR.
-
-.RE
-.sp
-.ne 2
-.na
-\fB\fBobsolete_counts\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.delphix:obsolete_counts
-READ\-ONLY COMPATIBLE yes
-DEPENDENCIES device_removal
-.TE
-
-This feature is an enhancement of device_removal, which will over time
-reduce the memory used to track removed devices. When indirect blocks
-are freed or remapped, we note that their part of the indirect mapping
-is "obsolete", i.e. no longer needed. See also the \fBzfs remap\fR
-subcommand in \fBzfs\fR(8).
-
-This feature becomes \fBactive\fR when the "zpool remove" command is
-used on a top-level vdev, and will never return to being \fBenabled\fR.
-
-.RE
-.sp
-.ne 2
-.na
-\fB\fBzpool_checkpoint\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.delphix:zpool_checkpoint
-READ\-ONLY COMPATIBLE yes
-DEPENDENCIES none
-.TE
-
-This feature enables the "zpool checkpoint" subcommand that can
-checkpoint the state of the pool at the time it was issued and later
-rewind back to it or discard it.
-
-This feature becomes \fBactive\fR when the "zpool checkpoint" command
-is used to checkpoint the pool.
-The feature will only return back to being \fBenabled\fR when the pool
-is rewound or the checkpoint has been discarded.
-
-.RE
-.sp
-.ne 2
-.na
-\fB\fBspacemap_v2\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.delphix:spacemap_v2
-READ\-ONLY COMPATIBLE yes
-DEPENDENCIES none
-.TE
-
+.Pp
+\*[instant-never]
+.
+.feature org.open-zfs large_blocks no extensible_dataset
+This feature allows the record size on a dataset to be set larger than 128 KiB.
+.Pp
+This feature becomes
+.Sy active
+once a dataset contains a file with a block size larger than 128 KiB,
+and will return to being
+.Sy enabled
+once all filesystems that have ever had their recordsize larger than 128 KiB
+are destroyed.
+.
+.feature org.zfsonlinux large_dnode no extensible_dataset
+This feature allows the size of dnodes in a dataset to be set larger than 512 B.
+.
+This feature becomes
+.Sy active
+once a dataset contains an object with a dnode larger than 512 B,
+which occurs as a result of setting the
+.Sy dnodesize
+dataset property to a value other than
+.Sy legacy .
+The feature will return to being
+.Sy enabled
+once all filesystems that have ever contained a dnode larger than 512 B
+are destroyed.
+Large dnodes allow more data to be stored in the bonus buffer,
+thus potentially improving performance by avoiding the use of spill blocks.
+.
+.feature com.delphix log_spacemap yes com.delphix:spacemap_v2
+This feature improves performance for heavily-fragmented pools,
+especially when workloads are heavy in random-writes.
+It does so by logging all the metaslab changes on a single spacemap every TXG
+instead of scattering multiple writes to all the metaslab spacemaps.
+.Pp
+\*[instant-never]
+.
+.feature org.illumos lz4_compress no
+.Sy lz4
+is a high-performance real-time compression algorithm that
+features significantly faster compression and decompression as well as a
+higher compression ratio than the older
+.Sy lzjb
+compression.
+Typically,
+.Sy lz4
+compression is approximately 50% faster on compressible data and 200% faster
+on incompressible data than
+.Sy lzjb .
+It is also approximately 80% faster on decompression,
+while giving approximately a 10% better compression ratio.
+.Pp
+When the
+.Sy lz4_compress
+feature is set to
+.Sy enabled ,
+the administrator can turn on
+.Sy lz4
+compression on any dataset on the pool using the
+.Xr zfs 8
+command.
+All newly written metadata will be compressed with the
+.Sy lz4
+algorithm.
+.Pp
+\*[instant-never]
+.
+.feature com.joyent multi_vdev_crash_dump no
+This feature allows a dump device to be configured with a pool comprised
+of multiple vdevs.
+Those vdevs may be arranged in any mirrored or raidz configuration.
+.Pp
+When the
+.Sy multi_vdev_crash_dump
+feature is set to
+.Sy enabled ,
+the administrator can use
+.Xr dumpadm 8
+to configure a dump device on a pool comprised of multiple vdevs.
+.Pp
+Under
+.Fx
+and Linux this feature is unused, but registered for compatibility.
+New pools created on these systems will have the feature
+.Sy enabled
+but will never transition to
+.Sy active ,
+as this functionality is not required for crash dump support.
+Existing pools where this feature is
+.Sy active
+can be imported.
+.
+.feature com.delphix obsolete_counts yes device_removal
+This feature is an enhancement of
+.Sy device_removal ,
+which will over time reduce the memory used to track removed devices.
+When indirect blocks are freed or remapped,
+we note that their part of the indirect mapping is
+.Dq obsolete
+– no longer needed.
+.Pp
+This feature becomes
+.Sy active
+when the
+.Nm zpool Cm remove
+command is used on a top-level vdev, and will never return to being
+.Sy enabled .
+.
+.feature org.zfsonlinux project_quota yes extensible_dataset
+This feature allows administrators to account the spaces and objects usage
+information against the project identifier
+.Pq ID .
+.Pp
+The project ID is an object-based attribute.
+When upgrading an existing filesystem,
+objects without a project ID will be assigned a zero project ID.
+When this feature is enabled, newly created objects inherit
+their parent directories' project ID if the parent's inherit flag is set
+.Pq via Nm chattr Sy [+-]P No or Nm zfs Cm project Fl s Ns | Ns Fl C .
+Otherwise, the new object's project ID will be zero.
+An object's project ID can be changed at any time by the owner
+.Pq or privileged user
+via
+.Nm chattr Fl p Ar prjid
+or
+.Nm zfs Cm project Fl p Ar prjid .
+.Pp
+This feature will become
+.Sy active
+as soon as it is enabled and will never return to being
+.Sy disabled .
+\*[remount-upgrade]
+.
+.feature com.datto resilver_defer yes
+This feature allows ZFS to postpone new resilvers if an existing one is already
+in progress.
+Without this feature, any new resilvers will cause the currently
+running one to be immediately restarted from the beginning.
+.Pp
+This feature becomes
+.Sy active
+once a resilver has been deferred, and returns to being
+.Sy enabled
+when the deferred resilver begins.
+.
+.feature org.illumos sha512 no extensible_dataset
+This feature enables the use of the SHA-512/256 truncated hash algorithm
+.Pq FIPS 180-4
+for checksum and dedup.
+The native 64-bit arithmetic of SHA-512 provides an approximate 50%
+performance boost over SHA-256 on 64-bit hardware
+and is thus a good minimum-change replacement candidate
+for systems where hash performance is important,
+but these systems cannot for whatever reason utilize the faster
+.Sy skein No and Sy edonr
+algorithms.
+.Pp
+.checksum-spiel sha512
+.
+.feature org.illumos skein no extensible_dataset
+This feature enables the use of the Skein hash algorithm for checksum and dedup.
+Skein is a high-performance secure hash algorithm that was a
+finalist in the NIST SHA-3 competition.
+It provides a very high security margin and high performance on 64-bit hardware
+.Pq 80% faster than SHA-256 .
+This implementation also utilizes the new salted checksumming
+functionality in ZFS, which means that the checksum is pre-seeded with a
+secret 256-bit random key
+.Pq stored on the pool
+before being fed the data block to be checksummed.
+Thus the produced checksums are unique to a given pool,
+preventing hash collision attacks on systems with dedup.
+.Pp
+.checksum-spiel skein
+.
+.feature com.delphix spacemap_histogram yes
+This features allows ZFS to maintain more information about how free space
+is organized within the pool.
+If this feature is
+.Sy enabled ,
+it will be activated when a new space map object is created, or
+an existing space map is upgraded to the new format,
+and never returns back to being
+.Sy enabled .
+.
+.feature com.delphix spacemap_v2 yes
This feature enables the use of the new space map encoding which
-consists of two words (instead of one) whenever it is advantageous.
+consists of two words
+.Pq instead of one
+whenever it is advantageous.
The new encoding allows space maps to represent large regions of
space more efficiently on-disk while also increasing their maximum
addressable offset.
-
-This feature becomes \fBactive\fR once it is \fBenabled\fR, and never
-returns back to being \fBenabled\fR.
-
-.RE
-.sp
-.ne 2
-.na
-\fB\fBlarge_blocks\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID org.open-zfs:large_block
-READ\-ONLY COMPATIBLE no
-DEPENDENCIES extensible_dataset
-.TE
-
-The \fBlarge_block\fR feature allows the record size on a dataset to be
-set larger than 128KB.
-
-This feature becomes \fBactive\fR once a \fBrecordsize\fR property has been
-set larger than 128KB, and will return to being \fBenabled\fR once all
-filesystems that have ever had their recordsize larger than 128KB are destroyed.
-.RE
-
-.ne 2
-.na
-\fB\fBlarge_dnode\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID org.zfsonlinux:large_dnode
-READ\-ONLY COMPATIBLE no
-DEPENDENCIES extensible_dataset
-.TE
-
-The \fBlarge_dnode\fR feature allows the size of dnodes in a dataset to be
-set larger than 512B.
-
-This feature becomes \fBactive\fR once a dataset contains an object with a
-dnode larger than 512B, which occurs as a result of setting the \fBdnodesize\fR
-dataset property to a value other than \fBlegacy\fR. The feature will return to
-being \fBenabled\fR once all filesystems that have ever contained a dnode larger
-than 512B are destroyed. Large dnodes allow more data to be stored in the
-bonus buffer, thus potentially improving performance by avoiding the use of
-spill blocks.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBsha512\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID org.illumos:sha512
-READ\-ONLY COMPATIBLE no
-DEPENDENCIES extensible_dataset
-.TE
-
-This feature enables the use of the SHA-512/256 truncated hash algorithm
-(FIPS 180-4) for checksum and dedup. The native 64-bit arithmetic of
-SHA-512 provides an approximate 50% performance boost over SHA-256 on
-64-bit hardware and is thus a good minimum-change replacement candidate
-for systems where hash performance is important, but these systems
-cannot for whatever reason utilize the faster \fBskein\fR and
-\fBedonr\fR algorithms.
-
-When the \fBsha512\fR feature is set to \fBenabled\fR, the administrator
-can turn on the \fBsha512\fR checksum on any dataset using the
-\fBzfs set checksum=sha512\fR command. This feature becomes
-\fBactive\fR once a \fBchecksum\fR property has been set to \fBsha512\fR,
-and will return to being \fBenabled\fR once all filesystems that have
-ever had their checksum set to \fBsha512\fR are destroyed.
-
-Booting off of pools utilizing SHA-512/256 is supported.
-
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBskein\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID org.illumos:skein
-READ\-ONLY COMPATIBLE no
-DEPENDENCIES extensible_dataset
-.TE
-
-This feature enables the use of the Skein hash algorithm for checksum
-and dedup. Skein is a high-performance secure hash algorithm that was a
-finalist in the NIST SHA-3 competition. It provides a very high security
-margin and high performance on 64-bit hardware (80% faster than
-SHA-256). This implementation also utilizes the new salted checksumming
-functionality in ZFS, which means that the checksum is pre-seeded with a
-secret 256-bit random key (stored on the pool) before being fed the data
-block to be checksummed. Thus the produced checksums are unique to a
-given pool, preventing hash collision attacks on systems with dedup.
-
-When the \fBskein\fR feature is set to \fBenabled\fR, the administrator
-can turn on the \fBskein\fR checksum on any dataset using the
-\fBzfs set checksum=skein\fR command. This feature becomes
-\fBactive\fR once a \fBchecksum\fR property has been set to \fBskein\fR,
-and will return to being \fBenabled\fR once all filesystems that have
-ever had their checksum set to \fBskein\fR are destroyed.
-
-Booting off of pools using \fBskein\fR is supported.
-
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBbookmark_v2\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.datto:bookmark_v2
-READ\-ONLY COMPATIBLE no
-DEPENDENCIES extensible_dataset
-.TE
-
-This feature enables the creation and management of larger bookmarks which are
-needed for other features in ZFS.
-
-This feature becomes \fBactive\fR when a v2 bookmark is created and will be
-returned to the \fBenabled\fR state when all v2 bookmarks are destroyed.
-
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBedonr\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID org.illumos:edonr
-READ\-ONLY COMPATIBLE no
-DEPENDENCIES extensible_dataset
-.TE
-
-This feature enables the use of the Edon-R hash algorithm for checksum,
-including for nopwrite (if compression is also enabled, an overwrite of
-a block whose checksum matches the data being written will be ignored).
-In an abundance of caution, Edon-R can not be used with dedup
-(without verification).
-
-Edon-R is a very high-performance hash algorithm that was part
-of the NIST SHA-3 competition. It provides extremely high hash
-performance (over 350% faster than SHA-256), but was not selected
-because of its unsuitability as a general purpose secure hash algorithm.
-This implementation utilizes the new salted checksumming functionality
-in ZFS, which means that the checksum is pre-seeded with a secret
-256-bit random key (stored on the pool) before being fed the data block
-to be checksummed. Thus the produced checksums are unique to a given
-pool.
-
-When the \fBedonr\fR feature is set to \fBenabled\fR, the administrator
-can turn on the \fBedonr\fR checksum on any dataset using the
-\fBzfs set checksum=edonr\fR command. This feature becomes
-\fBactive\fR once a \fBchecksum\fR property has been set to \fBedonr\fR,
-and will return to being \fBenabled\fR once all filesystems that have
-ever had their checksum set to \fBedonr\fR are destroyed.
-
-Booting off of pools using \fBedonr\fR is supported.
-
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBallocation_classes\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.intel:allocation_classes
-READ\-ONLY COMPATIBLE yes
-DEPENDENCIES none
-.TE
-
-This feature enables support for separate allocation classes.
-
-This feature becomes \fBactive\fR when a dedicated allocation class vdev
-(dedup or special) is created with zpool create or zpool add. With device
-removal, it can be returned to the \fBenabled\fR state if all the top-level
-vdevs from an allocation class are removed.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBencryption\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.datto:encryption
-READ\-ONLY COMPATIBLE no
-DEPENDENCIES extensible_dataset
-.TE
-
-This feature enables the creation and management of natively encrypted datasets.
-
-This feature becomes \fBactive\fR when an encrypted dataset is created
-and will be returned to the \fBenabled\fR state when all datasets that
-use this feature are destroyed.
-
-.RE
-.sp
-.ne 2
-.na
-\fB\fBresilver_defer\fR\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.datto:resilver_defer
-READ\-ONLY COMPATIBLE yes
-DEPENDENCIES none
-.TE
-
-This feature allows zfs to postpone new resilvers if an existing one is already
-in progress. Without this feature, any new resilvers will cause the currently
-running one to be immediately restarted from the beginning.
-
-This feature becomes \fBactive\fR once a resilver has been deferred, and
-returns to being \fBenabled\fR when the deferred resilver begins.
-.RE
-
-.sp
-.ne 2
-.na
-\fBuserobj_accounting\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID org.zfsonlinux:userobj_accounting
-READ\-ONLY COMPATIBLE yes
-DEPENDENCIES extensible_dataset
-.TE
-
+.Pp
+This feature becomes
+.Sy active
+once it is
+.Sy enabled ,
+and never returns back to being
+.Sy enabled .
+.
+.feature org.zfsonlinux userobj_accounting yes extensible_dataset
This feature allows administrators to account the object usage information
by user and group.
-
-This feature becomes \fBactive\fR as soon as it is enabled and will never
-return to being \fBenabled\fR.
-Each filesystem will be upgraded automatically when remounted, or when new
-files are created under that filesystem.
-The upgrade can also be started manually on filesystems by running
-`zfs set version=current <pool/fs>`.
-The upgrade process runs in the background and may take a while to complete
-for filesystems containing a large number of files.
-.RE
-
-.sp
-.ne 2
-.na
-\fBproject_quota\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID org.zfsonlinux:project_quota
-READ\-ONLY COMPATIBLE yes
-DEPENDENCIES extensible_dataset
-.TE
-
-This feature allows administrators to account the space and object usage
-information against the project identifier (ID).
-
-The project ID is a new object-based attribute.
-When upgrading an existing filesystem, an object without a project ID
-attribute will be assigned a zero project ID.
-After this feature is enabled, a newly created object will inherit
-its parent directory's project ID if the parent's inherit flag is set (via
-\fBzfs project [-s|-C]\fR).
-Otherwise, the new object's project ID will be set as zero.
-An object's project ID can be changed at any time by the owner (or privileged
-user) via \fBzfs project -p $prjid\fR.
-
-This feature will become \fBactive\fR as soon as it is enabled and will never
-return to being \fBdisabled\fR.
-Each filesystem will be upgraded automatically when remounted or when a new file
-is created under that filesystem.
-The upgrade can also be triggered on filesystems via `zfs set version=current
-<pool/fs>`.
-The upgrade process runs in the background and may take a while to complete
-for the filesystems containing a large number of files.
-.RE
-
-.sp
-.ne 2
-.na
-\fBlog_spacemap\fR
-.ad
-.RS 4n
-.TS
-l l .
-GUID com.delphix:log_spacemap
-READ\-ONLY COMPATIBLE yes
-DEPENDENCIES com.delphix:spacemap_v2
-.TE
-
-This feature improves performance for heavily-fragmented pools,
-especially when workloads are heavy in random-writes.
-It does so by logging all the metaslab changes on a single spacemap every TXG
-instead of scattering multiple writes to all the metaslab spacemaps.
-
-This feature becomes \fBactive\fR as soon as it is enabled and will never
-return to being \fBenabled\fR.
-.RE
-
-.SH "SEE ALSO"
-.BR zfs (8),
-.BR zpool (8)
+.Pp
+\*[instant-never]
+\*[remount-upgrade]
+.
+.feature org.openzfs zilsaxattr yes extensible_dataset
+This feature enables
+.Sy xattr Ns = Ns Sy sa
+extended attribute logging in the ZIL.
+If enabled, extended attribute changes
+.Pq both Sy xattrdir Ns = Ns Sy dir No and Sy xattr Ns = Ns Sy sa
+are guaranteed to be durable if either the dataset had
+.Sy sync Ns = Ns Sy always
+set at the time the changes were made, or
+.Xr sync 2
+is called on the dataset after the changes were made.
+.Pp
+This feature becomes
+.Sy active
+when a ZIL is created for at least one dataset and will be returned to the
+.Sy enabled
+state when it is destroyed for all datasets that use this feature.
+.
+.feature com.delphix zpool_checkpoint yes
+This feature enables the
+.Nm zpool Cm checkpoint
+command that can checkpoint the state of the pool
+at the time it was issued and later rewind back to it or discard it.
+.Pp
+This feature becomes
+.Sy active
+when the
+.Nm zpool Cm checkpoint
+command is used to checkpoint the pool.
+The feature will only return back to being
+.Sy enabled
+when the pool is rewound or the checkpoint has been discarded.
+.El
+.
+.Sh SEE ALSO
+.Xr zfs 8 ,
+.Xr zpool 8
diff --git a/usr/src/pkg/manifests/developer-build-onbld.p5m b/usr/src/pkg/manifests/developer-build-onbld.p5m
index efa509a89b..ca55e019f7 100644
--- a/usr/src/pkg/manifests/developer-build-onbld.p5m
+++ b/usr/src/pkg/manifests/developer-build-onbld.p5m
@@ -27,6 +27,7 @@
# Copyright 2019 Joyent, Inc.
# Copyright 2016 Toomas Soome <tsoome@me.com>
# Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
+# Copyright 2022 Jason King
#
set name=pkg.fmri value=pkg:/developer/build/onbld@$(PKGVERS)
@@ -59,6 +60,7 @@ file path=opt/onbld/bin/$(ARCH)/ctfstrip mode=0555
file path=opt/onbld/bin/$(ARCH)/cw mode=0555
link path=opt/onbld/bin/$(ARCH)/dmake target=make
$(i386_ONLY)file path=opt/onbld/bin/$(ARCH)/elfextract mode=0555
+file path=opt/onbld/bin/$(ARCH)/find_elf mode=0555
file path=opt/onbld/bin/$(ARCH)/findunref mode=0555
$(sparc_ONLY)file path=opt/onbld/bin/$(ARCH)/forth mode=0555
$(sparc_ONLY)file path=opt/onbld/bin/$(ARCH)/forth_preload.so.1 mode=0555
@@ -89,7 +91,6 @@ file path=opt/onbld/bin/copyrightchk mode=0555 \
pkg.depend.bypass-generate=.*(?:Checks|onbld|Copyright).*
file path=opt/onbld/bin/cstyle mode=0555
file path=opt/onbld/bin/elfcmp mode=0555
-file path=opt/onbld/bin/find_elf mode=0555
file path=opt/onbld/bin/findcrypto mode=0555
file path=opt/onbld/bin/flg.flp mode=0555
file path=opt/onbld/bin/genoffsets mode=0555
diff --git a/usr/src/tools/Makefile b/usr/src/tools/Makefile
index 5524ad71bd..2193a1bda9 100644
--- a/usr/src/tools/Makefile
+++ b/usr/src/tools/Makefile
@@ -26,6 +26,7 @@
# Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
# Copyright 2019 Joyent, Inc.
# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+# Copyright 2021 Jason King
#
include ../Makefile.master
@@ -53,6 +54,7 @@ COMMON_SUBDIRS= \
codesign \
cscope-fast \
env \
+ find_elf \
findunref \
lintdump \
make \
diff --git a/usr/src/tools/find_elf/Makefile b/usr/src/tools/find_elf/Makefile
new file mode 100644
index 0000000000..db723147bf
--- /dev/null
+++ b/usr/src/tools/find_elf/Makefile
@@ -0,0 +1,68 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2022 Jason King
+#
+
+PROG = find_elf
+MAN1ONBLDFILES = find_elf.1onbld
+
+#
+# Since libcustr is private, we just build and link in the code directly
+# into the binary. If more build utilities require it in the future, we
+# can transition to building a tools version of the library and link
+# against it.
+CUSTRDIR = $(SRC)/lib/libcustr/common
+
+OBJS = find_elf.o custr.o
+
+include $(SRC)/tools/Makefile.tools
+include $(SRC)/cmd/Makefile.ctf
+
+$(ROOTONBLDMAN1ONBLDFILES) := FILEMODE= 644
+
+LDLIBS = -lelf -lavl
+NATIVE_LIBS += libelf.so libc.so libavl.so
+
+CPPFLAGS += -I$(CUSTRDIR)
+LDFLAGS = \
+ -L$(ROOTONBLDLIBMACH) \
+ '-R$$ORIGIN/../../lib/$(MACH)' \
+ $(BDIRECT) $(ZLAZYLOAD)
+
+CSTD = $(CSTD_GNU99)
+
+.KEEP_STATE:
+
+.PARALLEL: $(OBJS)
+
+all: $(PROG)
+
+install: all .WAIT $(ROOTONBLDMACHPROG) $(ROOTONBLDDATAFILES) \
+ $(ROOTONBLDMAN1ONBLDFILES)
+
+clean:
+ $(RM) -f $(OBJS)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: %.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+%.o: $(CUSTRDIR)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+include $(SRC)/tools/Makefile.targ
diff --git a/usr/src/tools/scripts/find_elf.1onbld b/usr/src/tools/find_elf/find_elf.1onbld
index ac578ea9df..8fe9b7713b 100644
--- a/usr/src/tools/scripts/find_elf.1onbld
+++ b/usr/src/tools/find_elf/find_elf.1onbld
@@ -19,11 +19,13 @@
.\"
.\" CDDL HEADER END
.\"
-.TH FIND_ELF 1ONBLD "December 3, 2021"
+.\" Copyright 2022 Jason King
+.\"
+.TH FIND_ELF 1ONBLD "May 29, 2022"
.SH NAME
find_elf \- Locate ELF objects
.SH SYNOPSIS
-\fBfind_elf [-afrs] path\fP
+\fBfind_elf [-afhnrs] path\fP
.SH DESCRIPTION
The
.I find_elf
@@ -46,6 +48,21 @@ shared objects must end with a .so extension. Files that do not
meet these requirements are silently eliminated from consideration without
further analysis.
.TP 4
+.B \-h
+Show usage message.
+.TP 4
+.B \-n
+Do not treat well known hard-linked binaries as special. Certain well known
+binaries (currently \fBalias\fP and \fBisaexec\fP) are hard linked to many
+other names in a proto directory tree.
+.P
+By default,
+.I find_elf
+will use these well known names as the initial name and all other hard links
+to those binaries are treated as aliases. Disabling this behavior with the
+\fB-n\fR option will choose the first name encountered during directory
+traversal as the name, and all other hard links to the binary as aliases.
+.TP 4
.B \-r
Report file names as relative paths, relative to the given file or directory,
instead of fully qualified.
diff --git a/usr/src/tools/find_elf/find_elf.c b/usr/src/tools/find_elf/find_elf.c
new file mode 100644
index 0000000000..7c31ccc685
--- /dev/null
+++ b/usr/src/tools/find_elf/find_elf.c
@@ -0,0 +1,863 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 Joyent, Inc.
+ * Copyright 2022 Jason King
+ */
+
+#include <sys/debug.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/avl.h>
+#include <sys/fcntl.h>
+#include <sys/sysmacros.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <libcustr.h>
+#include <libelf.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static inline bool
+is_same(const struct stat *s1, const struct stat *s2)
+{
+ if ((s1->st_ino == s2->st_ino) && (s1->st_dev == s2->st_dev))
+ return (true);
+ return (false);
+}
+
+typedef enum dir_flags {
+ DF_NONE = 0,
+ DF_IS_SELF = 1 << 0,
+ DF_IS_SYMLINK = 1 << 2,
+} dir_flags_t;
+
+typedef struct path {
+ custr_t *p_name;
+ size_t p_pfxidx;
+} path_t;
+
+typedef struct name {
+ char *n_name;
+ bool n_is_symlink;
+} name_t;
+
+typedef struct names {
+ name_t *ns_names;
+ uint_t ns_num;
+ uint_t ns_alloc;
+} names_t;
+
+typedef struct elfinfo {
+ int ei_class;
+ uint16_t ei_type;
+ bool ei_hasverdef;
+} elfinfo_t;
+
+typedef struct obj {
+ avl_node_t obj_avlid;
+ avl_node_t obj_avlname;
+ dev_t obj_dev;
+ ino_t obj_inode;
+ names_t obj_names;
+ elfinfo_t obj_elfinfo;
+} obj_t;
+
+static void path_init(path_t *, const char *, bool);
+static size_t path_append(path_t *, const char *);
+static const char *path_name(const path_t *);
+static const char *path_fullpath(const path_t *);
+static void path_pop(path_t *, size_t);
+
+static bool maybe_obj(const char *, mode_t);
+static bool get_elfinfo(const path_t *, int, elfinfo_t *);
+static void add_name(obj_t *, const path_t *, bool);
+static int cmp_id(const void *, const void *);
+static int cmp_objname(const void *, const void *);
+static int cmp_names(const void *, const void *);
+
+static void process_dir(path_t *, int, const struct stat *, dir_flags_t);
+static void process_file(path_t *, int, const struct stat *, bool);
+static void process_arg(char *);
+
+static void sort_names(void *, void *);
+static void print_entry(void *, void *);
+
+static void foreach_avl(avl_tree_t *, void (*)(void *, void *), void *);
+
+static void nomem(void);
+static char *xstrdup(const char *);
+static void *xcalloc(size_t, size_t);
+
+static avl_tree_t avl_byid;
+static avl_tree_t avl_byname;
+
+static const char *special_paths[] = {
+ "usr/bin/alias",
+ "usr/lib/isaexec",
+};
+
+static int rootfd = -1;
+
+/* By default, we process aliases */
+static bool do_alias = true;
+
+/* By default, we treat certain well known names (e.g. isaexec) as special */
+static bool do_special = true;
+
+/* fast_mode, relpath, and so_only are all false by default */
+static bool fast_mode;
+static bool relpath;
+static bool so_only;
+
+static void __NORETURN
+usage(const char *name)
+{
+ (void) fprintf(stderr,
+ "Usage: %s [-afnrs] file | dir\n"
+ "\t[-a]\texpand symlink aliases\n"
+ "\t[-f]\tuse file name at mode to speed search\n"
+ "\t[-h]\tshow this help\n"
+ "\t[-n]\tdon\'t treat well known paths as special in sorting\n"
+ "\t[-r]\treport relative paths\n"
+ "\t[-s]\tonly remote shareable (ET_DYN) objects\n", name);
+ exit(EXIT_FAILURE);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+
+ if (elf_version(EV_CURRENT) == EV_NONE)
+ errx(EXIT_FAILURE, "elf library is out of date");
+
+ while ((c = getopt(argc, argv, "ahfnrs")) != -1) {
+ switch (c) {
+ case 'a':
+ do_alias = false;
+ break;
+ case 'f':
+ fast_mode = true;
+ break;
+ case 'n':
+ do_special = false;
+ break;
+ case 'r':
+ relpath = true;
+ break;
+ case 's':
+ so_only = true;
+ break;
+ case 'h':
+ usage(argv[0]);
+ case '?':
+ (void) fprintf(stderr, "Unknown option -%c\n", optopt);
+ usage(argv[0]);
+ }
+ }
+
+ if (optind == argc) {
+ (void) fprintf(stderr, "Missing file or dir parameter\n");
+ usage(argv[0]);
+ }
+
+ if (argv[optind][0] == '\0')
+ errx(EXIT_FAILURE, "Invalid file or dir value");
+
+ avl_create(&avl_byid, cmp_id, sizeof (obj_t),
+ offsetof(obj_t, obj_avlid));
+ avl_create(&avl_byname, cmp_objname, sizeof (obj_t),
+ offsetof(obj_t, obj_avlname));
+
+ process_arg(argv[optind]);
+ foreach_avl(&avl_byid, sort_names, &avl_byname);
+ foreach_avl(&avl_byname, print_entry, NULL);
+
+ return (EXIT_SUCCESS);
+}
+
+static void
+process_arg(char *arg)
+{
+ path_t path;
+ struct stat sb;
+ int fd;
+
+ if ((fd = open(arg, O_RDONLY)) == -1) {
+ err(EXIT_FAILURE, "could not open %s", arg);
+ }
+
+ if (fstat(fd, &sb) < 0) {
+ err(EXIT_FAILURE, "failed to stat %s", arg);
+ }
+
+ if (S_ISDIR(sb.st_mode)) {
+ path_init(&path, arg, relpath);
+ if (relpath) {
+ (void) printf("PREFIX %s\n", arg);
+ }
+
+ rootfd = fd;
+ /* process_dir() will close fd */
+ process_dir(&path, fd, &sb, DF_NONE);
+ return;
+ }
+
+ char *argcopy = xstrdup(arg);
+ char *dir = dirname(argcopy);
+
+ if (!S_ISREG(sb.st_mode)) {
+ err(EXIT_FAILURE, "not a file or directory: %s", arg);
+ }
+
+ rootfd = open(dir, O_RDONLY|O_DIRECTORY);
+ if (rootfd < 0) {
+ err(EXIT_FAILURE, "%s", dir);
+ }
+
+ path_init(&path, dir, relpath);
+ if (relpath) {
+ (void) printf("PREFIX %s\n", dir);
+ }
+ free(argcopy);
+
+ process_file(&path, fd, &sb, DF_NONE);
+}
+
+/*
+ * Processing a directory has some subtleties. When we encounter a
+ * subdirectory that's a symlink, we want any files we encounter when
+ * we traverse it to be treated as aliases. We may also have a symlink that's
+ * a link back to ourself (e.g. 32 -> .). In this special case, we still want
+ * to reprocess the directory to generate alias names, but we use the
+ * DFLAG_SELF flag to avoid recursing forever.
+ *
+ * A limitation of this approach is that we cannot handle a loop over multiple
+ * levels (e.g. foo -> ../..). Nothing currently in illumos-gate creates any
+ * such symlinks in the proto area, so we've opted to avoid complicating
+ * processing further to handle scenarios that aren't realistically expected
+ * to occur.
+ *
+ * Note that dirfd is always closed upon return from process_dir().
+ */
+static void
+process_dir(path_t *p, int dirfd, const struct stat *dirsb, dir_flags_t dflags)
+{
+ DIR *d;
+ struct dirent *de;
+
+ d = fdopendir(dirfd);
+ if (d == NULL) {
+ warn("opendir(%s)", path_fullpath(p));
+ VERIFY0(close(dirfd));
+ return;
+ }
+
+ while ((de = readdir(d)) != NULL) {
+ struct stat sb;
+ int fd;
+ bool is_link = false;
+ size_t plen;
+
+ if (strcmp(de->d_name, ".") == 0 ||
+ strcmp(de->d_name, "..") == 0) {
+ continue;
+ }
+
+ plen = path_append(p, de->d_name);
+
+ if (fstatat(rootfd, path_name(p), &sb,
+ AT_SYMLINK_NOFOLLOW) < 0) {
+ warn("%s", path_fullpath(p));
+ path_pop(p, plen);
+ continue;
+ }
+
+ fd = openat(dirfd, de->d_name, O_RDONLY);
+ if (fd < 0) {
+ /*
+ * Symlinks in the proto area may point to a path
+ * that's not present (e.g. /dev/stdin -> fd/0).
+ * Silently skip such entries.
+ */
+ if (errno != ENOENT || !S_ISLNK(sb.st_mode)) {
+ warn("%s", path_fullpath(p));
+ }
+ path_pop(p, plen);
+ continue;
+ }
+
+ if (S_ISLNK(sb.st_mode)) {
+ is_link = true;
+
+ if (fstat(fd, &sb) < 0) {
+ warn("stat %s", path_fullpath(p));
+ path_pop(p, plen);
+ continue;
+ }
+ }
+
+ if (S_ISDIR(sb.st_mode)) {
+ if ((dflags & DF_IS_SELF) != 0) {
+ VERIFY0(close(fd));
+ path_pop(p, plen);
+ continue;
+ }
+
+ dir_flags_t newflags = dflags;
+
+ /* The recursive process_dir() call closes fd */
+ if (is_link)
+ newflags |= DF_IS_SYMLINK;
+ if (is_same(&sb, dirsb))
+ newflags |= DF_IS_SELF;
+ process_dir(p, fd, &sb, newflags);
+ } else if (S_ISREG(sb.st_mode)) {
+ if (!maybe_obj(de->d_name, sb.st_mode)) {
+ VERIFY0(close(fd));
+ path_pop(p, plen);
+ continue;
+ }
+
+ if ((dflags & (DF_IS_SELF | DF_IS_SYMLINK)) != 0)
+ is_link = true;
+
+ /* process_file() closes fd */
+ process_file(p, fd, &sb, is_link);
+ }
+
+ path_pop(p, plen);
+ }
+
+ /* Closes dirfd */
+ VERIFY0(closedir(d));
+}
+
+/* Note that fd is always closed upon return */
+static void
+process_file(path_t *p, int fd, const struct stat *sb, bool is_link)
+{
+ avl_index_t where = { 0 };
+ obj_t templ = {
+ .obj_dev = sb->st_dev,
+ .obj_inode = sb->st_ino,
+ };
+ obj_t *obj = avl_find(&avl_byid, &templ, &where);
+ elfinfo_t elfinfo = { 0 };
+
+ if (obj != NULL)
+ goto done;
+
+ if (!get_elfinfo(p, fd, &elfinfo)) {
+ VERIFY0(close(fd));
+ return;
+ }
+
+ obj = xcalloc(1, sizeof (*obj));
+ obj->obj_dev = sb->st_dev;
+ obj->obj_inode = sb->st_ino;
+ obj->obj_elfinfo = elfinfo;
+ avl_add(&avl_byid, obj);
+
+done:
+ add_name(obj, p, is_link);
+ VERIFY0(close(fd));
+}
+
+static void
+print_entry(void *a, void *arg __unused)
+{
+ obj_t *obj = a;
+ const char *objname = obj->obj_names.ns_names[0].n_name;
+ const char *bits = "";
+ const char *type = "";
+ const char *verdef = obj->obj_elfinfo.ei_hasverdef ?
+ "VERDEF" : "NOVERDEF";
+
+ switch (obj->obj_elfinfo.ei_class) {
+ case ELFCLASS32:
+ bits = "32";
+ break;
+ case ELFCLASS64:
+ bits = "64";
+ break;
+ default:
+ errx(EXIT_FAILURE, "unknown elfclass value %x for %s",
+ obj->obj_elfinfo.ei_class, objname);
+ }
+
+ switch (obj->obj_elfinfo.ei_type) {
+ case ET_REL:
+ type = "REL";
+ break;
+ case ET_DYN:
+ type = "DYN";
+ break;
+ case ET_EXEC:
+ type = "EXEC";
+ break;
+ default:
+ errx(EXIT_FAILURE, "unexpected elf type %x for %s",
+ obj->obj_elfinfo.ei_type, objname);
+ }
+
+ names_t *names = &obj->obj_names;
+
+ VERIFY3U(names->ns_num, >, 0);
+ VERIFY(!names->ns_names[0].n_is_symlink);
+
+ (void) printf("OBJECT %2s %-4s %-8s %s\n", bits, type, verdef,
+ objname);
+
+ for (uint_t i = 1; i < names->ns_num; i++) {
+ if (do_alias) {
+ (void) printf("%-23s %s\t%s\n",
+ "ALIAS", objname, names->ns_names[i].n_name);
+ } else {
+ (void) printf("OBJECT %2s %-4s %-8s %s\n", bits, type,
+ verdef, names->ns_names[i].n_name);
+ }
+ }
+}
+
+/*
+ * Returns true and eip populated if name was an elf object, otherwise
+ * returns false.
+ */
+static bool
+get_elfinfo(const path_t *p, int fd, elfinfo_t *eip)
+{
+ Elf *elf = NULL;
+ Elf_Scn *scn = NULL;
+ GElf_Ehdr ehdr = { 0 };
+ int eval;
+
+ if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
+ goto fail_noend;
+
+ if ((eip->ei_class = gelf_getclass(elf)) == ELFCLASSNONE) {
+ VERIFY0(elf_end(elf));
+ return (false);
+ }
+
+ if (gelf_getehdr(elf, &ehdr) == NULL)
+ goto fail;
+
+ eip->ei_type = ehdr.e_type;
+ eip->ei_hasverdef = false;
+
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ Elf_Data *data = NULL;
+ GElf_Shdr shdr = { 0 };
+
+ if (gelf_getshdr(scn, &shdr) == NULL)
+ goto fail;
+
+ if (shdr.sh_type != SHT_DYNAMIC)
+ continue;
+
+ if ((data = elf_getdata(scn, NULL)) == NULL)
+ continue;
+
+ size_t nent = shdr.sh_size / shdr.sh_entsize;
+
+ for (size_t i = 0; i < nent; i++) {
+ GElf_Dyn dyn = { 0 };
+
+ if (gelf_getdyn(data, i, &dyn) == NULL) {
+ goto fail;
+ }
+
+ if (dyn.d_tag == DT_VERDEF) {
+ eip->ei_hasverdef = true;
+ break;
+ }
+ }
+ }
+
+ VERIFY0(elf_end(elf));
+ return (true);
+
+fail:
+ VERIFY0(elf_end(elf));
+
+fail_noend:
+ eval = elf_errno();
+
+ warnx("%s: %s", path_fullpath(p), elf_errmsg(eval));
+ return (false);
+}
+
+static bool
+is_special(const char *name)
+{
+ for (uint_t i = 0; i < ARRAY_SIZE(special_paths); i++) {
+ if (strcmp(special_paths[i], name) == 0)
+ return (true);
+ }
+ return (false);
+}
+
+static void
+sort_names(void *a, void *b)
+{
+ obj_t *obj = a;
+ avl_tree_t *name_avl = b;
+ names_t *names = &obj->obj_names;
+
+ /* We should always have at least one name */
+ VERIFY3U(names->ns_num, >, 0);
+
+ /* If there is only one, they get the prize and we're done */
+ if (names->ns_num == 1)
+ return;
+
+ name_t *first = NULL;
+
+ /*
+ * Find the first (in sorted order) pathname for this object
+ * that isn't a symlink.
+ */
+ for (uint_t i = 0; i < names->ns_num; i++) {
+ name_t *n = &names->ns_names[i];
+
+ if (n->n_is_symlink)
+ continue;
+
+ if (first == NULL) {
+ first = n;
+ continue;
+ }
+
+ /*
+ * If we're treating isaexec as special, we always
+ * want it to be the first entry. Otherwise, pick
+ * the 'lowest' sorted pathname.
+ */
+ if (do_special) {
+ if (is_special(n->n_name)) {
+ first = n;
+ break;
+ }
+
+ if (strcmp(n->n_name, first->n_name) < 0)
+ first = n;
+ }
+ }
+
+ /*
+ * If the first pathname (in sorted order) isn't the first
+ * name entry, we swap it into first place (while also updating
+ * the names AVL tree).
+ */
+ if (first != NULL && first != &names->ns_names[0]) {
+ name_t tmp = names->ns_names[0];
+
+ avl_remove(name_avl, obj);
+ (void) memcpy(&names->ns_names[0], first, sizeof (name_t));
+ (void) memcpy(first, &tmp, sizeof (name_t));
+ avl_add(name_avl, obj);
+ }
+
+ /*
+ * The remaining names (symlinks or not) are sorted by
+ * their pathname.
+ */
+ qsort(&names->ns_names[1], names->ns_num - 1, sizeof (name_t),
+ cmp_names);
+}
+
+/*
+ * We grow the names array by NAME_CHUNK entries every time we need to
+ * extend it.
+ */
+#define NAME_CHUNK 4
+
+static name_t *
+name_new(names_t *names)
+{
+ if (names->ns_num < names->ns_alloc)
+ return (&names->ns_names[names->ns_num++]);
+
+ name_t *newn = NULL;
+ uint_t newamt = names->ns_alloc + NAME_CHUNK;
+
+ /*
+ * Since we may be building on a machine that doesn't
+ * have reallocarray or the like, we avoid their use here.
+ * If we ever officially designate an illumos-gate build
+ * as the minimum required to build master that contains
+ * reallocarray and such, we can replace most of this logic
+ * with it.
+ *
+ * Also use xcalloc so we get the unused entries zeroed already.
+ * Not strictly necessary, but useful for debugging.
+ */
+ newn = xcalloc(newamt, sizeof (name_t));
+
+ /* Move over existing entries */
+ (void) memcpy(newn, names->ns_names, names->ns_num * sizeof (name_t));
+
+ free(names->ns_names);
+
+ names->ns_names = newn;
+ names->ns_alloc = newamt;
+ return (&names->ns_names[names->ns_num++]);
+}
+
+static void
+add_name(obj_t *obj, const path_t *p, bool is_symlink)
+{
+ names_t *ns = &obj->obj_names;
+ const char *srcname = path_name(p);
+
+ /* We should never have duplicates */
+ for (size_t i = 0; i < ns->ns_num; i++)
+ VERIFY3S(strcmp(ns->ns_names[i].n_name, srcname), !=, 0);
+
+ name_t *n = name_new(ns);
+
+ n->n_name = xstrdup(srcname);
+ n->n_is_symlink = is_symlink;
+
+ if (is_symlink)
+ return;
+
+ /*
+ * If the name was not a symlink, first determine if there are
+ * existing (hard) links to this name already, and if so, which entry
+ * is the first one. Typically this will be the name we just added
+ * unless the file does have multiple hard links (e.g. isaexec).
+ */
+ uint_t nhlink = 0;
+ uint_t firsthlink = UINT_MAX;
+
+ for (uint_t i = 0; i < ns->ns_num; i++) {
+ if (ns->ns_names[i].n_is_symlink)
+ continue;
+ if (nhlink == 0)
+ firsthlink = i;
+ nhlink++;
+ }
+
+ if (nhlink > 1)
+ return;
+
+ /*
+ * Put the first non-symlink name as the very first
+ * entry.
+ */
+ VERIFY3U(firsthlink, !=, UINT_MAX);
+
+ if (firsthlink != 0) {
+ name_t tmp = {
+ .n_name = ns->ns_names[0].n_name,
+ .n_is_symlink = ns->ns_names[0].n_is_symlink,
+ };
+
+ (void) memcpy(&ns->ns_names[0], &ns->ns_names[firsthlink],
+ sizeof (name_t));
+ (void) memcpy(&ns->ns_names[firsthlink], &tmp, sizeof (name_t));
+ }
+
+ avl_add(&avl_byname, obj);
+}
+
+/*
+ * This is an arbitrary initial value -- basically we can nest 16 directories
+ * deep before having to grow p->p_idx (which seems like a nice power of 2).
+ */
+#define PATH_INIT 16
+
+static void
+path_init(path_t *p, const char *name, bool relpath)
+{
+ (void) memset(p, '\0', sizeof (*p));
+
+ if (custr_alloc(&p->p_name) < 0) {
+ nomem();
+ }
+
+ if (name != NULL && custr_append(p->p_name, name) < 0)
+ nomem();
+
+ size_t len = custr_len(p->p_name);
+
+ /* Trim off any trailing /'s, but don't trim '/' to an empty path */
+ while (len > 1 && custr_cstr(p->p_name)[len - 1] == '/') {
+ VERIFY0(custr_rtrunc(p->p_name, 0));
+ len--;
+ }
+
+ p->p_pfxidx = relpath ? len + 1 : 0;
+}
+
+static size_t
+path_append(path_t *p, const char *name)
+{
+ size_t clen = custr_len(p->p_name);
+
+ if (clen > 0)
+ VERIFY0(custr_appendc(p->p_name, '/'));
+ VERIFY0(custr_append(p->p_name, name));
+
+ return (clen);
+}
+
+static const char *
+path_name(const path_t *p)
+{
+ return (custr_cstr(p->p_name) + p->p_pfxidx);
+}
+
+static const char *
+path_fullpath(const path_t *p)
+{
+ return (custr_cstr(p->p_name));
+}
+
+static void
+path_pop(path_t *p, size_t idx)
+{
+ VERIFY0(custr_trunc(p->p_name, idx));
+}
+
+static int
+cmp_id(const void *a, const void *b)
+{
+ const obj_t *l = a;
+ const obj_t *r = b;
+
+ if (l->obj_dev < r->obj_dev)
+ return (-1);
+ if (l->obj_dev > r->obj_dev)
+ return (1);
+ if (l->obj_inode < r->obj_inode)
+ return (-1);
+ if (l->obj_inode > r->obj_inode)
+ return (1);
+ return (0);
+}
+
+static int
+cmp_objname(const void *a, const void *b)
+{
+ const obj_t *l = a;
+ const obj_t *r = b;
+ const name_t *ln = &l->obj_names.ns_names[0];
+ const name_t *rn = &r->obj_names.ns_names[0];
+
+ return (cmp_names(ln, rn));
+}
+
+static int
+cmp_names(const void *a, const void *b)
+{
+ const name_t *l = a;
+ const name_t *r = b;
+ int cmp = strcmp(l->n_name, r->n_name);
+
+ if (cmp < 0)
+ return (-1);
+ if (cmp > 0)
+ return (1);
+ return (0);
+}
+
+/*
+ * Use the filename and permission bits to determine if this file could
+ * possibly be an executable.
+ */
+static bool
+maybe_obj(const char *name, mode_t mode)
+{
+ /* If not in fast mode, check everything, so always return true */
+ if (!fast_mode)
+ return (true);
+
+ size_t len = strlen(name);
+
+ /* If the file name ends in .so, we check */
+ if (len >= 3 && strcmp(&name[len - 4], ".so") == 0) {
+ return (true);
+ }
+
+ /* If the file name contains '.so', we check */
+ if (len >= 4 && strstr(name, ".so.") != NULL) {
+ return (true);
+ }
+
+ /* If the above checks fail, we assume it's not a shared library */
+ if (so_only)
+ return (false);
+
+ /*
+ * The original perl script used a -x test which only looked at
+ * file permissions. This may return slightly different results
+ * than using access(2) or faccessat(2).
+ */
+ if ((mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0)
+ return (false);
+
+ return (true);
+}
+
+static void
+foreach_avl(avl_tree_t *avl, void (*cb)(void *, void *), void *arg)
+{
+ void *obj;
+
+ for (obj = avl_first(avl); obj != NULL; obj = AVL_NEXT(avl, obj))
+ cb(obj, arg);
+}
+
+static char *
+xstrdup(const char *s)
+{
+ char *news = strdup(s);
+
+ if (news == NULL) {
+ nomem();
+ }
+ return (news);
+}
+
+static void *
+xcalloc(size_t nelem, size_t elsize)
+{
+ void *p = calloc(nelem, elsize);
+
+ if (p == NULL) {
+ nomem();
+ }
+
+ return (p);
+}
+
+#define NOMEM_MSG "out of memory\n"
+static void __NORETURN
+nomem(void)
+{
+ /* Try to get the error out before aborting */
+ (void) write(STDERR_FILENO, NOMEM_MSG, sizeof (NOMEM_MSG));
+ abort();
+}
diff --git a/usr/src/tools/scripts/Makefile b/usr/src/tools/scripts/Makefile
index d6cd4cbfda..f28b285b14 100644
--- a/usr/src/tools/scripts/Makefile
+++ b/usr/src/tools/scripts/Makefile
@@ -25,6 +25,7 @@
#
# Copyright 2020 Joyent, Inc.
# Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
+# Copyright 2022 Jason King
SHELL=/usr/bin/ksh93
@@ -51,7 +52,6 @@ SHFILES= \
PERLFILES= \
check_rtime \
- find_elf \
interface_check \
interface_cmp \
jstyle \
@@ -86,7 +86,6 @@ MAN1ONBLDFILES= \
check_rtime.1onbld \
ctfconvert.1onbld \
cstyle.1onbld \
- find_elf.1onbld \
flg.flp.1onbld \
git-pbchk.1onbld \
hdrchk.1onbld \
diff --git a/usr/src/tools/scripts/find_elf.pl b/usr/src/tools/scripts/find_elf.pl
deleted file mode 100644
index a1e0afa1f0..0000000000
--- a/usr/src/tools/scripts/find_elf.pl
+++ /dev/null
@@ -1,457 +0,0 @@
-#!/usr/bin/perl -w
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-
-#
-# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
-#
-
-#
-# Find ELF executables and sharable objects
-#
-# This script descends a directory hierarchy and reports the ELF
-# objects found, one object per line of output.
-#
-# find_elf [-frs] path
-#
-# Where path is a file or directory.
-#
-# Each line of output is of the form:
-#
-# ELFCLASS ELFTYPE VERDEF|NOVERDEF relpath
-#
-# where relpath is the path relative to the directory from which the
-# search started.
-
-use strict;
-
-use vars qw($Prog %Output @SaveArgv);
-use vars qw(%opt $HaveElfedit);
-
-# Hashes used to detect aliases --- symlinks that reference a common file
-#
-# id_hash - Maps the unique st_dev/st_ino pair to the real file
-# alias_hash - Maps symlinks to the real file they reference
-#
-use vars qw(%id_hash %alias_hash);
-
-use POSIX qw(getenv);
-use Getopt::Std;
-use File::Basename;
-use IO::Dir;
-use Config;
-
-BEGIN {
- if ($Config{useithreads}) {
- require threads;
- require threads::shared;
- threads::shared->import(qw(share));
- require Thread::Queue;
- }
-}
-
-#
-# We need to clamp the maximum number of threads that we use. Because
-# this program is mostly an exercise in fork1, more threads doesn't
-# actually help us after a certain point as we just spend more and more
-# time trying to stop all of our LWPs than making forward progress.
-# We've seen experimentally on both 2P systems with 128 cores and 1P
-# systems with 16 cores that around 8 threads is a relative sweet spot.
-#
-chomp (my $NPROCESSORS_ONLN = `getconf NPROCESSORS_ONLN 2>/dev/null` || 1);
-my $max_threads = $ENV{DMAKE_MAX_JOBS} || $NPROCESSORS_ONLN;
-if ($max_threads > 8) {
- $max_threads = 8;
-}
-
-my $tq;
-
-if ($Config{useithreads}) {
- share(%Output);
- share(%id_hash);
- share(%alias_hash);
-
- $tq = Thread::Queue->new;
-}
-
-## GetObjectInfo(path)
-#
-# Return a 3 element output array describing the object
-# given by path. The elements of the array contain:
-#
-# Index Meaning
-# -----------------------------------------------
-# 0 ELFCLASS of object (0 if not an ELF object)
-# 1 Type of object (NONE if not an ELF object)
-# 2 VERDEF if object defines versions, NOVERDEF otherwise
-#
-sub GetObjectInfo {
- my $path = $_[0];
-
- # If elfedit is available, we use it to obtain the desired information
- # by executing three commands in order, to produce a 0, 2, or 3
- # element output array.
- #
- # Command Meaning
- # -----------------------------------------------
- # ehdr:ei_class ELFCLASS of object
- # ehdr:ei_e_type Type of object
- # dyn:tag verdef Address of verdef items
- #
- # We discard stderr, and simply examine the resulting array to
- # determine the situation:
- #
- # # Array Elements Meaning
- # -----------------------------------------------
- # 0 File is not ELF object
- # 2 Object with no versions (no VERDEF)
- # 3 Object that has versions
- if ($HaveElfedit) {
- my $ecmd = "elfedit -r -o simple -e ehdr:ei_class " .
- "-e ehdr:e_type -e 'dyn:tag verdef'";
- my @Elf = split(/\n/, `$ecmd $path 2>/dev/null`);
-
- my $ElfCnt = scalar @Elf;
-
- # Return ET_NONE array if not an ELF object
- return (0, 'NONE', 'NOVERDEF') if ($ElfCnt == 0);
-
- # Otherwise, convert the result to standard form
- $Elf[0] =~ s/^ELFCLASS//;
- $Elf[1] =~ s/^ET_//;
- $Elf[2] = ($ElfCnt == 3) ? 'VERDEF' : 'NOVERDEF';
- return @Elf;
- }
-
- # For older platforms, we use elfdump to get the desired information.
- my @Elf = split(/\n/, `elfdump -ed $path 2>&1`);
- my $Header = 'None';
- my $Verdef = 'NOVERDEF';
- my ($Class, $Type);
-
- foreach my $Line (@Elf) {
- # If we have an invalid file type (which we can tell from the
- # first line), or we're processing an archive, bail.
- if ($Header eq 'None') {
- if (($Line =~ /invalid file/) ||
- ($Line =~ /$path(.*):/)) {
- return (0, 'NONE', 'NOVERDEF');
- }
- }
-
- if ($Line =~ /^ELF Header/) {
- $Header = 'Ehdr';
- next;
- }
-
- if ($Line =~ /^Dynamic Section/) {
- $Header = 'Dyn';
- next;
- }
-
- if ($Header eq 'Ehdr') {
- if ($Line =~ /e_type:\s*ET_([^\s]+)/) {
- $Type = $1;
- next;
- }
- if ($Line =~ /ei_class:\s+ELFCLASS(\d+)/) {
- $Class = $1;
- next;
- }
- next;
- }
-
- if (($Header eq 'Dyn') &&
- ($Line =~ /^\s*\[\d+\]\s+VERDEF\s+/)) {
- $Verdef = 'VERDEF';
- next;
- }
- }
- return ($Class, $Type, $Verdef);
-}
-
-
-## ProcFile(FullPath, RelPath, AliasedPath, IsSymLink, dev, ino)
-#
-# Determine whether this a ELF dynamic object and if so, add a line
-# of output for it to @Output describing it.
-#
-# entry:
-# FullPath - Fully qualified path
-# RelPath - Path relative to starting root directory
-# AliasedPath - True if RelPath contains a symlink directory component.
-# Such a path represents an alias to the same file found
-# completely via actual directories.
-# IsSymLink - True if basename (final component) of path is a symlink.
-#
-sub ProcFile {
- my($FullPath, $RelPath, $AliasedPath, $IsSymLink, $dev, $ino) = @_;
- my(@Elf, @Pvs, @Pvs_don, @Vers, %TopVer);
- my($Aud, $Max, $Priv, $Pub, $ElfCnt, $Val, $Ttl, $NotPlugin);
-
- my $uniqid = sprintf("%llx-%llx", $dev, $ino);
-
- # Remove ./ from front of relative path
- $RelPath =~ s/^\.\///;
-
- my $name = $opt{r} ? $RelPath : $FullPath;
-
- # If this is a symlink, or the path contains a symlink, put it in
- # the alias hash for later analysis. We do this before testing to
- # see if it is an ELF file, because that's a relatively expensive
- # test. The tradeoff is that the alias hash will contain some files
- # we don't care about. That is a small cost.
- if (($IsSymLink || $AliasedPath) && !$opt{a}) {
- $alias_hash{$name} = $uniqid;
- return;
- }
-
- # Obtain the ELF information for this object.
- @Elf = GetObjectInfo($FullPath);
-
- if ($Elf[1] eq 'NONE') {
- return;
- }
-
- # Return quietly if object is executable or relocatable but the -s
- # option was used.
- if ((($Elf[1] eq 'EXEC') || ($Elf[1] eq 'REL')) && $opt{s}) {
- return;
- }
-
- $Output{$name} = sprintf("OBJECT %2s %-4s %-8s %s\n",
- $Elf[0], $Elf[1], $Elf[2], $name);
-
- # Remember it for later alias analysis
- $id_hash{$uniqid} = $name;
-}
-
-
-## ProcDir(FullPath, RelPath, AliasedPath, SelfSymlink)
-#
-# Recursively search directory for dynamic ELF objects, calling
-# ProcFile() on each one.
-#
-# entry:
-# FullPath - Fully qualified path
-# RelPath - Path relative to starting root directory
-# AliasedPath - True if RelPath contains a symlink directory component.
-# Such a path represents an alias to the same file found
-# completely via actual directories.
-# SelfSymlink - True (1) if the last segment in the path is a symlink
-# that points at the same directory (i.e. 32->.). If SelfSymlink
-# is True, ProcDir() examines the given directory for objects,
-# but does not recurse past it. This captures the aliases for
-# those objects, while avoiding entering a recursive loop,
-# or generating nonsensical paths (i.e., 32/amd64/...).
-#
-sub ProcDir {
- if ($Config{useithreads}) {
- threads->create(sub {
- while (my $q = $tq->dequeue) {
- ProcFile(@$q)
- }
- }) for (1 .. $max_threads);
- }
-
- _ProcDir(@_);
-
- if ($Config{useithreads}) {
- $tq->end;
- $_->join for threads->list;
- }
-}
-
-sub _ProcDir {
- my($FullDir, $RelDir, $AliasedPath, $SelfSymlink) = @_;
- my($NewFull, $NewRel, $Entry);
-
- # Open the directory and read each entry, omit files starting with "."
- my $Dir = IO::Dir->new($FullDir);
- if (defined($Dir)) {
- foreach $Entry ($Dir->read()) {
-
- # In fast mode, we skip any file name that starts
- # with a dot, which by side effect also skips the
- # '.' and '..' entries. In regular mode, we must
- # explicitly filter out those entries.
- if ($opt{f}) {
- next if ($Entry =~ /^\./);
- } else {
- next if ($Entry =~ /^\.\.?$/);
- }
-
- $NewFull = join('/', $FullDir, $Entry);
-
- # We need to follow symlinks in order to capture
- # all possible aliases for each object. However,
- # symlinks that point back at the same directory
- # (e.g. 32->.) must be flagged via the SelfSymlink
- # argument to our recursive self in order to avoid
- # taking it more than one level down.
- my $RecurseAliasedPath = $AliasedPath;
- my $RecurseSelfSymlink = 0;
- my $IsSymLink = -l $NewFull;
- if ($IsSymLink) {
- my $trans = readlink($NewFull);
-
- $trans =~ s/\/*$//;
- $RecurseSelfSymlink = 1 if $trans eq '.';
- $RecurseAliasedPath = 1;
- }
-
- if (!stat($NewFull)) {
- next;
- }
- $NewRel = join('/', $RelDir, $Entry);
-
- # Descend into and process any directories.
- if (-d _) {
- # If we have recursed here via a $SelfSymlink,
- # then do not persue directories. We only
- # want to find objects in the same directory
- # via that link.
- next if $SelfSymlink;
-
- _ProcDir($NewFull, $NewRel, $RecurseAliasedPath,
- $RecurseSelfSymlink);
- next;
- }
-
- # In fast mode, we skip objects unless they end with
- # a .so extension, or are executable. We touch
- # considerably fewer files this way.
- if ($opt{f} && !($Entry =~ /\.so$/) &&
- !($Entry =~ /\.so\./) &&
- ($opt{s} || (! -x _))) {
- next;
- }
-
- # Process any standard files.
- if (-f _) {
- my ($dev, $ino) = stat(_);
- if ($Config{useithreads}) {
- $tq->enqueue([ $NewFull, $NewRel,
- $AliasedPath, $IsSymLink, $dev,
- $ino ]);
- }
- else {
- ProcFile($NewFull, $NewRel,
- $AliasedPath, $IsSymLink, $dev,
- $ino);
- }
- next;
- }
-
- }
- $Dir->close();
- }
-}
-
-
-# -----------------------------------------------------------------------------
-
-# Establish a program name for any error diagnostics.
-chomp($Prog = `basename $0`);
-
-# The onbld_elfmod package is maintained in the same directory as this
-# script, and is installed in ../lib/perl. Use the local one if present,
-# and the installed one otherwise.
-my $moddir = dirname($0);
-$moddir = "$moddir/../lib/perl" if ! -f "$moddir/onbld_elfmod.pm";
-require "$moddir/onbld_elfmod.pm";
-
-# Check that we have arguments.
-@SaveArgv = @ARGV;
-if ((getopts('afrs', \%opt) == 0) || (scalar(@ARGV) != 1)) {
- print "usage: $Prog [-frs] file | dir\n";
- print "\t[-a]\texpand symlink aliases\n";
- print "\t[-f]\tuse file name at mode to speed search\n";
- print "\t[-r]\treport relative paths\n";
- print "\t[-s]\tonly remote sharable (ET_DYN) objects\n";
- exit 1;
-}
-
-%Output = ();
-%id_hash = ();
-%alias_hash = ();
-$HaveElfedit = -x '/usr/bin/elfedit';
-
-my $Arg = $ARGV[0];
-my $Error = 0;
-
-ARG: {
- # Process simple files.
- if (-f $Arg) {
- my($RelPath) = $Arg;
-
- if ($opt{r}) {
- my $Prefix = $Arg;
-
- $Prefix =~ s/(^.*)\/.*$/$1/;
- $Prefix = '.' if ($Prefix eq $Arg);
- print "PREFIX $Prefix\n";
- }
- $RelPath =~ s/^.*\//.\//;
- my ($dev, $ino) = stat(_);
- my $IsSymLink = -l $Arg;
- ProcFile($Arg, $RelPath, 0, $IsSymLink, $dev, $ino);
- next;
- }
-
- # Process directories.
- if (-d $Arg) {
- $Arg =~ s/\/$//;
- print "PREFIX $Arg\n" if $opt{r};
- ProcDir($Arg, ".", 0, 0);
- next;
- }
-
- print STDERR "$Prog: not a file or directory: $Arg\n";
- $Error = 1;
-}
-
-# Build a hash, using the primary file name as the key, that has the
-# strings for any aliases to that file.
-my %alias_text = ();
-foreach my $Alias (sort keys %alias_hash) {
- my $id = $alias_hash{$Alias};
- if (defined($id_hash{$id})) {
- my $obj = $id_hash{$id};
- my $str = "ALIAS $id_hash{$id}\t$Alias\n";
-
- if (defined($alias_text{$obj})) {
- $alias_text{$obj} .= $str;
- } else {
- $alias_text{$obj} = $str;
- }
- }
-}
-
-# Output the main files sorted by name. Place the alias lines immediately
-# following each main file.
-foreach my $Path (sort keys %Output) {
- print $Output{$Path};
- print $alias_text{$Path} if defined($alias_text{$Path});
-}
-
-exit $Error;