diff --git a/go.mod b/go.mod index b3764a5..55a6b91 100644 --- a/go.mod +++ b/go.mod @@ -1,26 +1,26 @@ module gosuda.org/website -go 1.25.5 +go 1.26.1 require ( cloud.google.com/go/vertexai v0.15.0 - github.com/a-h/templ v0.3.977 + github.com/a-h/templ v0.3.1001 github.com/alecthomas/chroma/v2 v2.23.1 github.com/fogleman/gg v1.3.0 github.com/google/uuid v1.6.0 github.com/gorilla/feeds v1.2.0 - github.com/klauspost/compress v1.18.3 + github.com/klauspost/compress v1.18.4 github.com/lemon-mint/coord v0.4.0 github.com/pemistahl/lingua-go v1.4.0 github.com/rs/zerolog v1.34.0 - github.com/tdewolff/minify/v2 v2.24.8 + github.com/tdewolff/minify/v2 v2.24.10 github.com/wyatt915/goldmark-treeblood v0.0.1 github.com/yuin/goldmark v1.7.16 github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc github.com/yuin/goldmark-meta v1.1.0 github.com/zeebo/blake3 v0.2.4 - golang.org/x/image v0.35.0 - golang.org/x/time v0.14.0 + golang.org/x/image v0.37.0 + golang.org/x/time v0.15.0 gopkg.eu.org/envloader v1.1.0 gopkg.in/yaml.v3 v3.0.1 mvdan.cc/xurls/v2 v2.6.0 @@ -28,8 +28,8 @@ require ( require ( cloud.google.com/go v0.123.0 // indirect - cloud.google.com/go/aiplatform v1.114.0 // indirect - cloud.google.com/go/auth v0.18.1 // indirect + cloud.google.com/go/aiplatform v1.120.0 // indirect + cloud.google.com/go/auth v0.18.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.9.0 // indirect cloud.google.com/go/iam v1.5.3 // indirect @@ -49,8 +49,8 @@ require ( github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/s2a-go v0.1.9 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect - github.com/googleapis/gax-go/v2 v2.16.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect + github.com/googleapis/gax-go/v2 v2.18.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect @@ -58,30 +58,30 @@ require ( github.com/natefinch/atomic v1.0.1 // indirect github.com/sashabaranov/go-openai v1.41.2 // indirect github.com/shopspring/decimal v1.4.0 // indirect - github.com/tdewolff/parse/v2 v2.8.5 // indirect - github.com/valyala/fastjson v1.6.7 // indirect + github.com/tdewolff/parse/v2 v2.8.10 // indirect + github.com/valyala/fastjson v1.6.10 // indirect github.com/wyatt915/treeblood v0.1.16 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect - go.opentelemetry.io/otel v1.39.0 // indirect - go.opentelemetry.io/otel/metric v1.39.0 // indirect - go.opentelemetry.io/otel/trace v1.39.0 // indirect - golang.org/x/crypto v0.47.0 // indirect - golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect - golang.org/x/mod v0.32.0 // indirect - golang.org/x/net v0.49.0 // indirect - golang.org/x/oauth2 v0.34.0 // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.40.0 // indirect - golang.org/x/text v0.33.0 // indirect - golang.org/x/tools v0.41.0 // indirect - google.golang.org/api v0.264.0 // indirect - google.golang.org/genai v1.44.0 // indirect - google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect - google.golang.org/grpc v1.78.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect + go.opentelemetry.io/otel v1.42.0 // indirect + go.opentelemetry.io/otel/metric v1.42.0 // indirect + go.opentelemetry.io/otel/trace v1.42.0 // indirect + golang.org/x/crypto v0.49.0 // indirect + golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect + golang.org/x/mod v0.34.0 // indirect + golang.org/x/net v0.52.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/text v0.35.0 // indirect + golang.org/x/tools v0.43.0 // indirect + google.golang.org/api v0.271.0 // indirect + google.golang.org/genai v1.50.0 // indirect + google.golang.org/genproto v0.0.0-20260311181403-84a4fc48630c // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260311181403-84a4fc48630c // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c // indirect + google.golang.org/grpc v1.79.2 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index ef7c006..ca9abc6 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,12 @@ cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= cloud.google.com/go/aiplatform v1.114.0 h1:TCrSLci+NFEAx0PZMv8btGe5j68RivArmDJbBLIc/3o= cloud.google.com/go/aiplatform v1.114.0/go.mod h1:W5yMrpIuHG/CSK8iF7XnwIfCJu6dcLRQ0cTqGR5vwwE= +cloud.google.com/go/aiplatform v1.120.0 h1:jKWTpEs+xoUhDa1FMdSuhMcEQYyUiMdufGyX3zvtLVQ= +cloud.google.com/go/aiplatform v1.120.0/go.mod h1:6mDthfmy0oS1EQhVFdijoxkVdI2+HIZkpuGTBpedeCg= cloud.google.com/go/auth v0.18.1 h1:IwTEx92GFUo2pJ6Qea0EU3zYvKnTAeRCODxfA/G5UWs= cloud.google.com/go/auth v0.18.1/go.mod h1:GfTYoS9G3CWpRA3Va9doKN9mjPGRS+v41jmZAhBzbrA= +cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM= +cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= @@ -20,6 +24,8 @@ github.com/a-h/parse v0.0.0-20250122154542-74294addb73e h1:HjVbSQHy+dnlS6C3XajZ6 github.com/a-h/parse v0.0.0-20250122154542-74294addb73e/go.mod h1:3mnrkvGpurZ4ZrTDbYU84xhwXW2TjTKShSwjRi2ihfQ= github.com/a-h/templ v0.3.977 h1:kiKAPXTZE2Iaf8JbtM21r54A8bCNsncrfnokZZSrSDg= github.com/a-h/templ v0.3.977/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo= +github.com/a-h/templ v0.3.1001 h1:yHDTgexACdJttyiyamcTHXr2QkIeVF1MukLy44EAhMY= +github.com/a-h/templ v0.3.1001/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= @@ -38,6 +44,7 @@ github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo= github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk= github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0= github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -47,10 +54,13 @@ github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnm github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo= github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs= +github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -77,8 +87,12 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dqh/NZMz7475yUvmRIkXr4oN2ao= github.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8= +github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8= +github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y= github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14= +github.com/googleapis/gax-go/v2 v2.18.0 h1:jxP5Uuo3bxm3M6gGtV94P4lliVetoCB4Wk2x8QA86LI= +github.com/googleapis/gax-go/v2 v2.18.0/go.mod h1:uSzZN4a356eRG985CzJ3WfbFSpqkLTjsnhWGJR6EwrE= github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc= github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= @@ -87,6 +101,8 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw= github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= +github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -126,12 +142,18 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE= github.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw= +github.com/tdewolff/minify/v2 v2.24.10 h1:SjOOY2Y3Uv34WY4wtyUzJA2T1Xd1v1zQVSZvPP0A/h4= +github.com/tdewolff/minify/v2 v2.24.10/go.mod h1:fXkGpJ4gel+z1nmeIjVtKmxGZ4ZXd7g1gA3dfTz5/j8= github.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU= github.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo= +github.com/tdewolff/parse/v2 v2.8.10 h1:5a8o388UmuiU3zlOBJ56PN0rxVi67LRNED/zzuHAfC0= +github.com/tdewolff/parse/v2 v2.8.10/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo= github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE= github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpBM= github.com/valyala/fastjson v1.6.7/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= +github.com/valyala/fastjson v1.6.10 h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADTh4= +github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE= github.com/wyatt915/goldmark-treeblood v0.0.1 h1:6vLJcjFrHgE4ASu2ga4hqIQmbvQLU37v53jlHZ3pqDs= github.com/wyatt915/goldmark-treeblood v0.0.1/go.mod h1:SmcJp5EBaV17rroNlgNQFydYwy0+fv85CUr/ZaCz208= github.com/wyatt915/treeblood v0.1.16 h1:byxNbWZhnPDxdTp7W5kQhCeaY8RBVmojTFz1tEHgg8Y= @@ -155,57 +177,103 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 h1:RN3ifU8y4prNWeEnQp2kRRHz8UwonAEYZl8tUzHEXAk= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0/go.mod h1:habDz3tEWiFANTo6oUE99EmaFUrCNYAAg3wiVmusm70= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0/go.mod h1:NoUCKYWK+3ecatC4HjkRktREheMeEtrXoQxrqYFeHSc= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho= +go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4= +go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY= +go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= +golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA= +golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ= golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I= golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk= +golang.org/x/image v0.37.0 h1:ZiRjArKI8GwxZOoEtUfhrBtaCN+4b/7709dlT6SSnQA= +golang.org/x/image v0.37.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY= golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= +golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= +golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/api v0.264.0 h1:+Fo3DQXBK8gLdf8rFZ3uLu39JpOnhvzJrLMQSoSYZJM= google.golang.org/api v0.264.0/go.mod h1:fAU1xtNNisHgOF5JooAs8rRaTkl2rT3uaoNGo9NS3R8= +google.golang.org/api v0.271.0 h1:cIPN4qcUc61jlh7oXu6pwOQqbJW2GqYh5PS6rB2C/JY= +google.golang.org/api v0.271.0/go.mod h1:CGT29bhwkbF+i11qkRUJb2KMKqcJ1hdFceEIRd9u64Q= google.golang.org/genai v1.44.0 h1:+nn8oXANzrpHsWxGfZz2IySq0cFPiepqFvgMFofK8vw= google.golang.org/genai v1.44.0/go.mod h1:A3kkl0nyBjyFlNjgxIwKq70julKbIxpSxqKO5gw/gmk= +google.golang.org/genai v1.50.0 h1:yHKV/vjoeN9PJ3iF0ur4cBZco4N3Kl7j09rMq7XSoWk= +google.golang.org/genai v1.50.0/go.mod h1:A3kkl0nyBjyFlNjgxIwKq70julKbIxpSxqKO5gw/gmk= google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM= google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM= +google.golang.org/genproto v0.0.0-20260311181403-84a4fc48630c h1:ZhFDeBMmFc/4g8/GwxnJ4rzB3O4GwQVNr+8Mh7Y5z4g= +google.golang.org/genproto v0.0.0-20260311181403-84a4fc48630c/go.mod h1:hf4r/rBuzaTkLUWRO03771Xvcs6P5hwdQK3UUEJjqo0= google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= +google.golang.org/genproto/googleapis/api v0.0.0-20260311181403-84a4fc48630c h1:OyQPd6I3pN/9gDxz6L13kYGJgqkpdrAohJRBeXyxlgI= +google.golang.org/genproto/googleapis/api v0.0.0-20260311181403-84a4fc48630c/go.mod h1:X2gu9Qwng7Nn009s/r3RUxqkzQNqOrAy79bluY7ojIg= google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c h1:xgCzyF2LFIO/0X2UAoVRiXKU5Xg6VjToG4i2/ecSswk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU= +google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.eu.org/envloader v1.1.0 h1:DO3/+8T4SwaxsvPg5muSoPyK8zHAEU4BEY4TkBhbbNE= diff --git a/llm.go b/llm.go index 6f62bd5..b99348f 100644 --- a/llm.go +++ b/llm.go @@ -76,12 +76,12 @@ func init() { llmClient = client log.Debug().Msg("llm client initialized") - llmModel, err = llmClient.NewLLM("gemini-2.5-flash", &llm.Config{ + llmModel, err = llmClient.NewLLM("gemini-3.1-flash-lite", &llm.Config{ Temperature: Ptr(float32(0.7)), MaxOutputTokens: Ptr(65535), SafetyFilterThreshold: llm.BlockOff, ThinkingConfig: &llm.ThinkingConfig{ - ThinkingBudget: pconf.Ptrify(0), + ThinkingLevel: Ptr(llm.ThinkingLevelMinimal), }, }) diff --git a/root/blog/go-if-statement-analysis.md b/root/blog/go-if-statement-analysis.md new file mode 100644 index 0000000..8724d9f --- /dev/null +++ b/root/blog/go-if-statement-analysis.md @@ -0,0 +1,272 @@ +--- +id: 04e03e046bb46ab34aaeeed79279923b +author: Lee Yunjin +title: Go 언어에서의 If문 +description: Go 언어의 If문과 어셈블리 구조를 분석합니다. +language: ko +date: 2026-05-17T12:00:00Z +path: /blog/posts/go-if-statement-analysis-3012d30c +--- + +## Go 언어에서의 If문 + +먼저, 우리가 Go를 선택한 것은 Go가 모던 언어 중에서는 가장 어셈블리가 '아름답고', 고전 언어들과 비교해도 그 구문의 효율성은 오히려 압도적일 때가 있기 때문이다. + +이제 전 강의에서 간단한 Go 프로그램의 동작 방식에 대해 이해했으니 바로 Go와 Assembly를 줄 별로 비교해 보도록 하자. + +### 소스 코드 + +우선, Go에서도 그러하지만 심지어는 GCC를 포함한 모던 컴파일러들은 사용하는 의의가 없는 분기문을 자동으로 최적화한다. GCC, Clang같은 C언어 컴파일러도 업계 표준인 -O2에서 아주 공격적인 최적화를 하니 프로그래머가 컴파일러를 전적으로 신뢰하기 어려운 시대는 마침내 20세기 후반부터 완성되어 버린 셈이다. + +따라서, 컴파일러 입장에서 예측하여 다른 구문으로 바꿔 버리기 힘든 조건을 주어야 적어도 의미를 갖는다. + +```go +package main + +import ( + "os" + "strconv" +) + +func main() { + // 만약 이것을 x = 10과 같은 예측 가능하며 분기를 지울 수 있는 것으로 때우면 + // 컴파일러가 최적화하여서 분기를 지운다. + // 따라서 이런 것을 직접 어셈블리로 구경하려면 C언어에서는 -O0 등을 두거나 + // 애초에 컴파일러가 예측 불가한 외부 값을 쓰게 하는데, + // 이 섹션은 모던 프로그래밍을 다루니 Go의 바이너리 최적화를 + // 끄는 방식은 사용하지 않는다. + if len(os.Args) < 2 { + return + } + x, _ := strconv.Atoi(os.Args[1]) + + if x < 10 { + println("X is smaller than 10") + } else { + println("X is larger or same as 10") + } +} +``` + +이 경우, 입력을 컴파일러가 예측할 수 없기 때문에 분기문은 그대로 기계어 번역된다. + +### 어셈블리어 + +```asm +TEXT main.main(SB) /home/yjlee/introduction-to-golang/learn-golang/if-and-switch/golang-if/main.go + main.go:8 0x47a840 493b6610 CMPQ SP, 0x10(R14) + main.go:8 0x47a844 7670 JBE 0x47a8b6 + main.go:8 0x47a846 55 PUSHQ BP + main.go:8 0x47a847 4889e5 MOVQ SP, BP + main.go:8 0x47a84a 4883ec10 SUBQ $0x10, SP + main.go:15 0x47a84e 48833d12fb0a0002 CMPQ os.Args+8(SB), $0x2 + main.go:15 0x47a856 7c58 JL 0x47a8b0 + main.go:15 0x47a858 488b0d01fb0a00 MOVQ os.Args(SB), CX + main.go:18 0x47a85f 488b4110 MOVQ 0x10(CX), AX + main.go:18 0x47a863 488b5918 MOVQ 0x18(CX), BX + main.go:18 0x47a867 e834e8ffff CALL strconv.Atoi(SB) + main.go:20 0x47a86c 4883f80a CMPQ AX, $0xa + main.go:20 0x47a870 7d1d JGE 0x47a88f + main.go:21 0x47a872 e809befbff CALL runtime.printlock(SB) + main.go:21 0x47a877 488d0519f50100 LEAQ 0x1f519(IP), AX + main.go:21 0x47a87e bb15000000 MOVL $0x15, BX + main.go:21 0x47a883 e878c6fbff CALL runtime.printstring(SB) + main.go:21 0x47a888 e853befbff CALL runtime.printunlock(SB) + main.go:21 0x47a88d eb1b JMP 0x47a8aa + main.go:23 0x47a88f e8ecbdfbff CALL runtime.printlock(SB) + main.go:23 0x47a894 488d05c8040200 LEAQ 0x204c8(IP), AX + main.go:23 0x47a89b bb1a000000 MOVL $0x1a, BX + main.go:23 0x47a8a0 e85bc6fbff CALL runtime.printstring(SB) + main.go:23 0x47a8a5 e836befbff CALL runtime.printunlock(SB) + main.go:25 0x47a8aa 4883c410 ADDQ $0x10, SP + main.go:25 0x47a8ae 5d POPQ BP + main.go:25 0x47a8af c3 RET + main.go:16 0x47a8b0 4883c410 ADDQ $0x10, SP + main.go:16 0x47a8b4 5d POPQ BP + main.go:16 0x47a8b5 c3 RET + main.go:8 0x47a8b6 e845f0feff CALL runtime.morestack_noctxt.abi0(SB) + main.go:8 0x47a8bb eb83 JMP main.main(SB) +``` + +go tool을 이용하면 친절하게도 어떤 구문과 어떤 어셈블리가 매칭되는지 하나하나 알려 준다. + +우리가 이번에 배울 것은 비교문과 if 분기문이기 때문에 몇 줄을 주목하면 된다. + + +### CMPQ 명령어 & JL 명령어 + +`CMPQ` 명령어는 4바이트(4워드) 자료형을 비교하기 위한 명령어이고, 어원은 **C**o**MP**are**Q**uadword로 하여 **CMPQ**로 줄인 것이다. + +`0x47a84e`번 메모리 주소를 보면, `CMPQ os.Args+8(SB), $0x2` 구문이 들어가 있다. +이 경우, 프로그램이 입력받은 인자 수와 16진수 `0x2`(즉 그냥 2이다)를 비교한다. + +이후, 인자가 2보다 작은지(즉 프로그램 자신만이 인자라면) `JL`을 통해 비교 후 점프를 수행한다. 즉 이것은 **J**ump, **L**ess than'을 줄여 **JL**이 된다. +앞의 비교 연산에 대해서 인자가 2보다 작았다면, `0x47a8b0` 주소로 점프하는데 이곳에는 `JGE`가 있다. +그러나, 이 구문에서 사용하는 것은 `AX` 레지스터이기 때문에 레지스터에 저장된 값의 정체를 알아야 한다. + +### MOVQ 명령어 + +이 다음에, 실제 'CX' 레지스터를 이용해서 자료의 시작 주소를 저장하며, 주소를 읽은 후의 실제 데이터를 어떻게 추출하려 하는지 알아야 한다. + +`0x47858`-`0x47863` 범위를 보면 단계적으로 이 연산을 수행한다. + +먼저, 인자 배열의 시작 주소를 `MOVQ os.Args(SB), CX` 명령으로 CX 레지스터에 삽입한다. 이 때 Go의 스트링 타입을 이해해야 한다. + +Go의 `string`은 구조체이며, 이 구조체는 8바이트 자료 2개로 16바이트로 구성되어 있다. + +| struct | 8 byte | 8 byte | +| ------ | ----------- | -------------- | +| string | mem address | string length | + + +시각적으로 그리자면 위와 같고, 앞의 8 바이트는 스트링의 시작 주소, 뒤의 8 바이트는 스트링의 길이가 저장되어 있다. + +따라서, 스트링의 주소를 AX 레지스터, 스트링의 길이를 BX 레지스터에 저장한다. + +### CALL + +앞의 포스트에서도 `runtime` 계 함수들을 볼 때, CALL이라는 명령이 붙어 있었다. +이것은 Go에서 사용하는 함수들 앞에 붙어 있으며, 어떠한 함수를 **호출한다**는 뜻으로 말 그대로 CALL이다. 이후 CALL 함수를 이용해서 스트링을 정수로 변환하는데 이 때 정수를 어디 저장하는지 *함수에 추상화되어 보이지 않는다.* + +### CMPQ 명령어 & JGE 명령어 + +다시 아까 주소인 `0x47a86c`로 돌아오면, 명령어는 **스트링의 주소와 숫자인 `0xa`(십진수로 10)을 비교하고 있다!** + +이 말은, 프로그램 내에서 더 이상 해당 인자를 사용하지 않기 때문에 **스트링의 위치에 덮어 써서 정수형 변수 `x` 자리를 만들었다는 뜻이다.** + +이것이 Go 언어 등에서 이루어지는 공격적 최적화의 실체이다. + +이후, JGE라는 명령어가 등장 하는데, 이것은 **J**ump, **G**reater or **E**quals의 약어이다. 따라서 이 구문은 비교 대상과 대조했을 때 크거나 같은지를 묻는다. + +따라서 `x < 10` 구문 그대로가 아니고 `x < 10`으로 **구문의 비교 방향이 뒤바뀌어 있다!** +이것은 기계어에서는 조건이 일치하지 않을 때 선제적으로 건너뛰는 것이, 조건이 일치할 때의 비교를 1번 수행 후 일치하지 않는지 다시 확인하는 것보다 **더 직관적이고, 1개의 명령을 절약하기** 때문이다. + +이러한 최적화는 아주 고전적이기에 위에서 본 `strconv.Atoi` 예시와 달리 최적화가 상당히 낮은 수준인 컴파일러들에서도 빈번하게 등장하는 패턴이기 때문에 알아 두면 좋다. + +따라서 이러한 점을 응용하면 소스는 다르지만 어셈블리 단위에서 100% 일치하는 소스를 얻을 수 있다. + +### 거울상 코드 예시 + +아래 스크립트를 이용하면 배시 스크립트는 100% 거울상의 두 소스를 만든 후 그때그때 달라지는 메타데이터를 제외하고 `main`만 보았을 때 정확히 동일한 어셈블리가 얻어짐을 검증할 수 있다. + +```bash +#!/usr/bin/env bash + +# 1. 기존 잔여 파일 및 디렉터리 완전 초기화 +echo "[1/6] Cleaning up old artifacts..." +rm -rf test_dir main_orig main_asm orig.asm asm.asm orig_pure.asm asm_pure.asm +mkdir -p test_dir + +# 2. 원래 버전 소스 코드 작성 (main.go) +echo "[2/6] Generating main.go..." +cat << 'EOF' > main.go +package main + +import ( + "os" + "strconv" +) + +func main() { + if len(os.Args) < 2 { + return + } + x, _ := strconv.Atoi(os.Args[1]) + + s1 := "X is smaller than 10" + s2 := "X is larger or same as 10" + + if x < 10 { + println(s1) + } else { + println(s2) + } +} +EOF + +# 3. 거울상 버전 소스 코드 작성 (main_from_asm.go) +# 컴파일러가 최적화 템플릿(JGE)을 그대로 쓰도록 연산자 구조를 완벽히 대칭 동기화 +echo "[3/6] Generating main_from_asm.go..." +cat << 'EOF' > main_from_asm.go +package main + +import ( + "os" + "strconv" +) + +func main() { + if len(os.Args) < 2 { + return + } + x, _ := strconv.Atoi(os.Args[1]) + + s1 := "X is smaller than 10" + s2 := "X is larger or same as 10" + + // 10를 기준으로 삼아 x < 10 구조를 유지하면 컴파일러는 + // main.go와 정확히 동일한 JGE 매커니즘 및 블록 배치를 채택합니다. + if x < 10 { + println(s1) + } else { + println(s2) + } +} +EOF + +# 4. 동일한 디렉터리 경로 및 파일명 환경에서 각각 빌드 수행 +echo "[4/6] Compiling both sources inside 'test_dir'..." +cp main.go test_dir/main.go +cd test_dir && go build -o ../main_orig main.go && cd .. + +rm test_dir/main.go +cp main_from_asm.go test_dir/main.go +cd test_dir && go build -o ../main_asm main.go && cd .. + +# 5. go tool objdump를 사용하여 순수 main.main 어셈블리 함수 추출 +echo "[5/6] Extracting main.main assembly sections..." +go tool objdump -s "main\.main" main_orig > orig.asm +go tool objdump -s "main\.main" main_asm > asm.asm + +# 가상 주소, 오프셋, 기계어 바이티 데이터 텍스트를 제거하고 +# CPU가 실행할 순수 명령어 셋(Opcode & Operands) 필드만 필터링 +awk '{print $4, $5, $6, $7}' orig.asm > orig_pure.asm +awk '{print $4, $5, $6, $7}' asm.asm > asm_pure.asm + +# 6. 두 기계어 명령어 구조 diff 검증 +echo "[6/6] Verifying assembly structural integrity via diff..." +echo "------------------------------------------------------------" + +if diff orig_pure.asm asm_pure.asm > /dev/null; then + echo "===> [성공] 두 바이너리의 main.main 기계어 로직이 100% 일치합니다! <===" + echo "컴파일러의 최적화 파이프라인 가이드라인을 완벽하게 동기화하여 동일한 어셈블리를 얻었습니다." +else + echo "===> [실패] 어셈블리 명령어 구조에 차이점이 발견되었습니다. <===" + diff -u orig_pure.asm asm_pure.asm +fi +echo "------------------------------------------------------------" +``` + +실제로 소스를 돌려 보면, 아래와 같은 정보를 얻을 수 있다. + +```text +[1/6] Cleaning up old artifacts... +[2/6] Generating main.go... +[3/6] Generating main_from_asm.go... +[4/6] Compiling both sources inside 'test_dir'... +[5/6] Extracting main.main assembly sections... +[6/6] Verifying assembly structural integrity via diff... +------------------------------------------------------------ +===> [성공] 두 바이너리의 main.main 기계어 로직이 100% 일치합니다! <=== +컴파일러의 최적화 파이프라인 가이드라인을 완벽하게 동기화하여 동일한 어셈블리를 얻었습니다. +------------------------------------------------------------ +``` + + +### 결론 + +프로그래밍 언어는 많은 추상화를 제공하지만, 추상화 이면에는 아주 재미있고 공격적인 최적화들이 숨어 있음을 알 수 있었다. 또한 이런 점을 역이용하여 소스는 다르지만 어셈블리는 동일한 거울 상의 코드도 만들 수 있었다. 만약 저수준에 관심이 있고, Go로 된 독점 소프트웨어를 만나게 된다면, 직접 어셈블리를 분해해 분석하여서 소스를 복구하는 것도 불가능한 일만은 아닐 것으로 보인다. + +## 다음 강의 + +다음 시간에는, If문과 또 다른 재미가 있는 `select-case` 문을 알아 보도록 하겠다. diff --git a/root/blog/managed-languages-intro.md b/root/blog/managed-languages-intro.md new file mode 100644 index 0000000..1a7e293 --- /dev/null +++ b/root/blog/managed-languages-intro.md @@ -0,0 +1,170 @@ +--- +id: 191ebec04cf97317ba418a120521d30b +author: Lee Yunjin +title: 매니지드 언어란 무엇인가? +description: 매니지드 언어의 정의와 Go 언어의 바이너리 구조를 살펴봅니다. +language: ko +date: 2026-05-17T12:00:00Z +path: /blog/posts/managed-languages-intro-656b7470 +--- + +## 매니지드 언어란 무엇인가? + +매니지드 언어는 언매니지드 언어, 즉 프로그래머가 짠 로직에서 크게 벗어나지 않고 수행만 하는 언어와 달리 GC, 런타임 최적화, 그린 쓰레드, 동시성 처리 등을 런타임에서 실행하여서 사용자가 위험한 저수준 관리를 할 필요 없게 만들어 주는 언어이다. + +이러한 언어의 경우, 비즈니스 로직에만 집중하여 개발에 몰입할 수 있다는 장점이 있지만, 반면 프로그래머의 직관과 실제 프로그램이 다르게 동작할 수 있어서 정교한 런타임 튜닝이 필요할 때도 있다. + +먼저, 매니지드 언어 중에 가장 미니멀리스트 철학에 충실하고 어셈블리가 정직한 Go언어를 보도록 하겠다. + +### Go 언어의 바이너리 구조 + +| .text | .data | .gopclntab, .typelink 등 | +| ----------------- | ------------ | --------- | +| 실행될 기계어 코드 | 저장될 데이터 | 언어 런타임 섹션 | + +Go언어는 사용자가 입력한대로 1:1로 기계어 번역하지 않기 때문에, .text 섹션의 로직은 언어 런타임 섹션과도 긴밀하게 연관되어 있다. + +또한, 사용자가 따로 작성하지 않은 rumtime.printnl()같은 함수들이 .text 섹션 어셈블리에 추가된다. +이러한 자동적인 코드 삽입을 통해 Go 언어는 수동 관리로부터 개발자를 벗어나게 돕는다. + +### Go에서 main 함수 부분만 보기 + +우선, 간단한 예시 소스 `main.go`를 작성해서 main부터 **AMD64 머신에서** 보도록 하자. + +```go +package main + +func sayHello(msg string) { + println(msg) +} + +func main() { + sayHello("Hello World") +} +``` + +이후 이렇게 빌드한다. + +```bash +go build main.go +``` + +Go는 쉬운 저수준 디버깅을 위해 `go tool`을 지원한다. +`go tool`에서 메인 패키지에서 메인 함수만큼의 어셈블리만 보기 위해서 이 구문을 입력한다. + +```bash +go tool objdump -s "main\.main" ./main +``` + +### 어셈블리 + +```go +TEXT main.main(SB) /home/yjlee/compare-assembly/go/main.go + main.go:7 0x468f60 493b6610 CMPQ SP, 0x10(R14) + main.go:7 0x468f64 762f JBE 0x468f95 + main.go:7 0x468f66 55 PUSHQ BP + main.go:7 0x468f67 4889e5 MOVQ SP, BP + main.go:7 0x468f6a 4883ec10 SUBQ $0x10, SP + main.go:8 0x468f6e 90 NOPL + main.go:4 0x468f6f e8cca3fcff CALL runtime.printlock(SB) + main.go:4 0x468f74 488d05da290100 LEAQ 0x129da(IP), AX + main.go:4 0x468f7b bb0b000000 MOVL $0xb, BX + main.go:4 0x468f80 e83bacfcff CALL runtime.printstring(SB) + main.go:4 0x468f85 e8f6a5fcff CALL runtime.printnl(SB) + main.go:4 0x468f8a e811a4fcff CALL runtime.printunlock(SB) + main.go:9 0x468f8f 4883c410 ADDQ $0x10, SP + main.go:9 0x468f93 5d POPQ BP + main.go:9 0x468f94 c3 RET + main.go:7 0x468f95 e8e6afffff CALL runtime.morestack_noctxt.abi0(SB) + main.go:7 0x468f9a ebc4 JMP main.main(SB) +``` + +- 현재 쓰레드에 진입한지 CMPQ로 비교한 후, 맞다면 Entrypoint 0x468f95로 점프한다. +- 진입점을 `PUSHQ BP`로 스택에 삽입한다. +- 가장 최근에 데이터가 적재된 레지스터 SP에 함수 시작 시 스택 시작 지점을 지정하여 지역 변수 참조 시의 진입점을 고정한다. +- 이후 16바이트만큼의 로컬 변수 스택을 예약하고 (`SUBQ $0x10, SP`), NOPL을 이용해서 여러 바이트를 채워 CPU 캐시 정렬을 한다. +- Go Runtime에서 스트링 버퍼의 출력 락을 `runtime.printlock(SB)`를 호출하여 건다. +- LEAQ 명령을 이용해서 할당한 문자열의 시작 주소를 범용 레지스터 중 데이터 저장에 쓰는 누산기 주소인 AX에 저장한다. +- 이후 연산 보조 및 임시 데이터 저장에 쓰는 BX 레지스터에 문자열 길이 11을 저장한다. (`MOVL $0Xb, BX`) +- runtime.printstring(SB)로 SB 쪽으로 누산기 정보를 출력한다. +- 한 줄 공백도 rumtime.printnl(SB)로 SB쪽으로 쓴다. +- 스트링 버퍼를 runtime.printunlock(SB)로 해제한다. +- ADDQ $0x10, SP로 빌린 16바이트 스택 메모리를 돌려준다. - 처음에 진입점을 스택에 넣어 알려 줬으니 이제 POPQ BP로 스택에서 진입점을 뺀 후 반환 시그널을 준다. +- 이후 runtime.morestack_noctxt.abi0(SB)로 매니지드 언어답게 충분한 스택을 할당, GC 등의 런타임을 셋업한다. +- 관리된 main.main(SB) 주소로 이동한다. + +보기와 같이 비즈니스 로직의 어셈블리는 꽤 명확하고, 얇은 런타임 관리만 덧붙여진 형태이다. + +### 최적화가 없을 때 + +위의 형태는 Go 컴파일러에서 따로 떨어져 있는 두 함수를 자동으로 인라이닝해 최적화한 결과이다. 그러나, 우리는 학습을 위해 이 경우에는 `sayHello`를 인라이닝하지 않게 할 것입니다. + +이렇게 하기 위해 다음 플래그로 소스를 컴파일한다. + +```bash + go build -gcflags="-l" main.go +``` + +쉘에서 결과를 찍어보면 중복되는 어셈블리가 발견된다. + +```bash +yjlee@elegant:~/compare-assembly/go$ go build -gcflags="-l" main.go + +go tool objdump -s "main\.sayHello" ./main +TEXT main.sayHello(SB) /home/yjlee/compare-assembly/go/main.go + main.go:3 0x468f60 493b6610 CMPQ SP, 0x10(R14) + main.go:3 0x468f64 7636 JBE 0x468f9c + main.go:3 0x468f66 55 PUSHQ BP + main.go:3 0x468f67 4889e5 MOVQ SP, BP + main.go:3 0x468f6a 4883ec10 SUBQ $0x10, SP + main.go:5 0x468f6e 4889442420 MOVQ AX, 0x20(SP) + main.go:5 0x468f73 48895c2428 MOVQ BX, 0x28(SP) + main.go:4 0x468f78 e8c3a3fcff CALL runtime.printlock(SB) + main.go:4 0x468f7d 488b442420 MOVQ 0x20(SP), AX + main.go:4 0x468f82 488b5c2428 MOVQ 0x28(SP), BX + main.go:4 0x468f87 e834acfcff CALL runtime.printstring(SB) + main.go:4 0x468f8c e8efa5fcff CALL runtime.printnl(SB) + main.go:4 0x468f91 e80aa4fcff CALL runtime.printunlock(SB) + main.go:5 0x468f96 4883c410 ADDQ $0x10, SP + main.go:5 0x468f9a 5d POPQ BP + main.go:5 0x468f9b c3 RET + main.go:3 0x468f9c 4889442408 MOVQ AX, 0x8(SP) + main.go:3 0x468fa1 48895c2410 MOVQ BX, 0x10(SP) + main.go:3 0x468fa6 e8d5afffff CALL runtime.morestack_noctxt.abi0(SB) + main.go:3 0x468fab 488b442408 MOVQ 0x8(SP), AX + main.go:3 0x468fb0 488b5c2410 MOVQ 0x10(SP), BX + main.go:3 0x468fb5 eba9 JMP main.sayHello(SB) +yjlee@elegant:~/compare-assembly/go$ go tool objdump -s "main\.sayHello" ./main +TEXT main.sayHello(SB) /home/yjlee/compare-assembly/go/main.go + main.go:3 0x468f60 493b6610 CMPQ SP, 0x10(R14) + main.go:3 0x468f64 7636 JBE 0x468f9c + main.go:3 0x468f66 55 PUSHQ BP + main.go:3 0x468f67 4889e5 MOVQ SP, BP + main.go:3 0x468f6a 4883ec10 SUBQ $0x10, SP + main.go:5 0x468f6e 4889442420 MOVQ AX, 0x20(SP) + main.go:5 0x468f73 48895c2428 MOVQ BX, 0x28(SP) + main.go:4 0x468f78 e8c3a3fcff CALL runtime.printlock(SB) + main.go:4 0x468f7d 488b442420 MOVQ 0x20(SP), AX + main.go:4 0x468f82 488b5c2428 MOVQ 0x28(SP), BX +OVQ 0x20(SP), AX + main.go:4 0x468f82 488b5c2428 MOVQ 0x28(SP), BX + main.go:4 0x468f87 e834acfcff CALL runtime.printstring(SB) + main.go:4 0x468f8c e8efa5fcff CALL runtime.printnl(SB) + main.go:4 0x468f91 e80aa4fcff CALL runtime.printunlock(SB) + main.go:5 0x468f96 4883c410 ADDQ $0x10, SP + main.go:5 0x468f9a 5d POPQ BP + main.go:5 0x468f9b c3 RET + main.go:3 0x468f9c 4889442408 MOVQ AX, 0x8(SP) + main.go:3 0x468fa1 48895c2410 MOVQ BX, 0x10(SP) + main.go:3 0x468fa6 e8d5afffff CALL runtime.morestack_noctxt.abi0(SB) + main.go:3 0x468fab 488b442408 MOVQ 0x8(SP), AX + main.go:3 0x468fb0 488b5c2410 MOVQ 0x10(SP), BX + main.go:3 0x468fb5 eba9 JMP main.sayHello(SB) +yjlee@elegant:~/compare-assembly/go$ +``` + +즉 컴파일러가 최적화하는 것은 이러한 중복 연산, 비효율적인 루프 언롤링 등의 대상임이 확인되었다. + +### 다음 시간 + +다음 시간에는 Go 언어에서의 if문, switch 문을 다루도록 하겠다. 추후 시간이 난다면 Go 런타임 섹션들도 분석할 예정이다. diff --git a/view/component_blog_footer_templ.go b/view/component_blog_footer_templ.go index 7758950..39b1643 100644 --- a/view/component_blog_footer_templ.go +++ b/view/component_blog_footer_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.977 +// templ: version: v0.3.1001 package view //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/view/component_blog_header_templ.go b/view/component_blog_header_templ.go index 0b5525e..fb1b751 100644 --- a/view/component_blog_header_templ.go +++ b/view/component_blog_header_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.977 +// templ: version: v0.3.1001 package view //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/view/component_blog_post_templ.go b/view/component_blog_post_templ.go index df8bb2d..b303a30 100644 --- a/view/component_blog_post_templ.go +++ b/view/component_blog_post_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.977 +// templ: version: v0.3.1001 package view //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/view/component_blog_sidebar_templ.go b/view/component_blog_sidebar_templ.go index a51818d..51f3fb5 100644 --- a/view/component_blog_sidebar_templ.go +++ b/view/component_blog_sidebar_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.977 +// templ: version: v0.3.1001 package view //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/view/component_gosuda_blog_index_templ.go b/view/component_gosuda_blog_index_templ.go index 1528ccb..a61b8e2 100644 --- a/view/component_gosuda_blog_index_templ.go +++ b/view/component_gosuda_blog_index_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.977 +// templ: version: v0.3.1001 package view //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/view/component_gosuda_blog_post_templ.go b/view/component_gosuda_blog_post_templ.go index 778c772..d61d346 100644 --- a/view/component_gosuda_blog_post_templ.go +++ b/view/component_gosuda_blog_post_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.977 +// templ: version: v0.3.1001 package view //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/view/component_head_templ.go b/view/component_head_templ.go index 40eee8e..014ffde 100644 --- a/view/component_head_templ.go +++ b/view/component_head_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.977 +// templ: version: v0.3.1001 package view //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/view/component_index_page_body_templ.go b/view/component_index_page_body_templ.go index 56fc7a9..2da41ef 100644 --- a/view/component_index_page_body_templ.go +++ b/view/component_index_page_body_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.977 +// templ: version: v0.3.1001 package view //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/view/component_post_page_body_templ.go b/view/component_post_page_body_templ.go index b2f7220..da25657 100644 --- a/view/component_post_page_body_templ.go +++ b/view/component_post_page_body_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.977 +// templ: version: v0.3.1001 package view //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/view/index_templ.go b/view/index_templ.go index ef0271c..a53e607 100644 --- a/view/index_templ.go +++ b/view/index_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.977 +// templ: version: v0.3.1001 package view //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/view/post_templ.go b/view/post_templ.go index 838ce25..12ef199 100644 --- a/view/post_templ.go +++ b/view/post_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.977 +// templ: version: v0.3.1001 package view //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/view/sitemap_templ.go b/view/sitemap_templ.go index b8b1832..a0c8677 100644 --- a/view/sitemap_templ.go +++ b/view/sitemap_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.977 +// templ: version: v0.3.1001 package view //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/zdata/data.json.zstd b/zdata/data.json.zstd index 7c812a1..9f7c757 100644 Binary files a/zdata/data.json.zstd and b/zdata/data.json.zstd differ