mcommit's message

ソフトウェア開発の仕事をしているsimotinといいます。記事の内容でご質問やご意見がありましたらお気軽にコメントしてください\^o^/

Goでバイナリファイルを読み込んでHexダンプしてみた

最近触り始めたGo言語で、勉強としてバイナリファイルを読み込んでHexダンプしてみました。
バイナリファイルといってもfile.Readを使った読み込みなのでバイナリかどうかはあまり関係ないかもしれない。

package main

import (
  "fmt"
  "os"
)

func main() {
  argc := len(os.Args)
  if argc < 2 {
    fmt.Println("input file name.")
    return
  }

  filename := os.Args[1]
  f, err := os.Open(filename)
  if err != nil {
    fmt.Println("err:", err)
    return
  }

  c := make([]byte, 1)
  var buf []byte
  size := 0
  for {
    len, r_err := f.Read(c)
    if len == 0 {
      break
    }
    size += len
    if r_err != nil {
      fmt.Println("err:", err)
      return
    }
    buf = append(buf, c[0])
  }

  fmt.Printf("size:[%d]\n", size)
  for i := 0; i < size; i++ {
      fmt.Printf("%02X ", buf[i])
      if i % 16 == 15 {
        fmt.Println("")
      }
  }
}

感想

Go言語的な書き方がよくわからない

なんとなくC言語っぽく書いてみたけど、Go言語的に正しい書き方がよくわからない。
特にファイルから全データをリードする場合C言語だと大抵、EOFまで1byteずつreadするけどgoでもそんなのりでいいのかな・・・

配列とslice

RubyのArray的なのが使いたいけどgoだと配列とsliceを組み合わせて使うみたい。
sliceって機能と名前があってない気がする。

そもそも、goって名前自体英語だと普通の動詞になるので検索性がよくない。
例えば、for文について調べようとしたら

f:id:simotin13:20180326011052j:plain

みたいな結果になった。やはり名前重要。

参考

Go言語を勉強するにあたって「基礎からわかるGo言語」を手元に置いて読んでいます。

改訂2版 基礎からわかる Go言語

改訂2版 基礎からわかる Go言語

この書籍では前半で言語の基本的な機能について解説されていて、後半で逆引きサンプルが載っているのでかなり役に立っています。
今のところやりたいことに合わせて前半と後半をいったり来たりしながら読んでいます。

goらしい書き方についてはこの書籍だけでは掴めないので、go好きな人が書いたコードとかもっと読んだり写経した方がよさそう。

レジスタの値を取得するi386のアセンブラ関数を書いてみた

前回書いたPINE64+の続きとして、とりあえずBROMの処理が終わった状態でCPUがどうなっているか知りたいのですが、難航しています。

mcommit.hatenadiary.com

BROMのコードのダンプを見た結果、コプロセッサの設定とかをしていることは分かりましたがどうも追いきれないところもあるのでとりあえずCPUのレジスタの値をUARTでダンプ出力とかしてみたいと思いました。

レジスタを直接触るためにはアセンブラでコードを書く必要があり、少しハードルはあがります。
といってもARMのABIを理解して、出力したいレジスタからMOV命令で汎用レジスタにコピーするだけなのでそこまでは難しくはないはずです。

が、とりあえず練習もかねてi386で試してみました。

ABI的にポインタで渡す変数がスタックトップの次にあることを前提としています。関数側ではEBPの設定とかしないようにしたので、スタックトップはリターンアドレスになります。抜けるときもret命令だけで問題ありません。

reg.asm

section .text
global get_eax
global get_ecx
global get_edx
global get_ebx
global get_ebp
global get_esi
global get_edi
global get_esp
global get_eip

get_eax:
    mov ebx, [esp+0x04]
    mov [ebx], eax
    ret

get_ecx:
    mov eax, [esp+0x04]
    mov [eax], ecx
    ret

get_edx:
    mov eax, [esp+0x04]
    mov [eax], edx
    ret

get_ebx:
    mov eax, [esp+0x04]
    mov [eax], ebx
    ret

get_ebp:
    mov eax, [esp+0x04]
    mov [eax], ebp
    ret

get_esi:
    mov eax, [esp+0x04]
    mov [eax], esi
    ret

get_edi:
    mov eax, [esp+0x04]
    mov [eax], edi
    ret

get_esp:
    mov eax, [esp+0x04]
    mov [eax], esp
    sub [eax], dword 0x08
    ret

get_eip:
    mov eax, [esp]
    mov ebx, [esp+0x04]
    mov [ebx], eax	; return address on stack frame.
    ret

呼び出す側は、

main.c

#include <stdio.h>
#include <stdint.h>

extern void get_eax(uint32_t *reg);
extern void get_ecx(uint32_t *reg);
extern void get_edx(uint32_t *reg);
extern void get_ebx(uint32_t *reg);
extern void get_ebp(uint32_t *reg);
extern void get_esi(uint32_t *reg);
extern void get_edi(uint32_t *reg);
extern void get_esp(uint32_t *reg);
extern void get_eip(uint32_t *reg);

int main(int argc, char **argv)
{
    uint32_t eax, ecx, edx, ebx, esp, ebp, esi, edi, eip;

    get_eax(&eax);
    get_ecx(&ecx);
    get_edx(&edx);
    get_edx(&ebx);
    get_ebp(&ebp);
    get_esi(&esi);
    get_edi(&edi);
    get_esp(&esp);
    get_eip(&eip);

    printf("eax: [%08X]\n", eax);
    printf("ecx: [%08X]\n", ecx);
    printf("edx: [%08X]\n", edx);
    printf("ebx: [%08X]\n", ebx);
    printf("ebp: [%08X]\n", ebp);
    printf("esi: [%08X]\n", esi);
    printf("edi: [%08X]\n", edi);
    printf("esp: [%08X]\n", esp);
    printf("eip: [%08X]\n", eip);
    return 0;
}

のような感じで、ポインタ引数で受け取ることを想定した関数にしてみました。
※最初戻り値で作ってみたのですが、戻り値だとEAXが取れなくなってしまいます。
まだ全関数のデバッグはできていませんが、問題なく動いていそうです。

ビルドは、


$ nasm -f elf reg.asm
$ gcc main.c reg.o
のような感じです。nasmを使う場合は -f でフォーマットを指定する必要があります。

getがなんとなく書けたのでついでにsetも書きたい。それができたらARMv7版も書いてみよう。

PINE64のBROMのコードをダンプして逆アセンブルしてみた。

Pine64の続き。
記事はさっきあげましたが、昨日(2/26)Lチカを試したのですが、書きかけになっていたので公開していませんでした。
スタックポインタの設定の件とかモヤモヤしたので今日はBROMのコードをダンプしてみました。

有力な手がかり

http://linux-sunxi.org/Pine64#Boot_sequence

こちらのWiki

The A64 SoC is wired to come out of reset in 32-bit monitor mode. As other Allwinner devices, the A64 SoC starts executing BROM code (mapped at address 0), which is consequently ARM32 code. The complete BROM code can be found at address 0x2c00 and has a total length of 32KB

という記載がありましたので0x2C00から32KB分のコードをダンプして逆アセンブルしてみました。

ダンプに使ったコード

typedef unsigned int u32;

#define SUNXI_UART0_BASE	0x01C28000
#define SUNXI_PIO_BASE		0x01C20800
#define AW_CCM_BASE			0x01c20000

struct sunxi_gpio {
	u32 cfg[4];
	u32 dat;
	u32 drv[2];
	u32 pull[2];
};

struct sunxi_gpio_reg {
	struct sunxi_gpio gpio_bank[10];
};

#define GPIO_BANK(pin)		((pin) >> 5)
#define GPIO_NUM(pin)		((pin) & 0x1F)

#define GPIO_CFG_INDEX(pin)	(((pin) & 0x1F) >> 3)
#define GPIO_CFG_OFFSET(pin)	((((pin) & 0x1F) & 0x7) << 2)

#define GPIO_PULL_INDEX(pin)	(((pin) & 0x1f) >> 4)
#define GPIO_PULL_OFFSET(pin)	((((pin) & 0x1f) & 0xf) << 1)
/* SUNXI GPIO number definitions */
#define SUNXI_GPA(_nr)          (0 + (_nr))			// banl 1
#define SUNXI_GPB(_nr)          (0x20 + (_nr))		// bank 2(bank is 1~7, offset:0x20)

#define SUNXI_GPIO_INPUT        (0)
#define SUNXI_GPIO_OUTPUT       (1)
#define SUN4I_GPB_UART0         (2)
#define SUN50I_A64_GPB_UART0    (4)

/* GPIO pin pull-up/down config */
#define SUNXI_GPIO_PULL_DISABLE (0)
#define SUNXI_GPIO_PULL_UP      (1)
#define SUNXI_GPIO_PULL_DOWN    (2)

static void uart0_putc(char c);

void puts_char2Hex(unsigned char cVal)
{
	unsigned char tmp;
	unsigned char c;

	uart0_putc('0');
	uart0_putc('x');

	tmp = (cVal >> 4) & 0x0F;
	if (tmp < 0x0A)
	{
		// '0'~'9'
		c = 0x30 + tmp;
	}
	else
	{
		// 'A'~'F' 0x41
		c = (0x37 + tmp);
	}

	uart0_putc(c);

	tmp = cVal & 0x0F;
	if (tmp < 0x0A)
	{
		// '0'~'9'
		c = 0x30 + tmp;
	}
	else
	{
		// 'A'~'F' 0x41
		c = (0x37 + tmp);
	}
	uart0_putc(c);
	uart0_putc(',');
}

int sunxi_gpio_set_cfgpin(u32 pin, u32 val)
{
	u32 cfg;
	u32 bank = GPIO_BANK(pin);
	u32 index = GPIO_CFG_INDEX(pin);
	u32 offset = GPIO_CFG_OFFSET(pin);
	struct sunxi_gpio *pio = &( (struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank];
	cfg = *( (volatile unsigned long  *)(&pio->cfg[0] + index));
	cfg &= ~(0xf << offset);
	cfg |= val << offset;
	*(volatile unsigned long  *)(&pio->cfg[0] + index) = (unsigned long)(cfg);
	return 0;
}

int sunxi_gpio_set_pull(u32 pin, u32 val)
{
	u32 cfg;
	u32 bank = GPIO_BANK(pin);
	u32 index = GPIO_PULL_INDEX(pin);
	u32 offset = GPIO_PULL_OFFSET(pin);
	struct sunxi_gpio *pio = &((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank];
	cfg = *((volatile unsigned long  *)(&pio->pull[0] + index));
	cfg &= ~(0x3 << offset);
	cfg |= val << offset;
	*((volatile unsigned long  *)(&pio->pull[0] + index)) = (unsigned long)(cfg);
	return 0;
}

int sunxi_gpio_output(u32 pin, u32 val)
{
	u32 dat;
	u32 bank = GPIO_BANK(pin);
	u32 num = GPIO_NUM(pin);
	struct sunxi_gpio *pio = &((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank];
	dat = *((volatile unsigned long *)(&pio->dat));
	if(val)
		dat |= 1 << num;
	else
		dat &= ~(1 << num);

	*(volatile unsigned long *)(&pio->dat) = (unsigned long)(dat);
	return 0;
}

int sunxi_gpio_input(u32 pin)
{
	u32 dat;
	u32 bank = GPIO_BANK(pin);
	u32 num = GPIO_NUM(pin);
	struct sunxi_gpio *pio =
		&((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank];
	dat = *((volatile unsigned long  *)(&pio->dat));
	dat >>= num;
	return (dat & 0x1);
}

int gpio_direction_input(unsigned gpio)
{
	sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_INPUT);
	return sunxi_gpio_input(gpio);
}

int gpio_direction_output(unsigned gpio, int value)
{
	sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT);
	return sunxi_gpio_output(gpio, value);
}

/*****************************************************************************
 * UART is mostly the same on A10/A13/A20/A31/H3/A64, except that newer SoCs *
 * have changed the APB numbering scheme (A10/A13/A20 used to have APB0 and  *
 * APB1 names, but newer SoCs just have renamed them into APB1 and APB2).    *
 * The constants below are using the new APB numbering convention.           *
 * Also the newer SoCs have introduced the APB2_RESET register, but writing  *
 * to it effectively goes nowhere on older SoCs and is harmless.             *
 *****************************************************************************/

#define CONFIG_CONS_INDEX	1
#define APB2_CFG		(AW_CCM_BASE + 0x058)
#define APB2_GATE		(AW_CCM_BASE + 0x06C)
#define APB2_RESET		(AW_CCM_BASE + 0x2D8)
#define APB2_GATE_UART_SHIFT	(16)
#define APB2_RESET_UART_SHIFT	(16)

void clock_init_uart(void)
{
	/* Open the clock gate for UART0 */
	*((volatile unsigned long  *)(APB2_GATE)) |= (unsigned long)(1 << (APB2_GATE_UART_SHIFT + CONFIG_CONS_INDEX - 1));
	/* Deassert UART0 reset (only needed on A31/A64/H3) */
	*((volatile unsigned long  *)(APB2_RESET)) |= (unsigned long)(1 << (APB2_RESET_UART_SHIFT + CONFIG_CONS_INDEX - 1));
}

void gpio_init(void)
{
	sunxi_gpio_set_cfgpin(SUNXI_GPB(8), SUN50I_A64_GPB_UART0);
	sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN50I_A64_GPB_UART0);
	sunxi_gpio_set_pull(SUNXI_GPB(9), SUNXI_GPIO_PULL_UP);
}

/*****************************************************************************/

#define UART0_RBR (SUNXI_UART0_BASE + 0x0)    /* receive buffer register */
#define UART0_THR (SUNXI_UART0_BASE + 0x0)    /* transmit holding register */
#define UART0_DLL (SUNXI_UART0_BASE + 0x0)    /* divisor latch low register */

#define UART0_DLH (SUNXI_UART0_BASE + 0x4)    /* divisor latch high register */
#define UART0_IER (SUNXI_UART0_BASE + 0x4)    /* interrupt enable reigster */

#define UART0_IIR (SUNXI_UART0_BASE + 0x8)    /* interrupt identity register */
#define UART0_FCR (SUNXI_UART0_BASE + 0x8)    /* fifo control register */

#define UART0_LCR (SUNXI_UART0_BASE + 0xc)    /* line control register */

#define UART0_LSR (SUNXI_UART0_BASE + 0x14)   /* line status register */

#define BAUD_115200    (0xD) /* 24 * 1000 * 1000 / 16 / 115200 = 13 */
#define NO_PARITY      (0)
#define ONE_STOP_BIT   (0)
#define DAT_LEN_8_BITS (3)
#define LC_8_N_1       (NO_PARITY << 3 | ONE_STOP_BIT << 2 | DAT_LEN_8_BITS)

void uart0_init(void)
{
	clock_init_uart();

	/* select dll dlh */
	*(volatile unsigned long *)(UART0_LCR) = (unsigned long)(0x80);

	/* set baudrate */
	*(volatile unsigned long *)(UART0_DLH) = (unsigned long)(0);
	*(volatile unsigned long *)(UART0_DLL) = (unsigned long)(BAUD_115200);

	/* set line control */
	*(volatile unsigned long *)(UART0_LCR) = (unsigned long)(LC_8_N_1);
}

void uart0_putc(char c)
{
	while (!(*((volatile unsigned long  *)(UART0_LSR)) & (1 << 6))) {}
	*(volatile unsigned long  *)(UART0_THR) = (unsigned long)(c);
}

void uart0_puts(const char *s)
{
	while (*s) {
		if (*s == '\n')
			uart0_putc('\r');
		uart0_putc(*s++);
	}
}

void __attribute__((section(".start"))) __attribute__((naked)) start(void)
{
	asm volatile("b     main             \n"
		     ".long 0xffffffff       \n"
		     ".long 0xffffffff       \n"
		     ".long 0xffffffff       \n");
}

// sudo dd if=./uart0-helloworld-sdboot.sunxi of=/dev/sdb bs=1024 seek=8
int main(void)
{
	gpio_init();
	uart0_init();
	uart0_puts("Dump 32KB from 0x2c00.\n");
	unsigned int addr = 0x2c00;
	unsigned char c = 0x00;

	for (addr = 0; addr < (0 + (1024 * 32)); addr++)
	{
		c = *(unsigned char *)addr;
		puts_char2Hex(c);
	}

	while (1);

	return 0;
}

考察

アセンブルの結果をここで少し見てみたいと思います。

       0:	ea000007 	b	0x24
       4:	ea000007 	b	0x28
       8:	4e4f4765 	cdpmi	7, 4, cr4, cr15, cr5, {3}
       c:	4d52422e 	lfmmi	f4, 2, [r2, #-184]	; 0xffffff48
      10:	00000024 	andeq	r0, r0, r4, lsr #32
      14:	30303131 	eorscc	r3, r0, r1, lsr r1
      18:	30303131 	eorscc	r3, r0, r1, lsr r1
      1c:	33333631 	teqcc	r3, #51380224	; 0x3100000
      20:	00000000 	andeq	r0, r0, r0

      24:	ea000000 	b	0x2c
      28:	ea000001 	b	0x34
      2c:	e3a06000 	mov	r6, #0
      30:	ea000003 	b	0x44
      34:	e3a0605c 	mov	r6, #92	; 0x5c
      38:	ea00000e 	b	0x78
      
      3c:	e59f01e8 	ldr	r0, [pc, #488]	; 0x22c
      40:	e590f000 	ldr	pc, [r0]
      44:	ee100fb0 	mrc	15, 0, r0, cr0, cr0, {5}
      48:	e2001003 	and	r1, r0, #3
      4c:	e3510000 	cmp	r1, #0
      50:	1afffff9 	bne	0x3c
      54:	e2001cff 	and	r1, r0, #65280	; 0xff00
      58:	e3510000 	cmp	r1, #0
      5c:	1afffff6 	bne	0x3c
      60:	e59f11c8 	ldr	r1, [pc, #456]	; 0x230
      64:	e59f21c8 	ldr	r2, [pc, #456]	; 0x234
      68:	e5913000 	ldr	r3, [r1]
      6c:	e1520003 	cmp	r2, r3
      70:	1a000000 	bne	0x78
      74:	eafffff0 	b	0x3c
      78:	e3a00050 	mov	r0, #80	; 0x50
      7c:	e2500001 	subs	r0, r0, #1
      80:	1afffffd 	bne	0x7c
      84:	e10f0000 	mrs	r0, CPSR
      88:	e3c0001f 	bic	r0, r0, #31
      8c:	e3800013 	orr	r0, r0, #19
      90:	e38000c0 	orr	r0, r0, #192	; 0xc0
      94:	e3c00c02 	bic	r0, r0, #512	; 0x200
      98:	e121f000 	msr	CPSR_c, r0
      9c:	ee110f10 	mrc	15, 0, r0, cr1, cr0, {0}
      a0:	e3c00005 	bic	r0, r0, #5
      a4:	e3c00b06 	bic	r0, r0, #6144	; 0x1800
      a8:	ee010f10 	mcr	15, 0, r0, cr1, cr0, {0}
      ac:	e59f1184 	ldr	r1, [pc, #388]	; 0x238
      b0:	e5912000 	ldr	r2, [r1]
      b4:	e3c22001 	bic	r2, r2, #1
      b8:	e5812000 	str	r2, [r1]
      bc:	e59f1178 	ldr	r1, [pc, #376]	; 0x23c
      c0:	e3a02801 	mov	r2, #65536	; 0x10000
      c4:	e5913050 	ldr	r3, [r1, #80]	; 0x50
      c8:	e3c33803 	bic	r3, r3, #196608	; 0x30000
      cc:	e1834002 	orr	r4, r3, r2
      d0:	e5814050 	str	r4, [r1, #80]	; 0x50
      d4:	e3a02000 	mov	r2, #0
      d8:	e5913050 	ldr	r3, [r1, #80]	; 0x50
      dc:	e3c33003 	bic	r3, r3, #3
      e0:	e1834002 	orr	r4, r3, r2
      e4:	e5814050 	str	r4, [r1, #80]	; 0x50
      e8:	e3a02c01 	mov	r2, #256	; 0x100
      ec:	e5913054 	ldr	r3, [r1, #84]	; 0x54
      f0:	e3c330f0 	bic	r3, r3, #240	; 0xf0
      f4:	e3c33c03 	bic	r3, r3, #768	; 0x300
      f8:	e1834002 	orr	r4, r3, r2
      fc:	e5814054 	str	r4, [r1, #84]	; 0x54
     100:	e3a02a01 	mov	r2, #4096	; 0x1000
     104:	e5913054 	ldr	r3, [r1, #84]	; 0x54
     108:	e3c33a03 	bic	r3, r3, #12288	; 0x3000
     10c:	e1834002 	orr	r4, r3, r2
     110:	e5814054 	str	r4, [r1, #84]	; 0x54
     114:	e59f1120 	ldr	r1, [pc, #288]	; 0x23c
     118:	e59122c0 	ldr	r2, [r1, #704]	; 0x2c0
     11c:	e3a03040 	mov	r3, #64	; 0x40
     120:	e1822003 	orr	r2, r2, r3
     124:	e58122c0 	str	r2, [r1, #704]	; 0x2c0
     128:	e5912060 	ldr	r2, [r1, #96]	; 0x60
     12c:	e3a03040 	mov	r3, #64	; 0x40
     130:	e1822003 	orr	r2, r2, r3
     134:	e5812060 	str	r2, [r1, #96]	; 0x60
     138:	e5912068 	ldr	r2, [r1, #104]	; 0x68
     13c:	e3a03020 	mov	r3, #32
     140:	e1822003 	orr	r2, r2, r3
     144:	e5812068 	str	r2, [r1, #104]	; 0x68
     148:	e59fd0f0 	ldr	sp, [pc, #240]	; 0x240
     14c:	e59f30f0 	ldr	r3, [pc, #240]	; 0x244
     150:	e5932000 	ldr	r2, [r3]
     154:	e30f1fff 	movw	r1, #65535	; 0xffff
     158:	e0010002 	and	r0, r1, r2
     15c:	e30e1fe8 	movw	r1, #61416	; 0xefe8
     160:	e1500001 	cmp	r0, r1
     164:	0a00009e 	beq	0x3e4
     168:	e3a00e7d 	mov	r0, #2000	; 0x7d0
     16c:	e2500001 	subs	r0, r0, #1
     170:	1afffffd 	bne	0x16c
     174:	e59fd0cc 	ldr	sp, [pc, #204]	; 0x248
     178:	e3a01507 	mov	r1, #29360128	; 0x1c00000
     17c:	e3a02000 	mov	r2, #0
     180:	e5812000 	str	r2, [r1]
     184:	e59f10c0 	ldr	r1, [pc, #192]	; 0x24c
     188:	e5912000 	ldr	r2, [r1]
     18c:	e3a03001 	mov	r3, #1
     190:	e1822003 	orr	r2, r2, r3
     194:	e5812000 	str	r2, [r1]
     198:	e59f10b0 	ldr	r1, [pc, #176]	; 0x250
     19c:	e5912000 	ldr	r2, [r1]
     1a0:	e3a03001 	mov	r3, #1
     1a4:	e1822003 	orr	r2, r2, r3
     1a8:	e5812000 	str	r2, [r1]
     1ac:	e356005c 	cmp	r6, #92	; 0x5c
     1b0:	0a000042 	beq	0x2c0
     1b4:	e3a0006f 	mov	r0, #111	; 0x6f
     1b8:	eb000047 	bl	0x2dc
     1bc:	eafffffe 	b	0x1bc

ぱっと見た感じでは、24:の

      24:	ea000000 	b	0x2c

からが実行コードのように見えるけど、いきなりb命令(無条件分岐)で始まり、しかもb命令が2回続けてでるとかは普通に考えるとありえない気がする。

あと、4c:と50:にはcmpからのbneがあるのでここは間違いなく実行コードだろう。

      4c:	e3510000 	cmp	r1, #0
      50:	1afffff9 	bne	0x3c

そう考えると、開始は24:で、何か理由があってb命令で始まっているのかもしれない。

いずれにせよ、気合入れて見てみないとどこで何しているか分からんなこれは。

やってみて分かったこと

ARMアセンブラの縦棒

ARMの機械語は必ず上位バイトがEになるという例の噂を実感できました。
実行命令はEになっていて、確かに条件分岐命令のところは、1 になっています。

こうすることでどこまで効率が上がるんだろう・・・
分岐かどうかの識別が早くなると、なんとなくパイプラインとか分岐予測とかいろんなところのパフォーマンスが地味に改善されそうな気がする。

生のバイナリの逆アセンブル

生のバイナリをダンプを直接逆アセンブルしてもデータセクションとかが含まれているときれいなアセンブラコードが出力されるとは限らない。逆アセンブルというのは、そこにコードがあるという保証がなければ意味がない可能性がある。
いや、当然といえば当然ですが。
いつもreadelfとかobjdumpを何も気にせず叩いているだけだったのでそんな根本的なことも理解できて来ませんでした。

ARMは固定長命令だからいいけど、可変長命令のCPUだとデータセクションが含まれていると恐らく解析すら不可能になる気がする・・・
そう意味では命令コードがどこにあるか分からないというのは脆弱性攻撃の対策にも多少なり有効な手段なんですね。

あと、ELF形式がいかに人類にやさしいか分かりました。

PINE64をベアメタルで遊んでみる。

随分前にPine64+ 2GBというボードを買っていたのですが積み基板になっていました。
ちょっとARMで遊びたくなったのでベアメタルからのLチカしてみました。

ソース

とりあえずGithubで管理することにします。
github.com

ツールチェインについて

ロスコンパイラとイメージ作成用のツールが必要になります。


sudo apt install gcc-arm-none-eabi u-boot-tools

調べたこと

Pine64のブートについて

パワーオンリセットにより、BROM 内のプログラムから動き始めます。
Pine64はいくつかのブート方法に対応しています。
SDカードからブートする場合はセクタ16(オフセット8KB)の位置から32KB分読み込み、実行します。
この時点ではARM32bit命令で動作するそうです。

※要するに、BROMがBIOSとして機能し、SDカードのセクタ16がブートセクタになります。
注意点として、BROMはブートセクタの内容が適切かどうか、マジックナンバーとかチェックサムによって識別しているようです。

なので単純にARMの機械語のイメージがブートセクタに配置されていても正常に起動しません。
このため、コンパイル後、バイナリファイルに対してmksunxiboot コマンドによりブートセクタ用の変換を書ける必要があります。

LEDについて

いろいろ探してみたのですが、PINE64+には、ユーザーが制御できるオンボードのLEDは実装されていないようです。
回路図やネットの情報を見ると、PL7がLEDに割り当てられているようですが、このPL7は基板上ではホールになっており、ユーザによるオプション用のようです。

Lチカを試すにあたって、最初PL7をトグルさせてLチカを試そうとしたのですが、どうもうまく動きませんでした。
単純にIOアドレスとON/OFFの書き込みが間違っている可能性がありますが、何回か試してみてうまくいかなかったので
出力ポートをPB2にしてみて、トグル出力で無事Lチカできることを確認しました。


続き

BROMのコードが気になったのでダンプして逆アセンブルしてみました。まぁ、ただそれだけですが。
mcommit.hatenadiary.com

小学1年生の娘にC言語を教えてみて気付いた、プログラミング教育を通して子供に伝えるべきたった1つの大切なこと

意識高そうなタイトルをつけてみましたが、単純に自分の子供にプログラミングを教えてみました。

実際に教えてみると、子供にプログラミングを教えるという体験を通して逆に教えられたことがあったので書いておきたいと思います。

目次

  • 目次
  • 動機
  • 教育方針
    • 具体的な方法
    • 工夫した点
    • 土台となるタイピング・パソコンスキルについて
  • 結果
  • 良かったこと
  • 補足・ポエム
    • 反省
    • 報酬(お金)と教育
    • 子供に伝えたいこと

動機

昨年の年末(12/24頃)に誰かが我が家に任天堂Switchを置いて行ったのですが、それ以来うちの娘はSwitchにはまってしまい次から次へゲームソフトをねだってくるようになりました。

かわいい娘に頼まれると断りきれず、

を立て続けに購入し気がつくと家計はパンクしかかっておりました。

しかもどういうわけか、クレジットカード会社から

¥39,390 Nintendo Switch

という請求がきているのです。

おかしい。いやおかしくはない・・・


いや、そんなことよりもこのままでは家計がやばい・・・


そんな私の混乱をよそに娘は、

「ねぇパパァ、友達がね、みんなマリオカート8デラックス持ってるの。あたしも欲しい!」

マリオカート8 デラックス - Switch

マリオカート8 デラックス - Switch

とまた新しいゲームソフトをねだってきました。

続きを読む

gdbを使って386アセンブラの復習をする

結構前に買っていたセキュリティコンテストチャレンジブックという本を最近になって読んで、脆弱性のあるコードを動かしたりして遊んでいました。

セキュリティコンテストチャレンジブック -CTFで学ぼう! 情報を守るための戦い方-

セキュリティコンテストチャレンジブック -CTFで学ぼう! 情報を守るための戦い方-

スタックオーバーフローとかSEGVの結果が本で書いていた通りにはならなかったのでとりあえずgdbを使ってアセンブラとかスタックの状態を見て386の動作を思い出すための復習をしてみました。

復習がてらメモを取りたかったので、どうせならと思いブログに書いておきたいと思います。

だらだらとながくなりそうですので LT;DR;
※ちゃんとまとめれていないと思うので後から整理したいと思います。

続きを読む

TranscendのJDM820を買ってMacBookProのSSDを交換した

普段使っているMacBookPro(Late 2013)でS.M.A.R.Tエラーが出ていたため、タイトルの通り
MacBookProで使えるSSDであるJDM820を買ってSSDを交換してみました。

SSDの交換から復旧して使えるようになるまで比較的簡単でしたのでまとめておきたいと思います。

購入のきっかけ・S.M.A.R.Tエラーについて

ここ数か月前からSSDがS.M.A.R.Tエラーが発生する状態になっておりました。

Mac OS で S.M.A.R.Tエラーが発生するとOSの更新ができなくなります。
実はこのJDM820を購入する前にVMWare Fusionを購入していたのですが、OSのバージョンが古いためインストールができませんでした。しかもOSを更新しようとするとS.M.A.R.Tエラーで更新を拒絶されるというどつもにはまった状態でした。

抜け出すには、SSDを更新するしか手はありません。
ということで、この商品を購入することにしました。

商品内容

SSD本体。高いですが240GBでは少し心もとないのと、長く使うものだからということで思い切って480GBの商品を買いました。痛い出費ですがまぁ必要なものだし元は取れるはず・・・
※私が使っているのはMacBookPro late2013(latina)です。SSDは使っているMacBookによって異なるのでご注意を。

f:id:simotin13:20180113134949j:plain



交換するときに必要なドライバーも付属しています。
f:id:simotin13:20180114135812j:plain

SSDの交換の際には、

  1. TimeMachineによるバックアップ作業
  2. SSDの交換作業
  3. OS再インストール・データ復旧

TimeMachineによるバックアップ作業

交換前にデータのバックアップを取っておきます。
TimeMachineでのバックアップはデータ容量によると思いますが、私の場合5分程度で完了しました。

SSDの交換作業

交換作業にあたって、こちらの記事を参考にさせて頂きました。

www.machinist.work
参考にさせて頂き、ありがとうございます。

交換の作業自体はそれほど難しいものではありません。
ネジを外して、元々ついているSSDを外し、購入した新しいSSDに差し替えるだけです。

まずは商品に付属のドライバを使ってMacBookProの裏面にあるネジを外して、SSDを交換します。

カバーを外してみると・・・

なんということでしょう。

f:id:simotin13:20180113135747j:plain

埃まみれになって 時間(とき)の経つのも忘れた

4年程使ってますが、中はこんなに汚くなっているんですね・・・

一歩間違うと閲覧注意のグロ画像になるところでした。ホコリじゃなくてゴキブリの卵とか死体とかが入っていなかったのでよしとしましょう。

ということで交換の前に綺麗に掃除しました。

基板だけでなくカバーの方もかなり汚れていました。交換に関わらず掃除は定期的にした方がよいかもしれません。
f:id:simotin13:20180113140658j:plain

さて、交換作業ですが、この部分がSSDになります。
f:id:simotin13:20180113140445j:plain

左側にネジがついていて、写真はネジを外した状態です。
右側がコネクタになっているので優しく引き抜いて、新しいSSDを差し込み、再びネジを締めます。

後はカバーをもと通りに付け直して交換作業は完了です。時間にして5分程でしょうか。

OS再インストール

交換したSSDはまっさらな状態ですので、当然いままで使っていたMacOSは起動しません。
復旧するには、

  1. インストールDVDから復旧
  2. TimeMachineから復旧
  3. ネットワークリカバリから復旧

という選択肢があるそうですが、ネットワークリカバリからの復旧が簡単そうでしたので試してみました。

手順

ネットワークリカバリのためにはインターネットに接続している必要があります。

まずは、Option+ Command + R キーを押しながらMacBook Proを起動します。
交換前に使用していたWiFiの設定が残っているためか、勝手にネットに接続してくれたようでした。

起動すると、しばらく何かのダウンロードに時間がかかりますが、ダウンロード後はOS Xのユーティリティの画面が表示されます。

ディスクユーティリティで交換したSSDをフォーマットした後、OS Xのインストールを選択すると、OSのインストールが可能です。※OSのイメージはネットから取得しているようです。(私が試した時はOS X MAVERICKSがインストールされました)

無事インストールができればOSのアップデートやデータの復旧などをすればSSDの交換前と同じように使えます。
この間、Option+ Command + R キーを押して起動したから、40分~50分程度でしょうか。

感想

復旧作業は思っていたより簡単だったので大満足です。

SSDフラッシュメモリを長く使うためには

フラッシュメモリはコントローラがウェアラベリングを担当してくれるそうです。
ウェアラベリングは特定のブロックへの書き込みを集中させないように書き込み先を分散する技術になりますが、使用済みの領域が多いとそもそも分散させる先がなくなってしまいます。
このため、一般にSSD等のフラッシュメモリは容量をある程度余裕がある状態で使うと長く使えると言われています。※同じ人に仕事を割り当て続けると疲弊して最後は仕事ができなくなりますがそんなイメージでしょうか・・・負荷を分散できるような余裕があるっていうのは大事なことですね。

私の場合はVMWareをよく使っていたので常に容量は不足がちでした。(あとDropboxで大容量をローカルに同期したりとか・・・)

恐らくこの状態が長く続いた為に、S.M.A.R.Tエラーに陥ったのではないかと思います。

今回買ったJDM820は高かったので、できるだけ長く使えるよう容量は常に意識しながら使いたいと思います。