-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.json
1 lines (1 loc) · 25.4 KB
/
index.json
1
[{"content":"An ill-formed C++ code snippet Before the fix, this following C++ snippet would crash Clang. From the bug report, we can see how a user might accidentally omit the function name when they meant to declare a member function on line 3.\nTo many people\u0026rsquo;s surprise, line 3 is actually parsed as a member variable declaration. The parantheses are insignificant here unlike when declaring a pointer to function or array. So line 3 is essentially T A\u0026lt;T\u0026gt;{};. The template parameter there is ill-formed and is handled by 942c03910aef.\nWhy crash? The core of this problem is that the identifier part of the ill-formed member variable declaration is empty inside Clang\u0026rsquo;s AST, when handling the default member initializer (the {} on line 3 of crash.cpp) inside clang::Sema::BuildCXXDefaultInitExpr.\nHere\u0026rsquo;s an excerpt from clang::Sema::BuildCXXDefaultInitExpr. The range-based for loop is never run as the Lookup is empty! Pattern stays as nullptr. This triggers an asssertion failure at clang/lib/Sema/SemaDeclCXX.cpp:15540 if NDEBUG is not defined.\nOtherwise, Pattern is accessed 😱 at clang/lib/Sema/SemaDeclCXX.cpp:15542, the bahavior of which is undefined 💣 💥.\nclang::Sema::BuildCXXDefaultInitExpr is call stack #12 in the crash backtrace below: The fix Simply adding a line to set the identifier of an ill-formed declarator would suffice. Now the name clash with the injected-class-name can also be correctly diagnosed as shown below 🎊 🙌 🎉: ","permalink":"http://poyaoc97.github.io/posts/2022-03-24-clang/","summary":"\u003ch2 id=\"an-ill-formed-c-code-snippet\"\u003eAn ill-formed C++ code snippet\u003c/h2\u003e\n\u003cp\u003eBefore \u003ca href=\"https://github.com/llvm/llvm-project/commit/355f1c75aa66fc3d9bef897375f5e0979a55001d\"\u003ethe fix\u003c/a\u003e,\nthis following C++ snippet would crash Clang.\n\u003cimg alt=\"crash-cpp\" loading=\"lazy\" src=\"/img/2022-03-24-clang/crash-cpp.png\"\u003e\nFrom the \u003ca href=\"https://llvm.org/PR28101\"\u003ebug report\u003c/a\u003e,\nwe can see how a user might accidentally omit the function name when they meant to declare a member function on line 3.\u003c/p\u003e\n\u003cp\u003eTo many people\u0026rsquo;s surprise, line 3 is actually parsed as a member variable declaration.\nThe parantheses are insignificant here unlike when declaring a pointer to function or array. So line 3 is essentially \u003ccode\u003eT A\u0026lt;T\u0026gt;{};\u003c/code\u003e.\nThe template parameter there is \u003cem\u003eill-formed\u003c/em\u003e and is handled by \u003ca href=\"https://github.com/llvm/llvm-project/commit/942c03910aef43de1cd2c733a82370fd3e2c9981\"\u003e942c03910aef\u003c/a\u003e.\u003c/p\u003e","title":"Fixed a Bug Crashing Clang"},{"content":"I was browsing 🫣 the latest working draft of the ISO C++ Standard (https://eel.is/c++draft/) and then I found that what a single semicolon is called grammatically depends on where it appears. Cool beans 😎!\nHere\u0026rsquo;s a screenshot illustrating my findings (engorgio): The top half is a piece of valid C++ code stored in sc.cpp and the bottom half is its Clang AST dump with unimportant parts removed.\nThe ; in the global scope on line 1 of sc.cpp is an empty-declaration as noted in [decl.pre]p1. The grammar production goes:\ntranslation-unit\n-\u0026gt; declaration-seq\n-\u0026gt; declaration\n-\u0026gt; empty-declaration\n-\u0026gt; ;\nThe ; in the function-body on line 3 of sc.cpp is an expression-statement also called a null statement in this case, as noted in [stmt.expr]p1. The grammar production goes:\ntranslation-unit\n-\u0026gt; declaration-seq\n-\u0026gt; declaration\n-\u0026gt; function-definition\n-\u0026gt; function-body\n-\u0026gt; compound-statement\n-\u0026gt; statement-seq\n-\u0026gt; statement\n-\u0026gt; expression-statement\n-\u0026gt; ;\nNotice how the Clang AST dump align with this.\nThat\u0026rsquo;s it for this C++ trivia hope you like it as much as I do 🙃.\n","permalink":"http://poyaoc97.github.io/posts/2022-03-01-cpp-trivia/","summary":"\u003cp\u003eI was browsing 🫣 the latest working draft of the ISO C++ Standard (\u003ca href=\"https://eel.is/c++draft/\"\u003ehttps://eel.is/c++draft/\u003c/a\u003e) and then I found that what a single semicolon is called grammatically depends on where it appears. Cool beans 😎!\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s a screenshot illustrating my findings (\u003ca href=\"/img/2022-03-01-cpp-trivia/clang_ast_dump.png\"\u003eengorgio\u003c/a\u003e):\n\u003cimg alt=\"Clang AST Dump\" loading=\"lazy\" src=\"/img/2022-03-01-cpp-trivia/clang_ast_dump.png\"\u003e\nThe top half is a piece of valid C++ code stored in \u003ccode\u003esc.cpp\u003c/code\u003e and the bottom half is its Clang AST dump\nwith unimportant parts removed.\u003c/p\u003e\n\u003cp\u003eThe \u003ccode\u003e;\u003c/code\u003e in the global scope on line 1 of \u003ccode\u003esc.cpp\u003c/code\u003e is an \u003ccode\u003eempty-declaration\u003c/code\u003e as noted in \u003ca href=\"https://eel.is/c++draft/dcl.pre#nt:empty-declaration\"\u003e[decl.pre]p1\u003c/a\u003e.\nThe grammar production goes:\u003cbr\u003e\n\u003ca href=\"https://eel.is/c++draft/basic.link#nt:translation-unit\"\u003e\u003ccode\u003etranslation-unit\u003c/code\u003e\u003c/a\u003e\u003cbr\u003e\n-\u0026gt; \u003ca href=\"https://eel.is/c++draft/dcl.pre#nt:declaration-seq\"\u003e\u003ccode\u003edeclaration-seq\u003c/code\u003e\u003c/a\u003e\u003cbr\u003e\n-\u0026gt; \u003ca href=\"https://eel.is/c++draft/dcl.pre#nt:declaration\"\u003e\u003ccode\u003edeclaration\u003c/code\u003e\u003c/a\u003e\u003cbr\u003e\n-\u0026gt; \u003ca href=\"https://eel.is/c++draft/dcl.pre#nt:empty-declaration\"\u003e\u003ccode\u003eempty-declaration\u003c/code\u003e\u003c/a\u003e\u003cbr\u003e\n-\u0026gt; \u003ccode\u003e;\u003c/code\u003e\u003c/p\u003e","title":"C++ Trivia;"},{"content":"The Issue One day, I was wondering how dynamic function call works. I thought I\u0026rsquo;d start with a simple puts call like in this C snippet.\n#include \u0026lt;stdio.h\u0026gt; int main(void) { puts(\u0026#34;Hello\\n\u0026#34;); } To see it going through the Procedure Linkage Table (PLT) in the context of lazy binding on x86_64 Linux LIVE, naturally, LLDB came to my mind. To my dismay, instead of something relevant to puts there were only these ugly and unidentifiable ___lldb_unnamed_symbol36 in the disaseembly on LLDB 13.0.0 🫠: In comparison, this is GDB 11.1: Let\u0026rsquo;s see how an older version of LLDB is doing. Here\u0026rsquo;s LLDB version 12.0.1 🫥: Apparently, something went wrong between the creation of LLVM version 12 and 13 release branch. It turned out that it was a patch landing in July 2021 that changed the behavior. Someone also filed a GitHub issue just a few days before I found this. I then commented my findings on the code review page of the seemingly problematic patch.\nFinding the culprit Thankfully, LLDB has a nice \u0026ldquo;batch mode\u0026rdquo;, so it\u0026rsquo;s pretty easy to lauch it in a debugger as shown below. Otherwise I have no idea how to debug an REPL program.\nlldb -- ~/llvm-project/build-lldb/bin/lldb -b -o \u0026#39;image lookup -a 0x401030\u0026#39; a.out After some debugging of the debugger, I finally knew where the problem was. The aforementioned patch was innocent!\nHere\u0026rsquo;s the whole function added by the patch (comments removed for brevity) that\u0026rsquo;s seemingly causing the problem:\nvoid Symbol::SynthesizeNameIfNeeded() const { if (m_is_synthetic \u0026amp;\u0026amp; !m_mangled) { llvm::SmallString\u0026lt;256\u0026gt; name; llvm::raw_svector_ostream os(name); os \u0026lt;\u0026lt; GetSyntheticSymbolPrefix() \u0026lt;\u0026lt; GetID(); m_mangled.SetDemangledName(ConstString(os.str())); } } Let\u0026rsquo;s focus on the condition of the if statement, m_mangled is of type Mangled, so !m_mangled calls the broken Mangled::operator!, which according to git blame, dates back to Apple\u0026rsquo;s initial open-sourcing LLDB in 2010, untouched, unused, and untested for over a decade.\nTo my surprise, the implementation of Mangled::operator! and Mangled::operator void* differed from the intended behavior documented by comments in the header file. I then replaced them with one single explicit operator bool which implements Mangled\u0026rsquo;s documented bool semantics.\nNow, the disassmbly correctly shows: bringing balance to the Force.\nFinally, as per a reviewer\u0026rsquo;s request, I added unit tests for my new bool conversion operator, which was also my first time using GoogleTest. What a fun ride! The GitHub commit can be found at: https://github.com/llvm/llvm-project/commit/633b002944b9\n","permalink":"http://poyaoc97.github.io/posts/2022-01-07-lldb/","summary":"\u003ch2 id=\"the-issue\"\u003eThe Issue\u003c/h2\u003e\n\u003cp\u003eOne day, I was wondering how dynamic function call works.\nI thought I\u0026rsquo;d start with a simple \u003ccode\u003eputs\u003c/code\u003e call like in this C snippet.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-C\" data-lang=\"C\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;stdio.h\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e) { \u003cspan style=\"color:#a6e22e\"\u003eputs\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Hello\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e); }\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo see it going through the \u003cstrong\u003eProcedure Linkage Table (PLT)\u003c/strong\u003e\nin the context of lazy binding on x86_64 Linux \u003cem\u003eLIVE\u003c/em\u003e, naturally, LLDB came to my mind.\nTo my dismay, instead of something relevant to \u003ccode\u003eputs\u003c/code\u003e\nthere were only these ugly and unidentifiable \u003ccode\u003e___lldb_unnamed_symbol36\u003c/code\u003e in the disaseembly on LLDB 13.0.0 🫠:\n\u003cimg alt=\"lldb13\" loading=\"lazy\" src=\"/img/2022-01-07-lldb/lldb13.png\"\u003e\nIn comparison, this is GDB 11.1:\n\u003cimg alt=\"gdb\" loading=\"lazy\" src=\"/img/2022-01-07-lldb/gdb.png\"\u003e\nLet\u0026rsquo;s see how an older version of LLDB is doing.\nHere\u0026rsquo;s LLDB version 12.0.1 🫥:\n\u003cimg alt=\"lldb12\" loading=\"lazy\" src=\"/img/2022-01-07-lldb/lldb12.png\"\u003e\nApparently, something went wrong between the creation of LLVM version 12 and 13 release branch.\nIt turned out that it was\n\u003ca href=\"https://github.com/llvm/llvm-project/commit/ec1a49170129\"\u003ea patch landing in July 2021\u003c/a\u003e that changed the behavior.\nSomeone also filed a \u003ca href=\"https://llvm.org/PR52702\"\u003eGitHub issue\u003c/a\u003e just a few days before I found this.\nI then commented my findings on \u003ca href=\"https://reviews.llvm.org/D106837\"\u003ethe code review page of the seemingly problematic patch\u003c/a\u003e.\u003c/p\u003e","title":"The Bug Strikes Back: LLDB Bugs Lurking for Over a Decade"},{"content":"I ran into linker errors similar to the screenshot below in November 2021. After some close inspection, I realized this was a result of mixing 2 C++ standard libraries, namely libc++ and libstdc++. I was unkowningly 😱 compiling TVM with libc++\u0026rsquo;s headers while linking it against LLVM which was compiled with libstdc++\u0026rsquo;s headers and linked against libstdc++.\nA Simple Example Here is a simple example illustrating what\u0026rsquo;s happening.\nSource Files Given 2 C++ souce files:\nprint_all.cpp #include \u0026lt;cstdio\u0026gt; #include \u0026lt;string\u0026gt; void print_all(const std::string\u0026amp; str) { for (auto idx = 0uz; auto i : str) { printf(\u0026#34;%lu\\t%c\\n\u0026#34;, ++idx, i); } } Side note:\nThe uz suffix was introduced to C++23 by P0330, and the init-statement in range-based for was introduced to C++20 by P0614.\nmain.cpp #include \u0026lt;string\u0026gt; void print_all(const std::string\u0026amp;); int main() { print_all(\u0026#34;Hello World!\u0026#34;); } Object Files We compile print_all.cpp with headers of libc++ and libstdc++ respectively:\nclang++ -std=c++2b -O3 -c print_all.cpp -stdlib=libc++ -o print_all_libc++.o clang++ -std=c++2b -O3 -c print_all.cpp -stdlib=libstdc++ -o print_all_libstdc++.o And then compile main.cpp with libc++\u0026rsquo;s header files:\nclang++ -std=c++2b -O3 -c main.cpp -stdlib=libc++ -o main-libc++.o Symbol Tables We can inspect symbols of each object file by running nm --demangle *.o.\nmain-libc++.o: 0000000000000000 r GCC_except_table0 U _Unwind_Resume U print_all(std::__2::basic_string\u0026lt;char, std::__2::char_traits\u0026lt;char\u0026gt;, std::__2::allocator\u0026lt;char\u0026gt; \u0026gt; const\u0026amp;) U operator delete(void*) U __gxx_personality_v0 0000000000000000 T main print_all_libc++.o: 0000000000000000 T print_all(std::__2::basic_string\u0026lt;char, std::__2::char_traits\u0026lt;char\u0026gt;, std::__2::allocator\u0026lt;char\u0026gt; \u0026gt; const\u0026amp;) U printf print_all_libstdc++.o: 0000000000000000 T print_all(std::__cxx11::basic_string\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, std::allocator\u0026lt;char\u0026gt; \u0026gt; const\u0026amp;) U __gxx_personality_v0 U printf The print_all of print_all_libc++.o and print_all_libstdc++.o have different function signatures hence different symbol names! We will get to those different std::basic_string\u0026lt;char, char_traits\u0026lt;char\u0026gt;, allocator\u0026lt;char\u0026gt; \u0026gt; in a moment.\nLinking Let\u0026rsquo;s first link main-libc++ and print_all_libc++.o together.\nclang++ -stdlib=libc++ -fuse-ld=lld main-libc++.o print_all_libc++.o Voila, the resulting executable works as intended.\nNext, let\u0026rsquo;s see what would happen if we linked main-libc++ and print_all_libstdc++.o together.\n❯ clang++ -stdlib=libc++ -fuse-ld=lld main-libc++.o print_all_libstdc++.o ld.lld: error: undefined symbol: print_all(std::__2::basic_string\u0026lt;char, std::__2::char_traits\u0026lt;char\u0026gt;, std::__2::allocator\u0026lt;char\u0026gt; \u0026gt; const\u0026amp;) \u0026gt;\u0026gt;\u0026gt; referenced by main.cpp \u0026gt;\u0026gt;\u0026gt; main-libc++.o:(main) clang-14: error: linker command failed with exit code 1 (use -v to see invocation) It failed expectedly as the linker cannot find print_all(std::__2::basic_string\u0026lt;char, std::__2::char_traits\u0026lt;char\u0026gt;, std::__2::allocator\u0026lt;char\u0026gt; \u0026gt; const\u0026amp;) in print_all_libstdc++.o (see the nm output in the previous section). This is exactly the problem we came across!\nInline Namespace It turns out that there is this thing called inline namespace which is sort of transparent to library users but not to compilers and linkers in that it is soldered into symbol names.\n#include \u0026lt;cstdio\u0026gt; #include \u0026lt;string\u0026gt; namespace not_std { inline namespace v1 { void print_all(const std::string\u0026amp; str) { for (auto idx = 0uz; auto i : str) { printf(\u0026#34;%lu\\t%c\\n\u0026#34;, ++idx, i); } } } // namespace v1 } // namespace not_std int main() { not_std::print_all(\u0026#34;Hello World!\u0026#34;); // works not_std::v1::print_all(\u0026#34;Hello World!\u0026#34;); // also works } std::string Hence, when we write std::string, it is not what we think it is naively (type alias of std::basic_string\u0026lt;char\u0026gt;). Instead, for libc++, it is\nstd::__2::basic_string\u0026lt;char, std::__2::char_traits\u0026lt;char\u0026gt;, std::__2::allocator\u0026lt;char\u0026gt;\u0026gt; or\nstd::__1::basic_string\u0026lt;char, std::__1::char_traits\u0026lt;char\u0026gt;, std::__1::allocator\u0026lt;char\u0026gt;\u0026gt; or for libstdc++\nstd::__cxx11::basic_string\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, std::allocator\u0026lt;char\u0026gt;\u0026gt; Use cases Inline namespace can be used for library versioning. In the case of libc++, the inline namespace is used for ABI versioning and is used for nearly every name, contrary to libstdc++, which seems to use it only for differentiating SSO (small string optimization) string from the pre-C++11 one.\nMore on libc++ ABI versioning Notice in the Symbol Table section, there are __2 following std:: in all libc++ symbols. It\u0026rsquo;s __1 by default but I configured and built libc++ with LIBCXX_ABI_VERSION set to 2 just for fun. The differences from ABI version 1 can be found here. Version 2 includes alternate string layout, which is also used for Apple arm64.\nConclusion When faced with linker errors, we must be as patient as we can, as Yoda put it, \u0026ldquo;patience you must have, my young padawan\u0026rdquo;. Also, by no means do you ever want to mingle libc++ and libstdc++.\n","permalink":"http://poyaoc97.github.io/posts/2022-01-06-abi/","summary":"\u003cp\u003eI ran into linker errors similar to the screenshot below in November 2021.\n\u003cimg alt=\"abi_linker_error\" loading=\"lazy\" src=\"/img/abi_linker_error.png\"\u003e\nAfter some close inspection, I realized this was a result of mixing 2 C++ standard libraries,\nnamely \u003ca href=\"https://libcxx.llvm.org/\"\u003e\u003cstrong\u003elibc++\u003c/strong\u003e\u003c/a\u003e and \u003ca href=\"https://gcc.gnu.org/\"\u003e\u003cstrong\u003elibstdc++\u003c/strong\u003e\u003c/a\u003e.\nI was unkowningly 😱 compiling TVM with libc++\u0026rsquo;s headers while linking it against LLVM\nwhich was compiled with libstdc++\u0026rsquo;s headers and linked against libstdc++.\u003c/p\u003e\n\u003ch2 id=\"a-simple-example\"\u003eA Simple Example\u003c/h2\u003e\n\u003cp\u003eHere is a simple example illustrating what\u0026rsquo;s happening.\u003c/p\u003e\n\u003ch3 id=\"source-files\"\u003eSource Files\u003c/h3\u003e\n\u003cp\u003eGiven 2 C++ souce files:\u003c/p\u003e","title":"ABI Incompatibility of libc++ and libstdc++ and C++ Inline Namespace"},{"content":"\nForeword Tesla intriduced custom light show in its holiday update, and I would like to put Tesla dash cam, music, boombox, and custom light show all in one storage device (assumed to be an SSD in this post). By doing this, I can put my SSD in the glove box, which is locked in the case of a breakin. This can be achieved by partitioning the storage device into 4 partitions, during which I found it was a PITA to do with file system other than APFS. Then, I recalled that there was this system utility on macOS called diskutil. Thankfully, the man page is easy to grasp. Nonetheless, I decided to compile a blog post after some light reading.\nFun fact: diskutil along with many other binaries on my Intel based 16\u0026quot; MacBook Pro running macOS Monterey (version 12.1) is a universal binary or fat binary if you will.\nlipo -info `which diskutil` gives: Architectures in the fat file: /usr/sbin/diskutil are: x86_64 arm64e.\nNot that it matters, lipo can also extract the useful part of a fat binary and discard the irrelevant part.\nPrepare Your Device Plug in your storage device and open a terminal emulator by using the key stroke: ⌘ + Space, and search for instance Terminal.app or iTerm.app, followed by return to open the app.\nCheck The Device Identifier Type the following command into your favorite terminal emulator:\ndiskutil list You shoud see your device listed as the last entry, usually it\u0026rsquo;s disk2:\n/dev/disk2 (external, physical): #: TYPE NAME SIZE IDENTIFIER 0: GUID_partition_scheme *500.1 GB disk2 Partition Thy Drive 🛑 Make sure to make a backup, as this step wipe whatever is on your storage device. ⚠️\nRun:\ndiskutil partitionDisk disk2 GPT ExFat BoomboxP 256Mi ExFat LightShowP 256Mi ExFat MusicP 2Gi ExFat TeslaCamP R disk2 is the identifier obtained from previous step. Be sure to change it accordingly! GPT is the partitioning scheme. MBR works just as well. For each partition, a triplet specifying file system format, volume name, and size is given, following said partitioning scheme ExFat BoomboxP 256Mi denotes that there will be a partition named BoomboxP of file system format ExFat, and of size 256 MiB. Same goes for ExFat LightShowP 256Mi ExFat MusicP 2Gi, I would like my music partition to be 2 gigs, so there is that. The R in ExFat TeslaCamP R stands for remainder. When done, run diskutil list to make sure those partitions are created.\nOptional Step You will likely see this from diskutil list:\n/dev/disk2 (external, physical): #: TYPE NAME SIZE IDENTIFIER 0: GUID_partition_scheme *500.1 GB disk2 1: EFI EFI 209.7 MB disk2s1 2: Microsoft Basic Data BoomboxP 268.4 MB disk2s2 3: Microsoft Basic Data LightShowP 268.4 MB disk2s3 4: Microsoft Basic Data MusicP 2.1 GB disk2s4 5: Microsoft Basic Data TeslaCamP 497.2 GB disk2s5 You could remove the unneeded EFI volume by running:\ndiskutil eraseVolume \u0026#34;Free Space\u0026#34; EFI disk2s1 Last But Not Least For Tesla dash cam to work, a folder named TeslaCam have to be present in root directory. This is a simple one liner:\nmkdir -p /Volumes/TeslaCamP/TeslaCam Voila! The structure of the partition TeslaCamP:\n└── TeslaCam Now you can put at most 5 songs in a folder named Boombox in the partition BoomboxP, for example:\n└── Boombox ├── Bruh.mp3 ├── Cavalry.mp3 ├── Chewbacca.mp3 ├── DarthVader.mp3 └── MoveBitchGetOutTheWay.mp3 Structure of the partition LightShowP as instructed by the Tesla GitHub repo:\n└── LightShow ├── lightshow.fseq └── lightshow.mp3 Structure of the partition MusicP is rather flexible:\n├── Your music folder A │ ├── A.mp3 │ └── B.mp3 └── Your music folder B ├── A.mp3 └── B.mp3 ","permalink":"http://poyaoc97.github.io/posts/2021-12-31-tesla/","summary":"\u003cp\u003e\u003cimg alt=\"tesla_matrix_led.jpg\" loading=\"lazy\" src=\"/img/tesla_matrix_led.jpg\"\u003e\u003c/p\u003e\n\u003ch2 id=\"foreword\"\u003eForeword\u003c/h2\u003e\n\u003cp\u003eTesla intriduced custom light show in its holiday update, and I\nwould like to put \u003cstrong\u003eTesla dash cam\u003c/strong\u003e, \u003cstrong\u003emusic\u003c/strong\u003e, \u003cstrong\u003eboombox\u003c/strong\u003e, and \u003cstrong\u003ecustom light show\u003c/strong\u003e all in one storage device (assumed to be an SSD in this post).\nBy doing this, I can put my SSD in the glove box, which is locked in the case of a breakin.\nThis can be achieved by partitioning the storage device into 4 partitions, during which I found it was a PITA to\ndo with file system other than APFS. Then, I recalled that there was this system utility on macOS\ncalled \u003ccode\u003ediskutil\u003c/code\u003e. Thankfully, the man page is easy to grasp. Nonetheless, I decided to compile a blog post after some light reading.\u003c/p\u003e","title":"Set Up an SSD for Your Tesla on a Mac"},{"content":" My name is 張博堯(zhāng bó yáo) .\nI speak Mandarin Chinese and English.\nThe legal English name on my passport is CHANG, PO-YAO regardless of the pinyin.\nI also go by the name \u0026ldquo;Kevin\u0026rdquo;.\nContact Info GitHub: https://github.com/poyaoc97 LinkedIn: https://www.linkedin.com/in/poyaoc97/ ","permalink":"http://poyaoc97.github.io/about/","summary":"\u003c!-- \u003cbig\u003e\u003cbig\u003e\u003cbig\u003e\n[\u003ccenter\u003e **résumé (PDF)** \u003c/center\u003e](/resume_張博堯_po-yao_chang.pdf)\n\u003c/big\u003e\u003c/big\u003e\u003c/big\u003e --\u003e\n\u003cp\u003eMy name is\n\u003cbig\u003e\u003cbig\u003e\u003cbig\u003e\u003cbig\u003e\n\u003cruby\u003e\n張博堯\u003crp\u003e(\u003c/rp\u003e\u003crt\u003ezhāng bó yáo\u003c/rt\u003e\u003crp\u003e)\u003c/rp\u003e\n\u003c/ruby\u003e\n\u003c/big\u003e\u003c/big\u003e\u003c/big\u003e\u003c/big\u003e.\u003c/p\u003e\n\u003cp\u003eI speak Mandarin Chinese and English.\u003c/p\u003e","title":"About Me"},{"content":"本文紀錄如何用Hugo建立blog,並部署到GitHub Pages。\n安裝Hugo brew install hugo 建立site hugo new site \u0026lt;site-name\u0026gt; 選定theme cd \u0026lt;site-name\u0026gt; git init git submodule add \u0026lt;theme-git-repo-url\u0026gt; themes/\u0026lt;theme-name\u0026gt; 例如:\ncd \u0026lt;site-name\u0026gt; git init git submodule add https://github.com/xianmin/hugo-theme-jane.git themes/jane 拷貝主題的預設config和範例文章 cp themes/jane/exampleSite/config.toml ./ cp -r themes/jane/exampleSite/content ./ 建立文章 文章在content/posts裡面。\n新增文章輸入:\nhugo new posts/my-first-post.md 在本地觀看 hugo server -D\nD flag解釋:\n-D, --buildDrafts include content marked as draft\nBuild static pages hugo -D\nDepoly 在GitHub建立兩個repositories。一個名為\u0026lt;site-name\u0026gt;另一個為\u0026lt;USERNAME\u0026gt;.github.io\n把生成的site(public)加入submodule\ngit submodule add -b main https://github.com/\u0026lt;USERNAME\u0026gt;/\u0026lt;USERNAME\u0026gt;.github.io.git public\n參考 https://dev.to/zaracooper/create-your-developer-portfolio-using-hugo-and-github-pages-35en\nhttps://gohugo.io/getting-started/quick-start/\nhttps://github.com/xianmin/hugo-theme-jane\n","permalink":"http://poyaoc97.github.io/posts/2020-03-07-set-up-a-blog-using-hugo/","summary":"\u003cp\u003e本文紀錄如何用Hugo建立blog,並部署到GitHub Pages。\u003c/p\u003e","title":"Set up a blog using Hugo"}]