import Darwin
import Foundation

private enum DynamicLinkerError: Error, CustomStringConvertible {
    case openFailed(String)
    case symbolMissing(String)

    var description: String {
        switch self {
        case .openFailed(let detail): return "dlopen failed: \(detail)"
        case .symbolMissing(let name): return "dlsym could not resolve \(name)"
        }
    }
}

func runDynamicLinkerLab() throws {
    labHeader(
        "DYLD · Resolve a symbol from a loaded Mach-O image",
        mechanism: "The dynamic loader maps images and binds imported symbol names to addresses."
    )

    guard let handle = dlopen(nil, RTLD_NOW) else {
        throw DynamicLinkerError.openFailed(String(cString: dlerror()))
    }
    defer { dlclose(handle) }

    let symbolName = "malloc"
    guard let symbol = dlsym(handle, symbolName) else {
        throw DynamicLinkerError.symbolMissing(symbolName)
    }

    var info = Dl_info()
    let foundImage = dladdr(symbol, &info) != 0
    let image = foundImage && info.dli_fname != nil ? String(cString: info.dli_fname) : "unknown image"
    let resolvedName = foundImage && info.dli_sname != nil ? String(cString: info.dli_sname) : symbolName

    print("Requested symbol: \(symbolName)")
    print("Resolved symbol:  \(resolvedName)")
    print("Address:          \(symbol)")
    print("Loaded image:     \(image)")
    print("Observation: source compatibility and an already-compiled client's symbol expectations are different contracts.")
    print("Interview link: use otool, nm, swiftinterface files, and compatibility clients to inspect those contracts.")
}
