JJ's blog

为什么一个老 P/Invoke bool bug 在 .NET 8 才暴露:一次关于 SkipLocalsInit 优化的追查

记录一个快两年前修过的 bug。

当时 .NET 6 快要 end of life 了,我们把一个服务从 .NET 6 升级到了 .NET 8。升级之后,有用户报了一个看起来不太相关的数据异常 bug。一番略过不表的定位之后,发现问题的根源是其中一个 .NET 和 C++ 的交互面错误地使用了不同的 bool 数据长度,.NET 使用了默认的 4 字节,但是 C++ 期待的是 1 字节,因此高 3 个字节里的未初始化脏数据导致了数据异常。

不过我真正想分享的是后面多问的这一句:为什么之前没有用户报告这个问题,而且结果还挺稳定,但是升级到 .NET 8 之后,很快就有用户报了问题?到底为什么 .NET 6 下这个 bool 长度不匹配没有导致问题,但是 .NET 8 就会出问题?

Read more...

关于 LLM Wiki

• edited

有段时间没有在这个 blog 上写文章了,主要原因还是最近代码阅读相关的总结要不就直接写到代码注释,要不就用 Karpathy 的 LLM Wiki 来整理,所以也没啥机会写新的博客文章。

我感觉 LLM Wiki 这套模式在借助 LLM 学习的场景下还挺合适的。用在代码阅读上也没什么问题,只需要稍微调整一下他原始提出的 source,用 code commit + source location 代替 source file,就能很好地支持代码阅读的记录。

我倒是把我的 LLM Wiki 保存到了 GitHub 上,不过考虑到内容大多由 LLM 生成,其中很多是没有经过我手动整理,所以目前还是 private repo 的状态。我再考虑考虑后面要不要公开,先放一个链接在这里。等哪天它不再 404,可以去看看:wiki

Update:我把其中一部分内容渲染成了一个静态站点放在了 LLM Wiki,可以去看看效果。需要说明的是,里面的内容大多由 LLM 生成,并未经过我逐条人工校对,更适合作为通过 LLM 进行提问、检索和延展阅读的素材。这个站点本身主要是用来展示 LLM wiki 这种形式渲染出来的效果,实际使用场景下我觉得更适合作为本地的个人或团队 wiki 来部署。

Mesa/OpenGL 源码分析:framebuffer

之前看了 Mesa 的早期版本实现之后,我又开始看 Mesa 的最近版本的 OpenGL 实现了。不过,只能说有点选择错误,OpenGL 的实现实在是有太多历史包袱,驱动有非常多替应用层做的处理、回退逻辑。其中掺杂了大量固定管线(Fixed-function pipeline)时代的遗留代码,非 OpenGL Core profile 的部分也很影响阅读体验。所以我想先转去看 Vulkan 的实现,但是在此之前,想先总结一下 framebuffer 相关的一些阅读内容:一方面算是梳理了 OpenGL 底层的一些结构;另一方面,Framebuffer 的概念在 Vulkan 中也有对应的设计,这也为后续学习 Vulkan 打下基础。

在 Mesa 中,gl_framebuffer 主要包含多个 gl_renderbuffer 作为附件 (Attachment),可以想象成 Framebuffer 有多个插槽,每个插槽可以绑定到具体的 Renderbuffer 或 Texture。

对于 default framebuffer,即关联到 Window System 的 framebuffer 有:

  1. Double Buffer 机制: 用于 Window System 的 framebuffer 包含 front / back 缓冲(如果是立体视觉 stereo 模式,还会叠加 left / right 组合)。
  2. Depth / Stencil Buffers: 深度与模板缓冲。

而 user defined framebuffer 有:

  1. Color Buffers: 支持 Multiple Render Targets (MRT) 的 color0-7。

Read more...

libglvnd 实现分析

在以前的 Linux 环境下, OpenGL 库的加载存在一个问题:多个厂商的实现没法很好地共存。具体来说,应用会用 libGL.so 这个 SONAME 去加载 OpenGL 实现,但是动态加载器的搜索路径通常是固定的(当然可以通过修改环境变量之类的方式去改变哪个 libGL.so 被加载,但是这样的方式是不现实的),因此以前没法很好地让多个厂商的实现并存,无法让多个进程选择不同的实现,甚至动态地去决定用哪个厂商的实现。
为此,现代 Linux 下的 libGL.so 的实现通常是由 libglvnd (The GL Vendor-Neutral Dispatch library) 实现的。这个实现允许多个厂商的 OpenGL 实现同时存在于机器上,允许多个进程动态地选择具体的实现。
它通过实现一个中心化的 libGL.so + 不同厂商提供的 libGLX_{vendor}.so, 根据 context 转发到对应厂商提供的 GL/GLX 实现,从而解决上述提到的问题。

┌───────────────────────────┐
│        Application        │
└─────┬────────────────────┬┘
      │                    │
      │                    │
 glXGetProcAddress         glxxx return from glXGetProcAddress
┌─────▾────────────┐     ┌─▾────────────────┐
│      libGL       ├─────▸  libGLdispatch   │
└─────┬─────────┬──┘     └─┬────────────────┘
      │         gl functions   ┌────────────────────┐
      │         └──────────┴───▸ libGLX_{vendor}.so │
      glXxxx(Display*, ...)    └─▴──────────────────┘
┌─────▾────────────┐             glX extension function
│      libGLX      ├─────────────┘ ┌────────┐
│                  ├───────────────▸ libX11 │
└──────────────────┘               └────────┘

Read more...

ELF 文件结构速查

注1:这里只是简单描述 ELF 文件最核心的结构,作为 cheat sheet 以便于快速查看。不会展开具体的字节描述,有必要请查看 Wikipedia

注2:本文只包括常见模式,可能存在特殊情况。且目前只验证了一个 .so 文件

注3:一个在线 ELF viewer, 随便找的,不保证质量。 elfy.io

文件本身的结构

一个 ELF 文件由下面几个部分组成:

  1. 三个 header: ELF header, program header table, section header table.
  2. sections, 每个 section 用于一个目的。
  3. 用于满足 align 要求的 section 之间的垃圾字节。

Read more...