2010-04-14 6 views
12

x86 어셈블리에서 상대 점프를 시도하고 있지만 작동하지 않습니다. 웬일인지 나의 점프가 절대 점프 또는 무엇인가 다시 쓰여지고있는 것처럼 보인다. JMP 명령은 4 바이트이며, 상대 점프 점프 + 1의 주소로부터의 오프셋 (offset)되어 있기 때문에이,GCC/X86, 상대 점프 관련 문제

.global main 

main: 
    jmp 0x4 
    ret 

:

에 대한 간단한 예제 프로그램은 난 할 노력하고있어 이것이다 공상 노노 야. 그러나이 코드를 컴파일하고 실행하면 세그먼트 화 오류가 발생합니다.

나를위한 진짜 수수께끼는 오브젝트 레벨로 컴파일 한 다음 오브젝트 파일을 어셈블하는 것이 어셈블러가 상대 점프를 제대로하고있는 것처럼 보이지만 파일이 컴파일되면 링커가 다른 것으로 변경하는 것입니다 점프의 종류. 위의 코드라는 파일 asmtest.s 예를 들어

인 경우 : 어셈블러가 제대로 상대 점프를 만든 것처럼이 JMP 명령이 0으로 가득 것으로 의심 비록

$gcc -c asmtest.s 
$objdump -D asmtest.o 

... Some info from objdump 
00000000 <main>: 
    0: e9 00 00 00 00   jmp 5 <main+0x5> 
    5: c3      ret 

이 보인다.

나는 다음을 분해 한 다음 링크 GCC를 사용하고이 가지고 : 링커가 JMP 문을 재 작성하거나 다른 주소에서 5를 대체처럼 보이는 나에게

$gcc -o asmtest asmtest.o 
$objdump -d asmtest 

...Extra info and other disassembled functions 
08048394 <main>: 
8048394:  e9 6b 7c fb f7  jmp 4 <_init-0x8048274> 
8048399:  c3     ret 

이 있습니다.

제 질문은 무엇이 잘못 되었습니까?

오프셋을 잘못 지정 했습니까? 상대 점프가 어떻게 작동하는지 오해하고 있습니까? gcc가 코드에서 위험한 일을하지 않도록 노력하고 있습니까?

답변

15

사실, 어셈블러는 절대 점프를하려고한다고 생각했습니다. 그러나 opcode는 금속 레벨에서 상대적입니다. 따라서 어셈블러는 0xe9 바이트 다음에 쓸 내용을 알 수 없습니다. 어셈블러는 코드가 끝나는 주소를 알지 못하기 때문입니다.

어셈블러에서 알 수는 없지만 링커는 않습니다. 따라서 어셈블러는 asmtest.o 헤더에 링커 요청을 썼습니다. "코드가로드 될 주소를 알면 점프에 적합하도록 0xe9 바로 뒤의 바이트를 조정합니다 (상대 주소 지정을 사용하여) 절대 주소 '4'로 변환합니다. 링커가 그렇게했습니다. 그것은 0xe9가 0x08048394 번지에 있고 다음 opcode가 0x08048399에 있고, 0x08048399에서 0x00000004로 이동하기 위해 0x08048395를 빼야한다는 것을 알았습니다. (32 비트 컴퓨터에서) 0xf7fb7c6b를 추가하는 것과 같습니다. 따라서 결과 바이너리에서 "6b 7c fb f7"시퀀스.

.global main 
main: 
    .byte 0xe9 
    .long 0x4 
    ret 

따라서, 어셈블러가 0xe9 정말 jmp 것을 통지하지 않습니다, 그리고 당신을 압도하려고하지 않습니다

는이 같은 "수동"상대 점프를 인코딩 할 수 있습니다. 바이너리에서는 원하는 'e9 04 00 00 00'시퀀스를 얻을 수 있으며 링커와의 상호 작용은 없습니다.상대 오프셋 즉시 오프셋 (여기서는 다음의 오피 코드, 즉 어드레스 ret) 후의 어드레스 로부터 계산되기 때문에, 상기 코드가, 충돌이 발생할 수 있음은

참고. 이것은 ret 이후 4 마일 밖에없는 사람의 땅에서 뛰어 오를 것이고, segfault 나 이상한 것이있을 것 같습니다.

+0

:

void foo(void) { __asm__ ( "jmp . + 5\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" ); } 

는 조립 가져옵니다 점프는 일반적으로 어셈블리에서 수행됩니다. 이것은 매우 기본입니다. 나는 OP가 이미 그 사실을 알고 있고 일부 불특정 이유 때문에 상대 점프를 직접 코딩하길 바란다고 가정합니다. –

+0

나는 그 의견이 나의 답변이라고 생각한다. 내가 대답을 내 대답으로 옮겨서 삭제했습니다. –

+0

도움을 주셔서 감사합니다, 이것은 내가 필요로하는 정확하게 똑같이 작동한다. 또한 너무 멀리 점프하는 문제를 지적 해 주셔서 감사합니다, 나는 지시가 계산되는 곳을 정확히 알지 못했습니다. –

5

저는 어셈블러가 절대 주소를 가져 와서 주소 오프셋을 계산한다고 생각합니다. 첫 번째 경우의 0은 픽스 업 테이블의 일부이고 링크 단계에서 오프셋이 계산되기 때문에 발생했을 수 있습니다.

내 어셈블리 언어 능력

은 약간 녹슨,하지만 난 그냥이 할 수있는 생각 :

.global main 

main: 
    jmp getouttahere 
getouttahere: 
    ret 

을 또는 당신이 정말로 원하는 경우는 상대적보고 :

.global main 

main: 
    jmp .+5 
    ret 

하는 경우 부드러운주세요 내가 틀렸다; 그것은 오랜 시간이었습니다. 당신은 기본적으로 & T의 구문을 사용하는 GCC의 가스 어셈블러를 사용하는 경우

+0

절대 주소를 계산하는 것이 좋습니다 . 불행히도 점프를하기 위해 라벨을 사용할 수없는 여러 가지 이유로 $ + 5는 유효한 구문이 아닙니다. 도와 주셔서 감사합니다. –

+0

@Ian Kelly : 어셈블러 구문이 다를 수 있습니다. 나는 ". + 5"가 효과가있을 것이라고 생각한다. –

14

, 상대에 대한 구문합니다 ($ 의사 기호 인텔/MASM 어셈블리 구문에 사용되는 많은 같은) uses the dot ('.') to represent the current address being assembled를 해결. 당신은 같은 것을 사용하여 상대 점프를 얻을 수 있어야합니다 : 예를 들어

jmp . + 5 

다음 함수 : 어떻게 명명 된 라벨로 점프

71 0000 55   pushl %ebp 
    72 0001 89E5   movl %esp, %ebp 
    74    LM2: 
    75    /APP 
    76 0003 EB03   jmp . + 5 
    77 0005 90   nop 
    78 0006 90   nop 
    79 0007 90   nop 
    80 0008 90   nop 
    81 0009 90   nop 
    82      
    84    LM3: 
    85    /NO_APP 
    86 000a 5D   popl %ebp 
    87 000b C3   ret