문제

저는 현재 학습 운동으로 Linux에서 ARM 어셈블리를 가지고 놀고 있습니다. 나는 '베어'어셈블리, 즉 libcrt 또는 libgcc를 사용하고 있습니다. 누구든지 첫 번째 명령어가 호출되기 전에 프로그램 시작시 스택 포인터 및 기타 레지스터가 어떤 상태에 있는지에 대한 정보를 알려줄 수 있습니까? 분명히 _start에서 PC/R15 포인트, 나머지는 두 가지 예외가있는 0으로 초기화 된 것으로 보입니다. SP/R13은 내 프로그램 이외의 주소를 가리키고 R1은 약간 높은 주소를 가리 킵니다.

그래서 몇 가지 확실한 질문에 :

  • R1의 값은 무엇입니까?
  • SP의 값은 합법적 인 스택의 값이 커널에 의해 할당됩니까?
  • 그렇지 않다면, 스택을 할당하는 선호하는 방법은 무엇입니까? BRK를 사용하거나 정적 .BSS 섹션을 할당합니까?

모든 포인터는 감사 할 것입니다.

도움이 되었습니까?

해결책

다음은 컴파일러로 Linux/ARM 프로그램을 시작하는 데 사용하는 것입니다.

/** The initial entry point.
 */
asm(
"       .text\n"
"       .globl  _start\n"
"       .align  2\n"
"_start:\n"
"       sub     lr, lr, lr\n"           // Clear the link register.
"       ldr     r0, [sp]\n"             // Get argc...
"       add     r1, sp, #4\n"           // ... and argv ...
"       add     r2, r1, r0, LSL #2\n"   // ... and compute environ.
"       bl      _estart\n"              // Let's go!
"       b       .\n"                    // Never gets here.
"       .size   _start, .-_start\n"
);

보시다시피, 나는 [sp]의 스택에서 Argc, Argv 및 Environ 물건을 얻습니다.

약간의 설명 : 스택 포인터는 프로세스 메모리의 유효한 영역을 가리 킵니다. R0, R1, R2 및 R3은 호출되는 함수에 대한 첫 번째 3 가지 매개 변수입니다. 나는 각각 Argc, Argv 및 Environ으로 채워집니다.

다른 팁

이것은 Linux이므로 커널에서 어떻게 구현되는지 볼 수 있습니다.

레지스터는 전화로 설정된 것 같습니다. start_thread 끝에 load_elf_binary (최신 Linux 시스템을 사용하는 경우 거의 항상 ELF 형식을 사용합니다). ARM의 경우 레지스터는 다음과 같이 설정된 것 같습니다.

r0 = first word in the stack
r1 = second word in the stack
r2 = third word in the stack
sp = address of the stack
pc = binary entry point
cpsr = endianess, thumb mode, and address limit set as needed

분명히 당신은 유효한 스택이 있습니다. 나는의 가치를 생각한다 r0-r2 쓰레기이며, 대신 스택에서 모든 것을 읽어야합니다 (나중에이 생각을 알 수 있습니다). 이제 스택에 무엇이 있는지 살펴 보겠습니다. 스택에서 읽을 내용은 create_elf_tables.

여기서 주목해야 할 흥미로운 점은이 기능이 아키텍처 독립적이므로 모든 ELF 기반 Linux 아키텍처에 동일한 것 (주로)이 스택에 올려 있다는 것입니다. 다음은 스택에 있습니다. 순서대로 읽습니다.

  • 매개 변수 수 (이것은입니다 argc 안에 main()).
  • 각 매개 변수에 대한 C 문자열에 대한 하나의 포인터, 0이됩니다 (이것은 내용입니다. argv 안에 main(); argv 이 포인터 중 첫 번째를 가리킬 것입니다).
  • 각 환경 변수에 대한 C 문자열에 대한 하나의 포인터, 그리고 0이됩니다 (이것은 드물게 보는 것의 내용입니다. envp 세 번째 매개 변수 main(); envp 이 포인터 중 첫 번째를 가리킬 것입니다).
  • "보조 벡터"는 쌍의 시퀀스 (유형 뒤에 값이 뒤 따르는 값) 인 쌍이 0으로 끝납니다 (AT_NULL) 첫 번째 요소에서. 이 보조 벡터에는 흥미롭고 유용한 정보가 있습니다. LD_SHOW_AUXV 환경 변수로 설정되었습니다 1 (예를 들어 LD_SHOW_AUXV=1 /bin/true). 이것은 또한 건축에 따라 상황이 약간 변할 수있는 곳이기도합니다.

이 구조는 모든 아키텍처에 대해 동일하므로 예를 들어 54 페이지의 그림을 볼 수 있습니다. SYSV 386 ABI 사물이 어떻게 맞는지에 대한 더 나은 아이디어를 얻으려면 (그러나 해당 문서의 보조 벡터 유형 상수는 Linux가 사용하는 것과 다르므로 Linux 헤더를보아야합니다).

이제 내용의 이유를 알 수 있습니다 r0-r2 쓰레기입니다. 스택의 첫 번째 단어는입니다 argc, 두 번째는 프로그램 이름에 대한 포인터입니다 (argv[0]), 그리고 세 번째는 아마도 당신이 논쟁이없는 프로그램을 불렀기 때문에 아마도 당신에게는 0 일 것입니다 ( argv[1]). 나는 그들이 나이가 많은 사람들을 위해 이런 식으로 설정된 것 같아요 a.out 당신이 볼 수 있듯이 이진 형식 create_aout_tables 퍼팅 argc, argv, 그리고 envp 스택에서 (그래서 그들은 끝날 것입니다 r0-r2 전화로 예상되는 순서대로 main()).

마지막으로 왜 r0 하나 대신 당신을 위해 Zero (argc 논쟁이없는 프로그램에 전화하면 하나가되어야합니까)? Syscall 기계의 깊은 곳에서 시스템 호출의 반환 값으로 쓴 것 (Exec가 성공한 이후 0 일 것입니다). 당신은 볼 수 있습니다 kernel_execve (커널 모드에서 실행하려는 경우 커널이 호출되는 것이기 때문에 Syscall 기계를 사용하지 않습니다) 의도적으로 덮어 쓰는 것입니다. r0 반환 값과 함께 do_execve.

여기에 있습니다 uclibc crt. 모든 레지스터가 정의되지 않은 것을 제안하는 것 같습니다. r0 (등록 할 기능 포인터가 포함되어 있습니다. atexit()) 그리고 sp 유효한 스택 주소가 포함되어 있습니다.

그래서, 당신이 보는 가치 r1 아마도 당신이 의존 할 수있는 것이 아닐 것입니다.

일부 데이터는 스택에 배치됩니다.

나는 Arm Linux를 사용한 적이 없지만 libcrt의 소스를보고 그들이하는 일을보고 GDB를 사용하여 기존 실행 파일로 들어가는 것이 좋습니다. 소스 코드가 필요하지 않아야합니다.

찾아야 할 모든 것은 바이너리 실행 파일에서 실행 된 최초의 코드 내에서 발생해야합니다.

도움이 되었기를 바랍니다.

토니

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top