ぷるぷるの雑記

低レイヤーがんばるぞいなブログ. 記事のご利用は自己責任で.

リンク集

個人的にたまによく使うサイトのリンク集. 完全に自分用メモ.徐々に更新

MSVC

UCRT 関数リファレンス (アルファベット順)

learn.microsoft.com

コンパイラ コマンド ラインの構文

リンカーへのオプションの渡し方

learn.microsoft.com

コンパイラオプション

learn.microsoft.com

リンカー オプション

learn.microsoft.com

WinAPI(ヘッダ別ページへのポータル)

learn.microsoft.com

Windows コントロール(ヘッダ別)

learn.microsoft.com

winuser.h

BeginPaint(), BringWindowToTop(), CloseWindow(), CreateDialog(), CreateWindow(), DestroyWindow(), DispatchMessage(), DrawText(), FindWindow(), GetAncestor(), GetClientRect(), GetCursor(), GetDCEx(), GetDlgItem(), GetFocus(), GetKeyBoardType(), GetMessage(), GetParent(), GetWindowsDC(), LoadBitmap(), MessageBox(), PeekMessage(), PostMessage(), PostThreadMessage(), RegisterClassEx(), RegisterHotKey(), SetLayeredWindowAttributes(), SendMessage(), SetTimer(), SetWindowPos(), SetWindowsHookEx(), WaitMessage(), MSG構造体 など

learn.microsoft.com

プロセスおよびスレッド(ヘッダ別)

learn.microsoft.com

processthreadsapi.h

CreateProcess(), CreateThread(), GetCurrentProcessId(), GetCurrentProcessorNumberEx(), GetCurrentThread(), GetCurrentThreadId(), GetProcessId() など

learn.microsoft.com

その他(複数のテクノロジで利用)

windef.h

learn.microsoft.com

Direct3D 12 グラフィックス(ヘッダ別)

learn.microsoft.com

d3d12.h

D3D12CreateDevice()など

learn.microsoft.com

d3d12shader.h

D3D12_xxx_DESC構造体など

learn.microsoft.com

Windows全般

ウィンドウスタイル

learn.microsoft.com

拡張ウィンドウスタイル

learn.microsoft.com

GDIリファレンス(ヘッダ別)

learn.microsoft.com

wingdi.h

AlphaBlend(), BeginPath(), BitBlt(), CreateCompatibleDC(), GetObject(), MaskBlt(), RGB(), RoundRect(), SelectObject(), SetPixel(), など.

learn.microsoft.com

GDI+リファレンス(項目別)

learn.microsoft.com

GDI+リファレンス(ヘッダ別)

learn.microsoft.com

gdiplusheaders.h

Bitmapクラスなど

learn.microsoft.com

gdiplusimaging.h

ImageItemDataクラスなど

learn.microsoft.com

gdiplusmatrix.h

3x3のMatrixクラス

learn.microsoft.com

Windows OpenGLリファレンス(項目別)

learn.microsoft.com

Autoconf

Specifying target triplets

www.gnu.org

H8マイコン

12ステップで作る 組込みOS自作入門

kozos.jp

H8プログラミングメモ

bluefish.orz.hm

AKI-H8 CPU Hardware Manualより

nagasm.org

gcc

GCC

linuxjm.osdn.jp

リンカ

ld

linuxjm.osdn.jp

リンカ

リンク作業がどのように進行するかを詳しく説明している.

tanakamura.github.io

ARM Options

gcc.gnu.org

H8/300 Options

gcc.gnu.org

AVR Options

gcc.gnu.org

HTML5 Canvas

CanvasRenderingContext2D

developer.mozilla.org

WebGLRenderingContext

developer.mozilla.org

ImageData

developer.mozilla.org

HTMLCanvasElement: toDataURL()

developer.mozilla.org

size_t型とは

メモリ系のライブラリやコンテナを使っているとよく見るsize_t型についてまとめました.

検証環境

項目 説明
OS Windows11
MinGW-W64 gcc version 8.1.0

size_t型は処理系依存

そもそもですがsize_t型とstd::size_t型は処理系依存だそうです. 符号なし整数型であることさえ守られていればサイズは処理系が好きに決めて良いそうです.

したがって以下の事柄は検証環境においてはこうだったよという記録になります.

size_t型のtypedef宣言

Cの場合

crtdefs.hというファイルに以下のようなtypedef 宣言があります

// crtdefs.h

#ifndef _SIZE_T_DEFINED
#define _SIZE_T_DEFINED
#undef size_t
#ifdef _WIN64
__MINGW_EXTENSION typedef unsigned __int64 size_t;
#else
typedef unsigned int size_t;
#endif /* _WIN64 */
#endif /* _SIZE_T_DEFINED */

以上からsize_t型は64bit環境ではunsigned __int64 型と同等の型になります.

C++の場合

c++config.hというファイルに以下のようなtypedef宣言があります.

// c++config.h

namespace std
{
  typedef __SIZE_TYPE__    size_t;
  typedef __PTRDIFF_TYPE__ ptrdiff_t;

#if __cplusplus >= 201103L
  typedef decltype(nullptr) nullptr_t;
#endif
}

また、__SIZE_TYPE__はどうやら_mingw_mac.hで以下のように定義されています(macroのmac?).

// _mingw_mac.h

#ifndef __SIZE_TYPE__
#  ifdef _WIN64
#    define __SIZE_TYPE__ long long unsigned int
#  else
#    define __SIZE_TYPE__ long unsigned int
#  endif
#endif

以上からstd::size_t型は64bit環境ではlong long unsigned int 型と同等の型になります.

size_t型の意味

メモリ系のライブラリのインターフェースは確保したいメモリの大きさを引数で受け取ることが殆どです. その際に単なる整数型ではなく データの個数やバイト数 を表すために使われることを明確にするためにtypedef宣言されたのがsize_t型です. 逆に言うと、size_t型で宣言しただけではその変数が データの個数を表すのか、それともバイト数を表しているのかは分かりません. 実際MSのドキュメントを読んでみるとmallocとcallocの引数はいずれもsize_t型として宣言されています.

mallocのプロトタイプ宣言
callocのプロトタイプ宣言

learn.microsoft.com

learn.microsoft.com

データの個数を表すのか、それともバイト数を表しているのかは分からない ことを踏まえると、せめて変数名を工夫してあげるのが良いかと思います. 個数を表す変数はnum、バイト数を表す変数はbytesにするなどが有効と考えられます.

ARMマイコンでNewlibを使う

STM32Nucleo上のCortex-M0でNewlib(組み込み用標準Cライブラリ)を利用してprintfしました.

箇条書きでまとめると以下のことをします.

  • LinuxでNewlibをビルド
  • WindowsのSTM32CubeIDEでSTM32 Projectを作成する
  • コンパイラ、バイナリツール、libgccはIDEに付属のツールチェーンのものを利用する.
  • ドライバ、リンカスクリプトIDEによって自動生成されたものを利用する.
  • スタートアップはIDEによって自動生成されたものとツールチェーン付属のものを両方利用する.
  • 標準ライブラリは自前でビルドしたNewlibを使用する.
  • Newlibに必要なシステムコールIDEによって自動生成されたものを利用する. ただし、writeシステムコールは自分で実装してオーバーライドする.

実行環境

項目 説明
OS (Newlibのビルド用) Ubuntu 20.04
OS (STM開発用) Windows11 22H2
STM32CubeIDE 1.10.1
STM32CubeProgrammer 2.11.0

Newlibのビルド

linuxでビルドします. 下記を参考にしてください.

prupru-prune.hatenablog.com

Newlibのヘッダフォルダとビルド後の生成物をWindowsにコピーしておきます. 今回はWindowsのドキュメントフォルダにコピーしました.

以降はWindowsで操作をします.

プロジェクトの作成

STM32CubeIDEを起動しFile->New->STM32 Project を選択してSTM32 Project を作成します. 私が持っているSTM32Nucleoボードに合わせ赤い枠で囲まれた項目を選びました. プロジェクト名は好きなものを設定します. その他の設定はいじらずにFinishで終了します.

適切なNucleoボードを選択する

どの項目を選択したかはプロジェクトのプロパティからも確認することが出来ます. グレーアウトしているため後から変更はできないようです. ここでRuntime libraryがStandard Cになっていることを必ず確認してください. Reduced C が選択されているとリンク時にツールチェーン付属のlibc_nano.aがリンクされてしまいます.

ターゲットは後からも確認可能. Standard C を選択していることを確認

C/C++プロジェクトではなくSTM32 Projectにすることで以下の作業を省略できます.

  1. Newlibを使うためのシステムコールの実装(IDEによって自動生成されるため)
  2. スタートアップの実装(IDEによって自動生成されるため)
  3. メモリマップに適したリンカスクリプトの実装(IDEによって自動生成されるため)
  4. アセンブラによるペリフェラルの設定(GUIのPinout&Configurationを利用できるため)
  5. ドライバの実装(HALを利用できるため)

ソースコードの編集・ペリフェラルの設定

以下の記事の内容を参考にUARTでprintfするプログラムを書きます.

yukblog.net

主な変更点は、標準出力のクリアの追加とwriteシステムコールをHALを用いたものにすることです. Core/Src/main.cのすべての変更点は以下になります

~略~
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stdio.h"      // ヘッダの追加
#include "string.h"     // ヘッダの追加
~略~
int main(){
  /* USER CODE BEGIN 1 */
    char buff[256];
    setbuf(stdout, NULL);
    strncpy(buff, "Hello World", 12);
  /* USER CODE END 1 */
~略~
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
      printf("%s", buff);
      HAL_Delay(1000);
    /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */
~略~
/* USER CODE BEGIN 4 */
int _write(int file, char *ptr, int len)
{
    HAL_UART_Transmit(&huart2,(uint8_t *)ptr, len, 10);
    return len;
}
/* USER CODE END 4 */

Newlibが正しくビルド、リンクされていればstrncpyやprintfが正しく利用できるはずです.

また、デフォルトではUARTが無効になっているので有効にするためにペリフェラルの設定をします.

ペリフェラルの設定のために.iocファイルを開く

デフォルトではSTM32CubeIDEに付属するツールチェーンの標準Cライブラリをリンクしてしまうので、Newlibをリンクするようにプロジェクトのプロパティを変更します. プロジェクト名と同名の.iocファイルを開くとペリフェラル設定のためのGUIが表示されます. 参考記事に倣ってUSART2を有効にしてボーレートを115200 Bits/s、データ長8bit、パリティなし、ストップビット1Bit にします. 設定後は忘れずに歯車マークをクリックしてソースコードを自動生成します.

ペリフェラル(USART)の設定1

ペリフェラル(USART)の設定2

プロジェクトプロパティの変更

コンパイル時にサーチするフォルダの追加

Newlibのインクルードフォルダへのパスをサーチフォルダに追加します. 適宜自分の環境に適したパスを指定してください.

インクルードフォルダの追加

リンクの一般設定の変更

ビルドの詳細を表示するためにVerboseオプションを有効にします.

また、ツールチェーン付属の標準Cライブラリをリンクされないように設定します.

リンクの一般設定の変更

リンクするライブラリとサーチフォルダの追加

libc.aと(必要であれば)libm.aをリンクするためにライブラリとサーチディレクトリの追加をします. 適宜自分の環境に適したパスを指定してください.

リンクするライブラリとサーチディレクトリの追加

プロジェクトのビルドとフラッシュへの書き込み

プロジェクトフォルダを右クリックしてBuildをクリックするとDebugフォルダにelfファイルが生成されます. フラッシュへの書き込みにはSTM32CubeProgrammerを利用しますが、stripをする必要はないようです. 上記の設定でビルドをした時の全コマンドラインは次のようになりました.

arm-none-eabi-gcc -o "NewLib.elf" @"objects.list"  -lc -lm -mcpu=cortex-m0 -T"C:\Users\XXX\STM32CubeIDE\workspace_1.10.1\NewLib\STM32F072RBTX_FLASH.ld" --specs=nosys.specs -Wl,-Map="NewLib.map" -Wl,--gc-sections -Wl,--verbose -nodefaultlibs -static -L"C:\Users\XXX\Documents\build"  -mfloat-abi=soft -mthumb


-Wl,--verboseオプションのおかげでビルドの詳細が表示されます. 次の記述からツールチェーン付属ではなく自作のNewlibのバイナリをリンクできていることが確認できます. ただし、ツールチェーン付属のライブラリを全く使っていないわけではなく、libgcc.aはツールチェーン付属のものをリンクしていることも分かります. ただしlibgcc.aはC標準ライブラリとはまた別枠ですね.

attempt to open C:\Users\XXX\Documents\build\libc.a succeeded
C:\Users\XXX\Documents\build\libc.a
(C:\Users\XXX\Documents\build\libc.a)libc_a-printf.o
(C:\Users\XXX\Documents\build\libc.a)libc_a-puts.o
(C:\Users\XXX\Documents\build\libc.a)libc_a-findfp.o
~略~
attempt to open c:/st/stm32cubeide_1.10.1/stm32cubeide/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.10.3-2021.10.win32_1.0.0.202111181127/tools/bin/../lib/gcc/arm-none-eabi/10.3.1/thumb/v6-m/nofp\libgcc.a succeeded
c:/st/stm32cubeide_1.10.1/stm32cubeide/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.10.3-2021.10.win32_1.0.0.202111181127/tools/bin/../lib/gcc/arm-none-eabi/10.3.1/thumb/v6-m/nofp\libgcc.a
(c:/st/stm32cubeide_1.10.1/stm32cubeide/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.10.3-2021.10.win32_1.0.0.202111181127/tools/bin/../lib/gcc/arm-none-eabi/10.3.1/thumb/v6-m/nofp\libgcc.a)_udivsi3.o
(c:/st/stm32cubeide_1.10.1/stm32cubeide/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.10.3-2021.10.win32_1.0.0.202111181127/tools/bin/../lib/gcc/arm-none-eabi/10.3.1/thumb/v6-m/nofp\libgcc.a)_divsi3.o
(c:/st/stm32cubeide_1.10.1/stm32cubeide/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.10.3-2021.10.win32_1.0.0.202111181127/tools/bin/../lib/gcc/arm-none-eabi/10.3.1/thumb/v6-m/nofp\libgcc.a)_dvmd_tls.o


elfファイル生成後はSTM32CubeProgrammerを起動し一番上のタブのMemory & File editingでST-LINKのConnectを選択しましょう. ボードが見つかった場合右上のステータスがConnectedになります.

ボードと接続する

ボードと接続が出来たら2番目のタブのErasing & Programmingを開きます. File pathにビルドしてできたelfファイルを指定し、Start Programmingを押せばフラッシュへの書き込みが始まります. File download completeと書かれたモーダルが表示されれば書き込み完了です.

elfファイルを選択してボードへプログラミング

TeraTermで確認

ボードをリセット後TeraTermでprintf出来てるかを確認しましょう.

TeraTermでprintf出来てるか確認

無事printfすることが出来ました.

ARM用のNewlibをビルドする方法

Armマイコン(Cortex-M0)でNewlibを使いたいので自分でビルドしてみました.

たいていツールチェーンにバイナリとして付属してるけどね.

実行環境

項目 説明
OS Ubuntu 20.04 LTS
コンパイラ・バイナリツール (Toolchain) Arm GNU Toolchain 12.3Rel1
(x86_64 Linux hosted cross toolchains AArch32 bare-metal target (arm-none-eabi))

ツールチェーンの取得

Linuxで動くArch32用のクロスコンパイラを入手します. Arm公式から圧縮ファイルを入手し解凍しましょう.

developer.arm.com


ディレクトリを短い名前にリネームし、ユーザーフォルダ直下に置きます.

#クロスパイルツールチェーンをリネームしてユーザーフォルダ直下に配置

 ~/ ---  /arm-gcc   


このツールチェーン内のコマンドが置かれたディレクトリへのパスを通すため.bashrcを編集します. 必要であれば.bashrcを読み込みなおします.

# ~/.bashrc

#追記
export PATH=$PATH:path_to_arm_gnu_toolchain/bin
$ source ~/.bashrc

arm-none-eabi用のビルドの流れ

まずはNewlibのソースコードを入手します. 面倒なのでミラーからサクッと手に入れました. 公式から入手したい場合ははFTPかhttpを使えと書いてありました.

# ミラーからNewlibのソースを入手
$ git clone https://github.com/bminor/newlib.git

公式 https://sourceware.org/newlib/download.html

ミラー https://github.com/bminor/newlib.git


解凍しユーザー直下に置きましょう. ついでにout-of-sourceビルド用にbuildディレクトリも作っておきましょう.

# out-of-sourceビルドのための配置

 ~/ ---  /arm-gcc   
      |
      |-- /new-lib
      |
      |__ /build


buildフォルダに入りconfigureスクリプトを叩きます. 以下のオプションを指定すればとりあえずビルド出来ました.

#buildディレクトリに入る
$ cd ~/build

#configure
$ ../newlib/newlib/configure --target=arm-none-eabi --enable-newlib-io-long-long --enable-newlib-register-fini --enable-newlib-retargetable-locking --disable-newlib-supplied-syscalls CC=arm-none-eabi-gcc --host=arm-none-eabi --build=i686-pc-linux-gnu

#make
$ make

ビルドが無事終了するとbuildディレクトリ直下に libc.a、libg.a、libm.aが生成されます. また、ヘッダファイルは newlib/libc/include に入っています. math.hもこのフォルダに入っています.

また、バイナリのサイズはそれぞれ以下でした.

アーカイブ サイズ
libc.a 4,680kB
libg.a 4.680kB
libm.a 1,718kB

arm-none-eabi用のビルドは出来ましたが、 目的のCortex-M0では動きませんでした (実行可能ファイルを作ることは出来たが、正常に動作しませんでした).

動かなかった原因

正常に動くNewlibと何が違うのかを探るため、STM32CubeIDEに付属するツールチェーンに同梱されているNewlibのバイナリと、自分でビルドしたNewlibのバイナリをreadelfして差分を取りました. なお、ツールチェーンには各アーキテクチャごとにバイナリが分かれているため、一度-Wl,--verboseオプションをつけてビルドすることでCortex-M0用にリンクされるバイナリを同定しました. WinMergeで両者のreadelfの結果を比較すると、以下の決定的な違いが見つかりました.

正常なバイナリ(左)と正常でないバイナリ(右). アーキテクチャが違う

つまり、arm-none-eabiの中でもアーキテクチャが分かれているので、Cortex-M0用のアーキテクチャを明示的に指定しないといけないということですね. 前述のNewlibビルドにおいてarm-none-eabi以外にそれらしきものを指定した覚えはないので、デフォルトではARM v6Kというアーキテクチャ用にビルドされるのでしょう.

Cortex-M0用にビルド

ここが躓きポイントだったのですが、アーキテクチャの指定は configure時ではなく、make時に行います . これはアーキテクチャの指定の方法が汎用的なものではなくマシン依存オプションというcpuごとに異なるものだからだと推測されます. 前述のconfigureに加え、make時には以下のコマンドを打つことでCortex-M0用のバイナリをビルドすることが出来ました.

$ make CFLAGS="-mcpu=cortex-m0"

実際にCortex-M0で試した記事がこちらになります.

prupru-prune.hatenablog.com

まとめ

とりあえず以下のおまじないでCortex-M0用に動くNewlibがビルド出来ました.

# configure
$ configure --target=arm-none-eabi --enable-newlib-io-long-long --enable-newlib-register-fini --enable-newlib-retargetable-locking --disable-newlib-supplied-syscalls CC=arm-none-eabi-gcc --host=arm-none-eabi --build=i686-pc-linux-gnu

$ make CFLAGS="-mcpu=cortex-m0"

参考

www.asahi-net.or.jp

stackoverflow.com

msyksphinz.hatenablog.com

uchan.hateblo.jp

releases.llvm.org

www.gnu.org

warning: using cross tools not prefixed with host triplet の意味

Newlib(組み込み用標準Cライブラリ)をarm-none-eabi用にUbuntu上でビルドしていたところ、「warning: using cross tools not prefixed with host triplet」に遭遇しました.

実行環境

項目 説明
OS Ubuntu 20.04 LTS
コンパイラ x86_64 Linux hosted cross toolchains AArch32 bare-metal target (arm-none-eabi)

warningの内容

これはconfigureスクリプトを実行する際に--hostオプションで指定したprefix(接頭字)とは異なるprefixをもつクロスコンパイラツールを利用しようとしたときに出てくる警告のようです.

この警告に関連するオプションは以下の3点です.

  • --build...ビルドを行うマシンの情報. デフォルトではconfig.guessの結果自動的に設定される. 自分の環境ではi686-pc-linux-gnu.
  • --host...ビルドしたバイナリを実際に動かすマシンの情報. デフォルトではbuildと同じ値(セルフコンパイル)となる. この値を--buildの値と別のものにするとクロスコンパイルとなる. 自分の環境ではarm-none-eabi.
  • --target...コンパイラに出力させるシステムを指定. デフォルトではhostと同じ値になる. 滅多に指定する必要はない(rarely needed)とのこと. 自分の環境ではarm-none-eabi.

tripletとはa-b-cという形で指定されることを言っているようです. target tripletはcpu-vendor-osという形をとります. 今回の場合はarm-none-eabiなので、cpu=arm、 vendor=none、 os=eabiのことですね.

さて、クロスコンパイルをするのであればたいていの場合hostとtargetは同じ値になるはずです. そのうえ、使用するクロスコンパイルツールはこのhostの値をprefixに持つはずです. 具体的には、 --host=arm-none-eabiを指定した場合、arm-none-eabi-as やarm-none-eabi-stripなどを使うはずです . そこが食い違っていますが大丈夫ですか?という警告を出してくれているようです.

警告とはいえ、このメッセージが出る場合はほぼ間違いなく期待通りのビルドが出来ないと思われます(ビルドできたとしてもセルフコンパイルしただったとかになりそう).

warningの解決方法

configureスクリプト実行時にホストに適したクロスコンパイルツールを設定してやれば解決します. configureスクリプトの中身を見ると分かりますが、たいていは次のような変数の記述があります.

#configureスクリプトではたいていこれらの変数が定義されている

CC
AS
AR
OBJDUMP
OBJCOPY
STRIP
.
.
.

これら変数に使用したいツールのコマンド名(prefixだけじゃダメ!)を指定すればそのコマンドをビルドに利用するようになります.ただし、configureスクリプト パスが通っているディレクトリを巡回しコマンドを見つける ため、利用したいコンパイルツールのbinフォルダにパスを通しておきましょう.

結局のところ、.bashrcでArm GNU Toolchainへのパスを追加し、以下のようにconfigureするとビルドに成功しました.

#~/.bashrc

#追記
export PATH=$PATH:path_to_arm_gnu_toolchain/bin
#configureスクリプトの実行

buildDir$ ../newlib/newlib/configure --target=arm-none-eabi --enable-newlib-io-long-long --enable-newlib-register-fini --enable-newlib-retargetable-locking --disable-newlib-supplied-syscalls CC=arm-none-eabi-gcc --host=arm-none-eabi --build=i686-pc-linux-gnu

参考

www.gnu.org

developer.arm.com

ツールチェーンとコンパイラの違い

ロスコンパイラを探しているとツールチェーンというものをよく見かける. 何が違うかというと、ツールチェーンはコンパイラに加え、バイナリツール、デバッガ、ライブラリを含んでいる.

広義のコンパイラ(MinGWなど)はライブラリなども含んでいるので、正確に言えばツールチェーンというべきなのかもしれない.

なぜわざわざツールチェーンにするのかというと、おそらくライセンスの問題があるのだと思う.コンパイラGNUプロジェクトのものを使うが、GPLよりも緩いライセンスのライブラリ(newlibなど)を同梱しているツールチェーンが多い.

参考

ja.wikipedia.org

www.gnu.org

sourceware.org