2016-11-24 20 views
6

동적으로 링크 된 심볼을 포함하는 포인터 연산을 수행하면 잘못된 결과가 발생하는 이상한 상황이 발생했습니다. 에 일부 링커 매개 변수가 누락되어 있거나 링커 버그 인 경우 확실하지 않습니다. 누군가 가 다음 예제에서 잘못된 점을 설명 할 수 있습니까?동적으로 링크 된 심볼에서 C에서 포인터 산술을 수행 할 때 왜 잘못된 결과가 나옵니까?

#include <inttypes.h> 
#include <stdio.h> 

uintptr_t getmask() 
{ 
    return 0xffffffff; 
} 

int fn1() 
{ 
    return 42; 
} 

void fn2() 
{ 
    uintptr_t mask; 
    uintptr_t p; 

    mask = getmask(); 
    p = (uintptr_t)fn1 & mask; 
    printf("mask: %08x\n", mask); 
    printf("fn1: %p\n", fn1); 
    printf("p: %08x\n", p); 
} 

문제의 작업은 비트 AND fn1 및 변수 mask의 주소 사이 :

은 간단한 공유 라이브러리의 다음 코드 (lib.c)를 고려한다. 응용 프로그램 (app.c)는 바로 그런 fn2를 호출

extern int fn2(); 

int main() 
{ 
    fn2(); 

    return 0; 
} 

그것은 다음과 같은 출력에 이르게 ... 같은 결과가 fn1 예상되기 때문에, 분명히 잘못된 것입니다

mask: ffffffff 
fn1: 0x2aab43c0 
p: 000003c0 

... 및 p. 이 코드는 AVR32 아키텍처를 실행하고 다음 컴파일 :

컴파일러 생각
$ avr32-linux-uclibc-gcc -Os -Wextra -Wall -c -o lib.o lib.c 
$ avr32-linux-uclibc-gcc -Os -Wextra -Wall -shared -o libfoo.so lib.o 
$ avr32-linux-uclibc-gcc -Os -Wextra -Wall -o app app.c -L. -lfoo 

는 & 운전 동작 두 어셈블러 에 최적의 32 비트 레지스터 (7)에 가변 mask를 로딩 용액 분할이고 즉시 피연산자가있는 연산.

  • 가 의도적 인이 동작입니다 : 공유 라이브러리가 응용 프로그램의 주소 공간에로드 될 때
    $ avr32-linux-uclibc-objdump -d libfoo.so 
    
    000003ce <fn1>: 
    3ce: 32 ac   mov  r12,42 
    3d0: 5e fc   retal r12 
    
    000003d2 <fn2>: 
    ... 
    3f0: e4 17 00 00  andh r7,0x0 
    3f4: e0 17 03 ce  andl r7,0x3ce 
    

    은 내가 and 지침의 즉각적인 피연산자 fn1의로드 주소 을 이전하지 않는 것 같은데요?
  • 공유 라이브러리를 링크 할 때 또는 실행 파일을로드 할 때 문제가 발생하는지 어떻게 조사 할 수 있습니까?

배경 : 이것은 학문적 질문이 아닙니다. OpenSSL 및 LibreSSL 은 비슷한 코드를 사용하므로 C 소스를 변경하는 것은 옵션이 아닙니다. 이 코드는 을 다른 아키텍처에서도 잘 실행하지만 실제로는 함수 포인터에서 비트 연산을 수행하는 명백한 이유가 있습니다.

+2

'0xffffffff'을 반환 -> 'return ~ (uintptr_t) 0'? – Bathsheba

+0

'print ("fn1-x : % 08x \ n", (uintptr_t) fn1); –

+0

최적화하지 않고 컴파일하면 어떻게됩니까? –

답변

0

코드에서 모든 'slopiness'을 수정 한 후, 그 결과는 다음과 같습니다

#include <inttypes.h> 
#include <stdio.h> 

int fn1(void); 
void fn2(void); 
uintptr_t getmask(void); 

int main(void) 
{ 
    fn2(); 

    return 0; 
} 

uintptr_t getmask() 
{ 
    return 0xffffffff; 
} 

int fn1() 
{ 
    return 42; 
} 

void fn2() 
{ 
    uintptr_t mask; 
    uintptr_t p; 

    mask = getmask(); 
    p = (uintptr_t)fn1 & mask; 
    printf("mask: %08x\n", (unsigned int)mask); 
    printf("fn1: %p\n", fn1); 
    printf("p: %08x\n", (unsigned int)p); 
} 

과 (내 리눅스 64 비트 컴퓨터의) 출력 :

mask: ffffffff 
fn1: 0x4007c1 
p: 004007c1 
+1

'% p' 형식 지정자는'void *'인수를 취해야합니다. –

+0

@ M.M : 그렇지 않으면 UB가됩니다. – Destructor

+0

포인터는 매개 변수 목록에서'void *'로 사용될 수 있기 때문에 컴파일러는'printf ("fn1 : % p \ n", fn1);'명령문에 대해 불평하지 않고 올바른 결과를 산출합니다. – user3629249