2016-08-26 1 views
3

을 녹 :잘못된 매핑은 내가 녹에 <code>FILE</code> 구조체에 액세스하려고 교육 목적

bindgen /usr/include/stdio.h 

은 어떻게 든 _flags 항상 (읽기 모드 4) 쓰기 모드로 열려있는 파일에 대한 8, 그래서이 플래그가 꺼져 것 같다 (나는 그것이 참인지 확인하기 위해 C 코드를 테스트하지 4 또는 8). 그러나 파일 포인터는 옳은 것 같습니다. 무엇이 이것을 일으킬 수 있습니까? 잘못된 헤더 파일에서 바인딩을 추출합니까? #[repr(C,)] 속성에 추가해야 할 것이 있습니까?

Here은 구조체를 포함한 전체 코드입니다.

이것은 an earlier question

+0

게시 한 코드가 컴파일되지 않습니다 (fp와 passwd의 혼란이라고 생각합니다). 또한 C 함수에 전달한 문자열은 NUL로 끝나지 않으므로 코드에는 완전히 정의되지 않은 동작이 있습니다. –

+0

오 그래, NUL 종료를 수행하는 하나의 파일을 추가하는 것을 잊어 버렸습니다. 위의 요지를 업데이트했습니다. – hansaplast

+0

매번 문자열 누출이 걱정되지 않습니까? 어쨌든, 두 번째 주장은 똑같은 문제가 있다고 생각합니다. –

답변

2

첫 번째에서 후속 질문, ToPtr의 구현은 불건전 한 코드를 초대합니다. 여기에 재현 :

// code in italics is wrong 
impl ToPtr for str { 
    fn to_ptr(&self) -> *const i8 { 
     CString::new(self).unwrap().as_ptr() 
    } 
} 

이 새로운 CString를 할당하고 그 내용에 대한 포인터를 반환하지만 CStringto_ptr 수익률을 삭제, 그래서 이것은 허상 포인터입니다. 이 포인터의 역 참조는 정의되지 않은 동작입니다.documentation에는 이에 대한 큰 경고가 있지만 여전히 흔한 실수입니다.

*const c_char을 문자열 리터럴로 만드는 올바른 방법은 b"string here\0".as_ptr() as *const c_char입니다. 문자열은 null로 끝나며 문자열 리터럴이 전체 프로그램에 있기 때문에 매달린 포인터가 없습니다. 당신이 변환 할 아닌 상수 문자열이있는 경우는 사용하는 동안, 당신은 이처럼 CString 생존을 유지해야합니다 제외하고


let s = "foo"; 
let cs = CString::new(s).unwrap(); // don't call .as_ptr(), so the CString stays alive 
unsafe { some_c_function(cs.as_ptr()); } 
// CString is dropped here, after we're done with it 
은 : 편집자는 "제안"(난 ,

let s = "foo"; 
unsafe { 
    // due to temporary drop rules, the CString will be dropped at the end of the statement (the `;`) 
    some_c_function(CString::new(s).unwrap().as_ptr()); 
} 

를이 (올바른 최고의 종류) 기술적으로 정확하지만 : 새로운 스택 오버플로,하지만 그것을 언급하는 대신 위의 코드는 다음과 같이 기록 될 수 있다는) 내 대답을 다시 시도하는 것이 더 정중 보인다 관련된 "임시 삭제 규칙"은 미묘합니다. - as_ptrCString 참조 걸리기 때문에 (실제로 &에서는 CStr를 컴파일러하여 CString에있어서 사슬 : 새로운 (S) .unwrap()을 변경하기 때문에, DEREF()를 as_ptr()..)이 작동 그것을 소비하는 대신 C 함수를 하나만 호출 할 수 있기 때문입니다. 안전하지 않은 코드를 작성할 때 미묘하거나 분명하지 않은 것에 의존하고 싶지 않습니다. 방법 중 그와


, 당신의 코드에서 fixed that unsoundness (통화 모두 사용 문자열 리터럴 난 그냥 위의 내 첫 번째 전략을 사용하므로).OSX에서이 출력을 얻었습니다.

0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0 
0, 0, 8, 1, 0, 0, 0, 0, 0, 0, 0 
0, 0, 8, 2, 0, 0, 0, 0, 0, 0, 0 
0, 0, 4, 3, 0, 0, 0, 0, 0, 0, 0 

이렇게 결과가 일치합니까? 또한 다음과 같은 C 프로그램 작성 :

#include <stdio.h> 
#include <unistd.h> 

int main() { 
    struct __sFILE *fp1 = fdopen(STDIN_FILENO, "r"); 
    struct __sFILE *fp2 = fdopen(STDOUT_FILENO, "w"); 
    struct __sFILE *fp3 = fdopen(STDERR_FILENO, "w"); 
    struct __sFILE *passwd = fopen("/etc/passwd", "r"); 

    printf("%i %i %i %i\n", fp1->_flags, fp2->_flags, fp3->_flags, passwd->_flags); 
} 

을 그리고 출력있어 :

4 8 8 4 

녹 결과를 옹호 것으로 보인다.

/* 
* The following always hold: 
* 
* if (_flags&(__SLBF|__SWR)) == (__SLBF|__SWR), 
*  _lbfsize is -_bf._size, else _lbfsize is 0 
* if _flags&__SRD, _w is 0 
* if _flags&__SWR, _r is 0 
*/ 

그리고 찾는 사람들 상수 :

#define __SLBF 0x0001  /* line buffered */ 
#define __SRD 0x0004  /* OK to read */ 
#define __SWR 0x0008  /* OK to write */ 

이것은 우리가 얻을 출력과 일치하는 것 같다 읽기 모드에서 열 파일을 4, 8 말한다 /usr/include/stdio.h의 상단에 의견이 있습니다 쓰기. 그러면 여기서 어떤 문제가 발생합니까?

+0

매달려있는 포인터에 대한 설명 주셔서 감사합니다, 그 것들이 매우 중요합니다. 그것은 문서에 있고 나는 읽지 않았다. 그렇다면 : 내가 갖고있는 C 코드는 고급 유닉스 프로그래밍 책에서 나왔다. FILE 구조체 변수의 대부분은 스트림 읽기/쓰기시에만 채워진다는 것을 알게 된 후에 책에서이 문장을 발견했습니다. "버퍼링 상태를 인쇄하기 전에 각 스트림에서 I/O를 수행합니다. 첫 번째 I/O 작업은 대개 버퍼에 스트림을 할당합니다. "(다시, RTFM to ..) 요점 : https://gist.github.com/philippkeller/c683dda9d89ca0f8cb6830b6dc52910b – hansaplast