@+id/proelbtn

メモ書き・備忘録

H8マイコンでHello worldしてみた。

備忘録程度なので、適当に。

1. クロスコンパイル環境の構築

実際に走らせたスクリプトを示します。細かいオプションが分からない部分も多いのですが、できる限りデフォルトでコンパイルさせていると思います。

INSTALL_PATH=$HOME/Projects/baremetal/h8

wget https://ftp.gnu.org/gnu/binutils/binutils-2.29.tar.xz
tar fvx binutils-2.29.tar.xz
cd binutils-2.29
./configure --prefix=$INSTALL_PATH --target=h8300-elf --disable-nls
make -j32
make install
cd ..

wget https://gmplib.org/download/gmp/gmp-6.1.2.tar.xz
tar fvx gmp-6.1.2.tar.xz
cd gmp-6.1.2.tar.xz
./configure --prefix=$INSTALL_PATH
make -j32
make install
cd ..

wget http://www.mpfr.org/mpfr-current/mpfr-3.1.6.tar.xz
tar fvx mpfr-3.1.6.tar.xz
cd mpfr-3.1.6.tar.xz
./configure --prefix=$INSTALL_PATH --with-gmp=$INSTALL_PATH
make -j32
make install
cd ..

wget ftp://ftp.gnu.org/gnu/mpc/mpc-1.0.2.tar.gz
tar fvx mpc-1.0.2.tar.gz
cd mpc-1.0.2.tar.gz
./configure --prefix=$INSTALL_PATH --with-gmp=$INSTALL_PATH --with-mpfr=$INSTALL_PATH
make -j32
make install
cd ..

wget http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-7.2.0/gcc-7.2.0.tar.xz
tar fvx gcc-7.2.0.tar.xz
cd gcc-7.2.0.tar.xz
mkdir BUILD
cd BUILD
../configure --prefix=$INSTALL_PATH --with-gmp=$INSTALL_PATH --with-mpfr=$INSTALL_PATH --with-mpc=$INSTALL_PATH --target=h8300-elf --disable-nls --disable-threads --disable-shared --disable-libssp --enable-languages=c
make -j32
make install

2. 各種ファイルを用意する。

「12ステップで作る 組み込みOS自作入門」をそっくりそのままファイルを用意しています。

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

これのHello Worldソースコードを動かしています。

IBM Qを体験してみた。

IBM Qとは?

IBMは2017年3月、このクラウドサービスを発展させた量子コンピュータのプロジェクト「IBM Q」を発表した。まず量子コンピュータと従来型コンピュータを連携できるAPIアプリケーション・プログラミング・インタフェース)やSDK(ソフトウエア開発キット)を提供し、Webサービススマートフォンアプリを通じて量子コンピュータを自由に使えるようにした。さらにIBMは、50の量子ビットを備える新たなマシンを数年のうちに開発することを表明した。

量子コンピュータクラウドで触れることができる面白いシステムです。個人的に量子コンピュータが面白そうと思っていたので、試してみたいと思います。

itpro.nikkeibp.co.jp

利用するには?

  1. IBM Qのサイト右上の「Sign in」からログインする。

www.research.ibm.com

f:id:proelbtn:20170911235617p:plain

  1. 右上の「Composer」にアクセスする。

f:id:proelbtn:20170911235954p:plain

  1. 適当なExperiment Nameをつける。

f:id:proelbtn:20170912000054p:plain

「ibmqx2」の方は実際にIBM保有している(T. J. Watson Research Center内)5量子ビットを持つ量子コンピュータで計算することができますが、世界中の人が利用するので基本的には「Custom Topology」の方を選択したほうが良いです。

  1. 量子レジスタと、古典レジスタ?(Classical Register)の設定をする。

f:id:proelbtn:20170912001806p:plain

今回は、1bitの加算を試してみるので、Quantum Registersにはa, b, cをそれぞれ1量子ビット, 1量子ビット, 2量子ビット、Classical Registersにはcを4ビットで設定します。

すると、こんな画面になると思います。

f:id:proelbtn:20170912001824p:plain

これはComposerと呼ばれる画面で、横にあるidやX, Y, Zなどの量子ゲートをビットに作用させて、加算を表現します。

  1. 量子ゲートを配置する。

そして、こんな感じで配置します。ccXはGATESの横にあるAdvancedを押せば出てきます。

f:id:proelbtn:20170912003559p:plain

詳しい量子ゲートの説明についてはいつかするとして、Simulateの横にあるボタンから、Shotsの値を適当な値にしてください。(しなくてもよい。その回数だけ試行してその平均を結果として表示します)

f:id:proelbtn:20170912004022p:plain

  1. Simulateを押す。

すると以下のような結果が得られます。

f:id:proelbtn:20170912004253p:plain

Ansibleを使ってみる。

前々から知ってはいたものの、実際に必要としていなかったので使ってこなかったツールとして、構成管理ツールや監視ツールなどがありますが、その一つである「Ansible」について軽く使ってみようという感じです。

Ansibleとは?

www.ansible.com

いわゆる構成管理ツールと呼ばれる物です。設定ファイルに基づいて、インフラ構築を自動化してくれるツールです。Ansibleはエージェントレス(構成対象に対して特別なソフトウェアを導入しなくてよい)でシンプルなので人気があるそうです。

Red HatがAnsible Towerというエンタープライズ版のAnsibleも提供しています。

Infrastructure as codeを進める上で非常に大事なツールですね。

Infrastructure as codeとは?

インフラの構成や設定などをコードとして管理すること。そうすることでgitなどでも管理しやすくなるだけでなく、自動化や構成の可視化なども果たすことができる。

そうです。。。

Infrastructure as Code - Wikipedia

検証環境の構築

lxcでポンポン立てるのが個人的に好きなので、lxcを利用していきます。ですが、好きな環境で試してください。

ubuntu/zestyというイメージがある前提です。

(host)$ for n in controller target; do lxc launch ubuntu/zesty $n; done

まぁ、これだけなんだけどさ。。。

手元ではこんな感じになっています。

NAME IPV4
controller 10.138.176.112(controller.lxd)
target 10.138.176.199(target.lxd)

Ansibleの導入

(controller)# apt-get install software-properties-common
(controller)# apt-add-repository ppa:ansible/ansible
(controller)# apt-get update
(controller)# apt-get install ansible

これで以上です。ここから、Ansibleの動作に必要な設定をしていきます。以下のような手順で行っていきます。

  1. 対象サーバの指定
  2. ssh鍵の設定
  3. (Optional)対象サーバへのpythonの導入

対象サーバの指定

(controller)/root/ansible/hostsに以下のような設定を書き込みます。target.lxdの代わりに、10.138.176.199(targetのIPアドレス)を用いても構いません。

target.lxd

今回は対象サーバが一台だけなので、このような構成になっていますが、実際にはグルーピングをしたり、範囲の表現が使えたりと結構柔軟に指定することが出来るようです。

# This is the default ansible 'hosts' file.
#
# It should live in /etc/ansible/hosts
#
#   - Comments begin with the '#' character
#   - Blank lines are ignored
#   - Groups of hosts are delimited by [header] elements
#   - You can enter hostnames or ip addresses
#   - A hostname/ip can be a member of multiple groups

# Ex 1: Ungrouped hosts, specify before any group headers.

## green.example.com
## blue.example.com
## 192.168.100.1
## 192.168.100.10

# Ex 2: A collection of hosts belonging to the 'webservers' group

## [webservers]
## alpha.example.org
## beta.example.org
## 192.168.1.100
## 192.168.1.110

# If you have multiple hosts following a pattern you can specify
# them like this:

## www[001:006].example.com

# Ex 3: A collection of database servers in the 'dbservers' group

## [dbservers]
## 
## db01.intranet.mydomain.net
## db02.intranet.mydomain.net
## 10.25.1.56
## 10.25.1.57

# Here's another example of host ranges, this time there are no
# leading 0s:

## db-[99:101]-node.example.com

ANSIBLE_INVENTORY環境変数が指定されている場合、$ANSIBLE_INVENTORYをhostsファイルとして認識してくれます。

ssh鍵の設定

(controller)# ssh-keygen

(host)$ lxc file pull controller/root/.ssh/id_rsa.pub ./
(host)$ lxc file push ./id_rsa.pub target/root/.ssh/authorized_keys

(target)# chmod 600 ~/.ssh/authorized_keys
(target)# chown root:root ~/.ssh/authorized_keys

要は、controller->targetに対してパス無しでsshができるようにしておけということです。

これで無事にsshできるようになります。

(Optional)対象サーバへのpythonの導入

書いてある通り、lxdなどの特殊なイメージなどでは、pythonが入っていないことがあります。そのため、それだけは手動で入れてあげてください。(普通のディストリビューションなら、python2.7.13が入っているはず)

疎通確認

まず、はじめに2台のサーバがちゃんと疎通できるかを確認します。

(controller)# ansible all -i /root/ansible -m ping
target.lxd | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}

OKですね。

参考

www.slideshare.net

Icons8の紹介とか使い方とか。

Icons8とは?

(私が見た段階では、)56000点以上のフラットデザインのアイコンが配布されているサイトです。PNGで配布されています。Recolor機能もついていて、サイトや資料に合わせるのが簡単です。個人的には、一人のデザイナが一貫してデザインしているというところがとても特殊だと思っています。デザインの一貫性を出すためだそうです。

icons8.com

使うためには?

正直、ライセンス形態が面倒くさくて、他人のアセットは使いたくないというのが本音なのです。たくさん利用すると、ライセンスがごっちゃごちゃになってわからなくなるんですよね。

その点、Icons8は単純です。

Icons8へのリンクを貼るだけなのです!

https://intercom.help/icons8/is-it-free/can-i-use-icons8-for-freeintercom.help

まとめ

簡単に扱えるアイコンサイトはとても有用です、という紹介です。

(正確にはライセンス形態とかをまとめた備忘録です。)

アセンブリ勉強会(3) ~syscallからhello world!~

Linuxのsyscallを直接呼び出してhello world!してみるコードを書いてみます。

どのレジスタにどの値を入れればいいのかは、こちらのサイトを参考にすると分かると思います。

Linux System Call Table for x86 64 · Ryan A. Chapman

プログラムの内容

     .section    .rodata
content:
    .string "Hello world!\n"
    .text
    .globl  main
    .type   main, @function
main:
    pushq   %rbp
    movq    %rsp, %rbp
  
    movq    $13, %rdx
    leaq    content(%rip), %rsi
    movq    $1, %rdi
    movq    $1, %rax
    syscall

    movl    $0, %eax
    popq    %rbp
    ret

writeシステムコールについて見てみると、以下のようにレジスタを設定する必要があります。

register contents
%rax システムコール番号(writeシステムコールの場合は1)
%rdi unsigned int fd
%rbp const char *buf
%rdx size_t count

stdoutのfdは1なので、rdiレジスタには1を入れます。

rsiレジスタには文字列の先頭アドレスを入れます。(ripレジスタからオフセットのcontentを足した値をrsiに入れます)

rdxレジスタには、文字列の長さである13を入れます。

アセンブリ勉強会(2) ~hello world!~

前回の記事はこちら。

アセンブリ勉強会(1) ~何もしないプログラム~ - @+id/proelbtn

前回ので、関数を宣言するあたりの部分は何となくわかった気がするので、次はwrite関数を呼んでみる。

#include <unistd.h>

int main(void) {
  write(1, "Hello world!\n", 13);  
}
 .file "hello.c"
    .section  .rodata
.LC0:
    .string   "Hello world!\n"
    .text
    .globl    main
    .type main, @function
main:
.LFB0:
    .cfi_startproc
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq %rsp, %rbp
    .cfi_def_cfa_register 6
    movl $13, %edx
    leaq .LC0(%rip), %rsi
    movl $1, %edi
    call write@PLT
    movl $0, %eax
    popq %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size main, .-main
    .ident    "GCC: (Ubuntu 6.3.0-12ubuntu2) 6.3.0 20170406"
    .section  .note.GNU-stack,"",@progbits

細かい部分はおいておいて、前回との差分はここらへんです。

 .section    .rodata
.LC0:
    .string "Hello world!\n"
 movl    $13, %edx
    leaq    .LC0(%rip), %rsi
    movl    $1, %edi
    call    write@PLT

.section .rodata

.rodataセクションが始まることを示している。

.LC0:

略。

.string “Hello world!\n”

文字列を宣言するための物。

Using as: String

他にも、.byte, .short, .int, .long, .float, .double, .val, ちょっと特殊なものとして.zeroがある。(1, 2, 4, 8バイト整数, 4, 8バイト浮動小数点数, アドレス)

movl $13, %edx

第3引数である13がedxレジスタにコピーされている。

引数位置によって使われるレジスタが決まっている。詳しくはABIとかで調べれば出てくる。

Linux系では以下の方法で使われる。

呼出規約 - Wikipedia

整数・ポインタ引数 - RDI, RSI, RDX, RCX, R8, R9 浮動小数点引数 - XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7 戻り値 - RAX システムコールでは RCX の代わりに R10 を使用 レジスタだけでは引数の数が不足する場合はスタックを利用。

確かに、第3引数にrdxが使われてますね(edxはrdxの下位4byte分)。

leaq .LC0(%rip), %rsi

leaqは第1引数の実効アドレスを第2引数にコピーする、いわゆるアドレスのmov命令みたいな感じですかね。

.LC0(%rip)は、.LC0のアドレスにripレジスタの値を足した物という意味です。ripレジスタには次に実行される命令のアドレスが入っているそうです。

となると.LC0は相対アドレス?と気になるのでgdbで見てみます。

$ gcc hello.s
$ gdb ./a.out
(gdb) b main
Breakpoint 1 at 0x5555555546a4

(gdb) r
Starting program: /home/ryoga/Projects/x86_64_binary/hello_world/a.out 

Breakpoint 1, 0x00005555555546a4 in main ()
(gdb) disas
Dump of assembler code for function main:
   0x00005555555546a0 <+0>:   push   %rbp
   0x00005555555546a1 <+1>:   mov    %rsp,%rbp
=> 0x00005555555546a4 <+4>:    mov    $0xd,%edx
   0x00005555555546a9 <+9>:   lea    0xa4(%rip),%rsi        # 0x555555554754
   0x00005555555546b0 <+16>:  mov    $0x1,%edi
   0x00005555555546b5 <+21>:  callq  0x555555554560
   0x00005555555546ba <+26>:  mov    $0x0,%eax
   0x00005555555546bf <+31>:  pop    %rbp
   0x00005555555546c0 <+32>:  retq   
End of assembler dump.

(gdb) x 0x555555554754
0x555555554754: 0x6c6c6548

$ ipython

In [1]: [chr(byte) for byte in [0x6c, 0x6c, 0x65, 0x48]]
Out[1]: ['l', 'l', 'e', 'H']

.LC0はオフセットになっていますね。ここから察するに、.text領域よりアドレス末尾側に.rodataが入っているということも分かります。

では、デフォルトで用いられているリンカスクリプトを参照してみたいと思います。

$ ld --verbose
GNU ld (GNU Binutils for Ubuntu) 2.28
  Supported emulations:
   elf_x86_64
   elf32_x86_64
   elf_i386
   elf_iamcu
   i386linux
   elf_l1om
   elf_k1om
   i386pep
   i386pe
using internal linker script:
==================================================
/* Script for -z combreloc: combine and sort reloc sections */
/* Copyright (C) 2014-2017 Free Software Foundation, Inc.
   Copying and distribution of this script, with or without modification,
   are permitted in any medium without royalty provided the copyright
   notice and this notice are preserved.  */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
          "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib");
SECTIONS
{
  /* Read-only sections, merged into text segment: */
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
  .interp         : { *(.interp) }
  .note.gnu.build-id : { *(.note.gnu.build-id) }
  .hash           : { *(.hash) }
  .gnu.hash       : { *(.gnu.hash) }
  .dynsym         : { *(.dynsym) }
  .dynstr         : { *(.dynstr) }
  .gnu.version    : { *(.gnu.version) }
  .gnu.version_d  : { *(.gnu.version_d) }
  .gnu.version_r  : { *(.gnu.version_r) }
  .rela.dyn       :
    {
      *(.rela.init)
      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
      *(.rela.fini)
      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
      *(.rela.ctors)
      *(.rela.dtors)
      *(.rela.got)
      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
      *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*)
      *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*)
      *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*)
      *(.rela.ifunc)
    }
  .rela.plt       :
    {
      *(.rela.plt)
      PROVIDE_HIDDEN (__rela_iplt_start = .);
      *(.rela.iplt)
      PROVIDE_HIDDEN (__rela_iplt_end = .);
    }
  .init           :
  {
    KEEP (*(SORT_NONE(.init)))
  }
  .plt            : { *(.plt) *(.iplt) }
.plt.got        : { *(.plt.got) }
.plt.bnd        : { *(.plt.bnd) }
  .text           :
  {
    *(.text.unlikely .text.*_unlikely .text.unlikely.*)
    *(.text.exit .text.exit.*)
    *(.text.startup .text.startup.*)
    *(.text.hot .text.hot.*)
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf32.em.  */
    *(.gnu.warning)
  }
  .fini           :
  {
    KEEP (*(SORT_NONE(.fini)))
  }
  PROVIDE (__etext = .);
  PROVIDE (_etext = .);
  PROVIDE (etext = .);
  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
  .rodata1        : { *(.rodata1) }
  .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table
  .gcc_except_table.*) }
  .gnu_extab   : ONLY_IF_RO { *(.gnu_extab*) }
  /* These sections are generated by the Sun/Oracle C++ compiler.  */
  .exception_ranges   : ONLY_IF_RO { *(.exception_ranges
  .exception_ranges*) }
  /* Adjust the address for the data segment.  We want to adjust up to
     the same address within the page on the next page up.  */
  . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
  /* Exception handling  */
  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
  .gnu_extab      : ONLY_IF_RW { *(.gnu_extab) }
  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
  .exception_ranges   : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) }
  /* Thread Local Storage sections  */
  .tdata      : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
  .tbss       : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  }
  .init_array     :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
    KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
    PROVIDE_HIDDEN (__init_array_end = .);
  }
  .fini_array     :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
    KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
    PROVIDE_HIDDEN (__fini_array_end = .);
  }
  .ctors          :
  {
    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first.  Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard.  The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in.  */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*crtbegin?.o(.ctors))
    /* We don't want to include the .ctor section from
       the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
  }
  .dtors          :
  {
    KEEP (*crtbegin.o(.dtors))
    KEEP (*crtbegin?.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
  }
  .jcr            : { KEEP (*(.jcr)) }
  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
  .dynamic        : { *(.dynamic) }
  .got            : { *(.got) *(.igot) }
  . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
  .got.plt        : { *(.got.plt)  *(.igot.plt) }
  .data           :
  {
    *(.data .data.* .gnu.linkonce.d.*)
    SORT(CONSTRUCTORS)
  }
  .data1          : { *(.data1) }
  _edata = .; PROVIDE (edata = .);
  . = .;
  __bss_start = .;
  .bss            :
  {
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
   /* Align here to ensure that the .bss section occupies space up to
      _end.  Align after .bss to ensure correct alignment even if the
      .bss section disappears because there are no input sections.
      FIXME: Why do we need it? When there is no .bss section, we don't
      pad the .data section.  */
   . = ALIGN(. != 0 ? 64 / 8 : 1);
  }
  .lbss   :
  {
    *(.dynlbss)
    *(.lbss .lbss.* .gnu.linkonce.lb.*)
    *(LARGE_COMMON)
  }
  . = ALIGN(64 / 8);
  . = SEGMENT_START("ldata-segment", .);
  .lrodata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
  {
    *(.lrodata .lrodata.* .gnu.linkonce.lr.*)
  }
  .ldata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
  {
    *(.ldata .ldata.* .gnu.linkonce.l.*)
    . = ALIGN(. != 0 ? 64 / 8 : 1);
  }
  . = ALIGN(64 / 8);
  _end = .; PROVIDE (end = .);
  . = DATA_SEGMENT_END (.);
  /* Stabs debugging sections.  */
  .stab          0 : { *(.stab) }
  .stabstr       0 : { *(.stabstr) }
  .stab.excl     0 : { *(.stab.excl) }
  .stab.exclstr  0 : { *(.stab.exclstr) }
  .stab.index    0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment       0 : { *(.comment) }
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */
  /* DWARF 1 */
  .debug          0 : { *(.debug) }
  .line           0 : { *(.line) }
  /* GNU DWARF 1 extensions */
  .debug_srcinfo  0 : { *(.debug_srcinfo) }
  .debug_sfnames  0 : { *(.debug_sfnames) }
  /* DWARF 1.1 and DWARF 2 */
  .debug_aranges  0 : { *(.debug_aranges) }
  .debug_pubnames 0 : { *(.debug_pubnames) }
  /* DWARF 2 */
  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
  .debug_abbrev   0 : { *(.debug_abbrev) }
  .debug_line     0 : { *(.debug_line .debug_line.* .debug_line_end ) }
  .debug_frame    0 : { *(.debug_frame) }
  .debug_str      0 : { *(.debug_str) }
  .debug_loc      0 : { *(.debug_loc) }
  .debug_macinfo  0 : { *(.debug_macinfo) }
  /* SGI/MIPS DWARF 2 extensions */
  .debug_weaknames 0 : { *(.debug_weaknames) }
  .debug_funcnames 0 : { *(.debug_funcnames) }
  .debug_typenames 0 : { *(.debug_typenames) }
  .debug_varnames  0 : { *(.debug_varnames) }
  /* DWARF 3 */
  .debug_pubtypes 0 : { *(.debug_pubtypes) }
  .debug_ranges   0 : { *(.debug_ranges) }
  /* DWARF Extension.  */
  .debug_macro    0 : { *(.debug_macro) }
  .debug_addr     0 : { *(.debug_addr) }
  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}


==================================================

これだと見づらいので抜き出すと、、、

  .text           :
  {
    *(.text.unlikely .text.*_unlikely .text.unlikely.*)
    *(.text.exit .text.exit.*)
    *(.text.startup .text.startup.*)
    *(.text.hot .text.hot.*)
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf32.em.  */
    *(.gnu.warning)
  }
  .fini           :
  {
    KEEP (*(SORT_NONE(.fini)))
  }
  PROVIDE (__etext = .);
  PROVIDE (_etext = .);
  PROVIDE (etext = .);
  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
  .rodata1        : { *(.rodata1) }
  .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table
  .gcc_except_table.*) }

確かに.textよりも後方に.rodataが配置されていることが分かります。

革命の日々! システムのデフォルトリンカスクリプトを調べる方法

movl $1, %edi

第一引数として、1をediレジスタにコピーしています。これより前にripレジスタは使われていません。

call write@PLT

ここで関数を呼び出しています。

PLTとはProcedure Linkage Tableの略で、呼ばれる関数が乗っているテーブルです。@PLTがついている関数は、このテーブルの中から動的に呼び出されます(動的リンク)。詳しくはまた別記事にでもまとめます。

これが動作する秘密は、PLT(Procedure Linkage Table)と呼ばれるデータの塊、つまりプログラムが呼ぶ全てのファンクションをリストアップした、プログラム中のテーブルです。

共有ライブラリーを解剖する

まとめ

今回は、関数の呼び出しについてまとめただけですが、結構いろんなものが隠れてて面白いですね。

もう少し、自在にgdbを操れるようにならないとだめだな。

アセンブリ勉強会(1) ~何もしないプログラム~

ARMのアセンブリは多少読めるけど、x86アセンブリを読めるようになりたい!

ということで、一命令ずつ見ていくのもよかったけど、飽きるのでC言語でよく作るプログラムを見ながら勉強していく。

必要になったらその場で必要な知識を調べてまとめていくのでも十分いいと思うしね。

実を言うと今まで全くアセンブリを避けて生きてきてた。。。(それでもそれなりには生きていけちゃうんだよね)

各種命令は以下を参考にしています。

Tips IA32(x86)命令一覧

環境

以下のような環境で進めていく。(多分、binutilsさえあれば事足りるはず)

$ uname -a
Linux Sagiri 4.10.0-32-generic #36-Ubuntu SMP Tue Aug 8 12:10:06 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux 
$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 6.3.0-12ubuntu2' --with-bugurl=file:///usr/share/doc/gcc-6/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-6 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-6-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-6-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-6-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 6.3.0 20170406 (Ubuntu 6.3.0-12ubuntu2

 

何もしないプログラム

int main(void) {
  return 0;
}

ただreturn 0;するプログラムをアセンブリコンパイルして、それを見てみる。

$ gcc -Wall -S none.c
 .file "none.c"
    .text
    .globl    main
    .type main, @function
main:
.LFB0:
    .cfi_startproc
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq %rsp, %rbp
    .cfi_def_cfa_register 6
    movl $0, %eax
    popq %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size main, .-main
    .ident    "GCC: (Ubuntu 6.3.0-12ubuntu2) 6.3.0 20170406"
    .section  .note.GNU-stack,"",@progbits

各行について

.file “none.c”

そのまま。コンパイルする前のファイル名。ちなみに.で始まる命令はディレクティブといい、アセンブラに対して命令を行うもの。

このサイトがすごくGNUアセンブリのリファレンス的に利用できて良さげ。

Using as: Top

.text

おそらく.textセクション(プログラムの本体である部分)を示しているんだと思われる。

他にも.data(初期値を持つ大域変数)や.bss(初期値を持たない大域変数)、.rodata(読み込み専用のデータ)などがある。

また.textに変数のために領域をとってもWXという機構により、書き換えができないらしい。(と言われたら後で実験してやる)

W^Xについても面白そうだから別記事にまとめることにする。

What are .data and .text in x86? - Stack Overflow

https://en.wikipedia.org/wiki/W%5EXW^X - Wikipedia

.globl (.global)

リンカからその変数を見えるようにする命令。他のアセンブリからそのラベルにアクセスする必要が有る時に使うもの。

gasのディレクティブについて - suu-g's diary

.type main, @function

mainというラベルの指すものがが関数であることを示している。

main:

mainラベルをつける。

.LFB0:

略(調べてもただのラベルとか言われる)。

.cfi_startproc

関数の始まりに使われる。内部でいくつかのデータ構造を初期化しているらしい。(といわれてもピンとこない)

Using as: CFI directives

とりあえず、ここでは.cfiで始まるものは無視していきます。(重要な命令なのかもですが、知識が圧倒的に足りてないです)

pushq %rbp

ベースポインタ(スタックの開始位置)をスタックに積む命令。qはquadword(8bytes)の事。bはbyte, wはword, lはlongでそれぞれ1,2,4bytesの事。

レジスタに関しては、ここが詳しくて分かりやすい。

レジスタ - OS Project Wiki

movq %rsp, %rbp

スタックポインタ(スタックの先頭位置)の値をベースポインタに移す命令です。

関数に入ってすぐに行われる処理

以下のような流れが、必ず関数の呼び出し時に行われます。おかげで再帰とかができるようになるのです。(return addressは関数から抜けて直後に行われる命令のアドレスです)

f:id:proelbtn:20170822011415p:plain

ちなみに、rbpとrbpの間の事を、スタックフレームというらしいです。

movl $0, %eax

eaxレジスタに、返り値である0をコピーする(といっても即値ですが)。

cdecl(C言語の呼び出し規約)では、RET文で整数もしくはポインタを返す場合、eaxレジスタに入れるっぽいです。浮動小数点ではST0 x87レジスタが使われるらしい?

In cdecl, subroutine arguments are passed on the stack. Integer values and memory addresses are returned in the EAX register, floating point values in the ST0 x87 register

x86 calling conventions - Wikipedia

popq %rbp

スタックに積んだベースポインタを取り出す。

ret

呼び出し元にreturnする。

.LFE0:

略。

それより後も本質的な情報では情報ではないので、とりあえず飛ばします。

まとめ

とりあえず、push命令、pop命令、call命令(関数呼び出し)、ret命令、mov命令が軽くわかった感じですかね。

レジスタの大きさ順である、RAX(64bit) > EAX(32bit) > AX(16bit) > AH(8bit) = AL(8bit)も頭の中にいれといた方が良さそうですかね。RAX, RBX, RCX, RDX, RSI, RDIとかが汎用レジスタとして使えるそうです。

Assembly Programming on x86-64 Linux (04)