This is an archive of the discontinued LLVM Phabricator instance.

[llvm-objcopy] Allow 'protected' visibility to be set when using add-symbol
ClosedPublic

Authored by chrisjackson on Aug 7 2019, 10:04 AM.

Details

Summary

Currently add-symbol allows "hidden" and "default" as valid flags, but not "protected". This diff enables symbols to be added with STV_PROTECTED visibility.

Diff Detail

Repository
rL LLVM

Event Timeline

chrisjackson created this revision.Aug 7 2019, 10:04 AM

@jakehehrlich/@rupprecht, I looked at this offline with @chrisjackson, and I think it makes sense to add this functionality. I believe it's above what GNU does (at the moment), but I can't think of any reason why a user shouldn't be allowed to add STV_PROTECTED symbols, and we have a use-case for it internally.

It makes sense from the perspective of completeness but STV_PROTECTED is problematic and can hardly find a justified use case.

People use STV_PROTECTED when they want to avoid GOT/PLT costs in PIC code. However,

A protected STT_OBJECT prevents you from doing copy relocation. ld.bfd silently accepts this, which can cause ODR violations.
A protected STT_FUNC prevents you from making a canonical PLT (-fno-pic code in executable references an external function). If the linker fails to stop you, you'll get pointer equality problems. (It can be argued this case can be fixed by letting the compiler go through GOT when the address of a protected function is taken, but then the GOT indirection will likely break people's expectation. A symbol lookup change is also required on ld.so side.)

I think lld and gold reject both cases. So at the best, a protected symbol can be seen as a promise of the toolchain that the symbol will not be interposed.

Actually, in a DSO, a STV_PROTECTED use case can be easily replaced with the hidden+alias idiom:

__attribute__((visibility("hidden"))) void internal() {} // internal use
__attribute__((alias("internal"))) void external(); // external API

The reservation of STV_INTERNAL was requested by SGI when gABI was formulated. All other parties appear to treat STV_INTERNAL the same as STV_HIDDEN.

It makes sense from the perspective of completeness but STV_PROTECTED is problematic and can hardly find a justified use case.

People use STV_PROTECTED when they want to avoid GOT/PLT costs in PIC code. However,

A protected STT_OBJECT prevents you from doing copy relocation. ld.bfd silently accepts this, which can cause ODR violations.
A protected STT_FUNC prevents you from making a canonical PLT (-fno-pic code in executable references an external function). If the linker fails to stop you, you'll get pointer equality problems. (It can be argued this case can be fixed by letting the compiler go through GOT when the address of a protected function is taken, but then the GOT indirection will likely break people's expectation. A symbol lookup change is also required on ld.so side.)

I think lld and gold reject both cases. So at the best, a protected symbol can be seen as a promise of the toolchain that the symbol will not be interposed.

Actually, in a DSO, a STV_PROTECTED use case can be easily replaced with the hidden+alias idiom:

__attribute__((visibility("hidden"))) void internal() {} // internal use
__attribute__((alias("internal"))) void external(); // external API

The reservation of STV_INTERNAL was requested by SGI when gABI was formulated. All other parties appear to treat STV_INTERNAL the same as STV_HIDDEN.

I agree with your points for standard ELF toolchains. We use STV_PROTECTED in our toolchain, as we have a custom DSO format. The use of protected is not unprecedented, see ARM reference for example.

MaskRay accepted this revision.Aug 9 2019, 5:22 AM
This revision is now accepted and ready to land.Aug 9 2019, 5:22 AM
rupprecht accepted this revision.Aug 9 2019, 5:37 PM
This revision was automatically updated to reflect the committed changes.