跳转至

博客

实现一个简单的 Decaf LSP

背景

编译原理课程在做 Decaf 的 PA,之前做了一些比较简单的尝试,包括在线 Decaf、在线 TAC VM 等等,都是套一个前端,然后整个编译到 wasm 跑前端就可以了。如果要做 LSP 的话,工作量会稍微大一些,不过也更加实用。

然后有一天,助教 @equation314 写了 decaf-vscode 一个 VSCode 对 Decaf 的语法高亮插件,我就 Fork 了一份到 jiegec/decaf-vscode,然后添加了 LSP 的支持,让它有了一些更高级的功能。

实现

LSP 服务端一般是一个命令行程序,通过 JSONRPC 进行消息通讯,然后就上午找有没有现成的框架。比较重要的是 lsp-typestower-lsp ,前者封装了 LSP 协议的各个结构体,后者提供了服务端的大概实现。不过由于后者做的不大全,所以我自己 fork 了一份添加了一些。

实际实现的时候,需要实现几个函数,分别相应客户端的请求,比如在 initialize 的时候告诉客户端我都实现了哪些东西,然后相应地提供各种信息,如 symbol,hover,folding,definition 等等。为了实现简单,我要求客户端每次修改的时候都把完整的文件传过来,虽然不是很高效,但是很简单,目前也没有啥很长的 Decaf 程序嘛。

每次拿到 Decaf 程序之后,就按照 decaf-rs 的方法,Lex 然后 Parse,然后遍历 AST,分别把需要的各个信息都存下来,当客户端在请求的时候,直接返回即可。然后就会在 VSCode 中出现,比如实现了 document symbol,在左边的 Outline 中就会出现相应的结构;实现了 hover,当移动到一些地方的时候,客户端发出请求,服务端就把相应的 hover 信息返回给客户端。整个协议并不复杂,后面实际实现其实才是比较复杂的地方。

实现的功能中,symbols hovers ranges definition 都是在得到 AST 后一次遍历都计算好,然后返回,同时在遇到错误的时候,也通过 diagnostic 的形式把检查出来的错误汇报给用户。由于 VSCode 的良好支持,基本不需要写 TypeScript 代码。

至于代码补全,现在做的比较粗糙,仅仅补全了一些内置函数:Print ReadInteger 和 ReadLine。还在考虑支持函数调用的补全,但是在补全的时候会出现语法错误,意味着需要保证在补全的时候我还能拿到之前正确的类型信息,需要一些工作量,现在还没有去做。

使用

我自己测试的方法就是两个窗口,一个是 decaf-lsp ,首先克隆下来,然后 cargo install --path . --force 来安装到全局;另一个就是我 Fork 的 decaf-vscode ,克隆下来,然后按 F5 进入 VSCode 的调试模式,它会打开一个新窗口,里面启用了 Decaf for VSCode 插件。这个时候看 Decaf 代码就可以看到上面提到的那些东西了。

总结

感觉 LSP 是一个比较好实现的 Protocol,但 Protocol 承载的 Data 才是比较困难的东西。要实现一个完整的 completion 还需要很多东西,现在只能说是个 naive implementation 吧。

刚写完就发现 Neovim 发布了 官方的 LSP client

用 Rust Procedure Macro 实现 GLL Parser

背景

在编译原理课上,PA 框架采用的是 MashPlant/lalr1 ,是一个比较好用的 Lexer + Parser 的工具,它的大概语法见 一个完整的例子 。然后之前看到了 GLL Parser,想着可不可以照着类似的语法也写一个 GLL 的 Parser Generator,也是用 Rust Procedure Macro 的方法,就开始了研究。

尝试

首先是阅读 GLL 的论文,它并不长,大概的意思就是,LL(1) 文法需要考虑 PS 冲突的情况,而 GLL 的解决方法就是“都试一下”,然后为了效率,用了 GSS 表示解析过程和 SPPF 表示解析结果。然后就开始照着论文手写了不同版本的实现,见 jiegec/gll-test

第一种就是按照论文里第一段实现直接抄过来,每个可能性作为一个 Continuation 存下来,它有自己的栈和执行位置(Label)。这样 Work 以后呢,我又想到了 async/await,用类似的方法又写了一遍,相对要简洁一些,也是很平常的递归下降的写法,而不是 Loop + Label 的形式。但这些都不能做到合并栈的目的,所以遇到十分有歧义的文法的时候会很糟糕。

然后开始按照论文中的 GSS 进行编写,基本还是按照论文进行翻译,然后一步一步做,做好以后把 GSS 画出来,和论文的图可以对的上;然后照着 GLL parse-tree generation 的论文把 SPPF 实现了,这时候就可以从 recongizer 变成一个 parser 了。

得到一份可行的代码以后,就要扩展到通用的情况上。学习了一下 MashPlant/lalr1 的实现,实现了一个 proc macro,它读取了用户的程序,从一个模板文件开始,往里面插入一些生成的代码,丢给编译器去编译。这时候就涉及到编译期和运行时的不同了,我把运行时一些通用的结构放到了 gll-pg-core 中,把编译期的代码放到了 gll-pg-macros

代码生成的时候,基本按照之前自己写的样子抄,只不过这个时候要按照用户编写的产生式进行生成了,各种名字都要规范化,变得可以复用,然后尽量减少命名空间的污染等等这些常见的写宏需要注意的操作。

不过,考虑到现在还没有实现 Lexer,所以先用了 Logos 库作为 Lexer。但我其实不大喜欢它,因为它太简单,也没有行号的信息,不过暂且先这样吧,以后可能会自己实现。

然后 0.1.0 版本就诞生了,它的样例长这样:

//! This example is taken from MashPlant/lalr1

use gll_pg_core::LogosToken;
use gll_pg_macros::gll;
use logos::Logos;

#[derive(Logos, Debug, Eq, PartialEq, Clone)]
pub enum Token {
    #[end]
    End,
    #[error]
    Error,
    #[token = " "]
    _Eps,
    #[token = "+"]
    Add,
    #[token = "-"]
    Sub,
    #[token = "*"]
    Mul,
    #[token = "/"]
    Div,
    #[token = "%"]
    Mod,
    #[token = "("]
    LPar,
    #[token = ")"]
    RPar,
    #[regex = "[0-9]+"]
    IntLit,
}

#[gll(Expr, Token)]
impl Parser {
    #[rule(Expr -> Expr Add Expr)]
    fn expr_add(l: i32, _op: LogosToken<Token>, r: i32) -> i32 {
        l + r
    }
    #[rule(Expr -> Expr Sub Expr)]
    fn expr_sub(l: i32, _op: LogosToken<Token>, r: i32) -> i32 {
        l - r
    }
    #[rule(Expr -> Expr Mul Expr)]
    fn expr_mul(l: i32, _op: LogosToken<Token>, r: i32) -> i32 {
        l * r
    }
    #[rule(Expr -> Expr Div Expr)]
    fn expr_div(l: i32, _op: LogosToken<Token>, r: i32) -> i32 {
        l / r
    }
    #[rule(Expr -> Expr Mod Expr)]
    fn expr_mod(l: i32, _op: LogosToken<Token>, r: i32) -> i32 {
        l % r
    }
    #[rule(Expr -> Sub Expr)]
    fn expr_neg(_op: LogosToken<Token>, r: i32) -> i32 {
        -r
    }
    #[rule(Expr -> LPar Expr RPar)]
    fn expr_paren(_l: LogosToken<Token>, i: i32, _r: LogosToken<Token>) -> i32 {
        i
    }
    #[rule(Expr -> IntLit)]
    fn expr_int(i: LogosToken<Token>) -> i32 {
        i.slice.parse().unwrap()
    }
}

#[test]
fn gll() {
    let mut lexer = Token::lexer("1 + 2 * 3");
    let res = Parser::parse(&mut lexer);
    // two ways to parse
    assert_eq!(res, [7, 9]);
}

可以看到,它解析的结果是一个数组,对应所有可能出现的情况。这样比较简单,但是要求中间各种类型都是 Clone,因为同一个结点可能会被用多次。它的计算方法就是在最终的 SPPF 上递归找到所有可能性,然后调用用户代码,最后放到一个 Vec 中。

记忆化

但是,上面的做法有一个很大的问题,就是,虽然 SPPF 的空间复杂度是有限的,但所有可能的解析树可以有很多,如果把每一个情况都完整的存在一个 Vec 中,空间要求是很高的,中间也有很多重复计算的情况。所以需要做记忆化,然后每次给出一个。因为依赖自己内部的状态,所以不能是 Iterator 只能是 StreamingIterator。

记忆化也花了我一番功夫,现在用了一个比较土的办法,在每个结点上记录了当前遇到过的所有可能,这个是逐渐构造的,意味着如果只需要第一种解析树,不需要额外的空间。然后逐渐扩张,如果有可以重用的结构就重用,把涉及的所有的结构都放在一个 Vec 中,用完之后一起 drop 掉。

当然了,这个时候,各种东西都变成了引用:

//! This example is taken from MashPlant/lalr1

use gll_pg_core::*;
use gll_pg_macros::gll;
use logos::Logos;

#[derive(Logos, Debug, Eq, PartialEq, Clone)]
enum Token {
    #[end]
    End,
    #[error]
    Error,
    #[token = " "]
    _Eps,
    #[token = "+"]
    Add,
    #[token = "-"]
    Sub,
    #[token = "*"]
    Mul,
    #[token = "/"]
    Div,
    #[token = "%"]
    Mod,
    #[token = "("]
    LPar,
    #[token = ")"]
    RPar,
    #[regex = "[0-9]+"]
    IntLit,
}

#[derive(Default)]
struct Parser {
    literals: Vec<i32>,
}

#[gll(Expr, Token)]
impl Parser {
    // you can omit self
    #[rule(Expr -> Expr Add Expr)]
    fn expr_add(l: &i32, _op: &LogosToken<Token>, r: &i32) -> i32 {
        *l + *r
    }
    // you can use &self
    #[rule(Expr -> Expr Sub Expr)]
    fn expr_sub(&self, l: &i32, _op: &LogosToken<Token>, r: &i32) -> i32 {
        *l - *r
    }
    // you can use &mut self as well
    // but all of these have &mut self in fact
    #[rule(Expr -> Expr Mul Expr)]
    fn expr_mul(&mut self, l: &i32, _op: &LogosToken<Token>, r: &i32) -> i32 {
        *l * *r
    }
    #[rule(Expr -> Expr Div Expr)]
    fn expr_div(l: &i32, _op: &LogosToken<Token>, r: &i32) -> i32 {
        *l / *r
    }
    #[rule(Expr -> Expr Mod Expr)]
    fn expr_mod(l: &i32, _op: &LogosToken<Token>, r: &i32) -> i32 {
        *l % *r
    }
    #[rule(Expr -> Sub Expr)]
    fn expr_neg(_op: &LogosToken<Token>, r: &i32) -> i32 {
        -*r
    }
    #[rule(Expr -> LPar Expr RPar)]
    fn expr_paren(_l: &LogosToken<Token>, i: &i32, _r: &LogosToken<Token>) -> i32 {
        *i
    }
    // so you can make your IDE happy with &mut self here
    #[rule(Expr -> IntLit)]
    fn expr_int(&mut self, i: &LogosToken<Token>) -> i32 {
        let lit = i.slice.parse().unwrap();
        self.literals.push(lit);
        lit
    }
}

#[test]
fn ambiguous() {
    let mut lexer = Token::lexer("1 + 2 + 3");
    let mut parser = Parser { literals: vec![] };
    let res = parser.parse(&mut lexer).unwrap();
    // two ways to parse
    let res: Vec<_> = res.cloned().collect();
    assert_eq!(res, vec![6, 6]);
}

这时候就是 0.3.0 版本,基本达到了我一开始想要的程度。

错误处理

在之前写编译原理 PA1 的时候,遇到的一个问题就是,如果自己的代码有错,因为宏展开以后丢失了位置信息,所以报错都会在错误的位置。一番查找以后,找到了解决方案:原样记录下原来的代码(syn::Block),然后通过 quote 宏直接拼接到最终的 TokenStream 中,这样在结果里,虽然代码还是那些代码,但部分的 Token 就有了正确的位置,这样就很方便用户代码的修改了。不过还是不方便找模板部分的代码错误,毕竟那部分确实在原来的代码中没有出现过。

对于模板中的代码错误,我最终的解决方案是 cargo-expand ,把我的测试代码和展开后的代码拼接起来,然后在茫茫的无关报错下去找我的错误的地方。虽然不是很好用,但毕竟还是 work 的。另外,宏还需要对用户代码的一些类型进行检查,比如上面的 Expr 对应 i32,这个就需要在各处都保持一致,但这个就需要自己进行检查了。使用了一下 proc_macro_diagnostic 的 API,还不是很好用,等它 stable 吧。

总结

终于自己手写了一个 Procedure Macro,感觉现有的工具已经比较成熟了,有 syn quote 以后很多操作都很方便。但代码还有很多地方可以优化,慢慢搞吧。

每周分享第 49 期

  1. libuv wrapper in C++ https://github.com/skypjack/uvw

  2. Visual Studio Online https://visualstudio.microsoft.com/zh-hans/services/visual-studio-online/

  3. OpenSSH 的 U2F 支持 https://readhacker.news/s/4carE

  4. Rust 1.39 Rust 1.39 Released With Async-Await Support, Attributes On Function Parameters

  5. Windows 也在用 Rust https://msrc-blog.microsoft.com/2019/11/07/using-rust-in-windows/

  6. 用 Chrome Dev Tools 调试 Rust https://twitter.com/ChromeDevTools/status/1192803818024710145

  7. C++20 的新 Thread 类型 https://medium.com/@vgasparyan1995/a-new-thread-in-c-20-jthread-ebd121ae8906

每周分享第 48 期

  1. 一个特殊的用于显示数字的字体 https://blog.janestreet.com/commas-in-big-numbers-everywhere/
  2. Intel 的 SPMD 编译器 https://ispc.github.io/
  3. 基于 Scala 的 notebook https://polynote.org/
  4. 解析登机牌信息 https://github.com/georgesmith46/bcbp
  5. 常用的 React Hooks 库 https://github.com/streamich/react-use
  6. jwt 工具 https://github.com/mike-engel/jwt-cli
  7. 用过程宏实现的 delegation in Rust https://github.com/chancancode/rust-delegate

每周分享第 47 期

  1. CLion 完善了 Rust 调试支持 https://blog.jetbrains.com/clion/2019/10/debugging-rust-code-in-clion/
  2. Nginx HTTP3 的 docker 镜像 https://github.com/RanadeepPolavarapu/docker-nginx-http3
  3. 手算 Ed25519 https://dang.fan/zh-Hans/posts/25519
  4. Rust 的 QuickCheck https://github.com/BurntSushi/quickcheck
  5. Rust 另一个类似 QuickCheck 的测试框架 https://github.com/AltSysrq/proptest
  6. Cookie 策略要改了 https://readhacker.news/s/4bvGG

为 Cisco WLC 配置 Telegraf

最近想到可以给 Cisco WLC 也配置一下监控,查了一下,果然有一些方法。大概研究了一下,找到了方法:

把 https://github.com/haad/net-snmp/tree/master/mibs 和 https://github.com/zampat/neteye4/tree/master/monitoring/monitoring-plugins/wireless/cisco/mibs 目录下的所有 .txt 文件放到 /usr/share/snmp/mibs 目录下。

然后把 https://github.com/zampat/neteye4/blob/master/monitoring/monitoring-plugins/wireless/cisco/telegraf.conf 下面 snmp 的配置复制到 telegraf 配置中,然后修改一下 IP 地址。

确保 Cisco WLC 的 SNMP 的 Public Community 已经配置好,然后就可以拿到数据了。

目前可以拿到 WLC 自身的一些运行˙状态信息、AP 的信息、SSID 的信息和 Client 的信息,基本满足了我们的需求。

参考:https://www.neteye-blog.com/2019/08/monitoring-a-cisco-wireless-controller/

每周分享第 46 期

  1. LibC++ 正式支持 WASI https://releases.llvm.org/9.0.0/projects/libcxx/docs/ReleaseNotes.html
  2. 从文本反推 regex https://github.com/pemistahl/grex
  3. KDE 5.17 发布 https://www.omgubuntu.co.uk/2019/10/kde-plasma-5-17-features
  4. IDA 7.4 发布 支持 Py3 https://www.hex-rays.com/products/ida/7.4/index.shtml
  5. 新的 CPU Bug https://www.phoronix.com/scan.php?page=news_item&px=Chrome-Geminilake-Bug
  6. WAST -> WASM in Rust https://docs.rs/wast/

每周分享第 45 期

  1. Android ARM32/64 环境模拟 https://github.com/zhkl0228/unidbg
  2. Calibre 4.0 https://readhacker.news/s/4aukk
  3. Rust 的 gRPC 实现 https://github.com/hyperium/tonic
  4. JAVA API 历史 https://github.com/marchof/java-almanac
  5. Elm 开发 dev server https://github.com/wking-io/elm-live
  6. 另一个静态网站生成器 https://github.com/getzola/zola
  7. Catalina 发布
  8. Notarization 的相关情况 https://eclecticlight.co/2019/06/07/notarization-in-mojave-and-catalina/ https://eclecticlight.co/2019/05/31/can-you-tell-whether-code-has-been-notarized/
  9. 椭圆曲线加密算法 https://fangpenlin.com/posts/2019/10/07/elliptic-curve-cryptography-explained/
  10. Travis CI 添加 ARM 支持 https://blog.travis-ci.com/2019-10-07-multi-cpu-architecture-support
  11. CD 上的结构 https://readhacker.news/s/4aDHB
  12. 在线 TAC 的虚拟机 https://jia.je/online_tac_vm/
  13. 在线 Decaf 编译和运行 https://jia.je/online_decaf/

每周分享第 44 期

  1. Modern Script Loading https://jasonformat.com/modern-script-loading/

  2. 如何实现 closure http://craftinginterpreters.com/closures.html

  3. 加速 uniform_int_distribution 实现 https://lemire.me/blog/2019/09/28/doubling-the-speed-of-stduniform_int_distribution-in-the-gnu-c-library/

  4. 在 FPGA 上运行 Rocket Chip 教程 https://github.com/cnrv/fpga-rocket-chip

  5. Xilinx Bitstream 逆向 https://prjxray.readthedocs.io/en/latest/

  6. JVM 利用 SIGSEGV 进行 null 检测 https://jcdav.is/2015/10/06/SIGSEGV-as-control-flow/

  7. Async/Await 进入 Rust Beta https://blog.rust-lang.org/2019/09/30/Async-await-hits-beta.html

  8. 在 Rust 里实现 Plugin http://adventures.michaelfbryan.com/posts/plugins-in-rust/

每周分享第 43 期

  1. Async in C https://github.com/naasking/async.h
  2. Mozilla 的 QUIC 实现 https://github.com/mozilla/neqo/
  3. 在 PCI 上做的攻击 https://github.com/ufrisk/pcileech
  4. 对 X 的原理介绍 https://magcius.github.io/xplain/article/index.html
  5. Ethernet 物理层的介绍 https://www.analog.com/media/en/technical-documentation/application-notes/EE-269.pdf
  6. Overleaf 更新了 TeX Live 版本 https://www.overleaf.com/blog/tex-live-upgrade-september-2019
  7. 下一代 proc macro?https://github.com/alexcrichton/proc-macro2
  8. iOS 13 API 变化 https://nshipster.com/ios-13/
  9. iOS 13.1.1 发布
  10. Rust 编译期 HashMap https://github.com/sfackler/rust-phf
  11. V8 加入顶层 Await 支持 https://chromium.googlesource.com/v8/v8.git/+/0ceee9ad28c21bc4971fb237cf87eb742fc787b8%5E%21/
  12. ASCII 码表的一种理解 https://garbagecollected.org/2017/01/31/four-column-ascii/
  13. Cloudflare 启用 HTTP/3 https://blog.cloudflare.com/http3-the-past-present-and-future/
  14. ZFS 发布 0.8.2 https://github.com/zfsonlinux/zfs/releases/tag/zfs-0.8.2

用 Nginx 作为 RTMP 服务器并提供直播服务

Nginx 除了可以做 HTTP 服务器以外,还可以做 RTMP 服务器,同时转成 HLS 再提供给用户,这样可以实现一个直播的服务器,用 OBS 推上来即可。

首先要安装 nginx-rtmp-server 模块,很多的发行版都已经包含了,它的主页是 https://github.com/arut/nginx-rtmp-module,下面很多内容也是来自于它的教程中。

接着,配置 Nginx,在 nginx.conf 的顶层中添加如下的配置:

rtmp {
    server {
            listen 1935;
            chunk_size 4096;

            application live {
                    live on;
                    record off;

                    hls on;
                    hls_path /path/to/save/hls;
                    hls_fragment 1s;
                    hls_playlist_length 10s;
            }

    }
}

这里表示 Nginx 要在 1935 监听一个 RTMP 服务器,然后把 live 下的视频切成片然后存在目录下,提供一个 m3u8 文件以供播放器使用。这里的参数都可以按照实际需求进行调整。这时候应该可以看到 nginx 正确监听 1935 端口,这是 rtmp 的默认端口。

接着,需要在一个 HTTP server 路径下把 HLS serve 出去:

        location /hls {
            # Serve HLS fragments
            types {
                application/vnd.apple.mpegurl m3u8;
                video/mp2t ts;
            }
            root /path/to/save/hls;
            add_header Cache-Control no-cache;
        }

这时候,如果你用 rtmp 推一个流(比如用 OBS)到 rtmp://SERVER_IP/live/SOMETHING,那么在对应的目录下会看到 SOMETHING 开头的一系列文件;用播放器打开 http://SERVER_IP/hls/SOMETHING.m3u8 就可以看到直播的视频流了。

如果要直接在浏览器里播放 HLS,需要用 Flowplayer,直接参考官方的例子即可:

<script>
var player = flowplayer("#player", {
        clip: {
                sources: [
                {
                        type: "application/x-mpegurl",
                        src: "https://SERVER_IP/hls/SOMETHING.m3u8"
                }]
        },
        autoplay: true,
        loop: true,
        live: true
});
</script>

上面的各个路径可以按照实际需求改动。

每周分享第 42 期

  1. Dart 2.5 发布
  2. Rust USB device 库 https://github.com/mvirkkunen/usb-device
  3. 同步视频的 timecode https://github.com/cnbeining/autotimecode
  4. xv6 今年换到了 riscv https://github.com/mit-pdos/xv6-riscv-fall19
  5. pdf 文本搜索 https://pdfgrep.org/
  6. iOS 13 正式版发布
  7. exFAT 驱动进入 Linux 5.4 Microsoft exFAT File-System Mailed In For Linux 5.4 Along With Promoted EROFS & Greybus
  8. Bash 详细教程 https://github.com/dylanaraps/pure-bash-bible
  9. Rust 编译到 iOS bitcode https://github.com/getditto/rust-bitcode
  10. JS 运行时间 in V8 https://v8.dev/blog/cost-of-javascript-2019#json
  11. USB3.0 PIPE 接口实现 https://github.com/enjoy-digital/usb3_pipe
  12. LLVM 9.0 发布 https://lists.llvm.org/pipermail/llvm-dev/2019-September/135304.html
  13. Safari 13.0 发布
  14. Rust 模板引擎 https://github.com/botika/yarte
  15. systemd 开始掌握 home 目录 https://www.phoronix.com/scan.php?page=news_item&px=systemd-homed
  16. 命令行的 cheatsheet 工具 https://github.com/denisidoro/navi

每周分享第 41 期

  1. Wget2 1.99.2 beta 发布 https://lists.gnu.org/archive/html/info-gnu/2019-09/msg00001.html
  2. 独立的 printf 实现 https://github.com/mpaland/printf
  3. Unicode 字符串长度统计 https://hsivonen.fi/string-length/
  4. C 类型推断 https://github.com/ltcmelo/psychec#generic-programming
  5. Clang 9.0 支持 Linux 内核编译 The New Features Of LLVM 9.0 & Clang 9.0 - Includes Building The Linux x86_64 Kernel
  6. 可控制的 USB 设备 https://github.com/usb-tools/Facedancer
  7. USB 监听器 https://github.com/openvizsla/ov_ftdi
  8. 修复一个截断的视频 https://github.com/ponchio/untrunc

在 macOS 上创建 ESP 镜像文件

最近 rCore 添加了 UEFI 支持,在 QEMU 里跑自然是没有问题,然后尝试放到 VMWare 虚拟机里跑,这时候问题就来了:需要一个带有 ESP 盘的 vmdk 虚拟盘。搜索了一下网络,找到了解决方案:

hdiutil create -fs fat32 -ov -size 60m -volname ESP -format UDTO -srcfolder esp uefi.cdr

其中 60m espuefi.cdr 都可以按照实际情况修改。它会把 esp 目录下的文件放到 ESP 分区中,然后得到一个镜像文件:

uefi.cdr: DOS/MBR boot sector; partition 1 : ID=0xb, start-CHS (0x3ff,254,63), end-CHS (0x3ff,254,63), startsector 1, 122879 sectors, extended partition table (last)

接着转换为 vmdk:

qemu-img convert -O vmdk uefi.cdr uefi.vmdk

这样就可以了。

每周分享第 40 期

  1. TeX Typesetting Game https://texnique.xyz/
  2. Writing Linux Kernel Module in Rust https://github.com/fishinabarrel/linux-kernel-module-rust
  3. Sipeed Longan Nano RISC-V 开发板 https://readhacker.news/s/48EBG
  4. Systemd 加载 BPF 防火墙 https://kailueke.gitlab.io/systemd-bpf-firewall-loader/
  5. 方舟编译器 https://code.opensource.huaweicloud.com/HarmonyOS/OpenArkCompiler.git
  6. XLOOKUP 函数 https://techcommunity.microsoft.com/t5/Excel-Blog/Announcing-XLOOKUP/ba-p/811376
  7. SystemVerilog to Verilog 转译 https://github.com/zachjs/sv2v
  8. USB 4.0 发布 http://www.phoronix.com/scan.php?page=news_item&px=USB-4.0-Specification-Published
  9. Android 10 发布 http://www.phoronix.com/scan.php?page=news_item&px=Android-10-Released
  10. iOS 13.1 beta 2 发布
  11. 对 iOS 设备抓包 http://blog.imaou.com/opensource/2014/12/14/pcapd_diagnostic_packet_capture.html
  12. Google 的差分隐私库 https://developers.googleblog.com/2019/09/enabling-developers-and-organizations.html
  13. Apple Music 第三方 App https://github.com/Musish/Musish

每周分享第 39 期

  1. 另一个 Pattern Matching in C++ 库 https://github.com/mpark/patterns
  2. Rust 编写的一个 shell http://www.jonathanturner.org/2019/08/introducing-nushell.html
  3. Vim 的游戏教程 https://readhacker.news/s/48nbJ
  4. 在 throw 的时候同时抓取 SO 信息 https://github.com/shobrook/rebound/
  5. 颜色生成工具 https://github.com/sharkdp/pastel
  6. 转换 Escape Code 到 HTML https://github.com/theZiz/aha
  7. iOS 13.1 beta 发布
  8. 苹果特别活动定于 9.11 日
  9. exFAT 规范发布 http://www.phoronix.com/scan.php?page=news_item&px=Microsoft-exFAT-Specification
  10. 可视化的 5 阶段流水线 RISC-V 模拟器 https://github.com/mortbopet/Ripes

每周分享第 38 期

  1. 针对部分 HTTP/2 实现的新漏洞 https://github.com/Netflix/security-bulletins/blob/master/advisories/third-party/2019-002.md
  2. 只读 tmux 加魔改 https://brianmckenna.org/blog/guest_tmux
  3. Rust 编译期断言 https://github.com/nvzqz/static-assertions-rs
  4. Rust 运行时解压版的 include_bytes https://github.com/SOF3/include-flate
  5. 把 Rust 标准库进行 async 化的尝试 https://async.rs/blog/announcing-async-std/#
  6. git 添加 switch/restore 命令 https://github.blog/2019-08-16-highlights-from-git-2-23/
  7. Flash Player in Rust https://github.com/ruffle-rs/ruffle
  8. Matplotlib Cheatsheet https://github.com/rougier/matplotlib-cheatsheet
  9. macOS Catalina 10.15 beta 6 发布
  10. Flash Decompiler https://github.com/jindrapetrik/jpexs-decompiler
  11. Yubikey 发布 5Ci https://readhacker.news/s/489yu
  12. IBM 开放 Power 指令集 https://readhacker.news/s/48aiH
  13. iOS 13 beta 8 发布
  14. GitHub 支持 WebAuthn https://readhacker.news/s/48dXM
  15. 编译器优化带来的安全漏洞 https://readhacker.news/s/48dke
  16. obscure C features https://multun.net/obscure-c-features.html

每周分享第 37 期

  1. Rust netlink 库 https://github.com/jbaublitz/neli
  2. Rust 处理 signal 进行配置的 reload https://vorner.github.io/2019/08/11/runtime-configuration-reloading.html
  3. macOS 添加了 VirtIO 驱动支持 https://passthroughpo.st/mac-os-adds-early-support-for-virtio-qemu/
  4. 一个 GNU readline 替代品 https://github.com/AmokHuginnsson/replxx
  5. Rust 1.37.0 发布 https://blog.rust-lang.org/2019/08/15/Rust-1.37.0.html
  6. iOS 13 DB7 (17A5565b) 发布

macOS 下读取并解析 EDID

之前听说了 EDID 的存在,但是一直没有细究里面的格式和内容。今天了解了一下,发现其实非常简单,下面是方法:

首先获取所有显示器输出的 EDID:

ioreg -lw0 | grep IODisplayEDID

输出里会出现 "IODisplayEDID" = <00ffxxxxxxxxxxxxx> 的内容,尖括号内的就是 EDID 的内容。接着,我们采用 edid-decode 进行解析:

git clone git://linuxtv.org/edid-decode.git
cd edid-decode
make
./edid-decode
<Paste EDID here>

就可以看到很详细的 EDID 数据解析了。

ref: https://gist.github.com/OneSadCookie/641549 https://www.avsforum.com/forum/115-htpc-mac-chat/1466910-ability-dump-display-s-edid-mac.html

在 Linux 下捕获 Framebuffer

最近需要在 linux 下抓取 Framebuffer 的内容,在网上找到了两种方法,在我这里只有第二、第三种可以成功,没有细究具体原因,可能与我的 Framebuffer 配置有关。方法如下:

  1. fbgrab :命令就是 fbgrab image.png,直接得到 png 文件,格式是对的,但是用软件打开就是一片空白。用 ImageMagick 转换为 jpg 可以看到一些内容,但是和实际有些不一样。
  2. fbdump :命令就是 fbdump > image.ppm,得到裸的 ppm 文件,图像是正确的,也可以转换为别的格式正常打开。
  3. cat+脚本处理:直接 cat /dev/fb0 > image.rgb,然后用下面的脚本转换为 png。由于 Framebuffer 格式为 RGB,本来 A 所在的 channel 都为 0,所以用一些软件直接打开都是空白,只好写了脚本直接跳过 Alpha Channel。

Framebuffer 配置( fbset 输出):

mode "640x480-0"
        # D: 0.000 MHz, H: 0.000 kHz, V: 0.000 Hz
        geometry 640 480 1024 480 32
        timings 0 0 0 0 0 0 0
        accel false
        rgba 8/16,8/8,8/0,0/0
endmode

转换脚本(参考 [Tips] 擷取 framebuffer 畫面):

#!/usr/bin/perl -w

$w = shift || 240;
$h = shift || 320;
$pixels = $w * $h;

open OUT, "|pnmtopng" or die "Can't pipe pnmtopng: $!\n";

printf OUT "P6%d %d\n255\n", $w, $h;

while ((read STDIN, $raw, 4) and $pixels--) {
   $short = unpack('I', $raw);
   print OUT pack("C3",
      ($short & 0xff0000) >> 16,
      ($short & 0xff00) >> 8,
      ($short & 0xff));
}

close OUT;

用法: cat image.rgb | perl script.pl 1024 480 > console.png

每周分享第 36 期

  1. QuickJS + libuv https://github.com/saghul/qjsuv
  2. Rust 实现的 Ruby https://github.com/artichoke/artichoke
  3. 类似 JSON 的数据格式 https://cbor.io/
  4. rls 二代目 https://github.com/rust-analyzer/rust-analyzer
  5. 给 MacBook 添加触屏支持 https://github.com/bijection/sistine
  6. 用于 MBP with T2 的 Linux patch https://github.com/aunali1/linux-mbp-arch
  7. 在 tmux 中使用 Touch ID 认证 sudo https://blog.birkhoff.me/make-sudo-authenticate-with-touch-id-in-a-tmux/
  8. iOS 13 beta 6 发布
  9. Linux Journal 结束了它的使命
  10. ssh 8.0 Add support for ECDSA keys in PKCS#11 tokens

用 PulseView 配合 DSLogic 调试 SPI Flash

最近需要用到逻辑分析仪来调试 SPI Flash,设备是 DreamSourceLab 的 DSLogic,最开始用的是官方的 DSView,确实能够抓到 SPI 的信号,也可以解析出一些 SPI Flash 的数据,但是很多是不完整的。

后来把源码下载下来,发现是基于 sigrok 和 PulseView 做的一个魔改版,然后 sigrok 官网上最新的版本已经支持了 DSLogic,于是就用 PulseView 替代 DSView。一开始遇到的问题是没有 firmware,一番搜索找到了解决方案,按照脚本下载好文件即可。

进到 PulseView 以后,把 SPI 的四路信号接上,然后抓了一段信号,解析:

可以看到它正确地解析出来了 Fast Read 命令。由于 DSView 它 fork 自一个比较老的版本,所以它并不能正确解析出来。

P.S. Linux 下它界面显示比 macOS 下好看一些,估计是没有适配好。

每周分享第 35 期

  1. Apple Touch Bar 的 Windows 第三方支持 https://github.com/imbushuo/DFRDisplayKm
  2. WSL2 新的进展 http://www.phoronix.com/scan.php?page=news_item&px=Windows-WSL2-Localhost-Plus
  3. Lightning 转接头里面居然跑 iBoot 和 Darwin https://readhacker.news/s/46Zde
  4. iOS 13 beta 5 发布
  5. ES2019 前瞻 https://blog.tildeloop.com/posts/javascript-what%E2%80%99s-new-in-es2019
  6. Rust 可持久化数据结构 https://github.com/orium/rpds
  7. Rust 不可变数据结构 https://github.com/bodil/im-rs
  8. macOS Catalina 10.15 beta 5 发布
  9. Algebraic Effect https://overreacted.io/algebraic-effects-for-the-rest-of-us/
  10. Donald Knuth 对于 Sensitivity Proof 证明的简化 https://readhacker.news/s/47fsA 真是老当益壮
  11. TLS1.3 标准的简化版 https://readhacker.news/s/47eG9
  12. iTerm 3.3 发布 https://iterm2.com/downloads/stable/iTerm2-3_3_0.changelog

每周分享第 34 期

  1. macOS Mojave 10.14.6 iOS 12.4 正式版更新发布,还有老版本的更新 https://www.macrumors.com/2019/07/22/apple-releases-gps-bug-fix-older-iphones-ipads/
  2. CLion 2019.2 添加了 GDB Server 的调试支持,可以用 OpenOCD。

每周分享第 33 期

  1. 一个讲 Rust 比较硬核的博客 http://system.joekain.com/
  2. 利用 fzf 实现 shell 的 REPL https://github.com/pawelduda/fzf-live-repl
  3. 类似 Vue 的前端 Go 框架 https://www.vugu.org/doc/start
  4. iOS 13 beta 4 发布 14A5534f
  5. 针对 T2 NVMe 的 patch http://www.phoronix.com/scan.php?page=news_item&px=NVMe-Patches-LKML-Apple-Mac
  6. 一个前端非对称加密的问卷网站 https://blog.fugoes.xyz/crypto-q/