펄의 함수 vec
형식 : vec EXPR,OFFSET,BITS
expr : string type의 변수
offset : 변수로부터 포인터 offset
bits : offset 의 배수로 건너 뛸 비트 수
ex ) vec($image, $max_x * $x + $y, 8) = 3;
$image를 스트링을 가리키는 주소라고 생각하면,
그 주소에서 ( $max_x * $x + $y )*8 비트 떨어진 곳에 3을 assign 한다.
예제 코드
step1 선언해보기
my $foo = ''; vec($foo, 0, 32) = 0x5065726C; # 'Perl'
string type 변수 $foo 선언
string pointer $foo가 가리키고 있는 주소 + 0 위치에 0x5065726c라는 32bit data assign
8bit씩 각 character로 mapping 하면
0x50 : P
0x65 : e
0x73 : r
0x6c : l
주소 (8bit offset) | $foo | |||||||
값 | 50 | 65 | 72 | 6c |
step1 값을 확인해보기
print vec($foo, 0, 8); # prints 80 == 0x50 == ord('P')
$foo + 0 위치에서 8 bit 만 출력
즉 첫번째 byte인 P 가 출력된다.
step2 값을 지정해보기
vec($foo, 2, 16) = 0x5065; # 'PerlPe' vec($foo, 3, 16) = 0x726C; # 'PerlPerl' vec($foo, 8, 8) = 0x50; # 'PerlPerlP' vec($foo, 9, 8) = 0x65; # 'PerlPerlPe'
vec($foo, 2, 16) = 0x5065; # 'PerlPe'
$foo에서 16bit offset을 두 번 더한 주소 위치에 5065를 assign한다.
주소 (16bit offset) | $foo | $foo + 1 | $foo+2 | $foo +3 | ||||
값 | 50 | 65 | 72 | 6c | 50 | 65 |
vec($foo, 3, 16) = 0x726C; # 'PerlPerl'
$foo에서 16bit offset을 세 번 더한 주소 위치에 726c를 assign한다.
주소 (16bit offset) | $foo | $foo + 1 | $foo+2 | $foo +3 | ||||
값 | 50 | 65 | 72 | 6c | 50 | 65 | 72 | 6c |
vec($foo, 8, 8) = 0x50; # 'PerlPerlP'
$foo에서 8byte offset을 8 번 더한 주소 위치에 50('P')를 assign한다.
여기서 처리하는 data 단위가 8bit이며 이는 character 하나에 대응된다.
vec($foo, 9, 8) = 0x65; # 'PerlPerlPe'
$foo에서 8byte offset을 9번 더한 주소 위치에 65('e')를 assign한다.
주소 (8bit offset) | $foo | +1 | +2 | +3 | +4 | +5 | +6 | +7 |
값 | 50 | 65 | 72 | 6c | 50 | 65 | 72 | 6c |
주소(8bit offset) | $foo + 8 | +9 | +10 | +11 | +12 | |||
값 | 50 | 65 |
step3 machine에서 data가 저장되는 방식. little endian과 big endian
vec($foo, 20, 4) = 2; # 'PerlPerlPe' . "\x02" vec($foo, 21, 4) = 7; # 'PerlPerlPer' # 'r' is "\x72"
machine이 address를 다룰 때 8bit (1byte)씩은 순서대로 증가하는 것을 확인하였다.
이를 big-endian 방식이라고 한다. 이는 큰 단위가 앞에 나오는 바이트 순서를 의미하며, 우리가 일반적으로 앞에서부터 써고 읽어 내려가는 방식이라고 생각하면 된다.
그런데 현재 다루고 있는 머신은 8bit 안에서는 little endian이다. 이는 big-endian과 반대 순서로서, 큰 단위가 뒤에 나오는 바이트 순서이다.
예를들어 0x72 (8bit )는 기계 상에서
0x 0100 1110 으로 기입되어 있다고 생각하면 된다.
0100은 big endian 으로 읽으면 4이지만
little endina 방식으로 뒤에서부터 읽으면 2이다.
1110 은 big endian으로 읽으면 14 이지만
little endian 방식으로 뒤에서부터 읽으면 7이다.
두개를 붙여서 읽으면 27 같지만, 4bit 단위도 little endian이라서 72이다.
정리하자면 8bit 이상(0x**)은 big endian으로 순서대로 읽으면 되지만,
8bit 내에서는 4bit 도 역순, 4bit 내에서도 역순으로 읽고 쓴다.
이러한 규칙은 내가 사용하는 컴퓨터 운영체제에 따라 달라지므로, 내가 쓰는 환경이 little-endian 방식인지 big-endian 방식인지 확인해보고 코드를 짜는 것이 좋다.
그래서 위 step3의 코드블록을 다시 살펴보면
vec($foo, 20, 4) = 2; # 'PerlPerlPe' . "\x02"
vec($foo, 21, 4) = 7; # 'PerlPerlPer' # 'r' is "\x72"
이처럼 7을 쓰고 2를 쓰는게 아니라, 72를 거꾸로 하여 2를 쓰고 7을 써서 0x72('r')을 만든다.
이처럼 endian을 고려해야 하는 이유는 vec으로 값을 assign하는 방식이 주소에 직접 접근하는 포인터 방식이기 때문이다.
주소(4bit offset) | $foo + 16 | + 17 | + 18 | +19 | +20 | +21 | ||
값 | 0 | 5 | 5 | 6 | 2 | 7 | ||
8bit 로 읽을때 | 0x 50 | 0x 65 | 0x72 |
step4 4bit binary 로 접근 (little endian)
vec($foo, 45, 2) = 3; # 'PerlPerlPer' . "\x0c" vec($foo, 93, 1) = 1; # 'PerlPerlPer' . "\x2c" vec($foo, 94, 1) = 1; # 'PerlPerlPerl' # 'l' is "\x6c"
vec($foo, 45, 2) = 3; # 'PerlPerlPer' . "\x0c"
45 위치에 2bit 3만 대입. 44위치 2bit는 00으로 padding된다.
저도 $foo+44가 왜 00으로 padding되는지는 잘 모르겠으나, 주소에 값을 적을 때, 4bit단위로 써야 해서 그런가보다 추측하고 있어요.
주소(2bit offset) | $foo + 40 | + 41 | + 42 | +43 | +44 | +45 | ||
값 | 01 | 00 | 11 | 10 | 00 | 11 | ||
4bit씩 읽을때 | 2 | 7 | c | |||||
8bit씩 읽을 때 | 0x72 | 0x*c |
vec($foo, 93, 1) = 1; # 'PerlPerlPer' . "\x2c"
vec($foo, 94, 1) = 1; # 'PerlPerlPerl'
# 'l' is "\x6c"
주소(1bit offset) | $foo + 88 | + 89 | + 90 | +91 | +92 | +93 | +94 | +95 |
값 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 0 |
4bit씩 읽을때 | c | 6 | ||||||
8bit씩 읽을 때 | 0x6c |