Skip to content
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

Document how shimming works #16

Open
Miista opened this issue Jan 16, 2024 · 5 comments
Open

Document how shimming works #16

Miista opened this issue Jan 16, 2024 · 5 comments
Labels
documentation Improvements or additions to documentation non-code The issue is not related to non-code e.g. infrastructure or documentation

Comments

@Miista
Copy link
Owner

Miista commented Jan 16, 2024

Ideally, this should include some descriptive text and a flowchart. For starters, we can document it here in this issue. Ideally, though, it should be in its own page in the wiki of this repository.

@Miista Miista added the documentation Improvements or additions to documentation label Jan 16, 2024
@Miista
Copy link
Owner Author

Miista commented Jan 24, 2024

I've made the following diagram as an overview of the Rewrite method on MethodRewriter.

flowchart LR
    Start(( ))
    IsMethod{{Is method call?}}
    IsConstructor{{Is constructor?}}
    NotMethodOrConstructor[Emit instruction]
    MethodCall[Method call]
    EmitMethodStub[Emit method call]
    ConstructorCall[Constructor invocation]
    EmitConstructorStub[Emit constructor stub]
    End(( ))

    Start-->IsMethod
    IsMethod --> |Yes| MethodCallFlow
    IsMethod --> |No| IsConstructor
    
    subgraph MethodCallFlow [Method call]
    direction TB

    MethodCall-->EmitMethodStub
    end

    subgraph ConstructorCallFlow [Constructor call]
    direction TB

    ConstructorCall-->EmitConstructorStub
    end
    
    IsConstructor-->|Yes|ConstructorCallFlow
    IsConstructor--->|No|NotMethodOrConstructor
    
    NotMethodOrConstructor --> End
    ConstructorCallFlow ---> End
    MethodCallFlow --> End
Loading

I am still missing documentation on:

  • What happens when the stubs are called, and
  • How shims (and replacements) are located and used

@Miista Miista added the non-code The issue is not related to non-code e.g. infrastructure or documentation label Jan 30, 2024
@Miista
Copy link
Owner Author

Miista commented Feb 13, 2024

I have the following diagram for an extended view of how Rewrite works:

---
title: Rewrite (for each instruction)
---
flowchart TD
    subgraph Main
    Start
    IsLabelAtOffset{"Is there a label at the offset?"}
    MarkLabel
    IsOpCodeIgnored{"Is opcode ignored?"}
    Continue
    EmitIL

    Start --> EmitILForExceptionHandlers
    EmitILForExceptionHandlers --> IsLabelAtOffset
    IsLabelAtOffset -->|Yes| MarkLabel
    MarkLabel --> IsOpCodeIgnored
    IsLabelAtOffset -->|No| IsOpCodeIgnored
    IsOpCodeIgnored -->|Yes| Continue
    Continue --> Start
    IsOpCodeIgnored -->|No| EmitIL
    end

    subgraph EmitILForExceptionHandlers
    %% Try blocks
    GetTryBlocks["Get try-blocks (TryStart == instruction.Offset)"]
    AnyTryBlocks{"Any try-blocks?"}
    BeginExceptionBlock
    IncreaseExceptionBlockLevel

    GetTryBlocks --> AnyTryBlocks
    AnyTryBlocks -->|Yes| BeginExceptionBlock
    BeginExceptionBlock --> IncreaseExceptionBlockLevel
    IncreaseExceptionBlockLevel --> GetExceptionFilters

    AnyTryBlocks -->|No| GetExceptionFilters

    %% Exception filters
    GetExceptionFilters["Get exception filters"]
    ExceptionFilterNull{"Exception filter == null?"}
    BeginExceptionFilterBlock
    
    GetExceptionFilters --> ExceptionFilterNull
    ExceptionFilterNull -->|Yes| GetHandler
    ExceptionFilterNull -->|No| BeginExceptionFilterBlock
    BeginExceptionFilterBlock --> GetHandler

    %% Handlers
    GetHandler["Get handler (HandlerEnd == instruction.Offset)"]
    IsHandlerNull{"Handler == null?"}
    IsHandlerFinally{"Flags == Finally?"}
    EndExceptionBlock
    DecreaseExceptionBlockLevel
    IsLastCatchBlock{"Is last catch block?"}

    GetHandler --> IsHandlerNull
    IsHandlerNull -->|Yes| GetCatchOrFinallyBlock
    IsHandlerNull -->|No| IsHandlerFinally
    IsHandlerFinally -->|Yes| EndExceptionBlock
    EndExceptionBlock --> DecreaseExceptionBlockLevel
    IsHandlerFinally -->|No| IsLastCatchBlock
    IsLastCatchBlock -->|Yes| EndExceptionBlock
    DecreaseExceptionBlockLevel --> GetCatchOrFinallyBlock

    %% Catch or finally block
    GetCatchOrFinallyBlock["Get catch or finally block (HandlerStart == instruction.Offset)"]
    IsBlockCatchClause{"Flags == Clause?"}
    IsBlockFilter{"Flags == Filter?"}
    IsBlockFinally{"Flags == Finally?"}
    END

    BeginCatchBlock["Begin catch block"]
    BeginFilterBlock["Begin filter block"]
    BeginFinallyBlock["Begin finally block"]
    ThrowFaultBlock["Throw NotSupportedException (fault blocks)"]

    GetCatchOrFinallyBlock --> IsBlockCatchClause
    IsBlockCatchClause -->|Yes| BeginCatchBlock
    IsBlockCatchClause -->|No| IsBlockFilter
    IsBlockFilter -->|Yes| BeginFilterBlock
    IsBlockFilter -->|No| IsBlockFinally
    IsBlockFinally -->|Yes| BeginFinallyBlock
    IsBlockFinally -->|No| ThrowFaultBlock

    BeginFinallyBlock --> END

    end
Loading

@Miista
Copy link
Owner Author

Miista commented Feb 13, 2024

Following is the diagram for how a stub for a direct call (constructor or method) is emitted.

---
title: Stub execution (direct call)
---
flowchart TD
    Start
    GetMethodFromHandle["Invoke GetMethodFromHandle"]
    GetIndexOfMatchingShim["Try to find matching shim"]
    FoundMatchingShim{"Found matching shim?\n(Index >= 0)"}
    RewriteMethod["Label: RewriteMethod"]
    CreateRewriter["Create instance of MethodRewriter"]
    CallRewrite["Invoke MethodRewriter::Rewrite"]
    GetMethodPointerToRewrittenMethod["Get method pointer to rewritten method"]
    InvokeRewrittenMethod["Invoke rewritten method"]
    Return["Emit ret"]
    GetShimReplacementMethod["Get replacement method specified by shim"]
    GetShimMethodPointer["Get method pointer to replacement method"]
    GetShimDelegateTarget["Get shim delegate target"]
    InvokeShim["Invoke shim"]
    ReturnStub["Stub complete"]

    Start --> LoadToken
    LoadToken --> GetMethodFromHandle
    GetMethodFromHandle --> GetIndexOfMatchingShim
    GetIndexOfMatchingShim --> FoundMatchingShim
    FoundMatchingShim -->|No| RewriteMethod
    RewriteMethod --> CreateRewriter
    CreateRewriter --> CallRewrite
    CallRewrite --> GetMethodPointerToRewrittenMethod
    GetMethodPointerToRewrittenMethod --> InvokeRewrittenMethod
    InvokeRewrittenMethod --> Return

    FoundMatchingShim -->|Yes| GetShimReplacementMethod
    GetShimReplacementMethod --> GetShimMethodPointer
    GetShimMethodPointer --> GetShimDelegateTarget
    GetShimDelegateTarget --> InvokeShim

    InvokeShim --> ReturnStub
    Return --> ReturnStub
    subgraph LoadToken
    IsConstructor{"Is constructor?"}
    LoadConstructorInfo
    LoadMethodInfo
    END

    IsConstructor -->|Yes| LoadConstructorInfo
    IsConstructor -->|No| LoadMethodInfo
    LoadConstructorInfo --> END
    LoadMethodInfo --> END
    end
Loading

@Miista
Copy link
Owner Author

Miista commented Feb 13, 2024

Following is the diagram for how a stub for a virtual call (method only) is emitted.

---
title: Stub execution (virtual call)
---
flowchart TD
    Start
    GetMethodFromHandle["Invoke GetMethodFromHandle"]
    DeVirtualizeMethod["Invoke DeVirtualizeMethod"]
    GetIndexOfMatchingShim["Try to find matching shim"]
    FoundMatchingShim{"Found matching shim?\n(Index >= 0)"}
    RewriteMethod["Label: RewriteMethod"]
    CreateRewriter["Create instance of MethodRewriter"]
    CallRewrite["Invoke MethodRewriter::Rewrite"]
    GetMethodPointerToRewrittenMethod["Get method pointer to rewritten method"]
    InvokeRewrittenMethod["Invoke rewritten method"]
    Return["Emit ret"]
    GetShimReplacementMethod["Get replacement method specified by shim"]
    GetShimMethodPointer["Get method pointer to replacement method"]
    GetShimDelegateTarget["Get shim delegate target"]
    InvokeShim["Invoke shim"]
    ReturnStub["Stub complete"]

    Start --> LoadMethodInfo
    LoadMethodInfo --> GetMethodFromHandle
    GetMethodFromHandle --> DeVirtualizeMethod
    DeVirtualizeMethod --> GetIndexOfMatchingShim
    GetIndexOfMatchingShim --> FoundMatchingShim
    FoundMatchingShim -->|No| RewriteMethod
    RewriteMethod --> CreateRewriter
    CreateRewriter --> CallRewrite
    CallRewrite --> GetMethodPointerToRewrittenMethod
    GetMethodPointerToRewrittenMethod --> InvokeRewrittenMethod
    InvokeRewrittenMethod --> Return

    FoundMatchingShim -->|Yes| GetShimReplacementMethod
    GetShimReplacementMethod --> GetShimMethodPointer
    GetShimMethodPointer --> GetShimDelegateTarget
    GetShimDelegateTarget --> InvokeShim

    InvokeShim --> ReturnStub
    Return --> ReturnStub
Loading

@Miista
Copy link
Owner Author

Miista commented Feb 13, 2024

Missing diagrams:

  • Direct call
  • Virtual call
  • Virtual call (constrained type)
  • Object initialization
  • Direct load
  • Virtual load

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation non-code The issue is not related to non-code e.g. infrastructure or documentation
Projects
None yet
Development

No branches or pull requests

1 participant