SMALL_GICP / SMALL_VGICP external DSO and ODR gate¶
Date: 2026-08-21 Status: pluginlib discovery and replay compatibility Go for offline characterization; the scoped Small-only symbol-isolated DSO gate is Go for the pinned build and replays; live/default promotion remains No-Go.
Decision¶
The historical dependency-enabled install discovered and instantiated the
real external pluginlib classes lidarslam_default_plugins/SmallGicpPcl and
lidarslam_default_plugins/SmallVGicpPcl. Their receipts contain
backend_kind: pluginlib, the installed combined DSO path, and the
registration_plugins_with_small.xml manifest path; no
lidarslam_builtin/* host alias was selected. HILTI exp04 and MID-360
first-100 and full replays were byte-identical to the legacy
same-translation-unit path for both selectors.
The first set of runs used the ordinary combined DSO and was not an
independent implementation proof: that DSO exports default-visible weak
small_gicp::RegistrationPCL template symbols, and the process binding trace
shows those symbols—and the concrete adapter's align()—resolving to
libscanmatcher_component.so, which already contains the same-translation-unit
instantiations. The ordinary run therefore closes class discovery,
provenance, and replay-wiring only. The scoped symbol-isolation experiment
below repeats the gate with a dedicated Small DSO and function-local binding;
it is still not a production default or absolute-accuracy claim.
Isolated install and provenance¶
The normal system installation was not modified. The runs sourced the read-only vendor extraction and isolated workspace in this order:
/tmp/small-gicp-vendor.7YYK8k/extracted/opt/ros/jazzy/opt/small_gicp_vendor
/tmp/small-gicp-build.2auPmA/install
The installed artifacts used by every external run were:
| artifact | value |
|---|---|
| DSO | /tmp/small-gicp-build.2auPmA/install/lidarslam_default_plugins/lib/liblidarslam_default_plugins.so |
| DSO SHA-256 | a614240d6a7f96172babc581b263e5a7a03a1a944c765bb00d8aa36171b962ab |
| manifest | /tmp/small-gicp-build.2auPmA/install/lidarslam_default_plugins/share/lidarslam_default_plugins/registration_plugins_with_small.xml |
| manifest SHA-256 | 10e00a3e02a60a1ff7320954b41d9d1f72851b6d5947efc908b97f36227775b9 |
| vendor | ros-jazzy-small-gicp-vendor 2.1.0-1noble.20260309.122135, extracted under /tmp |
The XML declares these Small external classes (alongside the package's NDT and pclomp-GICP classes):
<class name="lidarslam_default_plugins/SmallGicpPcl" ... />
<class name="lidarslam_default_plugins/SmallVGicpPcl" ... />
The clean-install loader contract test passed for both classes, including
backend_kind == pluginlib, class identity, non-empty library path, and
non-empty manifest path:
source /opt/ros/jazzy/setup.bash
source install/setup.bash
source /tmp/small-gicp-build.2auPmA/install/setup.bash
/tmp/small-gicp-build.2auPmA/build/lidarslam_registration_loader/\
test_registration_plugin_loader \
--gtest_filter=RegistrationPluginLoader.DiscoversAndConfiguresOptionalSmallClassesOnlyWhenAdvertised
Result: 1 test passed, 0 failed. The receipts used by the replay runs are preserved outside the repository under the output directories listed below. Absolute receipt paths are provenance fields and must be normalized before receipt-byte comparisons between install roots.
Replay method¶
Each comparison used ndt_num_threads:=1, async_map_update:=false, the
same parameter file and topics on both sides, and max_clouds:=100 for the
smoke or max_clouds:=0 for the full run. The legacy side left
registration_plugin_enable false and therefore constructed the built-in
same-TU path. The external side set both:
The class line is selected per run, not supplied twice. Every run exported the
trajectory and submap CSV. Full runs also exported the world-frame PCD;
equality was checked with cmp, MD5, and (for maps) SHA-256.
First 100 scans¶
The external side was compared against the matching legacy artifact in each
directory. Every listed pair was cmp-equal.
| input | selector | external trajectory MD5 | external submap MD5 | output |
|---|---|---|---|---|
| HILTI exp04 | SmallGicpPcl |
829b23ed9e307f431af558546d27d000 |
21ee5902fc9208fab0b4502e32095b60 |
/tmp/small-gicp-dso-hilti-100.Kk1QYw/SMALL_GICP |
| HILTI exp04 | SmallVGicpPcl |
ecc6e402c4d67e23154a67914a2bd3d3 |
0344cb056b47e03ec19a9a71566dd48d |
/tmp/small-gicp-dso-hilti-100.Kk1QYw/SMALL_VGICP |
| MID-360 | SmallGicpPcl |
84e99e44d6a3fafebfed77b227064dfc |
d4d79270861753ab747f3d131a1dc0b9 |
/tmp/small-gicp-dso-mid360-100.pLBaDB/SMALL_GICP_dso |
| MID-360 | SmallVGicpPcl |
89de1da6f7058050099fd27fd4d34dc7 |
43068c9a737307b4f78a9e4d27945aa6 |
/tmp/small-gicp-dso-mid360-100.pLBaDB/SMALL_VGICP_dso |
HILTI used /media/user/aiueo1/datasets/hilti2022/exp04_ros2,
/hesai/pandar, /alphasense/imu, and
configs/hilti2022/lidarslam_competitive_v2.yaml. MID-360 used
/media/user/aiueo1/datasets/mid360_public/driving_slam_mid360/extracted/rosbag2_2024_04_16-14_17_01/rosbag2_2024_04_16-14_17_01,
/livox/lidar, /livox/imu, and lidarslam/param/lidarslam.yaml.
Full replay, one external run¶
The legacy column is the existing same-TU run 1 from the corresponding full
gate. The external run was a new ordinary pluginlib process. All four rows
were cmp-equal for trajectory, submaps, and map PCD; the map SHA-256 also
matched.
| input / selector | poses / submaps | trajectory MD5 | submap MD5 | map MD5 / bytes | map SHA-256 | external wall / peak RSS |
|---|---|---|---|---|---|---|
HILTI / SMALL_GICP |
1,258 / 26 | c6b98f87d0411a1167a4b26e285e90fa |
23bc8057ad3b22b1bcdb4c1868c886d0 |
3e6dd027db8937396eacc31c84c3fa77 / 6,229,590 |
051e1e48fe04970e5014592548cd5b0d363460c1aef052bcd709b4af2918d1c2 |
134.00 s / 214,832 KiB |
HILTI / SMALL_VGICP |
1,258 / 38 | f69ae783894db9e19a9c9af86c17d4a0 |
6d26d745e119e423df026875b5f351dd |
718a4679bd8b3ad6b06949f2a9f8f4d4 / 8,300,217 |
bb64790cd8d46248c26bd6939428cb94e1f7b745d3c056eb23a8aa0f6b2d3925 |
94.80 s / 259,828 KiB |
MID-360 / SMALL_GICP |
2,772 / 295 | 2e36e8d58cde280f2d12e7cad5cf850a |
baaa92dcee874e7047a753e5bb7052de |
947df4b495f92400137cb6a52c9a22ae / 39,676,100 |
684d2983215ac76d91c108eec82916933e3d6854728e85104f4d1d5479644115 |
237.75 s / 754,884 KiB |
MID-360 / SMALL_VGICP |
2,772 / 372 | c6b1d03e2ced7acc694d2aa8880f070b |
c975d42481af07077484109003069f27 |
f48f8e4c963675f7b57ca90ab7698647 / 49,469,494 |
ac7fd49dab3d0ea8f59c7c22c47cd235eac1c6d21c94aea6d0a677714e970b77 |
147.71 s / 896,584 KiB |
Full external artifacts are in
/tmp/small-gicp-external-dso-hilti-full.20260821 and
/tmp/small-gicp-external-dso-mid360-full.20260821. This gate did not rerun
the sparse HILTI GT or geometric evaluators: the existing HILTI APE and
map-quality values remain applicable because the external artifacts are
byte-identical, but no separate external-DSO accuracy claim is made here.
MID-360 has no paired GT; the prior full gate recorded 538 GICP and 332 VGICP
pose rejections. Those counts are identical across the byte-identical paths,
but hash equality is not an absolute tracking-quality result.
ODR / symbol-binding inspection¶
The ordinary DSO has no SYMBOLIC dynamic flag. readelf -Ws and
nm -D -C --defined-only show default-visible weak (W) definitions for
small_gicp::RegistrationPCL<pcl::PointXYZI, pcl::PointXYZI> and for the
corresponding pclomp template specializations. readelf -d shows no
libsmall_gicp.so NEEDED entry; the template code is present in the DSO.
An LD_DEBUG=bindings trace was captured while loading the real external
class in the scanmatcher process:
Representative bindings include:
liblidarslam_default_plugins.so -> libscanmatcher_component.so:
small_gicp::RegistrationPCL<...>::computeTransformation(...)
liblidarslam_default_plugins.so -> libscanmatcher_component.so:
lidarslam_default_plugins::SmallGicpRegistration::align(...)
The trace also records setInputTarget, RTTI, and destructor bindings for the
same small_gicp::RegistrationPCL specialization. Thus the real external
class is discovered and constructed, but ordinary ELF symbol interposition
allows the host's same-TU template definitions to satisfy the DSO. Exact
replay hashes must be treated as contaminated wiring evidence, not as proof
that an independently compiled DSO has the same numerical implementation.
Historical ordinary-DSO gate outcome¶
| gate | result |
|---|---|
| external class IDs discovered from clean install | Go |
| pluginlib loader/session lifetime and provenance | Go |
| HILTI/MID-360 first-100 compatibility | Go, exact artifacts |
| HILTI/MID-360 full ordinary-DSO compatibility | Go, one external run each, exact artifacts |
| independent DSO/ODR isolation of the ordinary combined DSO | No-Go, binding trace proves host interposition |
| production default / absolute accuracy promotion | No-Go |
The production defaults and README claims remain unchanged. The candidate
-Wl,-Bsymbolic-functions fix was then evaluated only for a dedicated Small
DSO; its evidence is recorded below. The NDT/GICP DSO and any live default
route were not changed by that experiment.
Scoped Small-only symbol-isolation experiment (2026-08-21)¶
The low-risk fix was applied only to the optional Small target. The normal
liblidarslam_default_plugins.so now contains NDT/GICP only, while
liblidarslam_small_gicp_plugins.so contains both Small classes and is linked
with -Wl,-Bsymbolic-functions. The plugin manifests are separate, valid XML
files, and pluginlib's ament resource lists both paths:
share/lidarslam_default_plugins/registration_plugins.xml
share/lidarslam_default_plugins/registration_plugins_small.xml
The clean dependency-enabled build was isolated under
/tmp/small-gicp-bsymbolic-build.5FLKtQ using the read-only vendor extraction
/tmp/small-gicp-vendor.7YYK8k. The loader test discovered and configured both
real Small classes; each receipt named
liblidarslam_small_gicp_plugins.so and registration_plugins_small.xml.
The normal DSO has no small_gicp NEEDED entry. Artifact SHA-256 values are:
| artifact | SHA-256 |
|---|---|
| normal DSO | 944272cfe2266e9fb708c710cde88720851c67f005dfac437cbf876f9ca47ca8 |
| Small DSO | 013cc91a8c1bab9681781066701464ac1e9a8ae2319247ea4d77c16fc6426280 |
| normal manifest | 5d412a2ecadc449aaed12622a3a042259944088e7db73026aa2c2d82d92d0f97 |
| Small manifest | fe3e55ba1d691e63c2142836071f86c4156ff3d0a3505b88f08aae40d1bd1fe6 |
nm -D -C --defined-only confirmed the Small DSO owns
SmallGicpRegistration::align and the
small_gicp::RegistrationPCL<...>::computeTransformation definition. For
each of the four first-100 runs, LD_DEBUG=bindings showed zero bindings in
which the Small DSO requested either of those symbols from
libscanmatcher_component.so; the trace files were captured under the run
directories below. RTTI/typeinfo bindings remain expected ABI sharing and
are not treated as algorithm implementation interposition. No
readelf -rW relocation for either requested function remains in the
dedicated DSO.
The independent replay comparison used ndt_num_threads=1,
async_map_update=false, the same bag/topics/parameter file as the legacy
receipts, and cmp on trajectory, submaps, and map PCD. First-100 artifacts
were exact for all four dataset/selector pairs:
| input / selector | trajectory MD5 | submap MD5 | output |
|---|---|---|---|
HILTI / SMALL_GICP |
829b23ed9e307f431af558546d27d000 |
21ee5902fc9208fab0b4502e32095b60 |
/tmp/small-gicp-bsymbolic-hilti-gicp-100.Uz5LqX |
HILTI / SMALL_VGICP |
ecc6e402c4d67e23154a67914a2bd3d3 |
0344cb056b47e03ec19a9a71566dd48d |
/tmp/small-gicp-bsymbolic-hilti-vgicp-100.oNLqg4 |
MID-360 / SMALL_GICP |
84e99e44d6a3fafebfed77b227064dfc |
d4d79270861753ab747f3d131a1dc0b9 |
/tmp/small-gicp-bsymbolic-mid-gicp-100.1Gx7IO |
MID-360 / SMALL_VGICP |
89de1da6f7058050099fd27fd4d34dc7 |
43068c9a737307b4f78a9e4d27945aa6 |
/tmp/small-gicp-bsymbolic-mid-vgicp-100.sZBS5N |
Because the smoke paths were exact and had no forbidden implementation-symbol
binding, one full run per selector and dataset was completed. The full
trajectory/submap/map PCD artifacts were also cmp-equal to the existing
legacy run-1 artifacts; map SHA-256 values matched as well:
| input / selector | trajectory MD5 | submap MD5 | map MD5 | map SHA-256 | wall / peak RSS |
|---|---|---|---|---|---|
HILTI / SMALL_GICP |
c6b98f87d0411a1167a4b26e285e90fa |
23bc8057ad3b22b1bcdb4c1868c886d0 |
3e6dd027db8937396eacc31c84c3fa77 |
051e1e48fe04970e5014592548cd5b0d363460c1aef052bcd709b4af2918d1c2 |
153.42 s / 211108 KiB |
HILTI / SMALL_VGICP |
f69ae783894db9e19a9c9af86c17d4a0 |
6d26d745e119e423df026875b5f351dd |
718a4679bd8b3ad6b06949f2a9f8f4d4 |
bb64790cd8d46248c26bd6939428cb94e1f7b745d3c056eb23a8aa0f6b2d3925 |
122.68 s / 263152 KiB |
MID-360 / SMALL_GICP |
2e36e8d58cde280f2d12e7cad5cf850a |
baaa92dcee874e7047a753e5bb7052de |
947df4b495f92400137cb6a52c9a22ae |
684d2983215ac76d91c108eec82916933e3d6854728e85104f4d1d5479644115 |
264.49 s / 753768 KiB |
MID-360 / SMALL_VGICP |
c6b1d03e2ced7acc694d2aa8880f070b |
c975d42481af07077484109003069f27 |
f48f8e4c963675f7b57ca90ab7698647 |
ac7fd49dab3d0ea8f59c7c22c47cd235eac1c6d21c94aea6d0a677714e970b77 |
169.05 s / 896808 KiB |
Full artifacts are under
/tmp/small-gicp-bsymbolic-hilti-full-gicp-WciSGK,
/tmp/small-gicp-bsymbolic-hilti-full-vgicp-BfZ2qf,
/tmp/small-gicp-bsymbolic-mid-full-gicp-7Pvg4X, and
/tmp/small-gicp-bsymbolic-mid-full-vgicp-jcwEoc. These are compatibility
and ODR-isolation receipts, not an absolute accuracy result: MID-360 has no
paired GT, and the existing HILTI accuracy/profile evidence is retained as
the same byte artifacts rather than rerun under a new accuracy claim.
The scoped independent Small DSO/replay gate is therefore Go for this pinned Jazzy/vendor/toolchain build. Production default promotion remains No-Go until live-node policy, broader compiler/ROS/vendor matrix, and paired-ground-truth accuracy criteria are separately closed. The default NDT/GICP route, README claims, and production scanmatcher selection are unchanged.