본 포스팅은 https://www.deeplearning.ai/ 에서 공개한 무료 gpt 강의의 프롬프팅을 잘 하기 위한 방법을 실습해보고 번역 및 요약해본 포스팅입니다. 

질문을 잘하기 위헤서는 모델의 약점을 파악해서
모델의 실수를 줄여나갈 수 있도록 프롬프팅을 하는 편이 확실히 도움된다는 사실을 학습하게 되었습니다.

 

 대주제1. 프롬프팅의 원칙

 

 -원칙1. Write clear and specific instructions

여기서 말하는 clear라는 것은 명확하게라는 뜻 입니다.
이를 short instructions 와 혼동하지 말라는 의미입니다.

clear하고 specific 한 설명문을 작성하기 위한 전략이 필요합니다.

자세한 내용은 세부 전략을 보고 파악하겠습니다.

 

 -원칙1-전략1. delimiters를 사용하자

delimiters는 구분문자 라는 뜻으로
Triple quotes(삼중 따옴표): """
Triple backticks(삼중 백틱): ```
Triple dashes(삼중 대시): ---
Angle brakets(꺾쇠 괄호): <>
XML tags(XML 태그): <tag> </tag>

등을 말합니다.

예를 들어

요약해야하는 내용 : 
```
교육의 자주성·전문성·정치적 중립성 및 대학의 자율성은 법률이 정하는 바에 의하여 보장된다. 
모든 국민은 직업선택의 자유를 가진다. 모든 국민은 법률이 정하는 바에 의하여 공무담임권을 가진다.
국가는 전통문화의 계승·발전과 민족문화의 창달에 노력하여야 한다. 
모든 국민은 근로의 권리를 가진다. 
국가는 사회적·경제적 방법으로 근로자의 고용의 증진과 적정임금의 보장에 노력하여야 하며, 
법률이 정하는 바에 의하여 최저임금제를 시행하여야 한다.
```

이렇게 프롬프트를 생성한다면 ai가 대답할 프롬프트에 delimiters 안에 있는 내용을 "주입"하는 것이 가능해집니다.

이는 뒤에서 보게 될

ai에게 시간을 좀 더 주고 리소스를 쓰게 만들어 좀 더 좋은 판단을 내리도록 하는 전략의 핵심입니다.

 

 -원칙1-전략2. Ask for a structured output

프롬프팅에서는
구조화된 output을 지정하는 것이 가능합니다.
예를 들어 html, json 등으로 지정할 수 있다는 뜻입니다.

"""
Generate a list of three made-up book titles along \ 
with their authors and genres. 
Provide them in JSON format with the following keys: 
book_id, title, author, genre.
"""

이와 같은 식으로 프롬프트를 생성해서 질문을 할 수 있습니다. 그 결과는?

[
  {
    "book_id": 1,
    "title": "The Lost City of Zorath",
    "author": "Aria Blackwood",
    "genre": "Fantasy"
  },
  {
    "book_id": 2,
    "title": "The Last Survivors",
    "author": "Ethan Stone",
    "genre": "Science Fiction"
  },
  {
    "book_id": 3,
    "title": "The Secret Life of Bees",
    "author": "Lila Rose",
    "genre": "Romance"
  }
]

이런 식으로 제공이 됩니다.
챗 gpt는 이러한 구조를 "이미" 이해하고 있습니다.

이는 프로그래밍에서도 사용이 가능하며 우리가 웹에서 사용하는 챗 지피티에도 똑같이 적용됩니다.

 

 -원칙1-전략3. Ask the model to check whether conditions are satisfied

즉, 모델에게 조건들이 충족되었는지 아닌지 체크하는 질문을 해야한다는 뜻 입니다.

예시 텍스트 :

예시 텍스트 = f"""
차 한 잔을 만드는 것은 쉽습니다! 먼저 물을 끓이세요. \
그 동안 찻잔을 준비하고 차(tea) 가방을 넣으세요. \
물이 충분히 뜨겁다면 차 가방 위에 물을 부어주세요. \
차가 우려지도록 조금 기다리세요. \
몇 분 후에 차 가방을 빼내세요. \
원한다면 설탕이나 우유를 맛에 따라 넣어도 됩니다. \
그리고 완성! 맛있는 차 한 잔을 즐길 수 있습니다.
"""

프롬프트 :

프롬프트 = f"""
세 개의 따옴표로 묶인 텍스트가 제공됩니다. \
그것이 일련의 지시사항을 포함하고 있다면, \
다음 형식으로 지시사항을 다시 작성하세요:

1단계 - ...
2단계 - ...
...
N단계 - ...

텍스트에 지시사항이 포함되어 있지 않다면, \
그냥 "단계가 제공되지 않았습니다."라고 적으세요.
"""

이것은 open ai 에서 제공하는 강의의 예제 프롬프트를 해석한 것 입니다.
~~ 하라는 것을 지시사항이라는 것을 이해하고 있으며

그것이 일련의 지시사항을 포함하고 있다면, 이라는 조건을 붙이고

다음 형식으로 지시사항을 다시 작성하세요:
를 통해서 내가 원하는 포맷을 지정해줍니다.

이런 식으로 if else 처리를 해서 내가 원하는 답을 간결하게 볼 수 있게 되었습니다.

위에서 제공하는 예시 텍스트에 지시하는 느낌이 없는 평서문을 이어 붙인 문장을 주면

No steps provided. 라고만 결과가 나오는 것을 확인할 수 있습니다.

 

 -원칙1-전략4. "Few-shot" prompting

강의에서는 
Give successful examples of completing tasks
Then ask model to perform the task 라고 되어 있는데 이를 적절하게 번역하자면

"성공적으로 완료되었던 적은(few) 예시를 통해 질문하라!" 라고 요약할 수 있습니다.
소수의 샘플을 사용하여 모델을 학습시키는 방법입니다. 이러한 학습 방식은 자연어 처리에서도 이용되는 기법입니다.
간단한 예시를 들면 다음과 같습니다.

프롬프트 내용 :

프롬프트 내용
```
당신의 가장 중요한 임무는 일관된 스타일로 답변하는 것 입니다.

<아이> : 인내에 대해서 알려주세요
<할아버지> : 인내란 것은 말이다~
가장 깊은 계곡을 만드는 강은 소박한 봄에서부터 시작되고
가장 웅장한 교향곡은 하나의 음에서부터 시작되며
가장 복잡한 타페스트리는 단 하나의 외로운 실로부터 시작된단다.

<아이> : 그러면 회복력에 대해서 알려주세요
<할아버지> :
```

이런 식으로 물어보게 되면 할아버지의 입장으로 대답을 해주게 됩니다.

 

 -원칙2.  Give the model time to “think”

모델에게 생각할 시간을 주라는 뜻 입니다. 
예를 들자면 이런 뜻입니다.

바로바로 생각나는 것을 질문하는 것은 잘못된 결론으로 달려가는 지름길

이라고 직원이 언급합니다.
우리는 반드시 모델이 최종 답변을 제공하기 전에
관련된 연쇄적인 혹은 시리즈화 시킨 논리적 추론을 요청해야합니다.

모델에게 일을 시킬 때 너무 복잡한 주제의 문제지만
적은 단어로 적은 양의 시간에 문제를 해결하려고 했을 때 잘못된 정보를 얻은 경험이 많을 것 입니다.
반대로 생각해보면, 이는 아무리 뛰어난 인간이라도 할 수 없는 일 입니다.
예를 들어서 천재라고 불리우는 사람한테 선형대수학과 복잡한 통계에 대한 문제를 길가는 중에 
대뜸 이 문제 풀 수 있냐고 단 한문장으로 물어본다면
그들은 실수할 수 밖에 없습니다. 

그렇기 때문에 우리들은 모델이 좀 더 생각할 시간을 가질 수 있도록 지휘할 필요가 있습니다.
이 말은 ai에게 일을 시킬 때 전산적으로 좀 더 많은 일을 처리할 수 있도록,
당신의 요청을 수행하는데 더 많은 정보를 참조할 수 있도록 질문해야한다는 뜻 입니다.
그 세부 전략과 예제도 함께 보겠습니다.

 -원칙2-전략1. Specify the steps required to complete a task

업무를 완료하는데 필요한 특정한 절차를 요구하세요.

예를 들어서 어떠한 동작을 수행할지 1-, 2- 와 같은 식으로 절차를 붙여서 요청을 한다는 뜻 입니다.

예를 들어서 질문을

Question :
"""
다음 동작을 수행하세요:
1 - 세 개의 역따옴표로 구분된 다음 텍스트를 1문장으로 요약합니다.
2 - 요약본을 프랑스어로 번역합니다.
3 - 프랑스어 요약본에서 각 이름을 나열합니다.
4 - 다음 키를 포함하는 json 객체를 출력합니다: french_summary, num_names.
"""

이런 형식으로 하게 되면
한번에 하나의 대답을 하는 것이 아니라 최종 답변이 4가 되는 것이고
1, 2, 3의 과정을 거치면서 모델에게 정보가 누적되게 됩니다.
4의 출력에서는 1, 2, 3의 과정의 결과들을 참고할 수 있게 되었습니다.

여기에서 우리는 질문을 하게 될 때 결과물의 형식 뿐만 아니라 각각의 단계에 존재하는 객체들의
타입에 대해서도 정해줄 수 있습니다.
예를 들어서 위의 예제를 들어서 설명하게 되면
<다음 텍스트>, <요약본>, <프랑스어 번역본>, <이름> 등의 타입을 지정할 수 있다는 뜻 입니다.
여기에서 <이름>의 타입을 "첫 요약에서 얻은 리스트에 존재하는 이름들" 이런 식으로 지정해줄 수 있다는 뜻 입니다.
타입을 지정하는 방법은 다음과 같습니다.

# 위의 내용과 이어집니다

다음 정해진 형식을 사용하세요:
텍스트 : <요약의 대상이 될 텍스트>
요약본 : <요약한 내용>
번역본 : <요약한 내용을 번역한 내용>
이름들 : <요약한 내용에 포함되어 있는 이름들의 리스트>
결과물 (JSON) : <"요약본"과 num_이름들을 key로 가진 json>

이름이라는 것을 모델이 착각할 수 있기 때문에 이런 식으로 확실하게 지정해주면 모델의 정확도가 올라갑니다.

현재 이름은 원본 텍스트, 요약본, 번역본에 존재하고 있습니다.

이런 식으로 챗 지피티에서도 활용이 가능합니다.

num_name 에 5라고 잘 출력이 되는 모습을 볼 수 있습니다.

 -원칙2-전략2. 모델이 결론을 급하게 내기 전에, 스스로 해결책을 찾도록 지시할 것

다음 사진과 같은 예제를 보게 되면 조금만 계산을 꼬아버려도 모델이 실수를 하는 모습을 볼 수 있습니다.
이는 생각보다 너무 빨리 결론을 내려버리기 때문입니다.

 

위의 사진도 설명을 잘 해낸 것 같지만 올바른 결론을 도출하기 위해서는 좀 더 절차가 필요합니다.
여기에서 학생이 간과한 것은 "평방 피트당 추가로 $10의 유지비용이 발생한다" 라는 점 입니다.
저는 기존에 이런 상황을 만난다면 총 비용을 구성하는 요소들에 포함될 항목들에 대해서 더 자세히 취하는 전략을 사용했는데요, 강의에서 제안한 방식은 상당히 효과적이었습니다.

그렇다면 다음 예제를 살펴보겠습니다.

prompt = f"""
Your task is to determine if the student's solution \
is correct or not.
To solve the problem do the following:
- First, work out your own solution to the problem. 
- Then compare your solution to the student's solution \ 
and evaluate if the student's solution is correct or not. 
Don't decide if the student's solution is correct until 
you have done the problem yourself.

Use the following format:
Question:
```
question here
```
Student's solution:
```
student's solution here
```
Actual solution:
```
steps to work out the solution and your solution here
```
Is the student's solution the same as actual solution \
just calculated:
```
yes or no
```
Student grade:
```
correct or incorrect
```

Question:
```
I'm building a solar power installation and I need help \
working out the financials. 
- Land costs $100 / square foot
- I can buy solar panels for $250 / square foot
- I negotiated a contract for maintenance that will cost \
me a flat $100k per year, and an additional $10 / square \
foot
What is the total cost for the first year of operations \
as a function of the number of square feet.
``` 
Student's solution:
```
Let x be the size of the installation in square feet.
Costs:
1. Land cost: 100x
2. Solar panel cost: 250x
3. Maintenance cost: 100,000 + 100x
Total cost: 100x + 250x + 100,000 + 100x = 450x + 100,000
```
Actual solution:
"""
response = get_completion(prompt)
print(response)

``` ``` 백틱 3개로 감싼 영역이 코드를 입력하는 곳처럼 표시가 되었습니다.
우선 질문까지는 위와 동일합니다.
그런데 여기에서 조건을 달았습니다.
그것은 바로 문제를 직접 풀어보지 않고 학생의 솔루션이 정확한지 결정하지 말라는 것 입니다.
원래 이러한 모델들은 빠르게 결론을 내려는 경향이 있습니다.
그러한 결론을 내는 행위 자체를 지연시킵니다.
이러한 생각하는 방식은 여러 방면으로 응용될 수 있으며 강력합니다.

아까 말했던 "내용 주입"이라는건 다른 무수히 많은 정보들보다 ``` ``` 안의 내용을 우선적으로 점검한다는 뜻 입니다.

지금까지 제공된 내용을 바탕으로 프롬프트를 구성한다면

모델이 제공하는 답변의 정확도가 올라갈 것 입니다.

결정을 유보한 결과

결정을 유보하고 스스로 정리한 정보를 바탕으로 학생이 잘못 되었다는 옳은 판단을 내렸습니다.

 

 대주제2. Model Limitations


여기에서는 세부적인 전략은 없습니다.

이는 현재 모델의 약점, 제한 사항에 대한 내용입니다.
이러한 언어 모델을 사용하여 개발할 때 "모델의 환각(Hallucinations)" 현상이 일어날 수 있음을 생각해야만 합니다.
이 현상은 낯설고 잘 모르는 주제라고 하더라도 대답하려고 그럴듯한 답변을 만드는 과정에서 자주 나타납니다.

모델이 교육을 하면서 방대한 양의 지식에 노출되지만, 이들을 완벽하게 기억하지는 못합니다.
따라서 지식의 경계, 즉 진실과 거짓의 판단부터 정확도에 대한 것들을 의미합니다.

예를 들어서 "에이스 침대"의 "파로마 브릭스 LED 모션베드에 대한 설명"를 요구하면
꽤나 그럴싸하게 말을 지어냅니다.

지금과 같은 경우는 "에이스 침대"는 실제로 존재하는 회사 이름이고
"파로마 브릭스 LED 모션베드"의 경우는 실제로 존재하지만 "시몬스 침대"의 제품입니다.
하지만 모델은 이를 착각하여 설명합니다.

또한 그럴듯한 경우라면 이 세상에 존재하지 않는 상품이라도 그럴듯하게 이야기를 지어냅니다.

 

이것이 위험한 이유는
"정말로 그럴듯해보여서"

만약에 어떤 업무를 하다가 귀찮은 나머지 더블체크를 하지 않았다면
모델의 환각 현상이 일어났던 부분을 나중에 찾아내기란 많이 힘들 수 있습니다.

그래도 강의에서는 지금까지 배운 원칙들을 중심으로 프롬프팅을 진행하면
이러한 현상을 최대한 막을 수 있고,

이러한 텍스트 기반의 모델이 최신 정보 혹은 무언가를 참고해서 
답변을 생성하도록 할 때 가장 좋은 원칙은

1. 먼저 모델에게 관련 인용구를 찾도록 요청하거나
2. 공식문서, 설명서 등의 내용을 긁어와서 모델에게 그 내용은 어떠한 공식문서다, 어떠한 설명서다 설명해주는 방법이 있습니다.

이렇게 또 다른 최신 아티클 혹은 문서를 가져와서 학습 시켰을 경우에는 

- 원하는 인용구를 찾아서 ``` ``` 안에 넣도록 합니다.
- 그 인용구를 찾기 전에는 최종 결론을 내리지 않도록 지시합니다.
- 새로운 문서에서 요청한 인용구를 찾고 선결과제를 끝낸 이후에 그 정보를 바탕으로 답변하도록 설정합니다.

이러한 과정들을 통해 모델의 환각 현상을 상당수 줄일 수 있습니다.

다음 포스팅으로는 좀 더 테크닉적인 부분을 다루게 됩니다.

이번 포스팅까지는 코딩을 잘 모르는 일반인이라도 프롬프팅이 무엇이고 어떻게 하면 좋은지에 대한 좋은 이정표가 될 것이라 생각합니다. 좀 더 자세한 정보를 얻고 싶으신 분은 현재 Open Ai 에서 무려 직원분께서 무료로 강의를 제공하니 참조하시면 좋을 것이라 생각합니다.

분석 용으로 받은 프로젝트가 5년 전의 레거시인 탓에 ubuntu의 버전이 상당히 낮은 버전이었고,
14, 16 버전으로는 테스팅이 되었지만 18 버전 이후로는 테스트가 된적이 없었다.
현재 microsoft shop 을 통해 다운 받을 수 있는 우분투는 18 20 22 등이 쉬운 편인데, 
그 밑으로 다운 그레이드는 생각보다 많은 시간이 소요될 것 같아 18 환경에서 돌려보기로 결정했고
프로젝트 분석을 하던 와중에... 예기치 못한 문제에 마주치게 되었다. 

>>> /etc/sudoers: syntax error near line 30 <<<

sudo: parse error in /etc/sudoers near line 30
sudo: no valid sudoers sources found, quitting
sudo: unable to initialize policy plugin

이 문제가 정말 사람 골때리게 만든다...
이 문제를 아직 close 하지는 못했지만 wsl로 설치한 우분투이기에 윈도우에서 root에 접속해서 비밀번호를 바꾸고

su

이 명령을 사용해서 작업할 수 있게 만들어서 작업은 진행할 수 있었다.

처음엔 다중 리눅스 운영 체제 환경 속에서 default만 수정 되는 줄 알았는데 다음의 명령어를 치니 18.04 LTS에 접근할 수 있었다.

wsl -d Ubuntu-18.04
// 윈도우의 cmd에서 실행, 설치된 ubuntu의 18.04 환경으로 진입합니다.

여기까지 왔다면 다음에 올 명령들은 간단하다.

sudo -i
// 루트 권한으로 실행 중인 셀을 시작하기 위한 명령어

// 루트 권한으로 접속

passwd
// 비밀번호를 바꿉니다.

pkexec visudo 명령으로도

etc/sudoers 를 다시 수정할 수 없는 문제를 close 하지는 못했지만, 이 방식으로

스크립트에서 실행될 mkdir 이 문제 없어지니 컴파일 및 실행도 문제 없이 잘 되었다.

chmod 777로 sudoers의 권한을 바꿔버렸을 경우 pkexec visudo 명령으로도 해결되지 않을 때의 해결법은 

close하는대로 추가 포스팅할 예정입니다.

하고 싶은 프로젝트의 레퍼런스 프로젝트가 C언어로 되어 있어서 요즘 그것을 분석하기 위한 시간을 보내는 중이었다.

빨리 하고 싶어서 모르는 상태로 챗 gpt에 물어보고 나오는 명령어를 급하게 생각 없이 옮겨 쓰다보니... 문제가 더욱 커졌다. 이번 기회로 gpt에 대한 의존은 좋지 못하다는걸 몸으로 깨닫게 되었다. 오히려 ai를 현명하게 쓰지 못할 경우 시간 낭비가 더욱 발생하게 된다.

그럼 본론으로 들어가보려고 한다.

 왜 Makefile과 make가 필요한가?

이 이슈에 대한걸 그대로 구글링을 해보았다.

https://www.includehelp.com/c-programming-questions/what-is-makefile.aspx

 

What is Makefile for c program compilation and how to create Makefile? - IncludeHelp

What is Makefile for C program compilation and How to create Makefile? Makefile in Linux for Compilation If you have multiple source files in c, c++ and others language and want to compile them from Terminal Command, it is hard to write every time. To solv

www.includehelp.com

이런 사이트를 찾을 수 있었다. 그 내용을 살펴보자.

C 프로그램 컴파일용 Makefile이란 무엇이며 Makefile을 만드는 방법은 무엇입니까?

컴파일을 위한 Linux의 Makefile

Makefile은 컴파일 프로세스를 단순화하거나 구성하는 데 사용되는 도구입니다. Makefile은 목적 파일을 만들고 제거하기 위한 변수 이름과 대상 명령어 집합으로 구성됩니다. 하나의 Makefile에서 여러 개의 대상을 만들어 이진 파일을 컴파일하고 제거할 수 있습니다. Makefile을 사용하여 프로젝트(프로그램)를 여러 번 컴파일할 수 있습니다.

c, c++ 등 여러 언어로 된 소스 파일이 여러 개 있고 이를 터미널 명령에서 컴파일하고 싶다면 매번 작성하기가 어렵습니다. 이러한 종류의 문제를 해결하기 위해 Makefile을 사용하는 이유는 대규모 프로젝트를 컴파일하는 동안 많은 수의 소스 파일을 작성해야 하고 링커 플래그가 필요하기 때문에 반복해서 작성하기가 쉽지 않습니다.

이 짧은 문장을 보고 든 생각을 정리해보았다.

1. C 프로그램 컴파일 이라함은 구체적으로 어떤 것을 의미하는가? 어떤 확장자를 해석해서 어디로 전달하고 무엇을 얻을 수 있는가?

2. Makefile 은 만들 수 있는 무언가다.

3. Makefile은 텍스트 기반 인터페이스에서 컴파일하는 과정에 도움을 주는 무엇인가다.

4. C 혹은 C++ 환경에서 사용된다.

5. "링커 플래그"는 무엇인가?

6. 많은 수의 소스 파일을 작성하는 수고를 덜어줄 수 있다.


여기서 C언어를 컴파일 하려면 어떻게 해야하는지를 잠시 언급하자면

GCC 혹은 G++ 등을 통해 C, C++ 파일을 컴파일하게 된다.

여기서 컴파일 하는 과정은 다음과 같다.

gcc 소스 파일 이름

 

 

이어서 밑의 설명을 보자.

Makefile 은 컴파일을 위해 코드를 단순화하거나 구성하는 도구입니다. Makefile 은 개체 파일을 만들고 제거하기 위한 변수 이름과 대상이 있는 명령 집합(터미널 명령과 유사)입니다. 단일 make 파일에서 우리는 바이너리 파일을 컴파일하고 제거하기 위해 여러 대상을 만들 수 있습니다. Makefile 을 사용하여 프로젝트(프로그램)를 여러 번 컴파일할 수 있습니다 .

예를 들어 이해해 보겠습니다. main.c (메인 소스 파일), misc.c (함수 정의를 포함하는 소스 파일), misc.h (함수 선언을 포함)
3개의 파일이 있다고 가정합니다 . 여기서 myFunc() 라는 함수를 선언하고 정의하여 무언가를 인쇄합니다. 이 함수는 각각 misc.c  misc.h 에서 정의되고 선언됩니다 .

myFunc() 는
misc.h(헤더 파일)에서 선언되었고
misc.c(소스 파일)에서 정의되었습니다.
main.c 에서는 사용됩니다.

이러한 파일들을 컴파일하려면 명령어를 계속해서 입력해주어야 합니다. 아때 Makefile을 사용하면 자동으로 명령어를 실행하여 컴파일을 수행합니다. Makefile을 사용하면 프로그램을 여러번 컴파일해야할 때 일일이 명령어를 입력하는 수고를 덜 수 있습니다.

Makefile을 하게 되면 object 파일들을 링크해서 실행 파일을 만들게 됩니다.

더 자세한 내용은 후속편에서 다루도록 하겠습니다.

깃허브에서 받은 프로젝트를 돌려보기 위해서 readme.md를 읽고
프로젝트를 분석해서 라이브러리들을 빌드하고, make를 해봤으나....!!
엄청 긴 에러 로그를 만나게 되었다.
처음엔 원인이 무엇인지도 모르고 뻘짓을 많이 하긴했었지만, 결국 에러 로그는 무언가 잘못한게 있기 때문에 나오는 것이니까 하나하나 파헤쳐보던 중에 알게 된 사실이 있다.

"C언어 makefile에서 라이브러리를 링크하는 과정에서는 그 순서도 정말 중요하다."

그래서 같은 라이브러리를 import 한다고 하더라도 그 순서에 따라서 빌드가 성공할수도, 실패할수도 있는 것이다.

이게 내가 기획하고 처음부터 빌드한 프로젝트면 몰라도 다른 사람이 만든 프로젝트를 다운 받아서 빌드해볼 때는 꽤나 자주 겪을 수 있는 문제라 기록을 남기려고 한다.

 ldd - 실행 파일 또는 공유 라이브러리 파일의 의존성 체크

 

"ldd program_name" 명령어를 실행하면 program_name이 의존하는 라이브러리들을 확인할 수 있습니다. 
일반적인 경우 ldd는 LD_TRACE_LOADED_OBJECTS 환경 변수를 사용하여 표준 동적 링커를 호출합니다.
이렇게 하면 동적 링커가 프로그램의 동적 종속성 및 찾기를 실행하여 이를 충족하는 객체를 로드합니다.

ldd가 a.out 공유 라이브러리에서 작동하지 않는다는 이슈를 확인했습니다.
이는 매우 오래된 일부 a.out 프로그램에서 작동하지 않기 때문에 언급되었던 이슈입니다. ldd 자원이 컴파일러 릴리스에 추가되기 전에 빌드 되었습니다. 

일부 ldd 버전은 종속성 정보를 얻으려고 시도할 수 있습니다.
그렇기 때문에 신뢰할 수 없는 실행 파일에 ldd 를 사용해서는 안됩니다. 보안 이슈가 있습니다.

ldd에도 옵션이 존재합니다.

--version ldd 
              의 버전 번호를 인쇄합니다 .

       -v , --verbose
              예를 들어 기호를 포함한 모든 정보를 인쇄합니다.
              버전 정보.

       -u , --미사용
              사용하지 않는 직접 종속성을 인쇄합니다. (glibc 2.3.4부터.)

       -d , --데이터-재배치
              재배치를 수행하고 누락된 개체를 보고합니다(ELF
              오직).

       -r , --function-relocs
              데이터 개체와 기능 모두에 대한 재배치를 수행합니다.
              누락된 개체 또는 기능을 보고합니다(ELF만 해당).

       --help 사용 정보.

 

https://man7.org/linux/man-pages/man1/ldd.1.html

참조

 

ldd(1) - Linux manual page

ldd(1) — Linux manual page LDD(1) Linux Programmer's Manual LDD(1) NAME         top ldd - print shared object dependencies SYNOPSIS         top ldd [option]... file... DESCRIPTION         top ldd prints the shared objects (shared libraries) r

man7.org

 

 readelf - ELF 파일이 의존하는 라이브러리의 정보 확인

ELF(Executable and Linkable Format)는 실행 파일, 목적 파일, 공유 라이브러리 그리고 코어 덤프를 위한 표준 파일 형식이다. 1999년 86open 프로젝트에 의해 x86 기반 유닉스, 유닉스 계열 시스템들의 표준 바이너리 파일 형식으로 선택되었다. - 위키 백과

ELF 파일은 코드와 데이터를 여러 섹션으로 구분하고, 각 섹션은 읽기 전용, 쓰기 가능 또는 실행 가능 등의 특정 권한을 가질 수 있습니다. 또한 ELF 파일은 컴파일된 코드 및 데이터 외에도 기호(symbol)와 같은 디버그 정보를 포함할 수 있으며, 동적 링크를 지원하기 위해 필요한 링크 섹션을 가지고 있습니다.

ELF에 대해서는 공부하고 정리할게 많아서 추후에 한번 더 정리할 계획이다.

 

 objdump -  GNU 바이너리 유틸리티의 일부

objdump 는 GNU 바이너리 유틸리티의 일부로서, 라이브러리, 컴파일된 오브젝트 모듈, 공유 오브젝트 파일, 독립 실행 파일 등의 바이너리 파일들의 정보를 보여주는 프로그램이다. objdump는 개체 파일 및 실행 파일을 검사하는데 일반적으로 사용되는 command line tool 입니다. objdjump 는 ELF 파일을 어셈블리어로 보여주는 역어셈블러로 사용될 수 있습니다. 역어셈블러는 기계어를 어셈블리어로 변환하는 컴퓨터 프로그램으로서, 리버스 엔지니어링 도구 중 하나로 볼 수 있는데, 이번 포스팅에서는 생략하도록 하겠습니다.

objdump 를 사용해서 얻을 수 있는 이점 중의 일부를 3가지 정도만 언급하겠습니다.

1. 디버깅 : objdump를 사용하여 실행 파일의 내용을 검사하고 기호 누락, 잘못된 재배치 정보 또는 프로그램이 충돌하거나 잘못 작동할 수 있는 기타 문제와 같은 문제를 찾을 수 있습니다.
2. 리버스 엔지니어링  : objdump를 사용하여 바이너리를 분석하고 작동 방식을 학습할 수 있습니다. 이 도구는 프로그램의 제어 흐름을 이해하고 기능과 변수를 식별하며 보안 취약성을 감지하는 데 도움이 될 수 있습니다.
3. 최적화 : objdump는 프로그램의 성능을 분석하고 코드의; 병목 현상을 식별하는 데 사용할 수 있습니다. 디스 어셈블리를 검사하여 자주 실행되는 코드 섹션을 식별하고 속도를 위해 최적화할 수 있습니다.

그러나 objdump 를 사용하면 몇가지 일어날 수 있는 문제도 있습니다.

1. 복잡성 : objdump 는 바이너리 파일에 대한 많은 정보를 제공하지만 이 정보를 해석하는 것은 어려울 수 있습니다. 기본 아키텍쳐 및 어셈블리 언어에 대한 충분한 이해가 필요합니다.
2. 보안 위험 : objdump 는 바이너리를 리버스 엔지니어링 하는 데 사용할 수 있으므로 공격자가 취약성을 식별하거나 바이너리에서 중요한 정보를 추출하는 데 사용할 수도 있습니다.
3. 호환성 : 다른 버전의 objdump는 이진 파일에 대해 다른 정보를 제공할 수 있습니다. 분석 중인 바이너리와 일치하는 올바른 버전의 objdump를 사용하는 것이 중요합니다.

간단한 예제와 함께 실습해보겠습니다.

file1.c

#include <stdio.h>
#include "header.h"

int main() {
   printf("Hello, World!\n");
   printf("The value of a is %d\n", a);
   return 0;
}


header.h

int a;


Bash

$ gcc -c file1.c
$ gcc -c header.h
$ objdump -p file1.o

 

이렇게 빌드하면 다음과 같은 출력이 나타납니다.

file1.o:     file format elf64-x86-64

.......

 

 pkg-config - 컴파일러나 링커에 필요한 라이브러리 정보를 확인

pkg-config는 C/C++ 소프트웨어 개발에서 사용되는 라이브러리 의존성 관리 도구입니다. 이 도구는 라이브러리의 이름, 버전 및 기타 정보를 검색하여 컴파일러 및 링커가 라이브러리를 찾을 수 있도록 도와줍니다. pkg-config는 소프트웨어 패키지 설치를 간편하게 하기 위해 GNU Build System과 함께 자주 사용됩니다.

pkg-config는 각 라이브러리의 .pc 파일을 읽어서 필요한 헤더 파일, 컴파일러 옵션, 링커 옵션 등을 찾아줍니다. 개발자는 pkg-config를 사용하여 필요한 라이브러리를 검색하고, 해당 라이브러리의 정보를 얻을 수 있습니다. 이를 통해 컴파일 및 링크 단계에서 필요한 라이브러리를 찾을 수 있습니다.

pkg-config를 사용하면 의존성이 있는 라이브러리를 찾기가 쉬워집니다. 또한, 여러 라이브러리를 사용할 때 각 라이브러리의 정보를 일일이 찾아볼 필요가 없습니다. 하지만, pkg-config를 사용하면 각 라이브러리의 .pc 파일이 존재해야 하기 때문에, 라이브러리 제작자가 .pc 파일을 작성하지 않았다면 pkg-config를 사용할 수 없습니다. 또한, pkg-config를 사용하면 Makefile 등의 빌드 스크립트를 복잡하게 만들 수 있습니다.

pkg-config는 3가지 정도의 사용법이 있습니다.

1. 라이브러리 설치 여부 확인
    pkg-config --exists <library-name>
    이 명령은 지정된 라이브러리가 설치된 경우 종료상태 0을 반환하고 그렇지 않은 경우 0이 아닌 값을 반환합니다.

2. 라이브러리에 대한 컴파일러 및 링커 플래그 검색
    pkg-config --cflags --libs <library-name>
    이 명령은 지정된 라이브러리에 대해 컴파일 및 링크하는 데 필요한 컴파일러 및 링커 플래그를 출력합니다.

3. Makefile 에서 pkg-config 사용 

    LIBS = `pkg-config --libs <library-name>`
    CFLAGS = `pkg-config --cflags <library-name>`
    
    myprogram: myprogram.c
        gcc $(CFLAGS) -o myprogram myprogram.c $(LIBS)

이런 식으로 Makefile 의 LIBS, CFLAGS 등의 변수에 pkg-config를 사용해서 make를 사용해서
링커 플래그를 검색하는 과정에서 .c 파일들을 컴파일 및 링크할 때 사용할 수 있습니다.


대표적으로 C언어에서 각 파일 간의 의존성을 체크하는 방법 들 중 4가지를 체크해보았습니다.

의존성을 체크할 때 상황에 맞는 명령, 도구를 사용해서 의존성을 체크하고 프로젝트를 빌드할 수 있도록 합시다.

'코딩이야기 > C' 카테고리의 다른 글

[c] Makefile과 make에 대해서 -1-  (0) 2023.03.17



회사에서 하게 되는 일로 Linux를 학습해야할 일이 생겼다.

언젠가 공부해야지 해야지 하고 있었는데 아는게 없다보니 실생활에 적용하기도 힘들어서 미루고 있던 찰나에 드디어 리눅스에 입문하게 되었다. 

일단 지금은 간단한 수준으로 WSL을 사용해서 windows에 Linux를 설치하고, prp, prp3, nodejs 등을 설치한 후에 git clone을 해보고 간단한 명령어들을 복습해볼 생각이다.

 WSL 을 사용하여 Windows에 Linux 설치

구글에 "WSL" 만 쳐도 사용이 가능하고,

https://learn.microsoft.com/ko-kr/windows/wsl/install

 

WSL 설치

wsl --install 명령을 사용하여 Linux용 Windows 하위 시스템을 설치합니다. Ubuntu, Debian, SUSE, Kali, Fedora, Pengwin, Alpine 등 원하는 Linux 배포판에서 실행되는 Windows 머신에서 Bash 터미널을 사용할 수 있습니

learn.microsoft.com

이 링크에서 설치 및 설명서를 볼 수 있다.

wsl --install

이 명령어를 통해 wsl 을 실행하는데 필요한 항목들을 설치할 수 있습니다.

cmd를 관리자 권한으로 실행해주셔야 합니다.

cmd에서 wsl 설치 하기 위한 wsl --install

설치가 다 끝나게 되면

"시스템 재시작"을 해줘야하니 주의하세요! 그렇지 않는다면 Bash에 아무리 명령어를 입력해도 듣지 않습니다!

 Ubuntu 초기 설정

오픈소스 리눅스 배포판의 디폴트 값은 Ubuntu로 되어 있어서 Ubuntu를 추가 설치하는 일은 불필요합니다.

재부팅을 하게 되면 자동적으로 bash가 열리게 되고, Ubuntu를 구동하면서 리소스를 다운받습니다.

그 후에 간단한 초기 세팅들을 요구합니다.

Enter new UNIX userrname에는 내가 원하는 이름으로 적어주시면 되고, 비밀번호도 설정할 수 있습니다.

여기까지 잘 완료된다면 이제부터 sudo 문을 사용해서 오픈소스 리눅스 배포판을 사용할 준비가 된 것입니다

 

$ sudo apt-get update

명령으로 패키지 목록들을 업데이트 하고,

 

$ sudo apt-get upgrade python3 

파이썬을 최신버전으로 업데이트 해줍니다.

 

$ sudo apt install python3-pip

을 통해 pip 또한 설치해줍니다.

 

이젠 우분투에서 깃을 설치한 후에 레파지토리로부터 clone 해옵니다.

1. $ sudo apt-get install git

2. $ sudo apt install git

3. $ git --version

4. $ git config --global user.name [이름]

5. $ git config --global user.mail [메일 주소]

6. $ git clone [url 주소]

이렇게 하면 깃 설치부터 자신의 계정 연결 후 clone 까지 완료할 수 있습니다.

이렇게 한 후 

$ ls

를 치게 되면

이런 식으로 밑에 클론해온 폴더가 잘 생성되어 있는 모습을 볼 수 있습니다.

이제부터는 cd 등의 명령어를 통해 이동할 수 있습니다.

 

이와 같은 내용은 chat gpt에 물어봐도 거의 똑같이 알려줍니다.

node.js 설치 및 npm 환경 구축에 대한건

https://velog.io/@ywoosang/Node.js-%EC%84%A4%EC%B9%98

 

우분투 Node.js 설치및 npm 환경구축

우분투 20.04에 Node.js 를 설치하는 세 가지 방법, npm 환경 구축과 REPL 을 이용한 실행

velog.io

이 블로그를 참조했습니다.

회사 코드나 로직은 제 마음대로 공개할 수가 없어서 포스팅할 거리가 애매한데,

백그라운드에서 스케쥴러 돌리면서 해두고 싶은 개인적인 업무(카카오톡 봇 관련)에 좀 더 도움이 될거 같아 공부할게 많아졌지만 즐거운 하루였습니다.

지난번 포스팅에 이어서 짧은 포스팅을 추가로 올리려고 한다.

지금 하는 작업은 하드 코딩에 가깝지만, 응용한다면 데이터를 가공하는데 도움이 될 것이라 생각한다.

// 데이터 가공해서 또 다른 Json 만들기 연습
// 학생, 학년, 학급, 학생 객체를 생성합니다.

let students = new Object();
let gradesX = new Object();
let classesX = new Object();
let studentX = new Array();

// 학생의 수만큼 k 변수로 반복문 돌립니다.
for(let k = 0; k<3; k++){
    let name = "";
    if( k%3 == 0 ){ name = "철수";
    }else if(k%3 == 1){ name = "영희";
    }else name = "동규";

    let studentIndex = {
        student_name : `${name}${k}`,
        student_height : Math.floor( (Math.random() * 120) ) + k,
        student_average : Math.floor( (Math.random() * 80) ) + k,
        student_report : 0 + k
    }
    
    studentX.push(studentIndex);
    
}  // k 변수 for문 종료

// classesX 객체에 key = student 에 value로 studentX 배열을 넣는다.
classesX.student = studentX;


// studentX 는 배열로 만들어둬서
/* classesX.student.description = "공부를 다들 열심히 합니다." */
// 와 같이 key, value를 추가할 수 없습니다.


// gradesX 객체에 key = class1 에 value로 바로 위의 classesX 객체를 넣는다. (객체 안에 객체 혹은 배열을 넣을 수 있다.)
gradesX.class1 = classesX;


// gradesX 객체에 key = class1 에 key가 teacher, 급훈인 value를 하나씩 넣는다. 우항은 string 값을 이용할 수 있어서 변수 혹은 for문 안에서도 가능하다.
gradesX.class1.teacher = "이민정쌤";
gradesX.class1.급훈 = "최선을 다하자";


// 위의 gradeX 객체를 students 객체의 key = grade1 의 value에 할당해준다.
students.grade1 = gradesX;

// 이외에 추가할만한 key, value를 할당해준다.
students.grade1.학주선생님 = "무서운 학주쌤";

// 아까 설정해둔 곳으로 접근해서 key, value 추가도 가능하다.
students.grade1.class1.특이사항 = "쉬는 시간엔 시끄럽지만 수업 때는 열심히 합니다.";


// 위와 비슷하게 학년 하나를 더 가공해봅니다.
// 원래라면 학생을 request로 얻어오던지 함수를 이용해서 다르게 하는게 맞지만 test를 위한 과정이라 같은 더미데이터를 사용하겠습니다.
let class2 = new Object();
class2 = classesX;
class2.teacher = "배용준쌤";
class2.급훈 = "잘생긴게 최고다";
students.grade1.class2 = class2;
students.description = "오지고 학생들";

console.log( JSON.stringify( students ) );

 

결과는 다음과 같다

{
  "grade1": {
    "class1": {
      "student": [
        {
          "student_name": "철수0",
          "student_height": 19,
          "student_average": 63,
          "student_report": 0
        },
        {
          "student_name": "영희1",
          "student_height": 57,
          "student_average": 25,
          "student_report": 1
        },
        {
          "student_name": "동규2",
          "student_height": 13,
          "student_average": 77,
          "student_report": 2
        }
      ],
      "teacher": "배용준쌤",
      "급훈": "잘생긴게 최고다",
      "특이사항": "쉬는 시간엔 시끄럽지만 수업 때는 열심히 합니다."
    },
    "학주선생님": "무서운 학주쌤",
    "class2": {
      "student": [
        {
          "student_name": "철수0",
          "student_height": 19,
          "student_average": 63,
          "student_report": 0
        },
        {
          "student_name": "영희1",
          "student_height": 57,
          "student_average": 25,
          "student_report": 1
        },
        {
          "student_name": "동규2",
          "student_height": 13,
          "student_average": 77,
          "student_report": 2
        }
      ],
      "teacher": "배용준쌤",
      "급훈": "잘생긴게 최고다",
      "특이사항": "쉬는 시간엔 시끄럽지만 수업 때는 열심히 합니다."
    }
  },
  "description": "오지고 학생들"
}

 

+ Recent posts