この記事は、CYBOZU SUMMER BLOG FES ‘24 (Garoon Stage) DAY 20 の記事です。 こんにちは! サイボウズ Garoon 開発チームの kofuk です。
CYBOZU SUMMER BLOG FES ‘24 も今日で最終日ですが、今日の話はまるっきり業務とは関係がありません。つまり夏の自由研究です。 普段はプロトコルの解析とは縁遠い生活を送っているのですが、たまにはバイト列と触れ合いたくなるのが人の性というものです。
いつもは意識することのないレイヤーに潜ってみることにしましょう!
Wireshark とは? Wireshark に馴染みのない人もいるかもしれないので、まず Wireshark 自体について説明します。 Wireshark はざっくり言うとネットワークを流れているデータをキャプチャして解析するソフトウェアです1。
これを使うことで、例えばブラウザで Web サイトにアクセスした際に(Wi-Fi アダプタを介して)やり取りされているデータを GUI で見ることができます。
全国の Hugo ファンの皆さん、こんにちは(?)
アドベントカレンダー以外でブログを書かない人になりつつあるので、 静的サイトジェネレータ (SSG) である Hugo を使って OGP 画像を自動生成した話でもしたいと思います1。
Hugo は Go 言語で書かれた SSG で、Go の template を使ってサイトのレイアウトを組んでいくことになります (テーマと呼ばれる、出来合いのレイアウトを使うこともでき、これも結局は Go の template で書かれています)。 テンプレートは記事の一覧や記事自体のページを作っていく、というのが基本の使い方になるのですが、 Hugo にはテンプレートの中で Sass や TypeScript のトランスパイルを行ったり、画像を生成したりする機能がついています2。 この機能を使って、OGP 画像を生成してみた、というのが今回やったことです。
Google Cloud Storage は実は S3 互換の API が生えていて、既存の S3 を使うコードのオブジェクトの保存先を簡単に GCS に向けることができるようになっています。 理想的にはエンドポイントやトークンを差し替えれば移行できるのですが、他の S3 互換を謳うオブジェクトストレージと同じように単純にいかないことも多々あります。 最も一般的なエラーは S3 でサポートされているエンドポイントを互換オブジェクトストレージがサポートしていないという問題だと思いますが、 今回細かい部分の仕様の差異で AWS SDK で GCS のエンドポイントが叩けないという問題があったので、そのときにとった方法をメモしておきます。
雑な対処なので全く良い方法ではないですが。
起こったことと原因 aws-sdk-go-v2 で GCS に HMAC キーを使用してアクセスすると、正しいキーを指定しているにもかかわらず SignatureNoesNotMatch エラーが出ます。
この記事は「Cybozu Frontend Advent Calendar 2023」 13 日目の記事です。 一家に一台 Mastodon とはよく言ったもので1、今や Mastodon のサーバは人々の生活に必要不可欠なものになっています。(そんなことはない)
そして、自分で Mastodon のサーバを立てると、Mastodon 自体を改造する必要に迫られたり、細かい不便を解消するために手を入れたくなったりしてくるものです2。 この記事では、私が Mastodon のフロントエンドに手を入れた際の知見を共有します。
ちなみに業務ではフロントエンドとはかなり遠い領域を触っていて、フロントエンドは何も分かりません。
Mastodon とは? (↑分かりやすいらしい動画。kofuk は真面目に見たことがないですが)
「初めて蟹を見た kofuk」というふざけた名前のチームで @uta8a と ISUCON13 に出ました。
当日のリポジトリも公開しています。 GitHub - uta8a/isucon13q-2023-11-25: ISUCON 13 https://github.com/uta8a/isucon13q-2023-11-25 ISUCON 13. Contribute to uta8a/isucon13q-2023-11-25 development by creating an account on GitHub. パフォーマンスに詳しくない人がほとんど準備せずに行くとこういう景色になるという感じで、結果は褒められたものではなかったです。 ただ ISUCON は人それぞれのレベルで頑張れるイベントで知識が薄くても暇になるということはないという感じで、良いイベントだなーと思いました。 めちゃくちゃ楽しかったです。
ちなみにチーム名はこれ↓が元ネタです。
蟹を初めて見た上田晋也みたいなツイートを見て電車で大笑いしていてマジで恥ずかしい
— kofuk (@man_2_fork) April 14, 2023 uta8a が詳しくブログに書いてくれました。 ISUCON13に出た https://blog.uta8a.net/post/2023-11-25-isucon13f 初めて蟹を見た kofuk というチームでISUCON13に出ました。最終スコアは1万ちょいくらいです
お久しぶりです。 どうでもいいことを気軽に書けるように(ブログサービスではなく)自分のブログを作っているのに、あんまり気軽に書けてないなと感じている今日この頃です。 で、普段からどうでもいいことを書いていないとどうでもいいことも書きづらくなってしまうな(?)と思ったわけで、質にはこだわらずにどうでもいい記事をぽんぽん出していこうと 思ったわけですね。でも書くからには何かネタがないと厳しいなというわけで、適当にそのへんの OSS のコードを読んでそれを記事にするというのをひとまず続けてみようと思っています。 コード読んで解説くらいだったらそんなに負荷も高くないので続けられそうという希望的観測もあります。 あと他人のコード読むといろいろ学べるところもありますしね。
備忘録。 出てこないなーとか思ってたら普通に書いてあった。
単に -D で指定するときに subproject名:オプション名 というふうに指定すればいいだけだった。
例えば subproject hoge でだけ warning_level を 3 にしたい場合は
$ meson configure -Dhoge:warning_level=3 のように指定すれば ok。
ちなみにモジュール (meson.build の中で import とかして使うやつ) ごとのオプションは . で区切る。
なので pkgconfig の relocatable を true にする場合とかは -Dpkgconfig.relocatable=true とかを指定すればいい。
参考 Built-in options
入ってるパッケージが諸々古すぎるので、周辺のライブラリのビルドから始める必要がある。
そのへんを自動でやってくれるようにシェルスクリプトにまとめた。 /tmp に tmpfs がいなかったり、ホームディレクトリが NFS だったりする環境のものなので、_build_dir 系の変数は変更して使ったほうがいいと思う。
ビルド手順 まず、標準で入っている GCC は LLVM をビルドするには古すぎるので、GCC のビルドから始める必要がある。 古い LLVM から徐々にバージョンアップしていく方法でもいいが、最新の GCC はビルドできるので。ついでに GCC も最新にしてやる方が首尾が良いと思う。
で、最新の GCC をビルドするには周辺のライブラリが古すぎるので、最初にとりかかるべきはこれらのビルドということになる。 具体的には、GMP と MPFR、MPC をビルドする必要がある。
いろいろと怪しいけどこれでフックすれば取れるはず。 まあ malloc をフックしている時点で怪しいので細かいことを気にしてはいけない (?)
#define _GNU_SOURCE #include <dlfcn.h> #include <string.h> #include <stdio.h> static size_t max_usage; static size_t cur_usage; static void *(*orig_malloc)(size_t); static void (*orig_free)(void *); static void *(*orig_realloc)(void *, size_t); __attribute__((constructor)) void initialize(void) { orig_malloc = dlsym(RTLD_NEXT, "malloc"); orig_free = dlsym(RTLD_NEXT, "free"); orig_realloc = dlsym(RTLD_NEXT, "realloc"); } extern char *__progname; __attribute__((destructor)) void print_stat(void) { size_t result = max_usage; fprintf(stderr, "%s: max memory usage: %zu\n", __progname, result); } void *malloc(size_t size) { void *ptr = orig_malloc(size + sizeof(size_t)); if (ptr == NULL) { return NULL; } memcpy(ptr, &size, sizeof(size_t)); cur_usage += size; if (cur_usage > max_usage) { max_usage = cur_usage; } return (void *)((unsigned char *)ptr + sizeof(size_t)); } void *calloc(size_t nmemb, size_t size) { void *ptr = malloc(nmemb * size); if (ptr != NULL) { memset(ptr, 0, size * nmemb); } return ptr; } void free(void *ptr) { if (ptr == NULL) { return; } void *head = (void *)((unsigned char *)ptr - sizeof(size_t)); size_t size; memcpy(&size, head, sizeof(size_t)); cur_usage -= size; orig_free(head); } void *realloc(void *ptr, size_t size) { size_t old_size = 0; void *result; if (ptr != NULL) { void *head = (void *)((unsigned char *)ptr - sizeof(size_t)); memcpy(&old_size, head, sizeof(size_t)); result = orig_realloc(head, size + sizeof(size_t)); } else { result = orig_realloc(ptr, size + sizeof(size_t)); } if (result != NULL) { cur_usage -= old_size; cur_usage += size; if (cur_usage > max_usage) { max_usage = cur_usage; } memcpy(result, &size, sizeof(size)); return (void *)((unsigned char *)result + sizeof(size_t)); } return NULL; } void *reallocarray(void *ptr, size_t nmemb, size_t size) { return realloc(ptr, nmemb * size); } $ gcc -shared -fPIC -ldl -o libmh.so mh.c でコンパイル、
$ LD_PRELOAD=/path/to/libmh.so hogehoge みたいに使う。
patchelf を使うと一発。elfedit というコマンドが binutils にあるっぽかったが、使い方がイマイチ分からなかった。
RPATH の追加 $ORIGIN/../lib を追加する例。ちなみに $ORIGIN はファイルがあるディレクトリを指すので、絶対パスを使うより柔軟にパスの指定ができる。
$ patchelf --add-rpath '$ORIGIN/../lib' NEEDED の追加 $ patchelf --add-needed libhoge.so.0 RPATH と NEEDED を組み合わせると LD_PRELOAD 的なことが環境変数なしでできる
下みたいな内容で適当なライブラリを作る。
int puts(const char *s) { return 0; } で、下みたいな内容の実行ファイルに patchelf する。
#include <stdio.h> int main(void) { puts("hoge"); } $ ./hoge hoge $ patchelf --add-rpath '$ORIGIN/lib' $ patchelf --add-needed 'libhoge.so.0' $ ./hoge (何も出ない) 実用性があるかは謎。