Skip to content

[lld][WebAssembly] Fix non-pie dynamic-linking executable #108146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 3, 2025

Conversation

yamt
Copy link
Contributor

@yamt yamt commented Sep 11, 2024

The commit 22b7b84
made the symbols provided by shared libraries "defined",
and thus effectively made it impossible to generate non-pie
dynamically linked executables using --unresolved-symbols=import-dynamic.

This commit, based on #109249,
fixes it by checking sym->isShared() explictly.
(as a bonus, you don't need to rely on --unresolved-symbols=import-dynamic
anymore.)

Fixes #107387

@llvmbot
Copy link
Member

llvmbot commented Sep 11, 2024

@llvm/pr-subscribers-lld

@llvm/pr-subscribers-lld-wasm

Author: YAMAMOTO Takashi (yamt)

Changes

Fixes #107387


Full diff: https://github.com/llvm/llvm-project/pull/108146.diff

3 Files Affected:

  • (modified) lld/wasm/Driver.cpp (+2-1)
  • (modified) lld/wasm/InputChunks.cpp (+1-1)
  • (modified) lld/wasm/Relocations.cpp (+2-1)
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index cb8fe2534f5fe7..79cacc63922ee5 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -913,7 +913,8 @@ static void createSyntheticSymbols() {
   }
 
   if (ctx.isPic ||
-      config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic) {
+      config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic ||
+      !config->isStatic) {
     // For PIC code, or when dynamically importing addresses, we create
     // synthetic functions that apply relocations.  These get called from
     // __wasm_call_ctors before the user-level constructors.
diff --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp
index 975225974aff6e..dbf800422e45b5 100644
--- a/lld/wasm/InputChunks.cpp
+++ b/lld/wasm/InputChunks.cpp
@@ -378,7 +378,7 @@ void InputChunk::generateRelocationCode(raw_ostream &os) const {
     uint64_t offset = getVA(rel.Offset) - getInputSectionOffset();
 
     Symbol *sym = file->getSymbol(rel);
-    if (!ctx.isPic && sym->isDefined())
+    if (!ctx.isPic && sym->isDefined() && !sym->hasGOTIndex())
       continue;
 
     LLVM_DEBUG(dbgs() << "gen reloc: type=" << relocTypeToString(rel.Type)
diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index 6f33a4f28a9d09..70229179afdcfc 100644
--- a/lld/wasm/Relocations.cpp
+++ b/lld/wasm/Relocations.cpp
@@ -146,7 +146,8 @@ void scanRelocations(InputChunk *chunk) {
 
     if (ctx.isPic ||
         (sym->isUndefined() &&
-         config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic)) {
+         config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic) ||
+        sym->isShared()) {
       switch (reloc.Type) {
       case R_WASM_TABLE_INDEX_SLEB:
       case R_WASM_TABLE_INDEX_SLEB64:

@@ -913,7 +913,8 @@ static void createSyntheticSymbols() {
}

if (ctx.isPic ||
config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic) {
config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic ||
!config->isStatic) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if can replace ctx.isPic with !config->isStatic above? Does it make sense to allow -shared + -static? Or -pie + -static?

I wonder if we need a better way to detect if dynamic linking used at all since you can imagine a case where isStatic is true, but we do have dynamic linking. For example:

$ wasm-ld foo.o -Bdynamic -ldylib -Bstatic -lstaticlib

In that case since -Bstatic comes last on the command line the config->isStatic will be false even though we include libdylib.so dyanmically.

Another example:

wasm-ld foo.o libdylib.so libstaticlib.a

Here config->isStatic will remain false even though dynamic linking is used.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it looks like that the latter case should reject the .so file. I see the ELF linker does this:

if (config->isStatic) {
error("attempted static link of dynamic object " + path);
return;
}
. I'll upload a PR for that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if can replace ctx.isPic with !config->isStatic above? Does it make sense to allow -shared + -static? Or -pie + -static?

I wonder if we need a better way to detect if dynamic linking used at all since you can imagine a case where isStatic is true, but we do have dynamic linking. For example:

$ wasm-ld foo.o -Bdynamic -ldylib -Bstatic -lstaticlib

In that case since -Bstatic comes last on the command line the config->isStatic will be false even though we include libdylib.so dyanmically.

i guess it's simpler to defer the decision to createApplyDataRelocationsFunction.
how do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess it's simpler to defer the decision to createApplyDataRelocationsFunction.

i pushed a commit to implement it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sbc100 do you have any more concerns?

@yamt yamt force-pushed the nonpie-dynamic-linking branch 2 times, most recently from 82dd674 to 9513512 Compare September 12, 2024 03:28
make<SyntheticFunction>(nullSignature, "__wasm_apply_data_relocs"));
def->markLive();

createFunction(def, bodyContent);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seems to incorporate a couple of different things now. Would you mind separating out the change to "Only create __wasm_apply_data_relocs conditionally". That seems like it could land separately and is somewhat unrelated to fix at hand?

Also, regarding the conditional creation of this symbol, what happens if somebody does --export=__wasm_apply_data_relocs or otherwise references this symbol, but we "skip" creating it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seems to incorporate a couple of different things now. Would you mind separating out the change to "Only create __wasm_apply_data_relocs conditionally". That seems like it could land separately and is somewhat unrelated to fix at hand?

#109249

Also, regarding the conditional creation of this symbol, what happens if somebody does --export=__wasm_apply_data_relocs or otherwise references this symbol, but we "skip" creating it?

is it expected to work at all? do you have real use cases? or is it just a theoretical concern?
i thought __wasm_apply_data_relocs was just for dynamic-linking.

@sbc100
Copy link
Collaborator

sbc100 commented Sep 18, 2024

Could you update the PR description describing what was broken and how this fixes the issue?

Also, can we add a new test or update one of the existing ones to make sure we don't regress again?

@yamt
Copy link
Contributor Author

yamt commented Sep 19, 2024

Could you update the PR description describing what was broken and how this fixes the issue?

Also, can we add a new test or update one of the existing ones to make sure we don't regress again?

i will update if/when #109249 is merged.
i will mark this PR a draft for now.

yamt added 2 commits October 1, 2024 14:19
The commit 22b7b84
made the symbols provided by shared libraries "defined",
and thus effectively made it impossible to generate non-pie
dynamically linked executables using --unresolved-symbols=import-dynamic.

This commit, based on llvm#109249,
fixes it by checking sym->isShared() explictly.
(as a bonus, you don't need to rely on --unresolved-symbols=import-dynamic
anymore.)

Fixes llvm#107387
@yamt yamt force-pushed the nonpie-dynamic-linking branch from 91f33a4 to 06482d7 Compare October 1, 2024 05:19
@yamt yamt changed the title [lld][WebAssembly]: Restore non-pie dynamic-linking executable [lld][WebAssembly] Fix non-pie dynamic-linking executable Oct 1, 2024
@yamt yamt marked this pull request as ready for review October 1, 2024 05:20
@yamt
Copy link
Contributor Author

yamt commented Oct 1, 2024

.type f,@function
f:
.functype f () -> ()
end_function
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than creating a new file here you can just pick an existing file such as ret32.s and compile that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok. i will try.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -146,7 +146,8 @@ void scanRelocations(InputChunk *chunk) {

if (ctx.isPic ||
(sym->isUndefined() &&
config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic)) {
config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic) ||
sym->isShared()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you put this new clause on line 147 (e.g. if (ctx.isPic || sym->isShared() ||)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.lib.o %p/Inputs/lib.s
# RUN: wasm-ld -m wasm32 --experimental-pic -shared --no-entry %t.lib.o -o %t.lib.so
# RUN: llvm-mc -filetype=obj -mattr=+reference-types -triple=wasm32-unknown-unknown -o %t.o %s
# RUN: wasm-ld -m wasm32 --features=mutable-globals -Bdynamic --export-table --growable-table --export-memory --export=__stack_pointer --export=__heap_base --export=__heap_end --entry=_start %t.o %t.lib.so -o %t.wasm
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove any link flags that you don't need here? e.g. all of the --export flags and the --entry flag?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i wanted to mimic the real situation to link a non-pie executable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

i32.const f_p@MBREL
i32.add
i32.load 0
call_indirect __indirect_function_table, () -> ()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need the __indirect_function_table symbol to make this test work. You can just do i32.const f_p followed by drop I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let me think a bit. thank you.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


# CHECK: - Type: EXPORT
# CHECK: - Name: __wasm_apply_data_relocs
# CHECK-NEXT: Kind: FUNCTION
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be checking for some more things here?

Perhaps we can check that GOT.ret32 is imported? Perhaps we can decompile __wasm_apply_data_relocs and verify that is using GOT.ret32 as expected?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you know any existing tests doing similar things which i can use as an example?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there are a few tests that disassembly stuff. Looks for DIS-NEXT in lld/test/wasm.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok. i will take a look.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.lib.o %p/Inputs/ret32.s
# RUN: wasm-ld -m wasm32 --experimental-pic -shared --no-entry %t.lib.o -o %t.lib.so
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
# RUN: wasm-ld -m wasm32 -Bdynamic %t.o %t.lib.so -o %t.wasm
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to confirm, this effectively generates a non-PIC output? And ctx.isPic not set?

I wonder if we can confirm this this test somehow? Perhaps its enough that we successfully use i32.const f_p (which would not work if the output was PIC, right?) instead of i32.const f_p@MBREL

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ctx.isPic is not set as neither of pie or shared is set.

the following is the generated output. it looks reasonable to me.

(module $dylink-non-pie.s.tmp.wasm
  (type (;0;) (func (param f32) (result i32)))
  (type (;1;) (func))
  (import "env" "ret32" (func $ret32 (type 0)))
  (import "GOT.func" "ret32" (global $ret32 (mut i32)))
  (func $__wasm_apply_data_relocs (type 1)
    i32.const 1024
    global.get $ret32
    i32.store)
  (func $_start (type 1)
    i32.const 1024
    drop)
  (table (;0;) 1 1 funcref)
  (memory (;0;) 2)
  (global $__stack_pointer (mut i32) (i32.const 66576))
  (export "memory" (memory 0))
  (export "_start" (func $_start))
  (export "__wasm_apply_data_relocs" (func $__wasm_apply_data_relocs))
  (data $.data (i32.const 1024) "\00\00\00\00"))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can confirm this this test somehow? Perhaps its enough that we successfully use i32.const f_p (which would not work if the output was PIC, right?) instead of i32.const f_p@MBREL

maybe we can confirm it doesn't have __memory_base/__table_base imports?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@yamt
Copy link
Contributor Author

yamt commented Oct 10, 2024

@sbc100 do you have any more concerns?

.functype ret32 (f32) -> (i32)
.functype _start () -> ()
.globl _start
.type _start,@function
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line is not needed I think

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

# RUN: llvm-objdump -d --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DIS

.functype ret32 (f32) -> (i32)
.functype _start () -> ()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is not needed since its already on line 13

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

end_function

.section .data.f_p,"",@
.globl f_p
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be global symbol? i.e. can you remove this line?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't seem necessary. removed. thank you.

Copy link
Collaborator

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm % nits

@yamt
Copy link
Contributor Author

yamt commented Nov 28, 2024

can this land?

@sbc100 sbc100 merged commit 9df375e into llvm:main Jan 3, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[WebAssembly] non-pie dynamic-linking executable seems broken (LLVM 19 regression)
3 participants